/* Gnome panel: basep widget
 * (C) 1997 the Free Software Foundation
 *
 * Authors:  George Lebl
 *           Jacob Berkman
 */
#include <config.h>
#include <math.h>
#include <string.h>
#include <X11/Xlib.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <gdk/gdkkeysyms.h>
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h>
#include <libgnome/gnome-macros.h>
#include <libgnomeui/gnome-window-icon.h>
#include "panel-marshal.h"
#include "panel-widget.h"
#include "basep-widget.h"
#include "panel-util.h"
#include "panel-config-global.h"
#include "foobar-widget.h"
#include "drawer-widget.h"
#include "border-widget.h"
#include "edge-widget.h"
#include "aligned-widget.h"
#include "xstuff.h"
#include "multiscreen-stuff.h"
#include "panel-typebuiltins.h"
#include "panel-gconf.h"

#undef BASEP_WIDGET_DEBUG

extern gboolean panel_applet_in_drag;
extern GSList *panel_list;

extern int panels_to_sync;

/*global settings*/

extern GtkTooltips *panel_tooltips;
extern GlobalConfig global_config;

static GtkWindowClass *basep_widget_parent_class = NULL;
static GtkObjectClass *basep_pos_parent_class = NULL;

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

static void basep_pos_class_init (BasePPosClass *klass);
static void basep_pos_instance_init (BasePPos *pos);

/* Forward declare some static functions for use in the class init */
static void basep_widget_move_focus_out (BasePWidget *basep);
static void basep_widget_mode_change (BasePWidget *basep, BasePMode mode);
static void basep_widget_state_change (BasePWidget *basep, BasePState state);
static void basep_widget_real_screen_change (BasePWidget *basep, int screen);
static void basep_widget_size_request (GtkWidget *widget, GtkRequisition *requisition);
static void basep_widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
static void basep_widget_realize (GtkWidget *w);
static void basep_widget_map (GtkWidget *w);
static gboolean basep_enter_notify (GtkWidget *widget, GdkEventCrossing *event);
static gboolean basep_leave_notify (GtkWidget *widget, GdkEventCrossing *event);
static void basep_style_set (GtkWidget *widget, GtkStyle *previous_style);
static void basep_widget_destroy (GtkObject *o);

static void basep_widget_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static void basep_widget_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);

static BasePPosClass * basep_widget_get_pos_class (BasePWidget *basep);

enum {
	/*TYPE_CHANGE_SIGNAL,*/
	MOVE_FOCUS_OUT_SIGNAL,
	MODE_CHANGE_SIGNAL,
	STATE_CHANGE_SIGNAL,
	SCREEN_CHANGE_SIGNAL,
	WIDGET_LAST_SIGNAL
};

enum { 
	PROP_0,
        PROP_MODE,
        PROP_STATE,
        PROP_LEVEL,
        PROP_AVOID_ON_MAXIMIZE,
        PROP_HIDEBUTTONS_ENABLED,
        PROP_HIDEBUTTON_PIXMAPS_ENABLED,
};

#define PANEL_SLOW_STEP_SIZE 10
#define PANEL_MEDIUM_STEP_SIZE 60
#define PANEL_FAST_STEP_SIZE 80

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

GType
basep_widget_get_type (void)					
{
	static GType object_type = 0;
	if (object_type == 0) {
		static const GTypeInfo object_info = {
		    sizeof (BasePWidgetClass),
		    (GBaseInitFunc)         NULL,
		    (GBaseFinalizeFunc)     NULL,
		    (GClassInitFunc)        basep_widget_class_init,
		    NULL,                   /* class_finalize */
		    NULL,                   /* class_data */
		    sizeof (BasePWidget),
		    0,                      /* n_preallocs */
		    (GInstanceInitFunc)     basep_widget_instance_init 
		};
		object_type = g_type_register_static
		    (GTK_TYPE_WINDOW, "BasePWidget", &object_info, 0);
		basep_widget_parent_class = g_type_class_ref (GTK_TYPE_WINDOW);
	}
	return object_type;
}

static guint basep_widget_signals[WIDGET_LAST_SIGNAL] = { 0 };

static void
basep_widget_class_init (BasePWidgetClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (klass);
	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
	GtkBindingSet *binding_set;
	
	basep_widget_parent_class = g_type_class_ref (gtk_window_get_type ());

	klass->move_focus_out = basep_widget_move_focus_out;
	klass->mode_change = basep_widget_mode_change;
	klass->state_change = basep_widget_state_change;
	klass->screen_change = basep_widget_real_screen_change;

	widget_class->size_request = basep_widget_size_request;
	widget_class->size_allocate = basep_widget_size_allocate;
	widget_class->realize = basep_widget_realize;
	widget_class->map = basep_widget_map;
	widget_class->enter_notify_event = basep_enter_notify;
	widget_class->leave_notify_event = basep_leave_notify;
	widget_class->style_set = basep_style_set;

	gtk_object_class->destroy = basep_widget_destroy;

	object_class->set_property = basep_widget_set_property;
	object_class->get_property = basep_widget_get_property;

        g_object_class_install_property (object_class,
                                        PROP_MODE,
                                        g_param_spec_enum ("mode",
                                                             _("mode"),
                                                             _("mode"),
							    PANEL_TYPE_BASE_PMODE,
                                                            BASEP_EXPLICIT_HIDE,
                                                            G_PARAM_READWRITE));
        g_object_class_install_property (object_class,
                                        PROP_STATE,
                                        g_param_spec_enum ("state",
                                                             _("state"),
                                                             _("state"),
							     PANEL_TYPE_BASE_PSTATE,
                                                             BASEP_SHOWN,
                                                             G_PARAM_READWRITE));

        g_object_class_install_property (object_class,
        			  	PROP_HIDEBUTTONS_ENABLED,
                                        g_param_spec_boolean ("hidebuttons_enabled",
                                                             _("hidebuttons_enabled"),
                                                             _("hidebuttons_enabled"),
                                                             TRUE,
                                                             G_PARAM_READWRITE));

        g_object_class_install_property (object_class,
        			  	PROP_HIDEBUTTON_PIXMAPS_ENABLED,
                                        g_param_spec_boolean ("hidebutton_pixmaps_enabled",
                                                             _("hidebutton_pixmaps_enabled"),
                                                             _("hidebutton_pixmaps_enabled"),
                                                             TRUE,
                                                             G_PARAM_READWRITE));

	/*basep_widget_signals[TYPE_CHANGE_SIGNAL] = 
		g_signal_new ("type_change",
			      G_TYPE_FROM_CLASS (object_class),
			      G_SIGNAL_RUN_LAST,
			      G_STRUCT_OFFSET (BasePWidgetClass, type_change),
			      NULL,
			      NULL,
			      panel_marshal_VOID__ENUM,
			      G_TYPE_NONE,
			      1,
			      PANEL_TYPE_OBJECT_TYPE); */

	basep_widget_signals[MOVE_FOCUS_OUT_SIGNAL] = 
		g_signal_new	("move_focus_out",
				G_TYPE_FROM_CLASS (object_class),
				G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
				G_STRUCT_OFFSET (BasePWidgetClass, move_focus_out),
				NULL,
				NULL,
				panel_marshal_VOID__VOID,
				G_TYPE_NONE,
				0);

	basep_widget_signals[MODE_CHANGE_SIGNAL] = 
		g_signal_new	("mode_change",
			       	G_TYPE_FROM_CLASS (object_class),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (BasePWidgetClass, mode_change),
				NULL,
				NULL,
				panel_marshal_VOID__ENUM,
				G_TYPE_NONE,
				1,
				PANEL_TYPE_BASE_PMODE);

	basep_widget_signals[STATE_CHANGE_SIGNAL] = 
		g_signal_new	("state_change",
				G_TYPE_FROM_CLASS (object_class),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (BasePWidgetClass, state_change),
				NULL,
				NULL,
				panel_marshal_VOID__ENUM,
				G_TYPE_NONE,
				1,
				PANEL_TYPE_BASE_PSTATE);

	basep_widget_signals[SCREEN_CHANGE_SIGNAL] = 
		g_signal_new	("screen_change",
				G_TYPE_FROM_CLASS (object_class),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (BasePWidgetClass, screen_change),
				NULL,
				NULL,
				panel_marshal_VOID__INT,
				G_TYPE_NONE,
				1,
				G_TYPE_INT);

	binding_set = gtk_binding_set_by_class (gtk_object_class);
	gtk_binding_entry_add_signal (binding_set,
				      GDK_Tab, GDK_CONTROL_MASK,
				      "move_focus_out", 0);
	gtk_binding_entry_add_signal (binding_set,
				      GDK_KP_Tab, GDK_CONTROL_MASK,
				      "move_focus_out", 0);
	gtk_binding_entry_add_signal (binding_set,
				      GDK_ISO_Left_Tab, GDK_CONTROL_MASK,
				      "move_focus_out", 0);
}

static void
basep_widget_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{

		/* Does nothing yet */
}

static void
basep_widget_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
	BasePWidget *basep;

	basep = BASEP_WIDGET (object);

	switch (prop_id) {
        	case PROP_MODE:
			basep->mode = g_value_get_enum (value);
			break;
        	case PROP_STATE:
			basep->state = g_value_get_enum (value);
			break;
        	case PROP_HIDEBUTTONS_ENABLED:
			basep->hidebuttons_enabled = g_value_get_boolean (value);
			break;
		case PROP_HIDEBUTTON_PIXMAPS_ENABLED:
			basep->hidebutton_pixmaps_enabled = g_value_get_boolean (value);
			break;
		default:
               		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
			break;
	}
}

