/* Gnome panel: general applet functionality
 * (C) 1997 the Free Software Foundation
 *
 * Authors:  George Lebl
 *           Federico Mena
 *           Miguel de Icaza
 */

#include <config.h>
#include <string.h>
#include <signal.h>
#include <libgnome/libgnome.h>
#include <libbonobo.h>
#include <gdk/gdkx.h>

#include "basep-widget.h"
#include "button-widget.h"
#include "drawer.h"
#include "launcher.h"
#include "logout.h"
#include "menu-properties.h"
#include "menu-util.h"
#include "menu.h"
#include "panel-config.h"
#include "panel-gconf.h"
#include "panel-config-global.h"
#include "session.h"
#include "status.h"
#include "swallow.h"
#include "panel-applet-frame.h"

#define SMALL_ICON_SIZE 20

extern GSList *panels;

GSList *applets = NULL;
int applet_count = 0;

/*config sync stuff*/
extern int applets_to_sync;
extern int need_complete_save;

extern gboolean commie_mode;
extern GlobalConfig global_config;

static GConfEnumStringPair object_type_enum_map [] = {
	{ APPLET_DRAWER,   "drawer-object" },
	{ APPLET_MENU,     "menu-object" },
	{ APPLET_LOGOUT,   "logout-object" },
	{ APPLET_SWALLOW,  "swallow-object" },
	{ APPLET_LAUNCHER, "launcher-object" }, 
	{ APPLET_EMPTY,    "empty-object" },
	{ APPLET_LOCK,     "lock-object" },
	{ APPLET_STATUS,   "status-object" },
	{ APPLET_BONOBO,   "bonobo-applet" },
};

static void
move_applet_callback (GtkWidget *widget, AppletInfo *info)
{
	PanelWidget *panel;

	g_return_if_fail (info != NULL);
	g_return_if_fail (info->widget != NULL);
	g_return_if_fail (info->widget->parent != NULL);
	g_return_if_fail (PANEL_IS_WIDGET (info->widget->parent));

	panel = PANEL_WIDGET (info->widget->parent);

	panel_widget_applet_drag_start (panel, info->widget,
					PW_DRAG_OFF_CENTER);
}

static void
panel_applet_clean_gconf (AppletInfo *info)
{
	GConfClient *client;
	GSList      *id_list, *l;
	const char  *temp_key = NULL;
	const char  *profile;

	g_return_if_fail (info != NULL);
	g_return_if_fail (info->gconf_key != NULL);

	client  = panel_gconf_get_client ();
	profile = panel_gconf_get_profile ();

	if (info->type == APPLET_BONOBO)
		temp_key = panel_gconf_general_key (profile, "applet-id-list");
	else
		temp_key = panel_gconf_general_key (profile, "object-id-list");

        id_list = gconf_client_get_list (
			panel_gconf_get_client (), temp_key,
			GCONF_VALUE_STRING, NULL);

        for (l = id_list; l; l = l->next)
                if (!strcmp (info->gconf_key, (char *) l->data))
                        break;

        if (l) {
                id_list = g_slist_remove_link (id_list, l);

		g_free (l->data);
		g_slist_free (l);

                gconf_client_set_list (client, temp_key, GCONF_VALUE_STRING, id_list, NULL);

		if (info->type == APPLET_BONOBO)
			temp_key = panel_gconf_sprintf (
					"/apps/panel/profiles/%s/applets/%s",
					profile, info->gconf_key);
		else
			temp_key = panel_gconf_sprintf (
					"/apps/panel/profiles/%s/objects/%s",
					profile, info->gconf_key);

		panel_gconf_clean_dir (client, temp_key);
        }

	panel_g_slist_deep_free (id_list);

	g_free (info->gconf_key);
	info->gconf_key = NULL;
}

/*destroy widgets and call the above cleanup function*/
void
panel_applet_clean (AppletInfo *info)
{
	g_return_if_fail (info != NULL);

	if (info->remove_idle != 0) {
		g_source_remove (info->remove_idle);
		info->remove_idle = 0;
	}

	applets = g_slist_remove (applets, info);

	if (info->widget) {
		GtkWidget *widget = info->widget;

		if (info->type == APPLET_STATUS)
			status_applet_put_offscreen (info->data);

		info->widget = NULL;
		gtk_widget_destroy (widget);
	}

	info->data = NULL;

	panel_applet_clean_gconf (info);

	g_free (info);
}

static gboolean
applet_idle_remove (gpointer data)
{
	AppletInfo *info = data;

	info->remove_idle = 0;

	if (info->type == APPLET_BONOBO) {
		PanelAppletFrame *frame = info->data;

		panel_applet_frame_set_clean_remove
			(PANEL_APPLET_FRAME (frame), TRUE);
	} else if (info->type == APPLET_SWALLOW) {
		Swallow *swallow = info->data;

		swallow->clean_remove = TRUE;
	}

	panel_applet_clean (info);

	return FALSE;
}

