/* GNOME panel: foobar widGET
 * Copyright 1999,2000 Helix Code, Inc.
 * Copyright 2000 Eazel, Inc.
 *
 * Author: Jacob Berkman
 *
 */

/* since BASEP_IS_WIDGET() is used throughout, it makes life easier if we
 * have a GtkWindow of our own.
 */

#include <config.h>
#include <unistd.h>
#include <string.h>
#include <time.h>

#include <libgnome/libgnome.h>
#include <libgnomeui/gnome-window-icon.h>
#include <gconf/gconf-client.h>
#include <gdk/gdkkeysyms.h>

/* Yes, yes I know, now bugger off ... */
#define WNCK_I_KNOW_THIS_IS_UNSTABLE
#include <libwnck/libwnck.h>

#include "foobar-widget.h"

#include "menu.h"
#include "menu-util.h"
#include "session.h"
#include "panel-widget.h"
#include "xstuff.h"
#include "basep-widget.h"
#include "panel-config-global.h"
#include "panel-util.h"
#include "drawer-widget.h"
#include "gnome-run.h"
#include "multiscreen-stuff.h"
#include "panel-gconf.h"
#include "panel-marshal.h"

#define ICON_SIZE 20

extern GlobalConfig global_config;
extern GSList *panel_list;

#define FORMAT_ITEM_NUM 4
static GtkWidget *format_items[FORMAT_ITEM_NUM] = { NULL };

extern GtkTooltips *panel_tooltips;

static void foobar_widget_class_init	(FoobarWidgetClass *klass);
static void foobar_widget_instance_init (FoobarWidget *foo);
static void foobar_widget_realize	(GtkWidget *w);
static void foobar_widget_destroy	(GtkObject *o);
static void foobar_widget_size_allocate	(GtkWidget *w,
					 GtkAllocation *alloc);
static gboolean foobar_leave_notify	(GtkWidget *widget,
					 GdkEventCrossing *event);
static gboolean foobar_enter_notify	(GtkWidget *widget,
					 GdkEventCrossing *event);
static void foobar_widget_move_focus_out   (FoobarWidget *foo);
static void append_task_menu (FoobarWidget *foo, GtkMenuShell *menu_bar);
static void setup_task_menu (FoobarWidget *foo);

static GList *foobars = NULL;

static GtkWindowClass *foobar_widget_parent_class = NULL;

enum {
	MOVE_FOCUS_OUT_SIGNAL,
	WIDGET_LAST_SIGNAL
};

static guint foobar_widget_signals[WIDGET_LAST_SIGNAL] = { 0 };

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

	if (object_type == 0) {
		static const GTypeInfo object_info = {
                    sizeof (FoobarWidgetClass),
                    (GBaseInitFunc)         NULL,
                    (GBaseFinalizeFunc)     NULL,
                    (GClassInitFunc)        foobar_widget_class_init,
                    NULL,                   /* class_finalize */
                    NULL,                   /* class_data */
                    sizeof (FoobarWidget),
                    0,                      /* n_preallocs */
                    (GInstanceInitFunc)     foobar_widget_instance_init
		};

		object_type = g_type_register_static (GTK_TYPE_WINDOW, "FoobarWidget", &object_info, 0);
        	foobar_widget_parent_class = g_type_class_ref (GTK_TYPE_WINDOW);

	}

	return object_type;
}