static void
basep_widget_realize (GtkWidget *w)
{
	BasePWidget *basep = BASEP_WIDGET (w);
	BasePPosClass *klass;

	g_return_if_fail (BASEP_IS_WIDGET (basep));

	gtk_window_set_wmclass (GTK_WINDOW (basep),
				"panel_window", "Panel");

	GTK_WIDGET_CLASS (basep_widget_parent_class)->realize (w);

	basep_widget_update_winhints (basep);
	xstuff_set_no_group_and_no_input (w->window);

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

	if (basep->strut_left != 0 ||
	    basep->strut_right != 0 ||
	    basep->strut_top != 0 ||
	    basep->strut_bottom != 0) {
		xstuff_set_wmspec_strut (w->window,
					 basep->strut_left,
					 basep->strut_right,
					 basep->strut_top,
					 basep->strut_bottom);
	}
	
	klass = basep_widget_get_pos_class (basep);
	g_return_if_fail (klass);
	if (klass->realize != NULL)
		klass->realize (w);
}

static void
basep_widget_map (GtkWidget *w)
{
        BasePWidget *basep = BASEP_WIDGET (w);

        g_return_if_fail (BASEP_IS_WIDGET (basep));

        if (GTK_WIDGET_CLASS (basep_widget_parent_class)->map != NULL)
		GTK_WIDGET_CLASS (basep_widget_parent_class)->map (w);

        basep_widget_update_winhints (basep);
}

static void
basep_widget_size_request (GtkWidget *widget,
			   GtkRequisition *requisition)
{
	GtkRequisition chreq;

	BasePWidget *basep = BASEP_WIDGET(widget);
	BasePPosClass *klass = basep_widget_get_pos_class (basep);

	g_assert (klass);

	if (basep->request_cube) {
		requisition->width = requisition->height =
			PANEL_MINIMUM_WIDTH;
		basep->request_cube = FALSE;
		return;
	}

	gtk_widget_size_request (basep->ebox, &chreq);

	/* this typically only does stuff on edge panels */
	if (klass->get_size) {
		int w,h;
		w = chreq.width;
		h = chreq.height;
		klass->get_size(basep, &w, &h);
		chreq.width = w;
		chreq.height = h;
	}

	if (basep->state != BASEP_SHOWN) {
		int w,h;
		PanelOrient hide_orient =
			klass->get_hide_orient (basep);
		w = chreq.width;
		h = chreq.height;
		klass->get_hide_size (basep, hide_orient, &w, &h);
		chreq.width = w;
		chreq.height = h;
	}

	requisition->width = chreq.width;
	requisition->height = chreq.height;
}

static void
basep_widget_size_allocate (GtkWidget *widget,
			    GtkAllocation *allocation)
{
	GtkAllocation challoc;
	GtkRequisition chreq;
	
	BasePWidget *basep = BASEP_WIDGET(widget);
	BasePPosClass *klass = basep_widget_get_pos_class (basep);
	
	/*we actually want to ignore the size_reqeusts since they
	  are sometimes a cube for the flicker prevention*/

#ifdef BASEP_WIDGET_DEBUG
	if (basep->moving)
		g_warning ("size_allocate whilst moving");
#endif

	gtk_widget_size_request (basep->ebox, &chreq);

	if (klass->get_size) {
		int w,h;
		w = chreq.width;
		h = chreq.height;
		klass->get_size(basep, &w, &h);
		chreq.width = w;
		chreq.height = h;
	}
	if (klass->get_pos) {
		int x,y;
		klass->get_pos (basep, &x, &y,
				chreq.width,
				chreq.height);
		allocation->x = x;
		allocation->y = y;
	}

	allocation->width = challoc.width = chreq.width;
	allocation->height = challoc.height = chreq.height;
	challoc.x = challoc.y = 0;

	basep->shown_alloc = *allocation;

	if (basep->state != BASEP_SHOWN) {
		int w,h,x,y;
		PanelOrient hide_orient = 
			klass->get_hide_orient (basep);

		w = allocation->width;
		h = allocation->height;
		klass->get_hide_size (basep, hide_orient, &w, &h);
		allocation->width = w;
		allocation->height = h;

		x = allocation->x;
		y = allocation->y;
		klass->get_hide_pos (basep, hide_orient,
				     &x, &y,
				     basep->shown_alloc.width,
				     basep->shown_alloc.height);
		allocation->x = x;
		allocation->y = y;

		basep_widget_get_position (basep, hide_orient,
					   &x, &y,
					   allocation->width,
					   allocation->height);
		challoc.x = x;
		challoc.y = y;
	}

	if (basep->keep_in_screen) {
		gint16 max;

		max = multiscreen_width (basep->screen) -
			allocation->width +
			multiscreen_x (basep->screen);

		if (allocation->x < multiscreen_x (basep->screen))
			allocation->x = multiscreen_x (basep->screen);
		else if (allocation->x > max)
			allocation->x = max;


		max = multiscreen_height (basep->screen) -
			allocation->height +
			multiscreen_y (basep->screen);

		if (allocation->y < multiscreen_y (basep->screen))
			allocation->y = multiscreen_y (basep->screen);
		else if (allocation->y > max)
			allocation->y = max;
				       
	}
	widget->allocation = *allocation;

	if (GTK_WIDGET_REALIZED(widget)) {
		xstuff_set_pos_size (widget->window,
				     allocation->x, 
				     allocation->y,
				     allocation->width,
				     allocation->height);
	}

	gtk_widget_size_allocate (basep->ebox, &challoc);

	/* FIXME: this should be handled in a nicer way */
	if (BORDER_IS_WIDGET (basep)) {
		int left = 0, right = 0, top = 0, bottom = 0;
		switch (BORDER_POS (basep->pos)->edge) {
		case BORDER_LEFT:
			if (basep->mode == BASEP_AUTO_HIDE)
				left = global_config.minimized_size ;
			else
				left = basep->shown_alloc.width;
                        left += allocation->x;
			break;
		case BORDER_RIGHT:
			if (basep->mode == BASEP_AUTO_HIDE)
				right = global_config.minimized_size ;
			else
				right = basep->shown_alloc.width;
                        right += multiscreen_width (basep->screen) -
                          (allocation->x + allocation->width);
			break;
		case BORDER_TOP:
			if (basep->mode == BASEP_AUTO_HIDE)
				top = global_config.minimized_size ;
			else
				top = basep->shown_alloc.height;
                        top += allocation->y;
			break;
		case BORDER_BOTTOM:
			if (basep->mode == BASEP_AUTO_HIDE)
				bottom = global_config.minimized_size ;
			else
				bottom = basep->shown_alloc.height;
                        bottom += multiscreen_height (basep->screen) -
                          (allocation->y + allocation->height);
			break;
		}
		if (basep->strut_left != left ||
		    basep->strut_right != right ||
		    basep->strut_top != top ||
		    basep->strut_bottom != bottom) {
			basep->strut_left = left;
			basep->strut_right = right;
			basep->strut_top = top;
			basep->strut_bottom = bottom;
			if (GTK_WIDGET_REALIZED (widget)) {
				xstuff_set_wmspec_strut (widget->window,
							 left, right,
							 top, bottom);
			}
		}
	} else if (basep->strut_left != 0 ||
		   basep->strut_right != 0 ||
		   basep->strut_top != 0 ||
		   basep->strut_bottom != 0) {
		/* If not border widget and it seems to be
		 * set to some bad values just whack the
		 * _NET_WM_STRUT */
		basep->strut_left =
			basep->strut_right =
			basep->strut_top =
			basep->strut_bottom = 0;
		if (GTK_WIDGET_REALIZED (widget)) {
			xstuff_delete_property (widget->window,
						"_NET_WM_STRUT");
		}
	}
}

static void
basep_widget_move_focus_out (BasePWidget *basep)
{
	if (DRAWER_IS_WIDGET (basep)) {
		Drawer *drawer = g_object_get_data (G_OBJECT (basep),
               		                             DRAWER_PANEL_KEY);
		PanelWidget *panel = PANEL_WIDGET (drawer->button->parent);
		GtkWidget *parent = panel->panel_parent;

		drawer_widget_close_drawer (DRAWER_WIDGET (basep), parent);
		drawer->moving_focus = TRUE;
		gtk_window_present (GTK_WINDOW (parent));
		gtk_widget_grab_focus (drawer->button);
	} else {
		panel_widget_focus (PANEL_WIDGET (basep->panel));
	}
}

static void
basep_widget_mode_change (BasePWidget *basep, BasePMode old_mode)
{
	if (BORDER_IS_WIDGET (basep))
		basep_border_queue_recalc (basep->screen);
}

static void
set_tip (GtkWidget *widget, gboolean showing) 
{
	if (showing) {
		gtk_tooltips_set_tip (panel_tooltips, widget,
		      _("Hide this panel"), NULL);
	} else {
		gtk_tooltips_set_tip (panel_tooltips, widget,
		      _("Show this panel"), NULL);
	}
}

