/* Gnome panel: basep widget
 * (C) 1997 the Free Software Foundation
 *
 * Authors:  George Lebl
 */
#include <math.h>
#include <config.h>
#include <X11/Xlib.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <gnome.h>
#include "panel-widget.h"
#include "basep-widget.h"
#include "panel-util.h"
#include "panel_config_global.h"
#include "gdkextra.h"

extern int panel_applet_in_drag;

static void basep_widget_class_init	(BasePWidgetClass *klass);
static void basep_widget_init		(BasePWidget      *basep);

static GtkWindowClass *parent_class = NULL;

/*global settings*/
extern int pw_explicit_step;
extern int pw_drawer_step;
extern int pw_auto_step;
extern int pw_minimized_size;
extern int pw_minimize_delay;
extern int pw_disable_animations;
extern PanelMovementType pw_movement_type;

extern GlobalConfig global_config;

/************************
 widget core
 ************************/

guint
basep_widget_get_type ()
{
	static guint basep_widget_type = 0;

	if (!basep_widget_type) {
		GtkTypeInfo basep_widget_info = {
			"BasePWidget",
			sizeof (BasePWidget),
			sizeof (BasePWidgetClass),
			(GtkClassInitFunc) basep_widget_class_init,
			(GtkObjectInitFunc) basep_widget_init,
			(GtkArgSetFunc) NULL,
			(GtkArgGetFunc) NULL,
		};

		basep_widget_type = gtk_type_unique (gtk_window_get_type (),
						     &basep_widget_info);
	}

	return basep_widget_type;
}

static void
basep_widget_realize(GtkWidget *w)
{
	BasePWidget *basep = BASEP_WIDGET(w);
	GTK_WIDGET_CLASS(parent_class)->realize(w);

	set_frame_colors(PANEL_WIDGET(basep->panel),
			 basep->frame,
			 basep->hidebutton_n,
			 basep->hidebutton_e,
			 basep->hidebutton_w,
			 basep->hidebutton_s);
}

static void
basep_widget_class_init (BasePWidgetClass *class)
{
	GtkObjectClass *object_class = (GtkObjectClass*) class;
	GtkWidgetClass *widget_class = (GtkWidgetClass*) class;

        parent_class = gtk_type_class (gtk_window_get_type ());

	class->set_hidebuttons = NULL;
	
	widget_class->realize = basep_widget_realize;
}


static int
basep_enter_notify(GtkWidget *basep,
		   GdkEventCrossing *event,
		   gpointer data)
{
	if (!gnome_win_hints_wm_exists() &&
	    global_config.autoraise)
		gdk_window_raise(basep->window);
	return FALSE;
}

void
basep_widget_get_position(BasePWidget *basep, PanelOrientType hide_orient,
			  gint16 *x, gint16 *y, gint16 w, gint16 h)
{
	GtkWidget *widget = GTK_WIDGET(basep);

	*x = *y = 0;
	switch(hide_orient) {
	case ORIENT_UP:
		if(h < basep->shown_alloc.height)
			*y -= basep->shown_alloc.height - h;
		break;
	case ORIENT_DOWN:
		break;
	case ORIENT_LEFT:
		if(w < basep->shown_alloc.width)
			*x -= basep->shown_alloc.width - w;
		break;
	case ORIENT_RIGHT:
		break;
	default:
		break;
	}
}

static void
basep_widget_set_ebox_orient(BasePWidget *basep,
			     PanelOrientType hide_orient)
{
	XSetWindowAttributes xattributes;

	switch(hide_orient) {
	case ORIENT_UP:
	case ORIENT_LEFT:
		xattributes.win_gravity = SouthEastGravity;
		break;
	case ORIENT_DOWN:
	case ORIENT_RIGHT:
	default:
		xattributes.win_gravity = NorthWestGravity;
		break;
	}

	XChangeWindowAttributes (GDK_WINDOW_XDISPLAY(basep->ebox->window),
				 GDK_WINDOW_XWINDOW(basep->ebox->window),
				 CWWinGravity,  &xattributes);
	
}