static void
foobar_widget_class_init (FoobarWidgetClass *klass)
{
	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
	GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
	GtkBindingSet *binding_set;

	object_class->destroy = foobar_widget_destroy;

	widget_class->realize = foobar_widget_realize;
	widget_class->size_allocate = foobar_widget_size_allocate;
	widget_class->enter_notify_event = foobar_enter_notify;
	widget_class->leave_notify_event = foobar_leave_notify;

	klass->move_focus_out = foobar_widget_move_focus_out;

	gtk_rc_parse_string ("style \"panel-foobar-menubar-style\"\n"
			     "{\n"
			     "GtkMenuBar::shadow-type = none\n"
			     "GtkMenuBar::internal-padding = 0\n"
			     "}\n"
			     "widget \"*.panel-foobar-menubar\" style \"panel-foobar-menubar-style\"");

	foobar_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 (FoobarWidgetClass, move_focus_out),				NULL,
                                NULL,
                                panel_marshal_VOID__VOID,
				G_TYPE_NONE,
				0);

	binding_set = gtk_binding_set_by_class (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 GtkWidget *
pixmap_menu_item_new (const char *text, const char *try_file, gboolean force_image)
{
	GtkWidget *item;
	GtkWidget *label;

	item = gtk_image_menu_item_new ();

	panel_load_menu_image_deferred (item, try_file, NULL /* fallback */,
					force_image);

	if (text) {
		label = gtk_label_new (text);
		gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
		gtk_container_add (GTK_CONTAINER (item), label);
	}

	return item;
}

static gboolean
foobar_leave_notify (GtkWidget *widget,
		     GdkEventCrossing *event)
{
	if (GTK_WIDGET_CLASS (foobar_widget_parent_class)->leave_notify_event)
		GTK_WIDGET_CLASS (foobar_widget_parent_class)->leave_notify_event (widget,
								     event);

	return FALSE;
}

static gboolean
foobar_enter_notify (GtkWidget *widget,
		     GdkEventCrossing *event)
{
	if (GTK_WIDGET_CLASS (foobar_widget_parent_class)->enter_notify_event)
		GTK_WIDGET_CLASS (foobar_widget_parent_class)->enter_notify_event (widget,
								     event);

	if (global_config.autoraise)
		gdk_window_raise (widget->window);

	return FALSE;
}

static void
gnomecal_client (GtkWidget *w, gpointer data)
{
	char *v[4] = { "gnomecal", "--view", NULL, NULL };
	v[2] = data;
	if(gnome_execute_async (g_get_home_dir (), 3, v) < 0)
		panel_error_dialog("cannot_execute_gnome_calendar",
				   _("Cannot execute the gnome calendar,\n"
				     "perhaps it's not installed.\n"
				     "It is in the gnome-pim package."));
}

static void
append_actions_menu (GtkWidget *menu_bar)
{
	GtkWidget *menu, *item;

	menu = panel_menu_new ();

	item = pixmap_menu_item_new (_("Run..."), "gnome-run.png",
				     FALSE /* force_image */);
	gtk_tooltips_set_tip (panel_tooltips, item,
			      _("Run applications, if you know the "
				"correct command to type in"),
			      NULL);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
	g_signal_connect (G_OBJECT (item), "activate",
			  G_CALLBACK (show_run_dialog), 0);
	setup_internal_applet_drag (item, "RUN:NEW");

	/* FIXME: search */
	/* FIXME: shutdown or reboot */

	if (panel_is_program_in_path  ("xscreensaver")) {
		item = pixmap_menu_item_new (_("Lock Display"), 
					     "gnome-lockscreen.png",
					     FALSE /* force_image */);
		gtk_tooltips_set_tip (panel_tooltips, item,
				      _("Protect your computer from "
					"unauthorized use"),
				      NULL);
		gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
		g_signal_connect (G_OBJECT (item), "activate",
				  G_CALLBACK (panel_lock), 0);
		setup_internal_applet_drag(item, "LOCK:NEW");
	}

	item = pixmap_menu_item_new (_("Log Out"), "gnome-term-night.png",
				     FALSE /* force_image */);
	gtk_tooltips_set_tip (panel_tooltips, item,
			      _("Quit from the GNOME desktop"),
			      NULL);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
	g_signal_connect (G_OBJECT (item), "activate",
			  G_CALLBACK (panel_quit), 0);
	setup_internal_applet_drag (item, "LOGOUT:NEW");

	item = gtk_menu_item_new_with_label (_("Actions"));
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), item);

	panel_stretch_events_to_toplevel (item, PANEL_STRETCH_TOP);
}

#if 0
/* sure we won't need this????
 * -George */
static GtkWidget *
append_folder_menu (GtkWidget *menu_bar, const char *label,
		    const char *pixmap, const char *path,
		    gboolean stretch_left,
		    gboolean stretch_top,
		    gboolean stretch_right)
{
	GtkWidget *item, *menu;

	menu = create_fake_menu_at (path,
				    FALSE /* launcher_add */,
				    label /* dir_name */,
				    NULL /* pixmap_name */,
				    FALSE /* title */);

	if (menu == NULL) {
		g_warning (_("menu wasn't created"));
		return NULL;
	}

	if (pixmap != NULL)
		item = pixmap_menu_item_new (label, pixmap,
					     FALSE /* force_image */);
	else
		item = gtk_menu_item_new_with_label (label);
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), item);

	g_signal_connect (G_OBJECT (menu), "show",
			  G_CALLBACK (submenu_to_display),
			  NULL);

	if (stretch_left || stretch_top || stretch_right) {
		PanelStretchFlags flags = PANEL_STRETCH_NONE;

		flags |= stretch_left  ? PANEL_STRETCH_LEFT  : 0;
		flags |= stretch_top   ? PANEL_STRETCH_TOP   : 0;
		flags |= stretch_right ? PANEL_STRETCH_RIGHT : 0;

		panel_stretch_events_to_toplevel (item, flags);
	}
		
	return menu;
}
#endif

static void
append_gnomecal_items (GtkWidget *menu)
{
	GtkWidget *item;
	int i;
	
	const char *cals[] = { 
		N_("Today"),      N_("View the calendar for today."),      "gnome-day.png",   "dayview",
		N_("This Week"),  N_("View the calendar for this week."),  "gnome-week.png",  "weekview",
		N_("This Month"), N_("View the calendar for this month."), "gnome-month.png", "monthview",
		NULL
	};
	
	for (i=0; cals[i]; i+=4) {
		item = pixmap_menu_item_new (_(cals[i]), cals[i+2],
					     FALSE /* force_image */);
		gtk_tooltips_set_tip (panel_tooltips, item,
			      	      _(cals[i+1]),
			      	      NULL);
	
		gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
		g_signal_connect (G_OBJECT (item), "activate",
				  G_CALLBACK (gnomecal_client),
				  (char *)cals[i+3]);
	}
}