static void
setup_hidebuttons (BasePWidget *basep)
{
	/*
	 * If the state is SHOWN or HIDDEN_LEFT or HIDDEN_RIGHT
	 * we update the tooltips on the buttons, and sensitivity.  Offscreen
	 * stuff is insensitive.
	 */
	if (basep->state == BASEP_SHOWN) {
		gtk_widget_set_sensitive (basep->panel, TRUE);
		gtk_widget_set_sensitive (basep->hidebutton_w, TRUE);
		gtk_widget_set_sensitive (basep->hidebutton_n, TRUE);
		gtk_widget_set_sensitive (basep->hidebutton_s, TRUE);
		gtk_widget_set_sensitive (basep->hidebutton_e, TRUE);

		set_tip (basep->hidebutton_w, TRUE);
		set_tip (basep->hidebutton_n, TRUE);
		set_tip (basep->hidebutton_s, TRUE);
		set_tip (basep->hidebutton_e, TRUE);

		/* move focus nicely in case orientation changed on us */
		if (PANEL_WIDGET (basep->panel)->orient ==
		    GTK_ORIENTATION_VERTICAL) {
			if (GTK_WINDOW (basep)->focus_widget == basep->hidebutton_e)
				gtk_window_set_focus (GTK_WINDOW (basep),
						      basep->hidebutton_s);
			else if (GTK_WINDOW (basep)->focus_widget == basep->hidebutton_w)
				gtk_window_set_focus (GTK_WINDOW (basep),
						      basep->hidebutton_n);
		} else {
			if (GTK_WINDOW (basep)->focus_widget == basep->hidebutton_n)
				gtk_window_set_focus (GTK_WINDOW (basep),
						      basep->hidebutton_w);
			else if (GTK_WINDOW (basep)->focus_widget == basep->hidebutton_s)
				gtk_window_set_focus (GTK_WINDOW (basep),
						      basep->hidebutton_e);
		}
	} else if (basep->state == BASEP_HIDDEN_LEFT) {
		GtkWidget *hb;
		gboolean set_focus = FALSE;

		if (PANEL_WIDGET (basep->panel)->orient ==
		    GTK_ORIENTATION_VERTICAL) {
			hb = basep->hidebutton_s;
			if (GTK_WINDOW (basep)->focus_widget == basep->hidebutton_n ||
			    GTK_WINDOW (basep)->focus_widget == basep->hidebutton_e ||
			    GTK_WINDOW (basep)->focus_widget == basep->hidebutton_w)
				set_focus = TRUE;
		} else {
			hb = basep->hidebutton_e;
			if (GTK_WINDOW (basep)->focus_widget == basep->hidebutton_w ||
			    GTK_WINDOW (basep)->focus_widget == basep->hidebutton_n ||
			    GTK_WINDOW (basep)->focus_widget == basep->hidebutton_s)
				set_focus = TRUE;
		}


		gtk_widget_set_sensitive (basep->panel, FALSE);

		gtk_widget_set_sensitive (basep->hidebutton_w, FALSE);
		gtk_widget_set_sensitive (basep->hidebutton_n, FALSE);
		gtk_widget_set_sensitive (basep->hidebutton_s, FALSE);
		gtk_widget_set_sensitive (basep->hidebutton_e, FALSE);

		set_tip (hb, FALSE);
		gtk_widget_set_sensitive (hb, TRUE);
		if (set_focus)
			gtk_window_set_focus (GTK_WINDOW (basep), hb);
	} else if (basep->state == BASEP_HIDDEN_RIGHT) {
		GtkWidget *hb;
		gboolean set_focus = FALSE;

		if (PANEL_WIDGET (basep->panel)->orient ==
		    GTK_ORIENTATION_VERTICAL) {
			hb = basep->hidebutton_n;
			if (GTK_WINDOW (basep)->focus_widget == basep->hidebutton_s ||
			    GTK_WINDOW (basep)->focus_widget == basep->hidebutton_e ||
			    GTK_WINDOW (basep)->focus_widget == basep->hidebutton_w)
				set_focus = TRUE;
		} else {
			hb = basep->hidebutton_w;
			if (GTK_WINDOW (basep)->focus_widget == basep->hidebutton_e ||
			    GTK_WINDOW (basep)->focus_widget == basep->hidebutton_n ||
			    GTK_WINDOW (basep)->focus_widget == basep->hidebutton_s)
				set_focus = TRUE;
		}

		gtk_widget_set_sensitive (basep->panel, FALSE);

		gtk_widget_set_sensitive (basep->hidebutton_w, FALSE);
		gtk_widget_set_sensitive (basep->hidebutton_n, FALSE);
		gtk_widget_set_sensitive (basep->hidebutton_s, FALSE);
		gtk_widget_set_sensitive (basep->hidebutton_e, FALSE);

		set_tip (hb, FALSE);
		gtk_widget_set_sensitive (hb, TRUE);
		if (set_focus)
			gtk_window_set_focus (GTK_WINDOW (basep), hb);
	}
}


static void
basep_widget_state_change (BasePWidget *basep, BasePState old_state)
{
	if (BORDER_IS_WIDGET (basep))
		basep_border_queue_recalc (basep->screen);

	setup_hidebuttons (basep);
}

static void
basep_widget_real_screen_change (BasePWidget *basep, int old_screen)
{
	basep_border_queue_recalc (old_screen);

	/* this will queue border recalc in the new screen */
	gtk_widget_queue_resize (GTK_WIDGET (basep));
	panels_to_sync = TRUE;

	update_config_screen (basep);
}

/* pos core */

static BasePPosClass *
basep_widget_get_pos_class (BasePWidget *basep) {
	BasePPosClass *klass;

	g_return_val_if_fail (BASEP_IS_WIDGET(basep), NULL);
	g_return_val_if_fail (BASEP_IS_POS(basep->pos), NULL);

	klass = BASEP_POS_GET_CLASS(basep);

	g_return_val_if_fail (BASEP_IS_POS_CLASS (klass), NULL);

	return klass;
}

GType									\
basep_pos_get_type (void)					
{
	static GType object_type = 0;
	if (object_type == 0) {
		static const GTypeInfo object_info = {
		    sizeof (BasePPosClass),
		    (GBaseInitFunc)         NULL,
		    (GBaseFinalizeFunc)     NULL,
		    (GClassInitFunc)        basep_pos_class_init,
		    NULL,                   /* class_finalize */
		    NULL,                   /* class_data */
		    sizeof (BasePPos),
		    0,                      /* n_preallocs */
		    (GInstanceInitFunc)     basep_pos_instance_init 
		};
		object_type = g_type_register_static
		    (G_TYPE_OBJECT, "BasePPos", &object_info, 0);
		basep_pos_parent_class = g_type_class_ref (G_TYPE_OBJECT);
	}
	return object_type;
}

static void
basep_pos_get_hide_size (BasePWidget *basep, 
			 PanelOrient hide_orient,
			 int *w, int *h)
{

#ifdef BASEP_WIDGET_DEBUG
	printf ("basep_pos_get_hide_size %d\n", global_config.minimized_size);
#endif
	switch (hide_orient) {
	case PANEL_ORIENT_UP:
	case PANEL_ORIENT_DOWN:
		*h = (basep->state == BASEP_AUTO_HIDDEN)
			? global_config.minimized_size
			: get_requisition_height (basep->hidebutton_n);
		break;
	case PANEL_ORIENT_RIGHT:
	case PANEL_ORIENT_LEFT:
		*w = (basep->state == BASEP_AUTO_HIDDEN)
			? global_config.minimized_size
			: get_requisition_width (basep->hidebutton_e);
		break;
	}
	*w = MAX (*w, 1);
	*h = MAX (*h, 1);
}

static void
basep_pos_get_hide_pos (BasePWidget *basep,
			PanelOrient hide_orient,
			int *x, int *y,
			int w, int h)
{
	switch (hide_orient) {
	case PANEL_ORIENT_UP:
	case PANEL_ORIENT_LEFT:
		break;
	case PANEL_ORIENT_RIGHT:
		*x += w - ((basep->state == BASEP_AUTO_HIDDEN)
			   ? global_config.minimized_size 
			   : get_requisition_width (basep->hidebutton_w));
		break;
	case PANEL_ORIENT_DOWN:
		*y += h - ((basep->state == BASEP_AUTO_HIDDEN)
			   ? global_config.minimized_size
			   : get_requisition_height (basep->hidebutton_s));
		break;
	}
}
		
static void
basep_pos_class_init (BasePPosClass *klass)
{
	/*GtkObjectClass *object_class = GTK_OBJECT_CLASS(klass);*/

	basep_pos_parent_class = g_type_class_ref (gtk_object_get_type ());

	klass->get_hide_size = basep_pos_get_hide_size;
	klass->get_hide_pos = basep_pos_get_hide_pos;
}	

/* nothing to see here... */
static void
basep_pos_instance_init (BasePPos *pos)
{
	return;
}

static gboolean
basep_leave_notify (GtkWidget *widget,
		    GdkEventCrossing *event)
{
	BasePWidget *basep = BASEP_WIDGET (widget);

#ifdef BASEP_WIDGET_DEBUG
	if (basep->moving)
		g_warning ("moving in leave_notify");

	if (basep->leave_notify_timer_tag != 0)
		g_warning ("timeout already queued");
#endif
	if (event->detail == GDK_NOTIFY_INFERIOR)
		return FALSE;
	
	basep_widget_queue_autohide (basep);

	if (GTK_WIDGET_CLASS (basep_widget_parent_class)->leave_notify_event)
		return GTK_WIDGET_CLASS (basep_widget_parent_class)->leave_notify_event (widget, event);
	else
		return FALSE;
}