static void
applet_remove_callback (GtkWidget  *widget,
			AppletInfo *info)
{
	if (info->remove_idle == 0)
		info->remove_idle = g_idle_add (applet_idle_remove, info);
}

static void
applet_callback_callback(GtkWidget *widget, gpointer data)
{
	AppletUserMenu *menu = data;

	g_return_if_fail(menu->info != NULL);

	switch(menu->info->type) {
	case APPLET_LAUNCHER:
		if (strcmp (menu->name, "properties") == 0) {
			launcher_properties (menu->info->data);
		} else if (strcmp (menu->name, "help") == 0) {
			panel_show_help ("launchers", NULL);
		} else if (strcmp (menu->name, "help_on_app") == 0) {
			Launcher * launcher = menu->info->data;
			if (launcher->ditem != NULL) {
				GError *error = NULL;
				const char *docpath =
					gnome_desktop_item_get_string
					    (launcher->ditem, "DocPath");
				if ( ! panel_show_gnome_kde_help (docpath, &error)) {
					panel_error_dialog ("cannot_show_gnome_kde_help",
							    _("<b>Cannot display help document</b>\n\n"
							      "Details: %s"), error->message);
					g_clear_error (&error);
				}
			}
		}
		break;
	case APPLET_DRAWER: 
		if (strcmp (menu->name, "properties")==0) {
			Drawer *drawer = menu->info->data;
			g_assert(drawer);
			panel_config(drawer->drawer);
		} else if (strcmp (menu->name, "help") == 0) {
			panel_show_help ("drawers", NULL);
		}
		break;
	case APPLET_SWALLOW:
 		if (strcmp (menu->name, "help") == 0)
			panel_show_help ("specialobjects", "SWALLOWEDAPP");
#if 0
		if(strcmp(menu->name,"properties")==0) {
			Swallow *swallow = info->data;
			g_assert(swallow);
			swallow_properties(swallow); /* doesn't exist yet*/
		}
#endif
		break; 
	case APPLET_MENU:
		if(strcmp(menu->name,"properties")==0)
			menu_properties(menu->info->data);
		else if(strcmp(menu->name,"edit_menus")==0) {
			char *tmp[3] = { NULL, NULL, NULL };
			if((tmp[0] = g_find_program_in_path ("nautilus")) != NULL)  {
				tmp[1] = "applications:/";
				gnome_execute_async (g_get_home_dir (), 2, tmp);
				g_free (tmp[0]);
			}
		} else if (strcmp (menu->name, "help") == 0) {
			Menu *menu2 = menu->info->data;
			char *page;
			page = menu2->main_menu ? "mainmenu" : "menus";
			panel_show_help (page, NULL);
		}
		break;
	case APPLET_LOCK: {
                /*
		  <jwz> Blank Screen Now
		  <jwz> Lock Screen Now
		  <jwz> Kill Daemon
		  <jwz> Restart Daemon
		  <jwz> Preferences
		  <jwz> (or "configuration" instead?  whatever word you use)
		  <jwz> those should do xscreensaver-command -activate, -lock, -exit...
		  <jwz> and "xscreensaver-command -exit ; xscreensaver &"
		  <jwz> and "xscreensaver-demo"
		*/
		char *command = NULL;
		gboolean freeit = FALSE;
		if (strcmp (menu->name, "help") == 0)
			panel_show_help ("specialobjects", "LOCKBUTTON");
		else if (!strcmp (menu->name, "restart")) {
			command = "xscreensaver-command -exit ; xscreensaver &";
		} else if (!strcmp (menu->name, "prefs")) {
			command = "xscreensaver-demo";
		} else {
			command = g_strdup_printf ("xscreensaver-command -%s",
						   menu->name);
			freeit = TRUE;
		}
		if (command)
			gnome_execute_shell (g_get_home_dir (), command);
		if (freeit)
			g_free (command);
		break;
	}
	case APPLET_LOGOUT:
		if (strcmp (menu->name, "help") == 0)
			panel_show_help ("specialobjects", "LOGOUTBUTTON");
		break;
	case APPLET_STATUS:
		if (strcmp (menu->name, "help") == 0)
			panel_show_help ("specialobjects", "STATUSDOC");
		break;
	case APPLET_BONOBO:
		/*
		 * Applet's menu's are handled differently
		 */
		break;
	default:
		g_assert_not_reached ();
		break;
	}
}

static void
applet_menu_deactivate(GtkWidget *w, AppletInfo *info)
{
	GtkWidget *panel = get_panel_parent(info->widget);
	info->menu_age = 0;
	
	if(BASEP_IS_WIDGET(panel))
		BASEP_WIDGET(panel)->autohide_inhibit = FALSE;
}