static int
move_step(int src, int dest, long start_time, long end_time, long cur_time)
{
	double percentage;
	
	if(src == dest)
		return dest;

	if(global_config.simple_movement) {
		percentage = ((double)(cur_time-start_time))/(end_time-start_time);
		if(percentage>1.0)
			percentage = 1.0;
		return  src + ((dest - src)*percentage);
	} else {
		double n = cur_time-start_time;
		double d = end_time-start_time;

		if(n<d) {
			/*blah blah blah just a simple function to make the
			  movement more "sin" like ... we run it twice to
			  pronounce make it more pronounced*/
			percentage = sin(M_PI*(n/d)-M_PI/2)/2+0.5;
			percentage = sin(M_PI*percentage-M_PI/2)/2+0.5;
			if(percentage<0.0)
				percentage = 0.0;
			else if(percentage>1.0)
				percentage = 1.0;
		} else
			percentage = 1.0;

		return  src + ((dest - src)*percentage);
	}
}


void
basep_widget_do_hiding(BasePWidget *basep, PanelOrientType hide_orient,
		       int leftover, int step)
{
	GtkWidget *wid;
	int ox,oy,ow,oh;
	int x,y,w,h;
	int dx,dy,dw,dh;
	int diff;
	
	g_return_if_fail(basep != NULL);
	g_return_if_fail(IS_BASEP_WIDGET(basep));

	wid = GTK_WIDGET(basep);
	
	ox = x = basep->shown_alloc.x;
	oy = y = basep->shown_alloc.y;
	ow = w = basep->shown_alloc.width;
	oh = h = basep->shown_alloc.height;
	
	switch(hide_orient) {
	case ORIENT_UP:
		diff = h-leftover;
		dx = x;
		dy = y;
		dw = w;
		dh = leftover;
		break;
	case ORIENT_DOWN:
		diff = h-leftover;
		dx = x;
		dy = y+h-leftover;
		dw = w;
		dh = leftover;
		break;
	case ORIENT_LEFT:
		diff = w-leftover;
		dx = x;
		dy = y;
		dw = leftover;
		dh = h;
		break;
	case ORIENT_RIGHT:
		diff = w-leftover;
		dx = x+w-leftover;
		dy = y;
		dw = leftover;
		dh = h;
		break;
	default:
		/*fix warning*/ dx = dy = dw = dh = 0;
		diff = 1;
		g_assert_not_reached();
		break;
	}

	if(!pw_disable_animations && step != 0) {
		GTimeVal tval;
		long start_secs;
		long start_time;
		long end_time;
		long cur_time;

		g_get_current_time(&tval);
		
		start_secs = tval.tv_sec;
		start_time = tval.tv_usec;
		
		end_time = start_time +
			(diff/1000.0)*200*(10001-(step*step));

		basep_widget_set_ebox_orient(basep, hide_orient);
		
		while(x != dx ||
		      y != dy ||
		      w != dw ||
		      h != dh) {
			g_get_current_time(&tval);
			
			cur_time = ((tval.tv_sec-start_secs)*1000000) +
				tval.tv_usec;

			x = move_step(ox,dx,start_time,end_time,cur_time);
			y = move_step(oy,dy,start_time,end_time,cur_time);
			w = move_step(ow,dw,start_time,end_time,cur_time);
			h = move_step(oh,dh,start_time,end_time,cur_time);
			gdk_window_move_resize(wid->window, x,y,w,h);
			gdk_flush();
			usleep(1000);
		}

		gdk_window_resize(wid->window,dw,dh);
		gdk_window_set_hints (wid->window,
				      dx,dy,0,0,0,0,
				      GDK_HINT_POS);
		basep_widget_set_ebox_orient(basep, -1);
	}
	
	gtk_widget_queue_resize(wid);
	gtk_widget_draw(basep->table, NULL);
}