static gboolean
basep_enter_notify (GtkWidget *widget,
		    GdkEventCrossing *event)
{
	BasePWidget *basep = BASEP_WIDGET (widget);

	if (basep->state == BASEP_AUTO_HIDDEN &&
	    event->detail != GDK_NOTIFY_INFERIOR) {

		g_assert (basep->mode == BASEP_AUTO_HIDE);
#ifdef BASEP_WIDGET_DEBUG
		g_print ("detail: %d\n", event->detail);
#endif
		if (basep->leave_notify_timer_tag != 0) {
			g_source_remove (basep->leave_notify_timer_tag);
			basep->leave_notify_timer_tag = 0;
		}

		basep_widget_queue_autoshow (basep);
	}  

	if (global_config.autoraise)
		gdk_window_raise (GTK_WIDGET(basep)->window);

	if (GTK_WIDGET_CLASS (basep_widget_parent_class)->enter_notify_event)
		return GTK_WIDGET_CLASS (basep_widget_parent_class)->enter_notify_event (widget, event);
	else
		return FALSE;
}

void
basep_widget_get_position (BasePWidget *basep, PanelOrient hide_orient,
			   int *x, int *y, int w, int h)
{
	*x = *y = 0;
	switch(hide_orient) {
	case PANEL_ORIENT_UP:
		if(h < basep->shown_alloc.height)
			*y = h - basep->shown_alloc.height;
		break;
	case PANEL_ORIENT_LEFT:
		if(w < basep->shown_alloc.width)
			*x = w - basep->shown_alloc.width;
		break;
	default:
		break;
	}
}

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

	switch(hide_orient) {
	case PANEL_ORIENT_UP:
	case PANEL_ORIENT_LEFT:
		xattributes.win_gravity = SouthEastGravity;
		break;
	case PANEL_ORIENT_DOWN:
	case PANEL_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 n, d, percentage;
	
	if(src == dest)
		return dest;

	n = cur_time-start_time;
	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, PanelOrient hide_orient,
		       int leftover, PanelSpeed animation_step)
{
	GtkWidget *wid;
	int ox,oy,ow,oh;
	int x,y,w,h;
	int dx,dy,dw,dh;
	int diff;
	int step;
	
	g_return_if_fail(BASEP_IS_WIDGET(basep));

	switch (animation_step) {
		case PANEL_SPEED_SLOW:
				step = PANEL_SLOW_STEP_SIZE;
				break;
		case PANEL_SPEED_MEDIUM:
				step = PANEL_MEDIUM_STEP_SIZE;
				break;
		case PANEL_SPEED_FAST:
				step = PANEL_FAST_STEP_SIZE;
				break;
		default:
				step = PANEL_MEDIUM_STEP_SIZE; 
	}

#ifdef BASEP_WIDGET_DEBUG
	g_warning ("do_hiding with step %d", step);
#endif

	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 PANEL_ORIENT_UP:
		diff = h-leftover;
		dx = x;
		dy = y;
		dw = w;
		dh = leftover;
		break;
	case PANEL_ORIENT_DOWN:
		diff = h-leftover;
		dx = x;
		dy = y+h-leftover;
		dw = w;
		dh = leftover;
		break;
	case PANEL_ORIENT_LEFT:
		diff = w-leftover;
		dx = x;
		dy = y;
		dw = leftover;
		dh = h;
		break;
	case PANEL_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(global_config.enable_animations && step != 0) {
		GTimeVal tval;
		long start_secs;
		long start_time;
		long end_time;
		long cur_time;

#ifdef BASEP_WIDGET_DEBUG
	printf ("We are sliding the panel since animations are turned ON\n");
#endif
		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();
			g_usleep (1000);
		}

		xstuff_set_pos_size (wid->window,
				     dx, dy, dw, dh);
		basep_widget_set_ebox_orient(basep, -1);
	}
	
	gtk_widget_queue_resize(wid);
	gtk_widget_queue_draw (basep->table);
}

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

	g_return_if_fail(BASEP_IS_WIDGET(basep));
	
	switch (animation_step) {
		case PANEL_SPEED_MEDIUM:
				step = PANEL_MEDIUM_STEP_SIZE;
				break;
		case PANEL_SPEED_SLOW:
				step = PANEL_SLOW_STEP_SIZE;
				break;
		case PANEL_SPEED_FAST:
				step = PANEL_FAST_STEP_SIZE;
				break;
		default:
				step = PANEL_MEDIUM_STEP_SIZE;
	}

#ifdef BASEP_WIDGET_DEBUG
		g_warning ("do_showing with step %d", step);
#endif

	wid = GTK_WIDGET(basep);
	
	ox = dx = x = basep->shown_alloc.x;
	oy = dy = y = basep->shown_alloc.y;
	dw = basep->shown_alloc.width;
	dh = basep->shown_alloc.height;
			      
	switch(hide_orient) {
	case PANEL_ORIENT_UP:
		ow = w = dw;
		oh = h = leftover;
		diff = dh-leftover;
		break;
	case PANEL_ORIENT_DOWN:
		oy = y + dh - leftover;
		ow = w = dw;
		oh = h = leftover;
		diff = dh-leftover;
		break;
	case PANEL_ORIENT_LEFT:
		ow = w = leftover;
		oh = h = dh;
		diff = dw-leftover;
		break;
	case PANEL_ORIENT_RIGHT:
		ox = x + dw - leftover;
		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(global_config.enable_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_show(wid->window);
		xstuff_set_pos_size (wid->window,
				     ox, oy, ow, oh);

		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*/
			/* FIXME: does it still flicker? */
			if (i++ % 10) {
				/* FIXME: is invalidation needed, and if so it should only be
				 * partial */
				gdk_window_invalidate_rect (basep->panel->window,
							    NULL, TRUE);
				gdk_window_process_updates (basep->panel->window, TRUE);
			} else {
				gdk_window_invalidate_rect (GTK_WIDGET (basep)->window,
							    NULL, TRUE);
				gdk_window_process_updates (GTK_WIDGET (basep)->window, TRUE);
			}
			gdk_flush();
			g_usleep (1000);
		}

		xstuff_set_pos_size (wid->window,
				     dx, dy, dw, dh);

		basep_widget_set_ebox_orient(basep, -1);
	}
	
	gtk_widget_queue_draw (basep->table);
	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);
	if(horizontal)
		gtk_widget_set_size_request (w, -1, PANEL_MINIMUM_WIDTH);
	else
		gtk_widget_set_size_request (w, PANEL_MINIMUM_WIDTH, -1);

	pixmap_name = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_PIXMAP, 
						 pixmaparrow, TRUE, NULL);

	if(pixmap_name) {
		pixmap = gtk_image_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);
	g_object_set_data (G_OBJECT (w), "gnome_disable_sound_events",
			   GINT_TO_POINTER (TRUE));

	set_tip (w, TRUE);

	return w;
}

static void
basep_widget_destroy (GtkObject *o)
{
	BasePWidget *basep = BASEP_WIDGET (o);
        /* check if there's a timeout set, and delete it if 
	 * there was */
	if (basep->leave_notify_timer_tag != 0)
		g_source_remove (basep->leave_notify_timer_tag);
	basep->leave_notify_timer_tag = 0;

	if (basep->pos != NULL) {
		if (BORDER_IS_WIDGET (basep))
			basep_border_queue_recalc (basep->screen);
		g_object_unref (G_OBJECT (basep->pos));
	}
	basep->pos = NULL;

	if (GTK_OBJECT_CLASS (basep_widget_parent_class)->destroy)
		GTK_OBJECT_CLASS (basep_widget_parent_class)->destroy (o);

}	

static void
reparent_button_widgets(GtkWidget *w, gpointer data)
{
	GdkWindow *newwin = data;
	if(BUTTON_IS_WIDGET (w)) {
		GtkButton *button = GTK_BUTTON (w);
		/* we can just reparent them all to 0,0 as the next thing
		 * that will happen is a queue_resize and on size allocate
		 * they will be put into their proper place */
		gdk_window_reparent(button->event_window, newwin, 0, 0);
	}
}