AppletUserMenu *
panel_applet_get_callback (GList      *user_menu,
			   const char *name)
{
	GList *l;

	for (l = user_menu; l; l = l->next) {
		AppletUserMenu *menu = l->data;

		if (!strcmp (menu->name, name))
			return menu;
	}

	return NULL;	
}

void
panel_applet_add_callback (AppletInfo *info,
			   const char *callback_name,
			   const char *stock_item,
			   const char *menuitem_text)
{
	AppletUserMenu *menu;

	g_return_if_fail (info != NULL);

	menu = panel_applet_get_callback (info->user_menu, callback_name);
	if (menu == NULL) {
		menu = g_new0 (AppletUserMenu, 1);
		menu->name = g_strdup (callback_name);
		menu->stock_item = g_strdup (stock_item);
		menu->text = g_strdup (menuitem_text);
		menu->sensitive = TRUE;
		menu->info = info;
		menu->menuitem = NULL;
		menu->submenu = NULL;
		info->user_menu = g_list_append (info->user_menu, menu);
	} else {
		g_free (menu->stock_item);
		menu->stock_item = NULL;
		g_free (menu->text);
		menu->text = NULL;
		menu->text = g_strdup (menuitem_text);
		menu->stock_item = g_strdup (stock_item);
	}

	/*make sure the menu is rebuilt*/
	if(info->menu) {
		GList *list;
		for(list=info->user_menu;list!=NULL;list=g_list_next(list)) {
			AppletUserMenu *menu = list->data;
			menu->menuitem=NULL;
			menu->submenu=NULL;
		}
		gtk_widget_unref(info->menu);
		info->menu = NULL;
		info->menu_age = 0;
	}
}

void
panel_applet_remove_callback (AppletInfo *info,
			      const char *callback_name)
{
	AppletUserMenu *menu;

	g_return_if_fail (info != NULL);
	
	menu = panel_applet_get_callback (info->user_menu, callback_name);
	if (menu != NULL) {
		info->user_menu = g_list_remove (info->user_menu, menu);
		g_free(menu->name);
		menu->name = NULL;
		g_free(menu->stock_item);
		menu->stock_item = NULL;
		g_free(menu->text);
		menu->text = NULL;
		g_free(menu);
	} else
		return; /*it just isn't there*/

	/*make sure the menu is rebuilt*/
	if (info->menu != NULL) {
		GList *list;
		for (list = info->user_menu; list != NULL; list = list->next) {
			AppletUserMenu *menu = list->data;
			menu->menuitem = NULL;
			menu->submenu = NULL;
		}
		gtk_widget_unref (info->menu);
		info->menu = NULL;
		info->menu_age = 0;
	}
}

static void
setup_an_item(AppletUserMenu *menu,
	      GtkWidget *submenu,
	      int is_submenu)
{
	menu->menuitem = gtk_image_menu_item_new ();

	g_signal_connect (G_OBJECT (menu->menuitem), "destroy",
			  G_CALLBACK (gtk_widget_destroyed),
			  &menu->menuitem);
	if(menu->stock_item && *(menu->stock_item))
		setup_menuitem (menu->menuitem,
				gtk_image_new_from_stock (menu->stock_item,
							  GTK_ICON_SIZE_MENU),
				menu->text);
	else
		setup_menuitem (menu->menuitem,
				NULL,
				menu->text);

	if(submenu)
		gtk_menu_shell_append (GTK_MENU_SHELL (submenu), menu->menuitem);

	/*if an item not a submenu*/
	if(!is_submenu) {
		g_signal_connect(G_OBJECT(menu->menuitem), "activate",
				 G_CALLBACK (applet_callback_callback),
				   menu);
		g_signal_connect(G_OBJECT (submenu), "destroy",
				 G_CALLBACK (gtk_widget_destroyed),
				 &menu->submenu);
	/* if the item is a submenu and doesn't have it's menu
	   created yet*/
	} else if(!menu->submenu) {
		menu->submenu = panel_menu_new ();
	}

	if(menu->submenu) {
		gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu->menuitem),
					  menu->submenu);
		g_signal_connect (G_OBJECT (menu->submenu), "destroy",
				    G_CALLBACK (gtk_widget_destroyed),
				    &menu->submenu);
	}
	
	gtk_widget_set_sensitive(menu->menuitem,menu->sensitive);
}