void
basep_widget_do_showing(BasePWidget *basep, PanelOrientType hide_orient,
			int leftover, int step)
{
	GtkWidget *wid;
	int ox,oy,ow,oh;
	int x,y,w,h;
	int dx,dy,dw,dh;
	int diff;

	g_return_if_fail(basep != NULL);
	g_return_if_fail(IS_BASEP_WIDGET(basep));

	wid = GTK_WIDGET(basep);
	
	ox = x = basep->shown_alloc.x;
	oy = y = basep->shown_alloc.y;
	dw = basep->shown_alloc.width;
	dh = basep->shown_alloc.height;
	
	switch(hide_orient) {
	case ORIENT_UP:
		dx = x;
		dy = y;
		ow = w = dw;
		oh = h = leftover;
		diff = dh-leftover;
		break;
	case ORIENT_DOWN:
		dx = x;
		dy = y - dh + leftover;
		ow = w = dw;
		oh = h = leftover;
		diff = dh-leftover;
		break;
	case ORIENT_LEFT:
		dx = x;
		dy = y;
		ow = w = leftover;
		oh = h = dh;
		diff = dw-leftover;
		break;
	case ORIENT_RIGHT:
		dx = x - dw + leftover;
		dy = y;
		ow = w = leftover;
		oh = h = dh;
		diff = dw-leftover;
		break;
	default:
		/*fix warning*/ dx = dy = ow = oh = w = h = 0;
		diff = 1;
		g_assert_not_reached();
		break;
	}
	
	if(!pw_disable_animations && step != 0) {
		int i;
		GTimeVal tval;
		long start_secs;
		long start_time;
		long end_time;
		long cur_time;

		g_get_current_time(&tval);
		
		start_secs = tval.tv_sec;
		start_time = tval.tv_usec;
		
		end_time = start_time +
			(diff/1000.0)*200*(10001-(step*step));
		
		basep_widget_set_ebox_orient(basep, hide_orient);
		gdk_window_resize(wid->window,ow,oh);
		gdk_window_set_hints (wid->window,
				      ox,oy,0,0,0,0,
				      GDK_HINT_POS);
		gdk_window_show(wid->window);
		gdk_window_move(wid->window,ox,oy);

		gtk_widget_show_now(wid);

		gdk_window_show(wid->window);
		i = 0;
		while(x != dx ||
		      y != dy ||
		      w != dw ||
		      h != dh) {
			g_get_current_time(&tval);
			
			cur_time = ((tval.tv_sec-start_secs)*1000000) +
				tval.tv_usec;
			
			x = move_step(ox,dx,start_time,end_time,cur_time);
			y = move_step(oy,dy,start_time,end_time,cur_time);
			w = move_step(ow,dw,start_time,end_time,cur_time);
			h = move_step(oh,dh,start_time,end_time,cur_time);
			gdk_window_move_resize(wid->window, x,y,w,h);
			gdk_flush();
			
			/*drawing the entire table flickers, so don't
			  do it often*/
			if(i++%10)
				gtk_widget_draw(basep->panel, NULL);
			else
				gtk_widget_draw(basep->table, NULL);
			gdk_flush();
			usleep(1000);
		}

		gdk_window_resize(wid->window,dw,dh);
		gdk_window_set_hints (wid->window,
				      dx,dy,0,0,0,0,
				      GDK_HINT_POS);

		basep_widget_set_ebox_orient(basep, -1);
	}
	
	gtk_widget_draw(basep->table, NULL);
	gtk_widget_queue_resize(wid);
}


static GtkWidget *
make_hidebutton(BasePWidget *basep,
		char *pixmaparrow,
		int horizontal)
{
	GtkWidget *w;
	GtkWidget *pixmap;
	char *pixmap_name;

	w=gtk_button_new();
	GTK_WIDGET_UNSET_FLAGS(w,GTK_CAN_DEFAULT|GTK_CAN_FOCUS);
	if(horizontal)
		gtk_widget_set_usize(w,0,PANEL_MINIMUM_WIDTH);
	else
		gtk_widget_set_usize(w,PANEL_MINIMUM_WIDTH,0);

	pixmap_name=gnome_pixmap_file(pixmaparrow);
	if(pixmap_name) {
		pixmap = gnome_pixmap_new_from_file(pixmap_name);
		g_free(pixmap_name);
	} else {
		pixmap = gtk_label_new("*");
	}
	gtk_widget_show(pixmap);

	gtk_container_add(GTK_CONTAINER(w),pixmap);
	gtk_object_set_user_data(GTK_OBJECT(w), pixmap);
	gtk_object_set_data(GTK_OBJECT(w), "gnome_disable_sound_events",
			    GINT_TO_POINTER(1));
	return w;
}

