/* Gnome panel: aligned (corner) widget
 * (C) 1999 the Free Software Foundation
 * 
 * Authores:  Jacob Berkman
 *            George Lebl
 */

#include "config.h"
#include "panel-marshal.h"
#include "aligned-widget.h"
#include "panel-config-global.h"
#include "foobar-widget.h"
#include "multiscreen-stuff.h"
#include "panel-typebuiltins.h"

static void aligned_pos_class_init (AlignedPosClass *klass);
static void aligned_pos_instance_init (AlignedPos *pos);

static void aligned_pos_set_pos (BasePWidget *basep,
				 int x, int y,
				 int w, int h,
				 gboolean force);
static void aligned_pos_get_pos (BasePWidget *basep,
				 int *x, int *y,
				 int w, int h);

static void aligned_pos_show_hide_left (BasePWidget *basep);
static void aligned_pos_show_hide_right (BasePWidget *basep);
static BorderPosClass *aligned_pos_parent_class;

GType
aligned_pos_get_type (void)
{
	static GType object_type = 0;

	if (object_type == 0) {
		static const GTypeInfo object_info = {
                    sizeof (AlignedPosClass),
                    (GBaseInitFunc)         NULL,
                    (GBaseFinalizeFunc)     NULL,
                    (GClassInitFunc)        aligned_pos_class_init,
                    NULL,                   /* class_finalize */
                    NULL,                   /* class_data */
                    sizeof (AlignedPos),
                    0,                      /* n_preallocs */
                    (GInstanceInitFunc)     aligned_pos_instance_init
		};

		object_type = g_type_register_static (BORDER_TYPE_POS, "AlignedPos", &object_info, 0);
		aligned_pos_parent_class = g_type_class_ref (BORDER_TYPE_POS);
	}
			       
	return object_type;
}

enum {
	ALIGN_CHANGE_SIGNAL,
	LAST_SIGNAL
};

static guint aligned_pos_signals[LAST_SIGNAL] = { 0 };

static void
aligned_pos_class_init (AlignedPosClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	BasePPosClass *pos_class = BASEP_POS_CLASS (klass);

	
	aligned_pos_signals[ALIGN_CHANGE_SIGNAL] =
		g_signal_new ("align_change",
			      G_TYPE_FROM_CLASS (object_class),
			      G_SIGNAL_RUN_LAST,
			      G_STRUCT_OFFSET (AlignedPosClass, align_change),
			      NULL,
			      NULL,
			      panel_marshal_VOID__ENUM,
			      G_TYPE_NONE,
			      1, 
			      PANEL_TYPE_ALIGNED_ALIGNMENT);

	pos_class->set_pos = aligned_pos_set_pos;
	pos_class->get_pos = aligned_pos_get_pos;
	pos_class->north_clicked = pos_class->west_clicked = 
		aligned_pos_show_hide_left;
	pos_class->south_clicked = pos_class->east_clicked =
		aligned_pos_show_hide_right;
}

static void
aligned_pos_instance_init (AlignedPos *pos) {
	/* Does nothing */
}

static void
aligned_pos_set_pos (BasePWidget *basep,
		     int x, int y,
		     int w, int h,
		     gboolean force)
{
	int innerx, innery;
	int screen_width, screen_height;

	BorderEdge newpos = BORDER_POS(basep->pos)->edge;
	AlignedAlignment newalign = ALIGNED_POS(basep->pos)->align;

	if ( ! force) {
		int minx, miny, maxx, maxy;
		gdk_window_get_geometry (GTK_WIDGET(basep)->window,
					 &minx, &miny, &maxx, &maxy, NULL);
		gdk_window_get_origin (GTK_WIDGET(basep)->window, &minx, &miny);
		maxx += minx;
		maxy += miny;
		if (x >= minx &&
		    x <= maxx &&
		    y >= miny &&
		    y <= maxy)
			return;
	}
	
	innerx = x - multiscreen_x (basep->screen);
	innery = y - multiscreen_y (basep->screen);
	screen_width = multiscreen_width (basep->screen);
	screen_height = multiscreen_height (basep->screen);

	/*if in the inner 1/3rd, don't change to avoid fast flickery
	  movement*/
	if ( innerx > (screen_width / 3) &&
	     innerx < (2*screen_width / 3) &&
	     innery > (screen_height / 3) &&
	     innery < (2*screen_height / 3))
		return;

	if (innerx * screen_height > innery * screen_width ) {
		if (screen_height * (screen_width-innerx) >
		    innery * screen_width ) {
			newpos = BORDER_TOP;
			if (innerx < screen_width/3)
				newalign = ALIGNED_LEFT;
			else if (innerx < 2*screen_width/3)
				newalign = ALIGNED_CENTER;
			else
				newalign = ALIGNED_RIGHT;
		} else {
			newpos = BORDER_RIGHT;
			if (innery < screen_height/3)
				newalign = ALIGNED_LEFT;
			else if (innery < 2*screen_height/3)
				newalign = ALIGNED_CENTER;
			else
				newalign = ALIGNED_RIGHT;
		}
	} else {
		if (screen_height * (screen_width-innerx) >
		    innery * screen_width ) {
			newpos = BORDER_LEFT;
			if (innery < screen_height/3)
				newalign = ALIGNED_LEFT;
			else if (innery < 2*screen_height/3)
				newalign = ALIGNED_CENTER;
			else
				newalign = ALIGNED_RIGHT;
		} else {
			newpos = BORDER_BOTTOM;
			if (innerx < screen_width/3)
				newalign = ALIGNED_LEFT;
			else if (innerx < 2*screen_width/3)
				newalign = ALIGNED_CENTER;
			else
				newalign = ALIGNED_RIGHT;
		}
	}

	if(newalign != ALIGNED_POS(basep->pos)->align)
		aligned_widget_change_align (ALIGNED_WIDGET(basep), newalign);
	
	if(newpos != BORDER_POS(basep->pos)->edge)
		border_widget_change_edge (BORDER_WIDGET(basep), newpos);

}