static void
add_to_submenus (AppletInfo *info,
		 const char *path,
		 const char *name,
		 AppletUserMenu *menu,
		 GtkWidget *submenu,
		 GList *user_menu)
{
	char *n = g_strdup (name);
	char *p = strchr (n, '/');
	char *t;
	AppletUserMenu *s_menu;

	/*this is the last one*/
	if (p == NULL) {
		g_free (n);
		setup_an_item (menu, submenu, FALSE);
		return;
	}
	
	/*this is the last one and we are a submenu, we have already been
	  set up*/
	if(p==(n + strlen(n) - 1)) {
		g_free(n);
		return;
	}
	
	*p = '\0';
	p++;
	
	t = g_strconcat (path, n, "/", NULL);
	s_menu = panel_applet_get_callback (user_menu, t);
	/*the user did not give us this sub menu, whoops, will create an empty
	  one then*/
	if (s_menu == NULL) {
		s_menu = g_new0 (AppletUserMenu,1);
		s_menu->name = g_strdup (t);
		s_menu->stock_item = NULL;
		s_menu->text = g_strdup (_("???"));
		s_menu->sensitive = TRUE;
		s_menu->info = info;
		s_menu->menuitem = NULL;
		s_menu->submenu = NULL;
		info->user_menu = g_list_append (info->user_menu,s_menu);
		user_menu = info->user_menu;
	}
	
	if (s_menu->submenu == NULL) {
		s_menu->submenu = panel_menu_new ();
		/*a more elegant way to do this should be done
		  when I don't want to go to sleep */
		if (s_menu->menuitem != NULL) {
			gtk_widget_destroy (s_menu->menuitem);
			s_menu->menuitem = NULL;
		}
	}
	if (s_menu->menuitem == NULL)
		setup_an_item (s_menu, submenu, TRUE);
	
	add_to_submenus (info, t, p, menu, s_menu->submenu, user_menu);
	
	g_free(t);
	g_free(n);
}

static GtkWidget *
applet_setup_panel_menu (gboolean is_basep)
{
	GtkWidget *menuitem;
	GtkWidget *panel_menu;
	GdkPixbuf *pixbuf;

	menuitem = gtk_image_menu_item_new ();

	pixbuf = panel_make_menu_icon ("gnome-panel.png",
				       NULL /* fallback */,
				       SMALL_ICON_SIZE /* size */,
				       NULL /* long_operation */);

	if (pixbuf != NULL) {
		GtkWidget *image = NULL;
		
		image = gtk_image_new_from_pixbuf (pixbuf);
		g_object_unref (pixbuf);
		
		setup_menuitem (menuitem, 
				image,
				_("Panel"));
	} else {
		g_message (_("Cannot find pixmap file %s"), "gnome-panel.png");

		setup_menuitem (menuitem, NULL, _("Panel"));
	}

	panel_menu = panel_menu_new ();

	make_panel_submenu (panel_menu, TRUE, is_basep);

	gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), panel_menu);

	return menuitem;
}

void
panel_applet_create_menu (AppletInfo *info,
			  gboolean    is_basep)
{
	GtkWidget *menuitem;
	GList     *l;

	info->menu = g_object_ref (panel_menu_new ());
	gtk_object_sink (GTK_OBJECT (info->menu));

	if (!commie_mode) {
		GtkWidget *image;

		menuitem = gtk_image_menu_item_new ();

		image = gtk_image_new_from_stock (GTK_STOCK_REMOVE,
						  GTK_ICON_SIZE_MENU);

		setup_menuitem (menuitem, image , _("Remove from panel"));

		g_signal_connect (G_OBJECT (menuitem),
				  "activate",
				  G_CALLBACK (applet_remove_callback),
				  info);

		gtk_menu_shell_append (GTK_MENU_SHELL (info->menu), menuitem);

		menuitem = gtk_image_menu_item_new();

		/*
		 * FIXME: should have a "Move" pixmap.
		 */
		setup_menuitem (menuitem, NULL, _("Move"));

		g_signal_connect (G_OBJECT (menuitem),
				  "activate",
				  G_CALLBACK (move_applet_callback),
				  info);

		gtk_menu_shell_append (GTK_MENU_SHELL (info->menu), menuitem);
	}

	menuitem = applet_setup_panel_menu (is_basep);

	gtk_menu_shell_append (GTK_MENU_SHELL (info->menu), menuitem);

	if (info->user_menu) {
		menuitem = gtk_image_menu_item_new ();

		gtk_menu_shell_append (GTK_MENU_SHELL (info->menu), menuitem);

		gtk_widget_set_sensitive (menuitem, FALSE);

		gtk_widget_show (menuitem);
	}

	for (l = info->user_menu; l; l = l->next) {
		AppletUserMenu *menu = (AppletUserMenu *)l->data;

		add_to_submenus (info, "", menu->name, menu, 
				 info->menu, info->user_menu);
	}

	/*
	 * connect the deactivate signal, so that we can "re-allow" 
	 * autohide when the menu is deactivated.
	 */
	g_signal_connect (G_OBJECT (info->menu),
			    "deactivate",
			     G_CALLBACK (applet_menu_deactivate),
			     info);
}