static void
basep_widget_init (BasePWidget *basep)
{
	/*if we set the gnomewm hints it will have to be changed to TOPLEVEL*/
	gnome_win_hints_init();
	if (gnome_win_hints_wm_exists())
		GTK_WINDOW(basep)->type = GTK_WINDOW_TOPLEVEL;
	else
		GTK_WINDOW(basep)->type = GTK_WINDOW_POPUP;
	GTK_WINDOW(basep)->allow_shrink = TRUE;
	GTK_WINDOW(basep)->allow_grow = TRUE;
	GTK_WINDOW(basep)->auto_shrink = TRUE;
	
	basep->shown_alloc.x = basep->shown_alloc.y =
		basep->shown_alloc.width = basep->shown_alloc.height = 0;
	
	/*this makes the popup "pop down" once the button is released*/
	gtk_widget_set_events(GTK_WIDGET(basep),
			      gtk_widget_get_events(GTK_WIDGET(basep)) |
			      GDK_BUTTON_RELEASE_MASK);

	basep->ebox = gtk_event_box_new();
	gtk_container_add(GTK_CONTAINER(basep),basep->ebox);
	gtk_widget_show(basep->ebox);

	basep->table = gtk_table_new(3,3,FALSE);
	gtk_container_add(GTK_CONTAINER(basep->ebox),basep->table);
	gtk_widget_show(basep->table);

	basep->frame = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(basep->frame),GTK_SHADOW_OUT);

	gtk_table_attach(GTK_TABLE(basep->table),basep->frame,1,2,1,2,
			 GTK_FILL|GTK_EXPAND|GTK_SHRINK,
			 GTK_FILL|GTK_EXPAND|GTK_SHRINK,
			 0,0);

	basep->innerebox = gtk_event_box_new();

	gtk_table_attach(GTK_TABLE(basep->table),basep->innerebox,1,2,1,2,
			 GTK_FILL|GTK_EXPAND|GTK_SHRINK,
			 GTK_FILL|GTK_EXPAND|GTK_SHRINK,
			 0,0);

	gtk_signal_connect(GTK_OBJECT(basep), "enter_notify_event",
			   GTK_SIGNAL_FUNC(basep_enter_notify),
			   NULL);

	basep->hidebuttons_enabled = TRUE;
	basep->hidebutton_pixmaps_enabled = TRUE;
}

static void
show_hidebutton_pixmap(GtkWidget *hidebutton, int show)
{
	GtkWidget *pixmap;

	pixmap = gtk_object_get_user_data(GTK_OBJECT(hidebutton));

	if (!pixmap) return;

	if (show)
		gtk_widget_show(pixmap);
	else
		gtk_widget_hide(pixmap);
}

static void
basep_widget_show_hidebutton_pixmaps(BasePWidget *basep)
{
	int show = basep->hidebutton_pixmaps_enabled;
	show_hidebutton_pixmap(basep->hidebutton_n, show);
	show_hidebutton_pixmap(basep->hidebutton_e, show);
	show_hidebutton_pixmap(basep->hidebutton_w, show);
	show_hidebutton_pixmap(basep->hidebutton_s, show);
}

static void
b_back_change(PanelWidget *panel,
	      PanelBackType type,
	      char *pixmap,
	      GdkColor *color,
	      BasePWidget *basep)
{
	if(type == PANEL_BACK_PIXMAP &&
	   basep->panel->parent == basep->frame) {
		gtk_widget_show(basep->innerebox);
		gtk_widget_reparent(basep->panel,basep->innerebox);
		gtk_widget_hide(basep->frame);
	} else if(type != PANEL_BACK_PIXMAP &&
		  basep->panel->parent == basep->innerebox) {
		gtk_widget_show(basep->frame);
		gtk_widget_reparent(basep->panel,basep->frame);
		gtk_widget_hide(basep->innerebox);
	}

	set_frame_colors(panel,
			 basep->frame,
			 basep->hidebutton_n,
			 basep->hidebutton_e,
			 basep->hidebutton_w,
			 basep->hidebutton_s);
}