static void
update_clock (FoobarWidget *foo)
{
	static int day = 0;
	struct tm *das_tm;
	time_t das_time;
	char hour[256];
	PangoLayout *layout;
	char *str_utf8;
	int width;

	if (foo->clock_label == NULL)
		return;

	time (&das_time);
	das_tm = localtime (&das_time);

	if (das_tm->tm_mday != day) {
		if(strftime(hour, sizeof(hour), _("%A %B %d"), das_tm) == 0) {
			/* according to docs, if the string does not fit, the
			 * contents of hour are undefined, thus just use
			 * ??? */
			strcpy(hour, "???");
		}
		hour[sizeof(hour)-1] = '\0'; /* just for sanity */

		str_utf8 = g_locale_to_utf8 (hour, -1, NULL, NULL, NULL);

		gtk_tooltips_set_tip (panel_tooltips, foo->clock_ebox,
				      sure_string (str_utf8), NULL);
		g_free (str_utf8);

		day = das_tm->tm_mday;
	}

	if(strftime(hour, sizeof(hour), foo->clock_format, das_tm) == 0) {
		/* according to docs, if the string does not fit, the
		 * contents of hour are undefined, thus just use
		 * ??? */
		strcpy(hour, "???");
	}
	hour[sizeof(hour)-1] = '\0'; /* just for sanity */

	str_utf8 = g_locale_to_utf8 (hour, -1, NULL, NULL, NULL);

	if (str_utf8 != NULL) {
		layout = gtk_widget_create_pango_layout (foo->clock_label, str_utf8);
		pango_layout_get_pixel_size (layout, &width, NULL);
		width += 8; /* Padding */
		g_object_unref (G_OBJECT(layout));

		if (width > foo->clock_label->requisition.width)
			gtk_widget_set_size_request (foo->clock_label, width, -1);

		gtk_label_set_text (GTK_LABEL (foo->clock_label), str_utf8);

		g_free (str_utf8);
	}
}

static int
timeout_cb (gpointer data)
{
	FoobarWidget *foo = FOOBAR_WIDGET (data);

	if (foo->clock_label == NULL) {
		foo->clock_timeout = 0;
		return FALSE;
	}

	update_clock (foo);

	return TRUE;
}

static void
set_fooclock_format (GtkWidget *w, const char *format)
{
	if ( ! GTK_CHECK_MENU_ITEM (w)->active)
		return;

	foobar_widget_global_set_clock_format (format);
}

static void
append_format_items (FoobarWidget *foo,
		     GtkWidget    *menu)
{
	char hour[256];
	GSList *group = NULL;
	struct tm *das_tm;
	time_t das_time = 0;
	const char *key;
	char *str_utf8;
	char *s;
	int i;
	const char *formats[FORMAT_ITEM_NUM] = {
		N_("%H:%M"),
		N_("%H:%M:%S"),
		N_("%l:%M %p"),
		N_("%l:%M:%S %p")
	};

	key = panel_gconf_full_key (
			PANEL_GCONF_PANELS,
			panel_gconf_get_profile (),
			PANEL_WIDGET (foo->panel)->unique_id,
			"clock-format");
	s = panel_gconf_get_string (key, _("%I:%M:%S %p"));
	
	for (i = 0; i < FORMAT_ITEM_NUM; i++) {
		das_tm = localtime (&das_time);
		if (strftime (hour, sizeof(hour), _(formats[i]), das_tm) == 0) {
 			/* according to docs, if the string does not fit, the
 		 	 * contents of hour are undefined, thus just use
 		 	 * ??? */
			strcpy(hour, "???");
		}
		hour[sizeof(hour)-1] = '\0'; /* just for sanity */

		str_utf8 = g_locale_to_utf8 (hour, -1, NULL, NULL, NULL);
		format_items[i] = gtk_radio_menu_item_new_with_label (group, sure_string (str_utf8));
		g_object_set_data (G_OBJECT (format_items[i]),
				   "Format", _(formats[i]));

		g_free (str_utf8);

		group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (format_items[i]));
	
		gtk_menu_shell_append (GTK_MENU_SHELL (menu), format_items[i]);	
		g_signal_connect (G_OBJECT (format_items[i]), "activate",
				  G_CALLBACK (set_fooclock_format),
				  _(formats[i]));

		if (!strcmp (sure_string (s), _(formats[i])))
			gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (format_items[i]), TRUE);
	}

	g_free (s);
}

static void
set_time_cb (GtkWidget *menu_item, char *path)
{
	char *v[2] = { path };
	
	if (gnome_execute_async (g_get_home_dir (), 1, v) < 0)
		panel_error_dialog ("could_not_call_time_admin",
				    _("Could not call time-admin\n"
				      "Perhaps time-admin is not installed"));
}

static GtkWidget *
append_clock_menu (FoobarWidget *foo, GtkWidget *menu_bar)
{
	GtkWidget *item, *menu, *menu2;
	gchar *time_admin_path;

	menu = panel_menu_new ();
	append_gnomecal_items (menu);

#if 0 /* put back when evolution can do this */
	item = gtk_image_menu_item_new_with_label (_("Add Appointment..."));
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
#endif

	add_menu_separator (menu);

	/* check for time-admin (part of ximian-setup-tools) */
	time_admin_path = g_find_program_in_path  ("time-admin");
	if (time_admin_path != NULL) {
		item = pixmap_menu_item_new (_("Set Time..."), "gnome-set-time.png",
					     FALSE /* force_image */);
		gtk_tooltips_set_tip (panel_tooltips, item,
			      	      _("Adjust the date and time."),
			      	      NULL);	
		
		gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
		g_signal_connect (G_OBJECT (item), "activate",
				  G_CALLBACK (set_time_cb),
				  time_admin_path);			
	}

	menu2 = panel_menu_new ();
	append_format_items (foo, menu2); 

	item = gtk_image_menu_item_new_with_label (_("Format"));
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu2);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);

	item = gtk_menu_item_new ();

	foo->clock_label = gtk_label_new ("");
	foo->clock_timeout = g_timeout_add (1000, timeout_cb, foo);

	foo->clock_ebox = item;
	gtk_container_add (GTK_CONTAINER (item), foo->clock_label);
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);

	gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), item);

	panel_stretch_events_to_toplevel (item, PANEL_STRETCH_TOP);

	return item;
}