static void
applet_menu_set_data_recursively (GtkMenu     *menu,
				  const gchar *key,
				  gpointer     data)
{
	GList *children;
	GList *l;

	g_object_set_data (G_OBJECT (menu), key, data);

	children = gtk_container_get_children (GTK_CONTAINER (menu));

	for (l = children; l; l = l->next) {
		GtkWidget *submenu = GTK_MENU_ITEM (l->data)->submenu;

		if (submenu)
			applet_menu_set_data_recursively (GTK_MENU (submenu), key, data);
	}

	g_list_free (children);
}

static void
applet_show_menu (AppletInfo     *info,
		  GdkEventButton *event)
{
	GtkWidget *panel;

	g_return_if_fail (info != NULL);

	panel = get_panel_parent (info->widget);

	if (!info->menu)
		panel_applet_create_menu (info, BASEP_IS_WIDGET (panel));

	g_assert (info->menu);

	if (BASEP_IS_WIDGET (panel)) {
		BASEP_WIDGET (panel)->autohide_inhibit = TRUE;
		basep_widget_queue_autohide (BASEP_WIDGET (panel));
	}

	info->menu_age = 0;

	applet_menu_set_data_recursively (GTK_MENU (info->menu),
					  "menu_panel",
					  info->widget->parent);

	if (!GTK_WIDGET_REALIZED (info->menu))
		gtk_widget_show (info->menu);

	gtk_menu_popup (GTK_MENU (info->menu),
			NULL,
			NULL,
			applet_menu_position,
			info,
			event->button,
			event->time);
}

static gboolean
applet_do_popup_menu (GtkWidget      *widget,
		      GdkEventButton *event,
		      AppletInfo     *info)
{
	if (panel_applet_in_drag)
		return FALSE;

	switch (info->type) {
	case APPLET_BONOBO:
			break;
	case APPLET_SWALLOW: {
		GtkHandleBox *handle_box;

		handle_box = GTK_HANDLE_BOX (((Swallow *)info->data)->handle_box);

		if (handle_box->child_detached)
			applet_show_menu (info, event);
		}
		break;
	default:
		applet_show_menu (info, event);
		break;
	}

	return TRUE;
}

static gboolean
applet_popup_menu (GtkWidget      *widget,
		   AppletInfo     *info)
{
	GdkEventButton event;

	event.button = 3;
	event.time = GDK_CURRENT_TIME;

	return applet_do_popup_menu (widget, &event, info);
}

static gboolean
applet_button_press (GtkWidget      *widget,
		     GdkEventButton *event,
		     AppletInfo     *info)
{
	if (event->button == 3) {
		return applet_do_popup_menu (widget, event, info);
	}

	if (BUTTON_IS_WIDGET (widget))
		return FALSE;
	else
		return TRUE;
}

static void
applet_destroy (GtkWidget *w, AppletInfo *info)
{
	GtkWidget *old_widget;
	GList *li;

	g_return_if_fail (info != NULL);

	old_widget = info->widget;
	info->widget = NULL;

	if (info->type == APPLET_DRAWER) {
		Drawer *drawer = info->data;
		g_assert(drawer);
		if(drawer->drawer) {
			GtkWidget *dw = drawer->drawer;
			drawer->drawer = NULL;
			PANEL_WIDGET(BASEP_WIDGET(dw)->panel)->master_widget = NULL;
			gtk_widget_destroy(dw);
		}
	} else if (info->type == APPLET_LAUNCHER) {
		Launcher    *launcher = info->data;
		const gchar *location;

		location = gnome_desktop_item_get_location (launcher->ditem);

		session_add_dead_launcher (location);
	}


	if (info->menu != NULL) {
		gtk_widget_unref(info->menu);
		info->menu = NULL;
		info->menu_age = 0;
	}

	if (info->data_destroy)
		info->data_destroy (info->data);
	info->data_destroy = NULL;
	info->data = NULL;

	/*free the user menu*/
	for(li = info->user_menu; li != NULL; li = g_list_next(li)) {
		AppletUserMenu *umenu = li->data;

		li->data = NULL;

		g_free(umenu->name);
		umenu->name = NULL;
		g_free(umenu->stock_item);
		umenu->stock_item = NULL;
		g_free(umenu->text);
		umenu->text = NULL;

		g_free(umenu);
	}
	g_list_free (info->user_menu);
	info->user_menu = NULL;

	/* If this was not called from the panel_applet_clean
	 * itself.  That is if the widget entry was still set */
	if (old_widget != NULL)
		panel_applet_clean (info);
}

static G_CONST_RETURN char *
panel_applet_get_full_gconf_key (AppletType   type,
				 const gchar *profile,
				 const gchar *object_id,
				 const gchar *key)
{
	PanelGConfKeyType  gconf_type;

	if (type == APPLET_BONOBO)
		gconf_type = PANEL_GCONF_APPLETS;
	else
		gconf_type = PANEL_GCONF_OBJECTS;

	return panel_gconf_full_key (gconf_type, profile, object_id, key);
}