static void
aligned_pos_get_pos (BasePWidget *basep, int *x, int *y,
		     int w, int h)
{
	int a, b;
	BorderEdge edge = BORDER_POS(basep->pos)->edge;

	*x = *y = 0;
	switch (edge) {
	case BORDER_BOTTOM:
		*y = multiscreen_height(basep->screen) - h - foobar_widget_get_height (basep->screen);
		/* fall thru */
	case BORDER_TOP:
		*y += foobar_widget_get_height (basep->screen);
		switch (ALIGNED_POS(basep->pos)->align) {
		case ALIGNED_LEFT:
			break;
		case ALIGNED_CENTER:
			*x = (multiscreen_width(basep->screen) - w) / 2;
			break;
		case ALIGNED_RIGHT:
			*x = multiscreen_width(basep->screen) - w;
			break;
		}
		break;
	case BORDER_RIGHT:
		*x = multiscreen_width(basep->screen) - w;
		basep_border_get (basep->screen, BORDER_TOP, NULL, NULL, &a);
		basep_border_get (basep->screen, BORDER_BOTTOM, NULL, NULL, &b);
		switch (ALIGNED_POS(basep->pos)->align) {
		case ALIGNED_LEFT:
			*y = foobar_widget_get_height (basep->screen) + a;
			break;
		case ALIGNED_CENTER:
			*y = (multiscreen_height(basep->screen) - h) / 2;
			break;
		case ALIGNED_RIGHT:
			*y = multiscreen_height(basep->screen) - h - b;
			break;
		}
		break;
	case BORDER_LEFT:
		basep_border_get (basep->screen, BORDER_TOP, &a, NULL, NULL);
		basep_border_get (basep->screen, BORDER_BOTTOM, &b, NULL, NULL);
		switch (ALIGNED_POS(basep->pos)->align) {
		case ALIGNED_LEFT:
			*y = foobar_widget_get_height (basep->screen) + a;
			break;
		case ALIGNED_CENTER:
			*y = (multiscreen_height(basep->screen) - h) / 2;
			break;
		case ALIGNED_RIGHT:
			*y = multiscreen_height(basep->screen) - h - b;
			break;
		}
		break;
	}

	*x += multiscreen_x (basep->screen);
	*y += multiscreen_y (basep->screen);

	basep_border_queue_recalc (basep->screen);
}

static void
aligned_pos_show_hide_left (BasePWidget *basep)
{
	switch (basep->state) {
	case BASEP_SHOWN:
		if (ALIGNED_POS (basep->pos)->align == ALIGNED_RIGHT)
			aligned_widget_change_align (ALIGNED_WIDGET (basep), 
						     ALIGNED_LEFT);
		else 
			basep_widget_explicit_hide (basep, BASEP_HIDDEN_LEFT);
		break;
	case BASEP_HIDDEN_RIGHT:
		basep_widget_explicit_show (basep);
		break;
	default:
		break;
	}
}