void
foobar_widget_global_set_clock_format (const char *format)
{
	GList *li;

	for (li = foobars; li != NULL; li = li->next) {
		foobar_widget_set_clock_format (FOOBAR_WIDGET (li->data),
						format);
	}
}

/*
 * FIXME: we should be only saving Foobar specific prefs
 */
static void
foobar_save_to_gconf (FoobarWidget *foo)
{
	PanelData *pd;

	pd = g_object_get_data (G_OBJECT (foo), "PanelData");

	if (pd)
		panel_save_to_gconf (pd);
}

void
foobar_widget_set_clock_format (FoobarWidget *foo, const char *clock_format)
{
	int i;

	g_return_if_fail (FOOBAR_IS_WIDGET (foo));

	if (foo->clock_format != NULL)
		g_free (foo->clock_format);
	foo->clock_format = g_strdup (clock_format);

	if(foo->clock_label) {
		time_t das_time;
		struct tm *das_tm;
		char hour[256];
		gchar *str_utf8;
		int width;
		PangoLayout *layout;

		das_time = 0;
		das_tm = localtime (&das_time);
		if (strftime (hour, sizeof(hour), _(clock_format), das_tm) == 0) {
			/* according to docs, if the string does not fit, the
			 * contents of hour are undefined, thus just use
			 * ??? */
			strcpy(hour, "???");
		}
		hour[sizeof(hour)-1] = '\0'; /* just for sanity */

		str_utf8 = g_locale_to_utf8 (hour, -1, NULL, NULL, NULL);
		if (str_utf8 != NULL) {
			layout = gtk_widget_create_pango_layout (foo->clock_label, str_utf8);
			pango_layout_get_pixel_size (layout, &width, NULL);
			width += 8; /* Padding */
			gtk_widget_set_size_request (foo->clock_label, width, -1);
			g_object_unref (G_OBJECT(layout));
		}
		g_free (str_utf8);
	}

	/* update the menu */
	for (i = 0; i < FORMAT_ITEM_NUM; i++) {
		const char *item_format;

		/* sanity (or insanity?) */
		if (format_items[i] == NULL)
			break;

		item_format = g_object_get_data (G_OBJECT (format_items[i]), "Format");
		if (strcmp (sure_string (item_format), sure_string (clock_format)) == 0) {
			if ( ! GTK_CHECK_MENU_ITEM (format_items[i])->active)
				gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (format_items[i]), TRUE);
			break;
		} else if (GTK_CHECK_MENU_ITEM (format_items[i])->active) {
			gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (format_items[i]), FALSE);
		}
	}

	update_clock (foo);

	foobar_save_to_gconf (foo);
}

void
foobar_widget_update_winhints (FoobarWidget *foo)
{
	GtkWidget *w = GTK_WIDGET (foo);

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

	xstuff_set_wmspec_dock_hints (w->window, FALSE /* autohide */);
}

static void
foobar_widget_realize (GtkWidget *w)
{
	gtk_window_set_wmclass (GTK_WINDOW (w),
				"panel_window", "Panel");

	if (GTK_WIDGET_CLASS (foobar_widget_parent_class)->realize)
		GTK_WIDGET_CLASS (foobar_widget_parent_class)->realize (w);

	foobar_widget_update_winhints (FOOBAR_WIDGET (w));
	xstuff_set_no_group_and_no_input (w->window);

	setup_task_menu (FOOBAR_WIDGET (w));

	xstuff_set_wmspec_strut (w->window,
				 0 /* left */,
				 0 /* right */,
				 w->allocation.height /* top */,
				 0 /* bottom */);
}

static void
programs_menu_to_display (GtkWidget *menu)
{
	if (menu_need_reread (menu)) {
		int flags;

		while (GTK_MENU_SHELL (menu)->children)
			gtk_widget_destroy (GTK_MENU_SHELL (menu)->children->data);
		flags = MAIN_MENU_SYSTEM;
		if (got_kde_menus ())
			flags |= MAIN_MENU_KDE_SUB;
		if (got_distro_menus ())
			flags |= MAIN_MENU_DISTRIBUTION_SUB;
		create_root_menu (menu, TRUE, flags, FALSE, FALSE /* run_item */);
	}
}

static void
set_the_task_submenu (FoobarWidget *foo, GtkWidget *item)
{
	foo->task_menu = gtk_menu_new ();
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), foo->task_menu);
	/*g_message ("setting...");*/
	g_object_set_data (G_OBJECT (foo->task_menu),
			   "menu_panel", foo->panel);
	g_signal_connect (G_OBJECT (foo->task_menu), "show",
			  G_CALLBACK (panel_make_sure_menu_within_screen),
			  NULL);
	g_signal_connect (G_OBJECT (foo->task_menu), "show",
			  G_CALLBACK (our_gtk_menu_position),
			  NULL);
}