typedef struct {
	PanelWidget *panel_widget;
	int          position;
	char        *unique_id;
} PanelAppletToLoad;

static GSList *panel_applets_to_load = NULL;

static gboolean
panel_applet_load_idle_handler (gpointer dummmy)
{
	PanelAppletToLoad *applet;

	if (!panel_applets_to_load)
		return FALSE;

	applet = (PanelAppletToLoad *) panel_applets_to_load->data;
	panel_applets_to_load =
		g_slist_remove (panel_applets_to_load, applet);

	panel_applet_frame_load_from_gconf (
			applet->panel_widget,
			applet->position,
			applet->unique_id);

	g_free (applet->unique_id);
	g_free (applet);

	return TRUE;
}

static void
panel_applet_load_from_unique_id (AppletType   type,
				  GConfClient *gconf_client,
				  const char  *profile,
				  const char  *unique_id)
{
	PanelWidget *panel_widget;
	AppletType   applet_type;
	const char  *temp_key;
	char        *type_string;
	char        *panel_id;
	int          position;
	gboolean     right_stick;

	temp_key = panel_applet_get_full_gconf_key (
			type, profile, unique_id, "object-type");
	type_string = gconf_client_get_string (gconf_client, temp_key, NULL);

	if (!gconf_string_to_enum (object_type_enum_map,
				   type_string,
				   (int *) &applet_type)) {
		g_free (type_string);
		g_warning ("Unkown applet type %s from %s", type_string, temp_key);
		return;
	}
	
	g_free (type_string);

	temp_key = panel_applet_get_full_gconf_key (
			type, profile, unique_id, "position");
	position = gconf_client_get_int (gconf_client, temp_key, NULL);
	
	temp_key = panel_applet_get_full_gconf_key (
			type, profile, unique_id, "panel-id");
	panel_id = gconf_client_get_string (gconf_client, temp_key, NULL);

	temp_key = panel_applet_get_full_gconf_key (
			type, profile, unique_id, "panel-right-stick");
	right_stick = gconf_client_get_bool (gconf_client, temp_key, NULL);

	panel_widget = panel_widget_get_by_id (panel_id);
	g_free (panel_id);

	/*
	 * A hack from the old panel to make sure the applet is on the right
	 */
	position += right_stick ? G_MAXINT/2 : 0;

	switch (applet_type) {
	case APPLET_BONOBO: {
		PanelAppletToLoad *applet;

		applet = g_new0 (PanelAppletToLoad, 1);

		applet->panel_widget = panel_widget;
		applet->position     = position;
		applet->unique_id    = g_strdup (unique_id);

		if (!panel_applets_to_load)
			g_idle_add (panel_applet_load_idle_handler, NULL);

		panel_applets_to_load = g_slist_prepend (panel_applets_to_load, applet);
		}
		break;
	case APPLET_DRAWER:
		drawer_load_from_gconf (panel_widget, position, unique_id);
		break;
	case APPLET_SWALLOW:
		swallow_load_from_gconf (panel_widget, position, unique_id);
		break;
	case APPLET_MENU:
		menu_load_from_gconf (panel_widget, position, unique_id);
		break;
	case APPLET_LAUNCHER:
		launcher_load_from_gconf (panel_widget, position, unique_id);
		break;
	case APPLET_LOGOUT:
		load_logout_applet (panel_widget, position, TRUE, unique_id);
		break;
	case APPLET_LOCK:
		load_lock_applet (panel_widget, position, TRUE, unique_id);
		break;
	case APPLET_STATUS:
		load_status_applet (panel_widget, position, TRUE, unique_id);
		break;
	default:
		break;
	}
}

static void
panel_applet_load_list (AppletType type)
{
	GConfClient *client;
	GSList      *id_list, *l;
	const char  *profile;
	const char  *temp_key = NULL;
	
	client  = panel_gconf_get_client ();
	profile = panel_gconf_get_profile ();

	if (type == APPLET_BONOBO)
		temp_key = panel_gconf_general_key (profile, "applet-id-list");
	else
        	temp_key = panel_gconf_general_key (profile, "object-id-list");

        id_list = gconf_client_get_list (
			panel_gconf_get_client (), temp_key,
			GCONF_VALUE_STRING, NULL);

        for (l = id_list; l; l = l->next)
		panel_applet_load_from_unique_id (type, client, profile, (char *) l->data);

	panel_g_slist_deep_free (id_list);
}

void
panel_applet_load_applets_from_gconf (void)
{
	panel_applet_load_list (APPLET_BONOBO);
	panel_applet_load_list (APPLET_EMPTY);
}

static GSList *queued_position_saves = NULL;
static guint   queued_position_source = 0;