static void
aligned_pos_show_hide_right (BasePWidget *basep)
{
	switch (basep->state) {
	case BASEP_SHOWN:
		if (ALIGNED_POS (basep->pos)->align == ALIGNED_LEFT)
			aligned_widget_change_align (ALIGNED_WIDGET (basep), 
						     ALIGNED_RIGHT);
		else
			basep_widget_explicit_hide (basep, BASEP_HIDDEN_RIGHT);
		break;
	case BASEP_HIDDEN_LEFT:
		basep_widget_explicit_show (basep);
		break;
	default:
		break;
	}
}

void
aligned_widget_change_params (AlignedWidget *aligned,
			      int screen,
			      AlignedAlignment align,
			      BorderEdge edge,
			      int sz,
			      BasePMode mode,
			      BasePState state,
			      gboolean hidebuttons_enabled,
			      gboolean hidebutton_pixmaps_enabled,
			      PanelBackType back_type,
			      char *pixmap_name,
			      gboolean fit_pixmap_bg,
			      gboolean stretch_pixmap_bg,
			      gboolean rotate_pixmap_bg,
			      GdkColor *back_color)
{
	AlignedPos *pos = ALIGNED_POS (BASEP_WIDGET (aligned)->pos);

	if (pos->align != align) {
		pos->align = align;
		g_signal_emit (G_OBJECT (pos),
			       aligned_pos_signals[ALIGN_CHANGE_SIGNAL],
			       0, align);
	}

	border_widget_change_params (BORDER_WIDGET (aligned),
				     screen,
				     edge,
				     sz,
				     mode,
				     state,
				     hidebuttons_enabled,
				     hidebutton_pixmaps_enabled,
				     back_type,
				     pixmap_name,
				     fit_pixmap_bg,
				     stretch_pixmap_bg,
				     rotate_pixmap_bg,
				     back_color);
}


void 
aligned_widget_change_align (AlignedWidget *aligned,
			     AlignedAlignment align)
{
	BasePWidget *basep = BASEP_WIDGET (aligned);
	PanelWidget *panel = PANEL_WIDGET (basep->panel);
	AlignedPos *pos = ALIGNED_POS (basep->pos);

	if (pos->align == align)
		return;

	aligned_widget_change_params (aligned,
				      basep->screen,
				      align,
				      BORDER_POS (pos)->edge,
				      panel->sz, basep->mode,
				      basep->state,
				      basep->hidebuttons_enabled,
				      basep->hidebutton_pixmaps_enabled,
				      panel->back_type,
				      panel->back_pixmap,
				      panel->fit_pixmap_bg,
				      panel->stretch_pixmap_bg,
				      panel->rotate_pixmap_bg,
				      &panel->back_color);
}

void 
aligned_widget_change_align_edge (AlignedWidget *aligned,
				  AlignedAlignment align,
				  BorderEdge edge)
{
	BasePWidget *basep = BASEP_WIDGET (aligned);
	PanelWidget *panel = PANEL_WIDGET (basep->panel);

	aligned_widget_change_params (aligned,
				      basep->screen,
				      align,
				      edge,
				      panel->sz,
				      basep->mode,
				      basep->state,
				      basep->hidebuttons_enabled,
				      basep->hidebutton_pixmaps_enabled,
				      panel->back_type,
				      panel->back_pixmap,
				      panel->fit_pixmap_bg,
				      panel->stretch_pixmap_bg,
				      panel->rotate_pixmap_bg,
				      &panel->back_color);
}
				 
GtkWidget *
aligned_widget_new (gchar *panel_id,
		    int screen,
		    AlignedAlignment align,
		    BorderEdge edge,
		    BasePMode mode,
		    BasePState state,
		    int sz,
		    gboolean hidebuttons_enabled,
		    gboolean hidebutton_pixmaps_enabled,
		    PanelBackType back_type,
		    char *back_pixmap,
		    gboolean fit_pixmap_bg,
		    gboolean stretch_pixmap_bg,
		    gboolean rotate_pixmap_bg,
		    GdkColor *back_color)
{
	AlignedWidget *aligned = g_object_new (ALIGNED_TYPE_WIDGET, NULL);
	AlignedPos *pos = g_object_new (ALIGNED_TYPE_POS, NULL);
	pos->align = align;

	BASEP_WIDGET (aligned)->pos = BASEP_POS (pos);

	border_widget_construct (panel_id,
				 BORDER_WIDGET (aligned),
				 screen,
				 edge, 
				 TRUE,
				 FALSE,
				 sz,
				 mode,
				 state,
				 hidebuttons_enabled,
				 hidebutton_pixmaps_enabled,
				 back_type,
				 back_pixmap,
				 fit_pixmap_bg,
				 stretch_pixmap_bg,
				 rotate_pixmap_bg,
				 back_color);

	return GTK_WIDGET (aligned);
}