static void
focus_window (GtkWidget *w, WnckWindow *window)
{
	WnckScreen *screen = wnck_screen_get (0 /* FIXME screen number */);
	WnckWorkspace *wspace = wnck_screen_get_active_workspace (screen);

	if (wspace != NULL)
		wnck_window_move_to_workspace (window, wspace);
	if (wnck_window_is_minimized (window)) 
		wnck_window_unminimize (window);
	wnck_window_activate (window);
}

/* No need to unref, in fact do NOT unref the return */
static GdkPixbuf *
get_default_image (void)
{
	static GdkPixbuf *pixbuf = NULL;
	static gboolean looked   = FALSE;

	if ( ! looked) {
		pixbuf = panel_make_menu_icon ("gnome-tasklist.png",
					       /* evil fallback huh? */
					       "apple-red.png",
					       20 /* size */,
					       NULL /* long_operation */);

		looked = TRUE;
	}

	return pixbuf;
}

static void
add_window (WnckWindow *window, FoobarWidget *foo)
{
	GtkWidget *item, *label;
	char *title = NULL;
	int slen;
	GtkWidget *image = NULL;
	GdkPixbuf *pb;
	const char *name;
	WnckScreen *screen = wnck_screen_get (0 /* FIXME screen number */);
	WnckWorkspace *wspace = wnck_screen_get_active_workspace (screen);

	g_assert (foo->windows != NULL);

	if (wnck_window_is_skip_tasklist (window))
		return;

	name = wnck_window_get_name (window);

	if (name != NULL) {
		slen = strlen (name);
		if (slen > 443)
			title = g_strdup_printf ("%.420s...%s", name, name + slen - 20);
		else
			title = g_strdup (name);
	} else {
		/* Translators: Task with no name, should not really happen, so
		 * this should signal that the panel is confused by this task
		 * (thus question marks) */
		title = g_strdup (_("???"));
	}

	if (wnck_window_is_minimized (window)) {
		char *tmp = title;
		title = g_strdup_printf ("[%s]", title);
		g_free (tmp);
	}
	
	item = gtk_image_menu_item_new ();
	pb = wnck_window_get_mini_icon (window);
	if (pb == NULL)
		pb = get_default_image ();
	if (pb != NULL) {
		double pix_x, pix_y;
		pix_x = gdk_pixbuf_get_width (pb);
		pix_y = gdk_pixbuf_get_height (pb);
		if (pix_x > ICON_SIZE || pix_y > ICON_SIZE) {
			double greatest;
			GdkPixbuf *scaled;

			greatest = pix_x > pix_y ? pix_x : pix_y;
			scaled = gdk_pixbuf_scale_simple (pb,
							  (ICON_SIZE / greatest) * pix_x,
							  (ICON_SIZE / greatest) * pix_y,
							  GDK_INTERP_BILINEAR);
			image = gtk_image_new_from_pixbuf (scaled);
			g_object_unref (G_OBJECT (scaled));
		} else {
			image = gtk_image_new_from_pixbuf (pb);
		}
		gtk_widget_show (image);
		gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
					       GTK_WIDGET (image));
	}

	label = gtk_label_new (title);
	g_free (title);

	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
	gtk_container_add (GTK_CONTAINER (item), label);
	g_hash_table_insert (foo->windows, window, item);
	g_signal_connect (G_OBJECT (item), "activate", 
			  G_CALLBACK (focus_window),
			  window);
	gtk_widget_show_all (item);
	
	if (wspace == wnck_window_get_workspace (window)) {
		gtk_menu_shell_prepend (GTK_MENU_SHELL (foo->task_menu), item);
	} else {
		gtk_menu_shell_append (GTK_MENU_SHELL (foo->task_menu), item);
	}
}

static void
create_task_menu (GtkWidget *w, gpointer data)
{
	FoobarWidget *foo = FOOBAR_WIDGET (data);
	GList *list;
	GtkWidget *separator;
	WnckScreen *screen = wnck_screen_get (0 /* FIXME screen number */);
	GList *windows = wnck_screen_get_windows (screen);

	/* g_message ("creating..."); */
	foo->windows = g_hash_table_new (g_direct_hash, g_direct_equal);

	separator = add_menu_separator (foo->task_menu);

	g_list_foreach (windows, (GFunc)add_window, foo);

	list = g_list_last (GTK_MENU_SHELL (foo->task_menu)->children);

	if (list != NULL &&
	    separator == list->data) {
		/* if the separator is the last item wipe it.
		 * We leave it as the first though */
		gtk_widget_destroy (separator);
	}

	list = g_list_last (GTK_MENU_SHELL (foo->task_menu)->children);
	if (list == NULL) {
		GtkWidget *item;
		item = gtk_image_menu_item_new_with_label (_("No windows open"));
		gtk_widget_set_sensitive (item, FALSE);
		gtk_widget_show_all (item);
	
		gtk_menu_shell_append (GTK_MENU_SHELL (foo->task_menu), item);
	}

	if (GTK_WIDGET_VISIBLE (foo->task_menu))
		our_gtk_menu_position (GTK_MENU (foo->task_menu));

	/* Owen: don't read the next line */
#if 0
	GTK_MENU_SHELL (foo->task_menu)->active = 1;
	our_gtk_menu_position (GTK_MENU (foo->task_menu));
#endif
}