static gboolean
panel_applet_position_save_timeout (gpointer dummy)
{
	GSList *l;

	queued_position_source = 0;

	for (l = queued_position_saves; l; l = l->next) {
		AppletInfo *info = l->data;

		panel_applet_save_position (info, info->gconf_key, TRUE);
	}

	g_slist_free (queued_position_saves);
	queued_position_saves = NULL;

	return FALSE;
}

void
panel_applet_save_position (AppletInfo *applet_info,
			    const char *gconf_key,
			    gboolean    immediate)
{
	GConfClient  *client;
	const char   *profile;
	const char   *temp_key;

	g_return_if_fail (applet_info != NULL);

	if (!immediate) {
		if (!queued_position_source)
			queued_position_source =
				g_timeout_add (1000,
					       (GSourceFunc) panel_applet_position_save_timeout,
					       NULL);

		if (!g_slist_find (queued_position_saves, applet_info))
			queued_position_saves =
				g_slist_prepend (queued_position_saves, applet_info);

		return;
	}

	client  = panel_gconf_get_client ();
	profile = panel_gconf_get_profile ();

	temp_key = panel_applet_get_full_gconf_key (
			applet_info->type, profile, gconf_key, "position");
	gconf_client_set_int (client, temp_key, panel_applet_get_position (applet_info), NULL);

	temp_key = panel_applet_get_full_gconf_key (
			applet_info->type, profile, gconf_key, "panel-id");
	gconf_client_set_string (client, temp_key, panel_applet_get_panel_id (applet_info), NULL);

	temp_key = panel_applet_get_full_gconf_key (
			applet_info->type, profile, gconf_key, "panel-right-stick");
	gconf_client_set_bool (client, temp_key, panel_applet_get_right_stick (applet_info), NULL);

	gconf_client_suggest_sync (client, NULL);
}

void
panel_applet_save_to_gconf (AppletInfo *applet_info)
{
	GConfClient *client;
	const char  *profile;
	const char  *temp_key;
	GSList      *id_list, *l;

	client  = panel_gconf_get_client ();
	profile = panel_gconf_get_profile ();

	if (applet_info->type == APPLET_BONOBO)
		temp_key = panel_gconf_general_key (profile, "applet-id-list");
	else
		temp_key = panel_gconf_general_key (profile, "object-id-list");

	id_list = gconf_client_get_list (client, temp_key, GCONF_VALUE_STRING, NULL);

	if (!applet_info->gconf_key)
		applet_info->gconf_key = gconf_unique_key ();

	for (l = id_list; l; l = l->next)
		if (!strcmp (applet_info->gconf_key, (char *) l->data))
			break;

	if (!l) {
		id_list = g_slist_prepend (id_list, g_strdup (applet_info->gconf_key));

		gconf_client_set_list (client, temp_key, GCONF_VALUE_STRING, id_list, NULL);
	}

	panel_g_slist_deep_free (id_list);

	temp_key = panel_applet_get_full_gconf_key (applet_info->type, profile,
						    applet_info->gconf_key,
						    "object-type");
	gconf_client_set_string (client, temp_key,
				 gconf_enum_to_string (object_type_enum_map, applet_info->type),
				 NULL);

	panel_applet_save_position (applet_info, applet_info->gconf_key, TRUE);

	switch (applet_info->type) {
	case APPLET_BONOBO:
		panel_applet_frame_save_to_gconf (PANEL_APPLET_FRAME (applet_info->widget),
						  applet_info->gconf_key);
		break;
	case APPLET_DRAWER:
		drawer_save_to_gconf ((Drawer *) applet_info->data,
				      applet_info->gconf_key);
		break;
	case APPLET_SWALLOW:
		swallow_save_to_gconf ((Swallow *) applet_info->data,
				       applet_info->gconf_key);
		break;
	case APPLET_MENU:
		menu_save_to_gconf ((Menu *) applet_info->data,
				    applet_info->gconf_key);
		break;
	case APPLET_LAUNCHER:
		launcher_save_to_gconf ((Launcher *) applet_info->data,
					applet_info->gconf_key);
		break;
	case APPLET_LOGOUT: /* no object specific stuff to save */
	case APPLET_LOCK:
	case APPLET_STATUS:
	default:
		break;
	}

	gconf_client_suggest_sync (client, NULL);
}