void
basep_widget_redo_window(BasePWidget *basep)
{
	GtkWindow *window;
	GtkWidget *widget;
	GdkWindowAttr attributes;
	gint attributes_mask;
	GdkWindow *oldwin;
	GdkWindow *newwin;
	gboolean comp;

	comp = xstuff_is_compliant_wm();
	if(comp == basep->compliant_wm)
		return;

	window = GTK_WINDOW(basep);
	widget = GTK_WIDGET(basep);

	basep->compliant_wm = comp;
	if(basep->compliant_wm) {
		window->type = GTK_WINDOW_TOPLEVEL;
		attributes.window_type = GDK_WINDOW_TOPLEVEL;
	} else {
		window->type = GTK_WINDOW_POPUP;
		attributes.window_type = GDK_WINDOW_TEMP;
	}

	if(!widget->window)
		return;

	/* this is mostly copied from gtkwindow.c realize method */
	attributes.title = window->title;
	attributes.wmclass_name = window->wmclass_name;
	attributes.wmclass_class = window->wmclass_class;
	attributes.width = widget->allocation.width;
	attributes.height = widget->allocation.height;
	attributes.wclass = GDK_INPUT_OUTPUT;
	attributes.visual = gtk_widget_get_visual (widget);
	attributes.colormap = gtk_widget_get_colormap (widget);
	attributes.event_mask = gtk_widget_get_events (widget);
	attributes.event_mask |= (GDK_EXPOSURE_MASK |
				  GDK_KEY_PRESS_MASK |
				  GDK_ENTER_NOTIFY_MASK |
				  GDK_LEAVE_NOTIFY_MASK |
				  GDK_FOCUS_CHANGE_MASK |
				  GDK_STRUCTURE_MASK);

	attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP;
	attributes_mask |= (window->title ? GDK_WA_TITLE : 0);
	attributes_mask |= (window->wmclass_name ? GDK_WA_WMCLASS : 0);
   
	oldwin = widget->window;

	newwin = gdk_window_new(NULL, &attributes, attributes_mask);
	gdk_window_set_user_data(newwin, window);

	xstuff_set_no_group_and_no_input (newwin);

	/* reparent our main panel window */
	gdk_window_reparent(basep->ebox->window, newwin, 0, 0);
	/* reparent all the base event windows as they are also children of
	 * the basep */
	gtk_container_foreach(GTK_CONTAINER(basep->panel),
			      reparent_button_widgets,
			      newwin);


	widget->window = newwin;

	gdk_window_set_user_data(oldwin, NULL);
	gdk_window_destroy(oldwin);

	widget->style = gtk_style_attach(widget->style, widget->window);
	gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);

	GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);

	gtk_widget_queue_resize(widget);

	basep_widget_update_winhints (basep);

	if (basep->strut_left != 0 ||
	    basep->strut_right != 0 ||
	    basep->strut_top != 0 ||
	    basep->strut_bottom != 0) {
		xstuff_set_wmspec_strut (widget->window,
					 basep->strut_left,
					 basep->strut_right,
					 basep->strut_top,
					 basep->strut_bottom);
	}

	gtk_drag_dest_set (widget, 0, NULL, 0, 0);

	gtk_widget_map(widget);
}


static void
basep_widget_instance_init (BasePWidget *basep)
{
	basep->screen = 0;

	/*if we set the gnomewm hints it will have to be changed to TOPLEVEL*/
	basep->compliant_wm = xstuff_is_compliant_wm();
	if(basep->compliant_wm)
		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;

	/*don't let us close the window*/                                       
	
	g_signal_connect(G_OBJECT(basep),"delete_event",                    
			 G_CALLBACK (gtk_true), NULL);                     

	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);
	
	g_object_set (G_OBJECT (basep),
		      "mode", BASEP_EXPLICIT_HIDE,
		      "state", BASEP_SHOWN,
		      "hidebuttons_enabled", TRUE,
		      "hidebutton_pixmaps_enabled", TRUE,
		      NULL);

	basep->leave_notify_timer_tag = 0;
	basep->autohide_inhibit = FALSE;
	basep->drawers_open = 0;
}

static void
show_hidebutton_pixmap (GtkWidget *hidebutton, gboolean show)
{
	GtkWidget *pixmap = GTK_BIN (hidebutton)->child;

	if (pixmap == NULL)
		return;

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

static void
basep_widget_show_hidebutton_pixmaps (BasePWidget *basep)
{
	gboolean 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);
}

void
basep_widget_update_winhints (BasePWidget *basep)
{
	GtkWidget *w = GTK_WIDGET (basep);

	gtk_window_set_decorated (GTK_WINDOW (w), FALSE);
	gtk_window_stick (GTK_WINDOW (w));

	xstuff_set_wmspec_dock_hints (w->window,
				      (basep->mode == BASEP_AUTO_HIDE));

	/* FIXME: non-compliance should be tested! */

	if (BASEP_POS_GET_CLASS (basep)->update_winhints != NULL)
		BASEP_POS_GET_CLASS (basep)->update_winhints (basep);
	
}

void
basep_update_frame (BasePWidget *basep)
{
	gboolean hide_frame = PANEL_WIDGET (basep->panel)->back_type == PANEL_BACK_PIXMAP;

	if (hide_frame && GTK_WIDGET_VISIBLE (basep->frame)) {
		gtk_widget_show (basep->innerebox);
		gtk_widget_reparent (basep->panel, basep->innerebox);
		gtk_widget_hide (basep->frame);
	} else if (!hide_frame && !GTK_WIDGET_VISIBLE (basep->frame)) {
		gtk_widget_show (basep->frame);
		gtk_widget_reparent (basep->panel, basep->frame);
		gtk_widget_hide (basep->innerebox);
	}
}