static void
destroy_task_menu (GtkWidget *w, gpointer data)
{
	FoobarWidget *foo = FOOBAR_WIDGET (data);
	/*g_message ("removing...");*/
	gtk_menu_item_remove_submenu (GTK_MENU_ITEM (w));
	g_hash_table_destroy (foo->windows);
	foo->windows = NULL;
	set_the_task_submenu (foo, w);
}

static void
set_das_pixmap (FoobarWidget *foo, WnckWindow *window)
{
	GdkPixbuf *pb;
	if ( ! GTK_WIDGET_REALIZED (foo))
		return;

	if (foo->task_image != NULL)
		gtk_widget_destroy (GTK_WIDGET (foo->task_image));
	foo->task_image = NULL;

	foo->icon_window = window;

	pb = NULL;
	if (window != NULL)
		pb = wnck_window_get_mini_icon (window);
	if (pb == NULL)
		pb = get_default_image ();

	if (pb != NULL) {
		double pix_x, pix_y;
		pix_x = gdk_pixbuf_get_width (pb);
		pix_y = gdk_pixbuf_get_height (pb);
		if (pix_x > ICON_SIZE || pix_y > ICON_SIZE) {
			double greatest;
			GdkPixbuf *scaled;

			greatest = pix_x > pix_y ? pix_x : pix_y;
			scaled = gdk_pixbuf_scale_simple (pb,
							  (ICON_SIZE / greatest) * pix_x,
							  (ICON_SIZE / greatest) * pix_y,
							  GDK_INTERP_BILINEAR);
			foo->task_image = gtk_image_new_from_pixbuf (scaled);
			g_object_unref (G_OBJECT (scaled));
		} else {
			foo->task_image = gtk_image_new_from_pixbuf (pb);
		}
		gtk_widget_show (foo->task_image);

		gtk_container_add (GTK_CONTAINER (foo->task_bin),
				   GTK_WIDGET (foo->task_image));
	}
}

static void
append_task_menu (FoobarWidget *foo, GtkMenuShell *menu_bar)
{
	foo->task_item = gtk_menu_item_new ();
	gtk_widget_show (foo->task_item);

	foo->task_bin = gtk_alignment_new (0.3, 0.5, 0.0, 0.0);
	gtk_widget_set_size_request (foo->task_bin, 25, 20);
	gtk_widget_show (foo->task_bin);
	gtk_container_add (GTK_CONTAINER (foo->task_item), foo->task_bin);

	gtk_menu_shell_append (menu_bar, foo->task_item);

	panel_stretch_events_to_toplevel (
		foo->task_item, PANEL_STRETCH_TOP | PANEL_STRETCH_RIGHT);
}

static void
icon_changed (WnckWindow *window, FoobarWidget *foo)
{
	if (foo->icon_window == window)
		set_das_pixmap (foo, window);
}

static void
bind_window_changes (WnckWindow *window, FoobarWidget *foo)
{
	panel_signal_connect_while_alive (G_OBJECT (window), "icon_changed",
					  G_CALLBACK (icon_changed),
					  foo,
					  G_OBJECT (foo));
	/* XXX: do we care about names changing? */
}

/* focused window changed */
static void
active_window_changed (WnckScreen *screen,
		       FoobarWidget *foo)
{
	WnckWindow *window = wnck_screen_get_active_window (screen);

	/* icon might have changed */
	if (foo->icon_window != window)
		set_das_pixmap (foo, window);
}

/* window added */
static void
window_opened (WnckScreen *screen,
	       WnckWindow *window,
	       FoobarWidget *foo)
{
	if (foo->windows != NULL)
		add_window (window, foo);
	bind_window_changes (window, foo);
}
/* window removed */
static void
window_closed (WnckScreen *screen,
	       WnckWindow *window,
	       FoobarWidget *foo)
{
	if (window == foo->icon_window)
		set_das_pixmap (foo, NULL);
	if (foo->windows != NULL) {
		GtkWidget *item;
		item = g_hash_table_lookup (foo->windows, window);
		if (item != NULL) {
			g_hash_table_remove (foo->windows, window);
			gtk_widget_hide (item);
			gtk_menu_reposition (GTK_MENU (item->parent));
		} else {
			g_warning ("Could not find item for task '%s'",
				   sure_string (wnck_window_get_name (window)));
		}
	}
}

static void
setup_task_menu (FoobarWidget *foo)
{
	GList *windows, *li;
	WnckScreen *screen;
	g_assert (foo->task_item != NULL);

	g_signal_connect (G_OBJECT (foo->task_item), "select",
			  G_CALLBACK (create_task_menu), foo);
	g_signal_connect (G_OBJECT (foo->task_item), "deselect",
			  G_CALLBACK (destroy_task_menu), foo);

	set_the_task_submenu (foo, foo->task_item);

	screen = wnck_screen_get (0 /* FIXME screen number */);

	/* setup the pixmap to the focused task */
	windows = wnck_screen_get_windows (screen);
	for (li = windows; li != NULL; li = li->next) {
		if (wnck_window_is_active (li->data)) {
			set_das_pixmap  (foo, li->data);
			break;
		}
	}

	/* if no focused task found, then just set it to default */
	if (li == NULL)
		set_das_pixmap  (foo, NULL);

	g_list_foreach (windows, (GFunc)bind_window_changes, foo);

	panel_signal_connect_while_alive (G_OBJECT (screen),
					  "active_window_changed",
					  G_CALLBACK (active_window_changed),
					  foo,
					  G_OBJECT (foo));
	panel_signal_connect_while_alive (G_OBJECT (screen),
					  "window_opened",
					  G_CALLBACK (window_opened),
					  foo,
					  G_OBJECT (foo));
	panel_signal_connect_while_alive (G_OBJECT (screen),
					  "window_closed",
					  G_CALLBACK (window_closed),
					  foo,
					  G_OBJECT (foo));

}