AppletInfo *
panel_applet_register (GtkWidget      *applet,
		       gpointer        data,
		       GDestroyNotify  data_destroy,
		       PanelWidget    *panel,
		       gint            pos,
		       gboolean        exactpos,
		       AppletType      type,
		       const char     *gconf_key)
{
	AppletInfo *info;
	int newpos;
	gboolean insert_at_pos;
	gboolean expand_major = FALSE, expand_minor = FALSE;
	
	g_return_val_if_fail (applet != NULL && panel != NULL, NULL);

	if ( ! GTK_WIDGET_NO_WINDOW (applet))
		gtk_widget_set_events (applet, (gtk_widget_get_events (applet) |
						APPLET_EVENT_MASK) &
				       ~( GDK_POINTER_MOTION_MASK |
					  GDK_POINTER_MOTION_HINT_MASK));

	info = g_new0 (AppletInfo, 1);
	info->applet_id = applet_count;
	info->type = type;
	info->widget = applet;
	info->menu = NULL;
	info->menu_age = 0;
	info->data = data;
	info->data_destroy = data_destroy;
	info->user_menu = NULL;

	g_object_set_data (G_OBJECT (applet), "applet_info", info);

	if (type == APPLET_DRAWER) {
		Drawer *drawer = data;
		PanelWidget *assoc_panel =
			PANEL_WIDGET (BASEP_WIDGET (drawer->drawer)->panel);

		g_object_set_data (G_OBJECT (applet),
				   PANEL_APPLET_ASSOC_PANEL_KEY, assoc_panel);
		assoc_panel->master_widget = applet;
	}

	g_object_set_data (G_OBJECT (applet),
			   PANEL_APPLET_FORBIDDEN_PANELS, NULL);

	if (type == APPLET_BONOBO) {
		panel_applet_frame_get_expand_flags (PANEL_APPLET_FRAME (applet),
						     &expand_major,
						     &expand_minor);
	}
	
	applets = g_slist_append (applets, info);

	applet_count++;

	/*we will need to save this applet's config now*/
	applets_to_sync = TRUE;

	/*add at the beginning if pos == -1*/
	if (pos >= 0) {
		newpos = pos;
		insert_at_pos = FALSE;
	} else {
		newpos = 0;
		insert_at_pos = FALSE;
	}
	/* if exact pos is on then insert at that precise location */
	if (exactpos)
		insert_at_pos = TRUE;


	

	if (panel_widget_add_full (panel, applet, newpos, TRUE,
				   insert_at_pos, expand_major, expand_minor)==-1) {
		GSList *list;
		for(list = panels; list != NULL; list = g_slist_next(list))
			if (panel_widget_add_full (panel, applet, 0,
						   TRUE, TRUE, expand_major, expand_minor)!=-1)
				break;
		if(!list) {
			/*can't put it anywhere, clean up*/
			info->widget = NULL;
			gtk_widget_destroy (applet);
			panel_applet_clean (info);
			g_warning(_("Can't find an empty spot"));
			return NULL;
		}
		panel = PANEL_WIDGET(list->data);
	}

	if(BUTTON_IS_WIDGET (applet) || !GTK_WIDGET_NO_WINDOW(applet)) {
		g_signal_connect(G_OBJECT(applet),
				   "button_press_event",
				   G_CALLBACK(applet_button_press),
				   info);
		g_signal_connect(G_OBJECT (applet),
				   "popup_menu",
                          	   G_CALLBACK(applet_popup_menu),
				   info);
	}

	g_signal_connect (G_OBJECT (applet), "destroy",
			  G_CALLBACK (applet_destroy),
			  info);

	gtk_widget_show_all(applet);

	/*g_signal_connect (G_OBJECT (applet), 
			    "drag_request_event",
			    G_CALLBACK(panel_dnd_drag_request),
			    info);

	gtk_widget_dnd_drag_set (GTK_WIDGET(applet), TRUE,
				 applet_drag_types, 1);*/

	orientation_change (info, panel);
	size_change (info, panel);
	back_change (info, panel);

	if (gconf_key)
		info->gconf_key = g_strdup (gconf_key);

	panel_applet_save_to_gconf (info);

	return info;
}

int
panel_applet_get_position (AppletInfo *applet)
{
	AppletData *applet_data;

	g_return_val_if_fail (applet != NULL, 0);
	g_return_val_if_fail (G_IS_OBJECT (applet->widget), 0);

	applet_data = g_object_get_data (G_OBJECT (applet->widget), PANEL_APPLET_DATA);

	return applet_data->pos;
}

gchar *
panel_applet_get_panel_id (AppletInfo *applet)
{
	PanelWidget *panel;

	g_return_val_if_fail (applet != NULL, NULL);
	g_return_val_if_fail (G_IS_OBJECT (applet->widget), NULL);

	panel = PANEL_WIDGET (applet->widget->parent);

	return panel->unique_id;
}

gboolean
panel_applet_get_right_stick (AppletInfo *applet)
{
	PanelWidget *panel;

	g_return_val_if_fail (applet != NULL, FALSE);
	g_return_val_if_fail (G_IS_OBJECT (applet->widget), FALSE);

	panel = PANEL_WIDGET (applet->widget->parent);

	return panel_widget_is_applet_stuck (panel, applet->widget);
}