static void
basep_back_change (PanelWidget *panel,
		   PanelBackType type,
		   char *pixmap,
		   GdkColor *color,
		   BasePWidget *basep)
{
	basep_update_frame (basep);

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

static void
basep_orient_change (PanelWidget *panel,
		     GtkOrientation orient,
		     BasePWidget *basep)
{
	setup_hidebuttons (basep);
}

static void
basep_style_set (GtkWidget *widget, GtkStyle *previous_style)
{
	BasePWidget *basep;
	PanelWidget *panel;

	g_return_if_fail (BASEP_IS_WIDGET (widget));

	basep = BASEP_WIDGET (widget);

	g_return_if_fail (basep->panel != NULL);
	g_return_if_fail (PANEL_IS_WIDGET (basep->panel));

	panel = PANEL_WIDGET (basep->panel);

	if (GTK_WIDGET_CLASS (basep_widget_parent_class)->style_set)
		GTK_WIDGET_CLASS (basep_widget_parent_class)->style_set (widget, previous_style);

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

static void
basep_widget_north_clicked (GtkWidget *widget, gpointer data)
{
	BasePWidget *basep = data;
	BasePPosClass *klass =
		basep_widget_get_pos_class (basep);

	gtk_widget_set_state (widget, GTK_STATE_NORMAL);
	gtk_widget_queue_draw (widget);

	if (klass && klass->north_clicked)
		klass->north_clicked(basep);
}

static void
basep_widget_south_clicked (GtkWidget *widget, gpointer data)
{
	BasePWidget *basep = data;
	BasePPosClass *klass =
		basep_widget_get_pos_class (basep);

	gtk_widget_set_state (widget, GTK_STATE_NORMAL);
	gtk_widget_queue_draw (widget);
	
	if (klass && klass->south_clicked)
		klass->south_clicked(basep);
}

static void
basep_widget_east_clicked (GtkWidget *widget, gpointer data)
{
	BasePWidget *basep = data;
	BasePPosClass *klass =
		basep_widget_get_pos_class (basep);

	gtk_widget_set_state (widget, GTK_STATE_NORMAL);
	gtk_widget_queue_draw (widget);
	
	if (klass && klass->east_clicked)
		klass->east_clicked(basep);
}

static void
basep_widget_west_clicked (GtkWidget *widget, gpointer data)
{
	BasePWidget *basep = data;
	BasePPosClass *klass =
		basep_widget_get_pos_class (basep);

	gtk_widget_set_state (widget, GTK_STATE_NORMAL);
	gtk_widget_queue_draw (widget);
	
	if (klass && klass->west_clicked)
		klass->west_clicked(basep);
}

GtkWidget*
basep_widget_construct (gchar *panel_id,
			BasePWidget *basep,
			gboolean packed,
			gboolean reverse_arrows,
			int screen,
			GtkOrientation orient,
			int sz,
			BasePMode mode,
			BasePState state,
			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)
{
	BasePPosClass *klass = basep_widget_get_pos_class (basep);
	int x = 0, y = 0;
	char *panel_icon;
	GList *focus_chain = NULL;

	panel_icon = panel_pixmap_discovery ("gnome-panel.png", 
					     FALSE);

	if (panel_icon) {
		gnome_window_icon_set_default_from_file (panel_icon);
		g_free (panel_icon);
	}

	basep->panel = panel_widget_new(panel_id,
					packed,
					orient,
					sz,
					back_type,
					back_pixmap,
					fit_pixmap_bg,
					stretch_pixmap_bg,
					rotate_pixmap_bg,
					back_color);

	g_signal_connect_after (G_OBJECT (basep->panel), "orient_change",
				G_CALLBACK (basep_orient_change),
				basep);

	g_signal_connect_after (G_OBJECT (basep->panel), "back_change",
				G_CALLBACK (basep_back_change),
				basep);

	PANEL_WIDGET(basep->panel)->panel_parent = GTK_WIDGET(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*/
	/*WEST*/
	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);
	g_signal_connect (G_OBJECT (basep->hidebutton_w), "clicked",
			  G_CALLBACK (basep_widget_west_clicked),
			  basep);
	/*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);
	g_signal_connect (G_OBJECT (basep->hidebutton_n), "clicked",
			  G_CALLBACK (basep_widget_north_clicked),
			    basep);
	/*EAST*/
	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);
	g_signal_connect (G_OBJECT (basep->hidebutton_e), "clicked",
			  G_CALLBACK (basep_widget_east_clicked),
			    basep);
	/*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);
	g_signal_connect (G_OBJECT (basep->hidebutton_s), "clicked",
			  G_CALLBACK (basep_widget_south_clicked),
			    basep);

	basep->screen = screen;

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

	/*
	 * Set focus chain so that focus goes initially to panel.
	 */
	focus_chain = g_list_prepend (focus_chain, basep->hidebutton_s);
	focus_chain = g_list_prepend (focus_chain, basep->hidebutton_e);
	focus_chain = g_list_prepend (focus_chain, basep->hidebutton_n);
	focus_chain = g_list_prepend (focus_chain, basep->hidebutton_w);
	focus_chain = g_list_prepend (focus_chain, basep->frame);
	focus_chain = g_list_prepend (focus_chain, basep->innerebox);
	gtk_container_set_focus_chain (GTK_CONTAINER (basep->table), focus_chain);
	g_list_free (focus_chain);

	basep_widget_set_hidebuttons (basep);
	basep_widget_show_hidebutton_pixmaps (basep);

	basep->mode = mode;
	basep->state = state;

	g_object_ref (basep->pos);
	basep->pos->basep = basep;

	if (state == BASEP_AUTO_HIDDEN &&
	    mode != BASEP_AUTO_HIDE)
		basep->state = BASEP_SHOWN;

	/* setup the hide buttons stuff according to state */
	setup_hidebuttons (basep);
	       
	if (klass->get_pos)
		klass->get_pos (basep, &x, &y, 
				PANEL_MINIMUM_WIDTH,
				PANEL_MINIMUM_WIDTH);
	gtk_window_move (GTK_WINDOW (basep), x, y);
	
	return GTK_WIDGET (basep);
}

void
basep_widget_change_params (BasePWidget *basep,
			    int screen,
			    GtkOrientation orient,
			    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)
{
	g_return_if_fail(GTK_WIDGET_REALIZED(GTK_WIDGET(basep)));

	if (PANEL_WIDGET (basep->panel)->orient != orient)
		basep->request_cube = TRUE;
	
	basep->hidebuttons_enabled = hidebuttons_enabled;
	basep->hidebutton_pixmaps_enabled = hidebutton_pixmaps_enabled;

	if (state == BASEP_AUTO_HIDDEN &&
	    mode != BASEP_AUTO_HIDE)
		state = BASEP_SHOWN;

	if (mode != basep->mode) {
		BasePMode old_mode = basep->mode;
		basep->mode = mode;
		if (mode == BASEP_AUTO_HIDE)
			basep_widget_queue_autohide (basep);
		g_signal_emit (G_OBJECT(basep),
			       basep_widget_signals[MODE_CHANGE_SIGNAL],
			       0, old_mode);
	}
	
	if (state != basep->state) {
		BasePState old_state = basep->state;
		basep->state = state;
		if (state != BASEP_AUTO_HIDDEN)
			basep_widget_autoshow (basep);
		g_signal_emit (G_OBJECT(basep),
			       basep_widget_signals[STATE_CHANGE_SIGNAL],
			       0, old_state);
		panels_to_sync = TRUE;
	}

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

	basep_widget_set_hidebuttons (basep);
	basep_widget_show_hidebutton_pixmaps (basep);

	basep_widget_screen_change (basep, screen);

	gtk_widget_queue_resize (GTK_WIDGET (basep));
}

#if FIXME 
gboolean
basep_widget_convert_to (BasePWidget *basep,
			 PanelType type)
{
	BasePPosClass *klass =
		basep_widget_get_pos_class (basep);
	BasePPos *old_pos, *new_pos;
	gint16 x=0, y=0;
	gboolean temp_keep;

	g_return_val_if_fail (BASEP_IS_WIDGET(basep), FALSE);

	g_return_val_if_fail (create_panel_type[type], FALSE);

	basep_widget_get_pos(basep, &x, &y);

	old_pos = basep->pos;
	new_pos = gtk_type_new (create_panel_type[type] ());

	if (!new_pos)
		return FALSE;

	basep->pos = new_pos;
	new_pos->basep = basep;

	g_object_unref (G_OBJECT (old_pos));

	klass = basep_widget_get_pos_class (basep);
	if (klass->pre_convert_hook)
		klass->pre_convert_hook (basep);

	temp_keep = basep->keep_in_screen;
	basep->keep_in_screen = FALSE;
	gtk_widget_set_uposition (GTK_WIDGET (basep), -100, -100);
	gdk_flush ();
	basep_widget_set_pos (basep, -100, -100);
	gdk_flush ();
	g_print ("-------------------------------------\n");
	basep_widget_set_pos (basep, x, y);
	basep->keep_in_screen = temp_keep;
	g_signal_emit (G_OBJECT(basep),
		       basep_widget_signals[TYPE_CHANGE_SIGNAL],
		       0, type);

	/*gtk_widget_queue_resize (GTK_WIDGET (basep));*/
	return TRUE;
}
#endif

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

void
basep_widget_set_hidebuttons (BasePWidget *basep)
{
	BasePPosClass *klass = basep_widget_get_pos_class (basep);
	if (!basep->hidebuttons_enabled) {
		gtk_widget_hide(basep->hidebutton_n);
		gtk_widget_hide(basep->hidebutton_e);
		gtk_widget_hide(basep->hidebutton_w);
		gtk_widget_hide(basep->hidebutton_s);	

		/* if we removed hidebuttons we need to show ourselves,
		 * except for the drawers case that is */
		if ((basep->state == BASEP_HIDDEN_LEFT ||
		     basep->state == BASEP_HIDDEN_RIGHT) &&
		    ! DRAWER_IS_WIDGET (basep))
			basep_widget_explicit_show (basep);
	} else {
		g_return_if_fail (klass && klass->set_hidebuttons);
		klass->set_hidebuttons(basep);
	}
}

/* FIXME: perhaps we could get rid of the MOVING state, it's kind of
 * useless, isn't it? */
void
basep_widget_explicit_hide (BasePWidget *basep, BasePState state)
{
	BasePState old_state;

	g_assert ( (state == BASEP_HIDDEN_RIGHT) ||
		   (state == BASEP_HIDDEN_LEFT) );

	if((basep->state != BASEP_SHOWN))
		return;

	if (basep->moving) {
#ifdef BASEP_WIDGET_DEBUG
		g_warning ("explicit_hide whilst moving");
#endif
		return;
	}

	basep->moving = TRUE;

	old_state = basep->state;
	basep->state = state;

	g_signal_emit (G_OBJECT (basep),
		       basep_widget_signals[STATE_CHANGE_SIGNAL],
		       0, old_state);
	panels_to_sync = TRUE;

	/* if the app did any updating of the interface, flush that for us*/
	gdk_flush();

	if (GTK_WIDGET_REALIZED(GTK_WIDGET(basep))) {
		BasePPosClass *klass = basep_widget_get_pos_class (basep);
		PanelOrient hide_orient;
		int w, h, size;

		hide_orient = klass->get_hide_orient (basep);
		basep_widget_get_size (basep, &w, &h);
		klass->get_hide_size (basep,
				      hide_orient,
				      &w, &h);

		size = (hide_orient == PANEL_ORIENT_UP ||
			hide_orient == PANEL_ORIENT_DOWN) ?
			h : w;
		
		basep_widget_update_winhints (basep);
		basep_widget_do_hiding (basep, hide_orient,
					size, 
					global_config.animation_speed);
	}

	/*
	 * If the button being hidden has focus move it to the button which
	 * remains shown.
	 */
	if (state == BASEP_HIDDEN_RIGHT) {
		if (gtk_widget_is_focus (basep->hidebutton_e))
			gtk_widget_grab_focus (basep->hidebutton_w);
		else if (gtk_widget_is_focus (basep->hidebutton_s))
			gtk_widget_grab_focus (basep->hidebutton_n);
	} else if (state == BASEP_HIDDEN_LEFT) {
		if (gtk_widget_is_focus (basep->hidebutton_w))
			gtk_widget_grab_focus (basep->hidebutton_e);
		else if (gtk_widget_is_focus (basep->hidebutton_n))
			gtk_widget_grab_focus (basep->hidebutton_s);
	}

	basep->moving = FALSE;

	basep_widget_update_winhints (basep);
}

void
basep_widget_explicit_show (BasePWidget *basep)
{
	BasePState old_state;

	if ( (basep->state != BASEP_HIDDEN_LEFT &&
	      basep->state != BASEP_HIDDEN_RIGHT))
		return;
 
	if (basep->moving) {
#ifdef BASEP_WIDGET_DEBUG
		g_warning ("explicit_show whilst moving");
#endif
		return;
	}

	basep->moving = TRUE;

	if (GTK_WIDGET_REALIZED(GTK_WIDGET(basep))) {
		BasePPosClass *klass = basep_widget_get_pos_class (basep);
		PanelOrient hide_orient;
		int w, h, size;

		hide_orient = klass->get_hide_orient (basep);
		basep_widget_get_size (basep, &w, &h);
		klass->get_hide_size (basep,
				      hide_orient,
				      &w, &h);

		size = (hide_orient == PANEL_ORIENT_UP ||
			hide_orient == PANEL_ORIENT_DOWN) ?
			h : w;

		basep_widget_update_winhints (basep);
		basep_widget_do_showing (basep, hide_orient,
					 size, 
					 global_config.animation_speed);
					 
	}

	basep->moving = FALSE;
	old_state = basep->state;
	basep->state = BASEP_SHOWN;
	basep_widget_update_winhints (basep);

	g_signal_emit (G_OBJECT(basep),
		       basep_widget_signals[STATE_CHANGE_SIGNAL],
		       0, old_state);
	panels_to_sync = TRUE;
}

gboolean
basep_widget_autoshow (gpointer data)
{
	BasePState old_state;
	BasePWidget *basep = data;

	g_return_val_if_fail (BASEP_IS_WIDGET(basep), FALSE);

	if (basep->moving) {
#ifdef BASEP_WIDGET_DEBUG
		g_warning ("autoshow whilst moving");
#endif
		return TRUE;
	}
	
	if ( (basep->mode != BASEP_AUTO_HIDE) ||
	     (basep->state != BASEP_AUTO_HIDDEN))
		return TRUE;

	basep->moving = TRUE;

	if (GTK_WIDGET_REALIZED(basep)) {
		BasePPosClass *klass = basep_widget_get_pos_class (basep);
		PanelOrient hide_orient;
		int w, h, size;

		hide_orient = klass->get_hide_orient (basep);
		basep_widget_get_size (basep, &w, &h);
		klass->get_hide_size (basep, 
				      hide_orient,
				      &w, &h);

		size = (hide_orient == PANEL_ORIENT_UP ||
			hide_orient == PANEL_ORIENT_DOWN) ?
			h : w;

		basep_widget_update_winhints (basep);
		basep_widget_do_showing (basep,
					 hide_orient,
					 size,
					 global_config.animation_speed);
	}
	
	basep->moving = FALSE;

	old_state = basep->state;
	basep->state = BASEP_SHOWN;
	basep_widget_update_winhints (basep);

	g_signal_emit (G_OBJECT(basep),
		       basep_widget_signals[STATE_CHANGE_SIGNAL],
		       0, old_state);

	basep->enter_notify_timer_tag = 0;
	return FALSE;
}

/* FIXME - there's a problem if the show and hide delays are set to 0 */

void
basep_widget_queue_autoshow (BasePWidget *basep)
{
        /* check if there's already a timeout set, and delete it if 
         * there was */

	if (basep->moving) {
#ifdef BASEP_WIDGET_DEBUG
		g_print ("basep_widget_queue_autoshow: return 2");
#endif
		return; 
	}

        if (basep->leave_notify_timer_tag != 0) {
                g_source_remove (basep->leave_notify_timer_tag);
                basep->leave_notify_timer_tag = 0;
	}

        if (basep->enter_notify_timer_tag != 0) {
                g_source_remove (basep->enter_notify_timer_tag);
#ifdef BASEP_WIDGET_DEBUG
		g_print ("basep_widget_queue_autoshow: <timeout removed>\n");
#endif
	}

        if ((basep->mode != BASEP_AUTO_HIDE) ||
            (basep->state == BASEP_SHOWN)) {
#ifdef BASEP_WIDGET_DEBUG
		g_print ("basep_widget_queue_autoshow: return 1\n");
#endif
                return;
	}


	if (global_config.hide_delay == 0) {
		/* set up our idle for popup. */
		basep->enter_notify_timer_tag =
			g_idle_add (basep_widget_autoshow, basep);
	} else {
		/* set up our delay for popup. */
		basep->enter_notify_timer_tag =
			g_timeout_add (global_config.show_delay,
				       basep_widget_autoshow, basep);
	} 
}

gboolean
basep_widget_autohide (gpointer data)
{
	BasePState old_state;
	BasePWidget *basep = data;

	g_return_val_if_fail (BASEP_IS_WIDGET(basep), TRUE);

	if (basep->autohide_inhibit)
		return TRUE;
	
	if (basep->moving) {
#ifdef BASEP_WIDGET_DEBUG
		g_warning ("autohide whilst moving");
#endif
		return TRUE;
	}

	if ( (basep->state != BASEP_SHOWN) ||
	     (basep->mode != BASEP_AUTO_HIDE) ||
	     (panel_widget_is_cursor(PANEL_WIDGET(basep->panel), 0)) ) {
		return TRUE;
	}
	
	if (panel_applet_in_drag || basep->drawers_open>0)
		return TRUE;

	if (!gdk_pointer_is_grabbed ()) {
		if (gdk_pointer_grab (gdk_get_default_root_window (), FALSE, 
				      0, NULL, NULL, GDK_CURRENT_TIME)
		    != GrabSuccess) {
			return TRUE;
		} else {
			gdk_pointer_ungrab (GDK_CURRENT_TIME);
		}
	}

	basep->moving = TRUE;
	old_state = basep->state;
	basep->state = BASEP_AUTO_HIDDEN;

	g_signal_emit(G_OBJECT(basep),
		      basep_widget_signals[STATE_CHANGE_SIGNAL],
		      0, old_state);

	/* if the app did any updating of the interface, flush that for us*/
	gdk_flush();

	if (GTK_WIDGET_REALIZED(basep)) {
		BasePPosClass *klass = basep_widget_get_pos_class (basep);
		PanelOrient hide_orient;
		int w, h, size;

		hide_orient = klass->get_hide_orient (basep);
		basep_widget_get_size (basep, &w, &h);
		klass->get_hide_size (basep, 
				      hide_orient,
				      &w, &h);
		size =  (hide_orient == PANEL_ORIENT_UP ||
			 hide_orient == PANEL_ORIENT_DOWN) 
			? h : w;

		basep_widget_update_winhints (basep);
		basep_widget_do_hiding (basep,
					hide_orient,
					size,
					global_config.animation_speed);
	}

	basep->moving = FALSE;

	basep_widget_update_winhints (basep);

	basep->leave_notify_timer_tag = 0;
	return FALSE;
}

void
basep_widget_queue_autohide(BasePWidget *basep)
{
        /* check if there's already a timeout set, and delete it if 
         * there was */
	if (basep->moving) {
#ifdef BASEP_WIDGET_DEBUG
		g_print ("basep_widget_queue_autohide: return 2");
#endif
		return; 
	}

        if (basep->enter_notify_timer_tag != 0) {
                g_source_remove (basep->enter_notify_timer_tag);
                basep->enter_notify_timer_tag = 0;
	}

        if (basep->leave_notify_timer_tag != 0) {
                g_source_remove (basep->leave_notify_timer_tag);
#ifdef BASEP_WIDGET_DEBUG
		g_print ("basep_widget_queue_autohide: <timeout removed>\n");
#endif
	}

        if ((basep->mode != BASEP_AUTO_HIDE) ||
            (basep->state != BASEP_SHOWN)) {
#ifdef BASEP_WIDGET_DEBUG
		g_print ("basep_widget_queue_autohide: return 1\n");
#endif
                return;
	}
                
	/* set up our delay for popup. */
	if (global_config.hide_delay == 0) {
		basep->leave_notify_timer_tag =
			g_idle_add (basep_widget_autohide, basep);
	} else {
		basep->leave_notify_timer_tag =
			g_timeout_add (global_config.hide_delay,
				       basep_widget_autohide, basep);
	}
}

void
basep_widget_get_menu_pos (BasePWidget *basep,
			   GtkWidget *menu,
			   int *x, int *y,
			   int wx, int wy,
			   int ww, int wh)
{
	GtkRequisition mreq;
	BasePPosClass *klass = 
		basep_widget_get_pos_class (basep);
	g_return_if_fail (klass && klass->get_menu_pos);

	gtk_widget_get_child_requisition(menu, &mreq);

#ifdef BASEP_WIDGET_DEBUG
	g_print ("basep_widget_get_menu_pos: menu req: w = %d, h = %d\n", 
		 mreq.width, mreq.height);
#endif

	klass->get_menu_pos (basep, menu, &mreq,
			     x, y, wx, wy, 
			     ww, wh);

#ifdef BASEP_WIDGET_DEBUG
	g_print ("basep_widget_get_menu_pos: x = %d, y = %d\n", *x, *y);
#endif

	if (*x + mreq.width >
	    multiscreen_x (basep->screen) + multiscreen_width (basep->screen))
		*x = multiscreen_x (basep->screen) + 
			multiscreen_width (basep->screen) - mreq.width;
	if (*x < multiscreen_x (basep->screen))
		*x = multiscreen_x (basep->screen);

	if (*y + mreq.height >
	    multiscreen_y (basep->screen) + multiscreen_height (basep->screen))
		*y = multiscreen_y (basep->screen) + 
			multiscreen_height (basep->screen) - mreq.height;
	if (*y < multiscreen_y (basep->screen))
		*y = multiscreen_y (basep->screen);
}

PanelOrient
basep_widget_get_applet_orient (BasePWidget *basep)
{
	BasePPosClass *klass = 
		basep_widget_get_pos_class (basep);

	g_return_val_if_fail (klass &&
			      klass->get_applet_orient, -1);

	return klass->get_applet_orient(basep);
}

void
basep_widget_get_size (BasePWidget *basep,
		       int *w, int *h)
{
	GtkRequisition req;
	BasePPosClass *klass = basep_widget_get_pos_class (basep);

	gtk_widget_size_request (basep->ebox, &req);
	*w = req.width;
	*h = req.height;
	
	g_return_if_fail (klass);
	if (klass->get_size)
		klass->get_size(basep, w, h);
}

void
basep_widget_get_pos (BasePWidget *basep,
		      int *x, int *y)
{
	int w, h;
	BasePPosClass *klass = 
		basep_widget_get_pos_class (basep);

	g_return_if_fail (klass && klass->get_pos);

	basep_widget_get_size (basep, &w, &h);
	klass->get_pos(basep, x, y, w, h);
}

void
basep_widget_init_offsets (BasePWidget *basep)
{
	g_return_if_fail (BASEP_IS_WIDGET (basep));

	if (GTK_WIDGET (basep)->window != NULL) {
		int x, y;
		gdk_window_get_pointer (GTK_WIDGET (basep)->window,
					&x, &y, NULL);
		basep->offset_x = x;
		basep->offset_y = y;
	} else {
		basep->offset_x = GTK_WIDGET (basep)->requisition.width / 2;
		basep->offset_y = GTK_WIDGET (basep)->requisition.height / 2;
	}
}

void
basep_widget_set_pos (BasePWidget *basep,
		      int x, int y)
{
	int w, h;
	int newscreen;
	gboolean force = FALSE;
	BasePPosClass *klass = 
		basep_widget_get_pos_class (basep);

	g_return_if_fail (klass && klass->set_pos);

	/* first take care of switching screens */
	newscreen = multiscreen_screen_from_pos (x, y);
	if (newscreen >= 0 &&
	    newscreen != basep->screen) {
		force = TRUE;
		basep_widget_screen_change (basep, newscreen);
	}

	basep_widget_get_size (basep, &w, &h);
	klass->set_pos (basep, x, y, w, h, force);
}

void
basep_widget_pre_convert_hook (BasePWidget *basep)
{
	BasePPosClass  *klass = basep_widget_get_pos_class (basep);
	g_return_if_fail (klass && klass->pre_convert_hook);

	klass->pre_convert_hook (basep);
}

void
basep_widget_screen_change (BasePWidget *basep, int screen)
{
	int old_screen;

	g_return_if_fail (BASEP_IS_WIDGET (basep));
	g_return_if_fail (screen >= 0);

	if (basep->screen == screen)
		return;

	old_screen = basep->screen;
	basep->screen = screen;

	g_signal_emit (G_OBJECT (basep),
		       basep_widget_signals[SCREEN_CHANGE_SIGNAL],
		       0, old_screen);
}

/*****
 * Collision avoidance stuff
 *****/
/* FIXME: needs to be per screen! */
typedef struct {
	int left;
	int center;
	int right;
} Border;

typedef struct {
	int screen;
	Border borders[4];
	int left;
	int right;
	int top;
	int bottom;
} ScreenBorders;

static GList *border_list = NULL;

static ScreenBorders *
get_borders (int screen)
{
	GList *li;
	ScreenBorders *sb;

	for (li = border_list; li != NULL; li = li->next) {
		sb = li->data;
		if (sb->screen == screen)
			return sb;
	}

	sb = g_new0 (ScreenBorders, 1);
	sb->screen = screen;
	border_list = g_list_prepend (border_list, sb);

	return sb;
}

static void
basep_calculate_borders (int screen)
{
	GSList *li;
	ScreenBorders *sb;

	sb = get_borders (screen);

	for (li = panel_list; li != NULL; li = li->next) {
		PanelData *pd = li->data;
		BasePWidget *basep;
		GtkRequisition chreq;
		BorderEdge edge;

		g_assert (pd != NULL);

		if ( ! EDGE_IS_WIDGET (pd->panel) &&
		     ! ALIGNED_IS_WIDGET (pd->panel))
			continue;

		basep = BASEP_WIDGET (pd->panel);

		if (basep->mode == BASEP_AUTO_HIDE ||
		    basep->screen != screen)
			continue;

		gtk_widget_get_child_requisition (basep->ebox, &chreq);

		edge = BORDER_POS (basep->pos)->edge;

		if (EDGE_IS_WIDGET (basep)) {
			BasePState state = basep->state;
			if (PANEL_WIDGET (basep->panel)->orient ==
			    GTK_ORIENTATION_VERTICAL) {
				if (sb->borders[edge].left < chreq.width &&
				    state != BASEP_HIDDEN_RIGHT)
					sb->borders[edge].left = chreq.width;
				if (sb->borders[edge].center < chreq.width &&
				    state != BASEP_HIDDEN_RIGHT &&
				    state != BASEP_HIDDEN_LEFT)
					sb->borders[edge].center = chreq.width;
				if (sb->borders[edge].right < chreq.width &&
				    state != BASEP_HIDDEN_LEFT)
					sb->borders[edge].right = chreq.width;
			} else {
				if (sb->borders[edge].left < chreq.height &&
				    state != BASEP_HIDDEN_RIGHT)
					sb->borders[edge].left = chreq.height;
				if (sb->borders[edge].center < chreq.height &&
				    state != BASEP_HIDDEN_RIGHT &&
				    state != BASEP_HIDDEN_LEFT)
					sb->borders[edge].center = chreq.height;
				if (sb->borders[edge].right < chreq.height &&
				    state != BASEP_HIDDEN_LEFT)
					sb->borders[edge].right = chreq.height;
			}
		} else /* ALIGNED */ {
			AlignedAlignment align = ALIGNED_POS(basep->pos)->align;
			if (PANEL_WIDGET (basep->panel)->orient ==
			    GTK_ORIENTATION_VERTICAL) {
				if (align == ALIGNED_LEFT &&
				    sb->borders[edge].left < chreq.width)
					sb->borders[edge].left = chreq.width;
				else if (align == ALIGNED_CENTER &&
					 sb->borders[edge].center < chreq.width)
					sb->borders[edge].center = chreq.width;
				else if (align == ALIGNED_RIGHT &&
					 sb->borders[edge].right < chreq.width)
					sb->borders[edge].right = chreq.width;
			} else {
				if (align == ALIGNED_LEFT &&
				    sb->borders[edge].left < chreq.height)
					sb->borders[edge].left = chreq.height;
				else if (align == ALIGNED_CENTER &&
					 sb->borders[edge].center < chreq.height)
					sb->borders[edge].center = chreq.height;
				else if (align == ALIGNED_RIGHT &&
					 sb->borders[edge].right < chreq.height)
					sb->borders[edge].right = chreq.height;
			}
		}
	}
}

static int
border_max (ScreenBorders *sb, BorderEdge edge)
{
	return MAX (sb->borders[edge].center,
		    MAX (sb->borders[edge].left, sb->borders[edge].right));
}

void
basep_border_recalc (int screen)
{
	int i;
	GSList *li;
	ScreenBorders *sb;
	ScreenBorders old;

	sb = get_borders (screen);

	memcpy (&old, sb, sizeof (ScreenBorders));

	for (i = 0; i < 4; i++) {
		sb->borders[i].left = 0;
		sb->borders[i].center = 0;
		sb->borders[i].right = 0;
	}

	basep_calculate_borders (screen);

	sb->left = border_max (sb, BORDER_LEFT);
	sb->right = border_max (sb, BORDER_RIGHT);
	sb->top = border_max (sb, BORDER_TOP) +
		foobar_widget_get_height (screen);
	sb->bottom = border_max (sb, BORDER_BOTTOM);

#ifdef BASEP_WIDGET_DEBUG
	g_print ("[basep_border_recalc] SCREEN %d (%d %d %d %d)\n", screen,
		 sb->left, sb->right, sb->top, sb->bottom);
#endif

	if (memcmp (&old, sb, sizeof (ScreenBorders)) != 0) {
		for (li = panel_list; li != NULL; li = li->next) {
			PanelData *pd = li->data;
			GtkWidget *panel;

			g_assert (pd != NULL);

			panel = pd->panel;

			if (BORDER_IS_WIDGET (panel) &&
			    BASEP_WIDGET (panel)->screen == screen) {
				gtk_widget_queue_resize (panel);
			}
		}

		if (sb->left != old.left ||
		    sb->right != old.right ||
		    sb->top != old.top ||
		    sb->bottom != old.bottom)
			xstuff_setup_desktop_area (screen,
						   sb->left,
						   sb->right,
						   sb->top,
						   sb->bottom);
	}
}

static guint queue_recalc_id = 0;
static GList *recalc_list = NULL;

static gboolean
queue_recalc_handler (gpointer data)
{
	GList *list, *li;

	queue_recalc_id = 0;

	list = recalc_list;
	recalc_list = NULL;

	for (li = list; li != NULL; li = li->next) {
		int screen = GPOINTER_TO_INT (li->data);
		basep_border_recalc (screen);
	}

	g_list_free (list);

	return FALSE;
}

void
basep_border_queue_recalc (int screen)
{
	if (!g_list_find (recalc_list, GINT_TO_POINTER (screen)))
		recalc_list = g_list_prepend (recalc_list,
					      GINT_TO_POINTER (screen));

	if (!queue_recalc_id)
		queue_recalc_id = g_idle_add (queue_recalc_handler, NULL);
}

void
basep_border_get (int screen, BorderEdge edge,
		  int *left, int *center, int *right)
{
	ScreenBorders *sb;

	g_assert (screen >=0);
	g_assert (edge >=0 && edge <= 3);

	sb = get_borders (screen);

	if (left != NULL)
		*left = sb->borders[edge].left;
	if (center != NULL)
		*center = sb->borders[edge].center;
	if (right != NULL)
		*right = sb->borders[edge].right;

}