static void
foobar_widget_instance_init (FoobarWidget *foo)
{
	char *path;
	GtkWindow *window = GTK_WINDOW (foo);
	GtkWidget *bufmap;
	GtkWidget *menu_bar, *bar;
	GtkWidget *menu, *menuitem;
	GtkWidget *align;
	gint flags;

	foo->screen = 0;

	foo->windows    = NULL;
	foo->task_item  = NULL;
	foo->task_menu  = NULL;
	foo->task_image = NULL;
	foo->task_bin   = NULL;
	foo->icon_window = NULL;

	foo->clock_format = g_strdup (_("%H:%M"));
	foo->clock_timeout = 0;
	foo->clock_label = NULL;

	foo->compliant_wm = xstuff_is_compliant_wm ();
	if(foo->compliant_wm)
		GTK_WINDOW(foo)->type = GTK_WINDOW_TOPLEVEL;
	else
		GTK_WINDOW(foo)->type = GTK_WINDOW_POPUP;

	window->allow_shrink = TRUE;
	window->allow_grow   = TRUE;

	g_signal_connect (G_OBJECT (foo), "delete_event",
			  G_CALLBACK (gtk_true), NULL);

	gtk_window_move (GTK_WINDOW (foo),
			 multiscreen_x (foo->screen),
			 multiscreen_y (foo->screen));
	g_object_set (G_OBJECT (foo),
		      "width_request", (int)multiscreen_width (foo->screen),
		      NULL);

	foo->ebox = gtk_event_box_new ();
	foo->hbox = gtk_hbox_new (FALSE, 0);
	gtk_container_add(GTK_CONTAINER(foo->ebox), foo->hbox);

	path = panel_pixmap_discovery ("panel-corner-left.png", FALSE /* fallback */);
	if (path != NULL) {
		bufmap = gtk_image_new_from_file (path);
		g_free (path);
		align = gtk_alignment_new (0.0, 0.0, 1.0, 0.0);
		gtk_container_add (GTK_CONTAINER (align), bufmap);
		gtk_box_pack_start (GTK_BOX (foo->hbox), align, FALSE, FALSE, 0);
	}

	menu_bar = gtk_menu_bar_new ();	
	gtk_widget_set_name (menu_bar,
			     "panel-foobar-menubar");
	
	menuitem = pixmap_menu_item_new (_("Applications"),
					 "gnome-logo-icon-transparent.png",
					 TRUE /* force_image */);
	flags = MAIN_MENU_SYSTEM;
	if (got_kde_menus ())
		flags |= MAIN_MENU_KDE_SUB;
	if (got_distro_menus ())
		flags |= MAIN_MENU_DISTRIBUTION_SUB;

	menu = create_root_menu (NULL, TRUE, flags, FALSE, FALSE /* run_item */);
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), menuitem);
	g_signal_connect (G_OBJECT (menu), "show",
			  G_CALLBACK (programs_menu_to_display),
			  NULL);
	foo->programs = menu;

	/* Strech the applications menu to the corner */
	panel_stretch_events_to_toplevel (
		menuitem, PANEL_STRETCH_TOP | PANEL_STRETCH_LEFT);

	append_actions_menu (menu_bar);

	gtk_box_pack_start (GTK_BOX (foo->hbox), menu_bar, FALSE, FALSE, 0);
	
	
	/* panel widget */
	foo->panel = panel_widget_new (NULL, FALSE, GTK_ORIENTATION_HORIZONTAL,
				       PANEL_SIZE_X_SMALL, PANEL_BACK_NONE,
				       NULL, FALSE, FALSE, FALSE, NULL);
	PANEL_WIDGET (foo->panel)->panel_parent = GTK_WIDGET (foo);
	PANEL_WIDGET (foo->panel)->drop_widget = GTK_WIDGET (foo);

	gtk_container_add (GTK_CONTAINER (foo->hbox), foo->panel);

	g_object_set_data (G_OBJECT (menu_bar), "menu_panel", foo->panel);

	path = panel_pixmap_discovery ("panel-corner-right.png", FALSE /* fallback */);
	if (path != NULL) {
		bufmap = gtk_image_new_from_file (path);
		g_free (path);
		align = gtk_alignment_new (1.0, 0.0, 1.0, 0.0);
		gtk_container_add (GTK_CONTAINER (align), bufmap);
		gtk_box_pack_end (GTK_BOX (foo->hbox), align, FALSE, FALSE, 0);
	}

	bar = menu_bar = gtk_menu_bar_new ();
	gtk_widget_set_name (menu_bar,
			     "panel-foobar-menubar");

	append_clock_menu (foo, menu_bar);

	append_task_menu (foo, GTK_MENU_SHELL (bar));


	gtk_box_pack_end (GTK_BOX (foo->hbox), menu_bar, FALSE, FALSE, 0);
	gtk_container_add (GTK_CONTAINER (foo), foo->ebox);
	gtk_widget_show_all (foo->ebox);
}