GtkWidget*
basep_widget_construct (BasePWidget *basep,
			int packed,
			int reverse_arrows,
			PanelOrientation orient,
			int hidebuttons_enabled,
			int hidebutton_pixmaps_enabled,
			PanelBackType back_type,
			char *back_pixmap,
			int fit_pixmap_bg,
			GdkColor *back_color)
{
	basep->panel = panel_widget_new(packed,
					orient,
					back_type,
					back_pixmap,
					fit_pixmap_bg,
					back_color);
	gtk_signal_connect_after(GTK_OBJECT(basep->panel), "back_change",
				 GTK_SIGNAL_FUNC(b_back_change),
				 basep);
	gtk_object_set_data(GTK_OBJECT(basep->panel),PANEL_PARENT,
			    basep);
	PANEL_WIDGET(basep->panel)->drop_widget = GTK_WIDGET(basep);

	gtk_widget_show(basep->panel);

	if(back_type != PANEL_BACK_PIXMAP) {
		gtk_widget_show(basep->frame);
		gtk_container_add(GTK_CONTAINER(basep->frame),basep->panel);
	} else {
		gtk_widget_show(basep->innerebox);
		gtk_container_add(GTK_CONTAINER(basep->innerebox),basep->panel);
	}

	/*we add all the hide buttons to the table here*/
	/*EAST*/
	basep->hidebutton_w = make_hidebutton(basep,
					      reverse_arrows?
					      "panel-arrow-right.png":
					      "panel-arrow-left.png",
					      TRUE);
	gtk_table_attach(GTK_TABLE(basep->table),basep->hidebutton_w,
			 0,1,1,2,GTK_FILL,GTK_FILL,0,0);
	/*NORTH*/
	basep->hidebutton_n = make_hidebutton(basep,
					      reverse_arrows?
					      "panel-arrow-down.png":
					      "panel-arrow-up.png",
					      FALSE);
	gtk_table_attach(GTK_TABLE(basep->table),basep->hidebutton_n,
			 1,2,0,1,GTK_FILL,GTK_FILL,0,0);
	/*WEST*/
	basep->hidebutton_e = make_hidebutton(basep,
					      reverse_arrows?
					      "panel-arrow-left.png":
					      "panel-arrow-right.png",
					      TRUE);
	gtk_table_attach(GTK_TABLE(basep->table),basep->hidebutton_e,
			 2,3,1,2,GTK_FILL,GTK_FILL,0,0);
	/*SOUTH*/
	basep->hidebutton_s = make_hidebutton(basep,
					      reverse_arrows?
					      "panel-arrow-up.png":
					      "panel-arrow-down.png",
					      FALSE);
	gtk_table_attach(GTK_TABLE(basep->table),basep->hidebutton_s,
			 1,2,2,3,GTK_FILL,GTK_FILL,0,0);

	basep->hidebuttons_enabled = hidebuttons_enabled;
	basep->hidebutton_pixmaps_enabled = hidebutton_pixmaps_enabled;

	basep_widget_set_hidebuttons(basep);
	basep_widget_show_hidebutton_pixmaps(basep);

	return GTK_WIDGET(basep);
}

void
basep_widget_change_params(BasePWidget *basep,
			   PanelOrientation orient,
			   int hidebuttons_enabled,
			   int hidebutton_pixmaps_enabled,
			   PanelBackType back_type,
			   char *pixmap_name,
			   int fit_pixmap_bg,
			   GdkColor *back_color)
{
	g_return_if_fail(basep);
	g_return_if_fail(GTK_WIDGET_REALIZED(GTK_WIDGET(basep)));
	
	basep->hidebuttons_enabled = hidebuttons_enabled;
	basep->hidebutton_pixmaps_enabled = hidebutton_pixmaps_enabled;

	panel_widget_change_params(PANEL_WIDGET(basep->panel),
				   orient,
				   back_type,
				   pixmap_name,
				   fit_pixmap_bg,
				   back_color);

	basep_widget_set_hidebuttons(basep);
	basep_widget_show_hidebutton_pixmaps(basep);
}

void
basep_widget_enable_buttons(BasePWidget *basep)
{
	gtk_widget_set_sensitive(basep->hidebutton_n,TRUE);
	gtk_widget_set_sensitive(basep->hidebutton_e,TRUE);
	gtk_widget_set_sensitive(basep->hidebutton_w,TRUE);
	gtk_widget_set_sensitive(basep->hidebutton_s,TRUE);
}


void
basep_widget_disable_buttons(BasePWidget *basep)
{
	gtk_widget_set_sensitive(basep->hidebutton_n,FALSE);
	gtk_widget_set_sensitive(basep->hidebutton_e,FALSE);
	gtk_widget_set_sensitive(basep->hidebutton_w,FALSE);
	gtk_widget_set_sensitive(basep->hidebutton_s,FALSE);
}

void
basep_widget_set_hidebuttons(BasePWidget *basep)
{
	BasePWidgetClass *class = BASEP_WIDGET_CLASS(GTK_OBJECT(basep)->klass);
	if(class->set_hidebuttons)
		(*class->set_hidebuttons)(basep);
}