static void
queue_panel_resize (gpointer data, gpointer user_data)
{
	PanelData *pd = data;
	GtkWidget *panel;

	g_assert (pd);

	panel = pd->panel;

	g_return_if_fail (GTK_IS_WIDGET (panel));

	if (!DRAWER_IS_WIDGET (panel) && !FOOBAR_IS_WIDGET (panel))
		gtk_widget_queue_resize (panel);
}

static void
foobar_widget_destroy (GtkObject *o)
{
	FoobarWidget *foo = FOOBAR_WIDGET (o);

	foobars = g_list_remove (foobars, foo);

	if (foo->clock_timeout != 0)
		g_source_remove (foo->clock_timeout);
	foo->clock_timeout = 0;
	
	foo->clock_label = NULL;

	g_free (foo->clock_format);
	foo->clock_format = NULL;

	g_slist_foreach (panel_list, queue_panel_resize, NULL);

	if (foo->windows != NULL)
		g_hash_table_destroy (foo->windows);
	foo->windows = NULL;

	if (GTK_OBJECT_CLASS (foobar_widget_parent_class)->destroy)
		GTK_OBJECT_CLASS (foobar_widget_parent_class)->destroy (o);
}

static void
foobar_widget_size_allocate (GtkWidget *w, GtkAllocation *alloc)
{
	if (GTK_WIDGET_CLASS (foobar_widget_parent_class)->size_allocate)
		GTK_WIDGET_CLASS (foobar_widget_parent_class)->size_allocate (w, alloc);

	if (GTK_WIDGET_REALIZED (w)) {
		FoobarWidget *foo = FOOBAR_WIDGET (w);
		xstuff_set_pos_size (w->window,
				     multiscreen_x (foo->screen),
				     multiscreen_y (foo->screen),
				     alloc->width,
				     alloc->height);

		g_slist_foreach (panel_list, queue_panel_resize, NULL);
		basep_border_queue_recalc (foo->screen);

		xstuff_set_wmspec_strut (w->window,
					 0 /* left */,
					 0 /* right */,
					 alloc->height /* top */,
					 0 /* bottom */);
	}
}

GtkWidget *
foobar_widget_new (const char *panel_id, int screen)
{
	FoobarWidget *foo;
	char *panel_icon;

	g_return_val_if_fail (screen >=0, NULL);

	if (foobar_widget_exists (screen))
		return NULL;

	foo = g_object_new (FOOBAR_TYPE_WIDGET, 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);
        }

	if (panel_id)
		panel_widget_set_id (PANEL_WIDGET (foo->panel), panel_id);

	foo->screen = screen;
	gtk_window_move (GTK_WINDOW (foo),
			 multiscreen_x (foo->screen),
			 multiscreen_y (foo->screen));
	g_object_set (G_OBJECT (foo),
		      "width_request", (int)multiscreen_width (foo->screen),
		      NULL);

	foobars = g_list_prepend (foobars, foo);

	return GTK_WIDGET (foo);
}

gboolean
foobar_widget_exists (int screen)
{
	GList *li;

	for (li = foobars; li != NULL; li = li->next) {
		FoobarWidget *foo = li->data;

		if (foo->screen == screen)
			return TRUE;
	}
	return FALSE;
}

void
foobar_widget_force_menu_remake (void)
{
	FoobarWidget *foo;
	GList *li;

	for (li = foobars; li != NULL; li = li->next) {
		foo = FOOBAR_WIDGET(li->data);

		if (foo->programs != NULL)
			g_object_set_data (G_OBJECT (foo->programs),
					   "need_reread",
					   GINT_TO_POINTER (TRUE));
	}
}

gint
foobar_widget_get_height (int screen)
{
	GList *li;

	g_return_val_if_fail (screen >= 0, 0);

	for (li = foobars; li != NULL; li = li->next) {
		FoobarWidget *foo = FOOBAR_WIDGET(li->data);

		if (foo->screen == screen)
			return GTK_WIDGET (foo)->allocation.height;
	}
	return 0; 
}

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
foobar_widget_redo_window(FoobarWidget *foo)
{
	GtkWindow *window;
	GtkWidget *widget;
	GdkWindowAttr attributes;
	gint attributes_mask;
	GdkWindow *oldwin;
	GdkWindow *newwin;
	gboolean comp;

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

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

	foo->compliant_wm = comp;
	if (foo->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 == NULL)
		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(foo->ebox->window, newwin, 0, 0);
	/* reparent all the base event windows as they are also children of
	 * the foobar */
	gtk_container_foreach(GTK_CONTAINER(foo->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);

	foobar_widget_update_winhints (foo);

	xstuff_set_wmspec_strut (widget->window,
				 0 /* left */,
				 0 /* right */,
				 widget->allocation.height /* top */,
				 0 /* bottom */);

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

	gtk_widget_map(widget);
}

static void
foobar_widget_move_focus_out (FoobarWidget *foobar)
{
 	panel_widget_focus (PANEL_WIDGET (foobar->panel));
}


