summaryrefslogtreecommitdiff
path: root/mate-panel/applet.c
diff options
context:
space:
mode:
authorPerberos <[email protected]>2011-12-01 22:56:10 -0300
committerPerberos <[email protected]>2011-12-01 22:56:10 -0300
commitc51ef797a707f4e2c6f9688d4378f2b0e9898a66 (patch)
tree019ae92bb53c19b30077545cb14743cbd1b57aef /mate-panel/applet.c
downloadmate-panel-c51ef797a707f4e2c6f9688d4378f2b0e9898a66.tar.bz2
mate-panel-c51ef797a707f4e2c6f9688d4378f2b0e9898a66.tar.xz
moving from https://github.com/perberos/mate-desktop-environment
Diffstat (limited to 'mate-panel/applet.c')
-rw-r--r--mate-panel/applet.c1449
1 files changed, 1449 insertions, 0 deletions
diff --git a/mate-panel/applet.c b/mate-panel/applet.c
new file mode 100644
index 00000000..e20e1faa
--- /dev/null
+++ b/mate-panel/applet.c
@@ -0,0 +1,1449 @@
+/* Mate 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 <unistd.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+
+#include <libpanel-util/panel-show.h>
+
+#include "button-widget.h"
+#include "drawer.h"
+#include "launcher.h"
+#include "panel-addto.h"
+#include "panel-mateconf.h"
+#include "panel-config-global.h"
+#include "mate-panel-applet-frame.h"
+#include "panel-action-button.h"
+#include "panel-menu-bar.h"
+#include "panel-separator.h"
+#include "panel-compatibility.h"
+#include "panel-toplevel.h"
+#include "panel-util.h"
+#include "panel-profile.h"
+#include "panel-menu-button.h"
+#include "panel-globals.h"
+#include "panel-properties-dialog.h"
+#include "panel-lockdown.h"
+
+#define SMALL_ICON_SIZE 20
+
+static GSList *registered_applets = NULL;
+static GSList *queued_position_saves = NULL;
+static guint queued_position_source = 0;
+
+
+static inline PanelWidget *
+mate_panel_applet_get_panel_widget (AppletInfo *info)
+{
+ return PANEL_WIDGET (gtk_widget_get_parent (info->widget));
+}
+
+static void
+mate_panel_applet_set_dnd_enabled (AppletInfo *info,
+ gboolean dnd_enabled)
+{
+ switch (info->type) {
+ case PANEL_OBJECT_DRAWER:
+ panel_drawer_set_dnd_enabled (info->data, dnd_enabled);
+ break;
+ case PANEL_OBJECT_MENU:
+ panel_menu_button_set_dnd_enabled (PANEL_MENU_BUTTON (info->widget),
+ dnd_enabled);
+ break;
+ case PANEL_OBJECT_LAUNCHER:
+ panel_launcher_set_dnd_enabled (info->data, dnd_enabled);
+ break;
+ case PANEL_OBJECT_APPLET:
+ break;
+ case PANEL_OBJECT_LOGOUT:
+ case PANEL_OBJECT_LOCK:
+ case PANEL_OBJECT_ACTION:
+ panel_action_button_set_dnd_enabled (PANEL_ACTION_BUTTON (info->widget),
+ dnd_enabled);
+ break;
+ case PANEL_OBJECT_MENU_BAR:
+ case PANEL_OBJECT_SEPARATOR:
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+}
+
+gboolean
+mate_panel_applet_toggle_locked (AppletInfo *info)
+{
+ PanelWidget *panel_widget;
+ gboolean locked;
+
+ panel_widget = mate_panel_applet_get_panel_widget (info);
+
+ locked = panel_widget_toggle_applet_locked (panel_widget, info->widget);
+
+ mate_panel_applet_save_position (info, info->id, TRUE);
+ mate_panel_applet_set_dnd_enabled (info, !locked);
+
+ return locked;
+}
+
+static void
+mate_panel_applet_lock (GtkCheckMenuItem *menuitem,
+ AppletInfo *info)
+{
+ gboolean locked;
+
+ locked = mate_panel_applet_toggle_locked (info);
+
+ gtk_check_menu_item_set_active (menuitem, locked);
+
+ if (info->move_item)
+ gtk_widget_set_sensitive (info->move_item, !locked);
+}
+
+static void
+move_applet_callback (GtkWidget *widget, AppletInfo *info)
+{
+ GtkWidget *parent;
+ PanelWidget *panel;
+
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (info->widget != NULL);
+
+ parent = gtk_widget_get_parent (info->widget);
+
+ g_return_if_fail (parent != NULL);
+ g_return_if_fail (PANEL_IS_WIDGET (parent));
+
+ panel = PANEL_WIDGET (parent);
+
+ panel_widget_applet_drag_start (panel, info->widget,
+ PW_DRAG_OFF_CENTER,
+ GDK_CURRENT_TIME);
+}
+
+/* permanently remove an applet - all non-permanent
+ * cleanups should go in mate_panel_applet_destroy()
+ */
+void
+mate_panel_applet_clean (AppletInfo *info)
+{
+ g_return_if_fail (info != NULL);
+
+ if (info->type == PANEL_OBJECT_LAUNCHER)
+ panel_launcher_delete (info->data);
+
+ if (info->widget) {
+ GtkWidget *widget = info->widget;
+
+ info->widget = NULL;
+ gtk_widget_destroy (widget);
+ }
+}
+
+static void
+mate_panel_applet_recreate_menu (AppletInfo *info)
+{
+ GList *l;
+
+ if (!info->menu)
+ return;
+
+ for (l = info->user_menu; l; l = l->next) {
+ AppletUserMenu *menu = l->data;
+
+ menu->menuitem =NULL;
+ menu->submenu =NULL;
+ }
+
+ g_object_unref (info->menu);
+ info->menu = mate_panel_applet_create_menu (info);
+}
+
+static void
+mate_panel_applet_locked_change_notify (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ GtkWidget *applet)
+{
+ MateConfValue *value;
+ gboolean locked;
+ gboolean applet_locked;
+ AppletInfo *info;
+ PanelWidget *panel_widget;
+
+ g_assert (applet != NULL);
+
+ info = (AppletInfo *) g_object_get_data (G_OBJECT (applet),
+ "applet_info");
+ if (info == NULL)
+ return;
+
+ value = mateconf_entry_get_value (entry);
+ if (value == NULL || value->type != MATECONF_VALUE_BOOL)
+ return;
+
+ locked = mateconf_value_get_bool (value);
+
+ panel_widget = mate_panel_applet_get_panel_widget (info);
+ applet_locked = panel_widget_get_applet_locked (panel_widget,
+ info->widget);
+
+ if ((locked && applet_locked) || !(locked || applet_locked))
+ return;
+
+ mate_panel_applet_toggle_locked (info);
+
+ if (info->type == PANEL_OBJECT_APPLET)
+ mate_panel_applet_frame_sync_menu_state (MATE_PANEL_APPLET_FRAME (info->widget));
+ else
+ mate_panel_applet_recreate_menu (info);
+}
+
+static void
+applet_remove_callback (GtkWidget *widget,
+ AppletInfo *info)
+{
+
+ if (info->type == PANEL_OBJECT_DRAWER)
+ drawer_query_deletion (info->data);
+ else
+ panel_profile_delete_object (info);
+}
+
+static inline GdkScreen *
+applet_user_menu_get_screen (AppletUserMenu *menu)
+{
+ PanelWidget *panel_widget;
+
+ panel_widget = mate_panel_applet_get_panel_widget (menu->info);
+
+ return gtk_window_get_screen (GTK_WINDOW (panel_widget->toplevel));
+}
+
+static void
+applet_callback_callback (GtkWidget *widget,
+ AppletUserMenu *menu)
+{
+ GdkScreen *screen;
+
+ g_return_if_fail (menu->info != NULL);
+
+ screen = applet_user_menu_get_screen (menu);
+
+ switch (menu->info->type) {
+ case PANEL_OBJECT_LAUNCHER:
+ if (!strcmp (menu->name, "launch"))
+ launcher_launch (menu->info->data, widget);
+ else if (!strcmp (menu->name, "properties"))
+ launcher_properties (menu->info->data);
+ break;
+ case PANEL_OBJECT_DRAWER:
+ if (strcmp (menu->name, "add") == 0) {
+ Drawer *drawer = menu->info->data;
+
+ panel_addto_present (GTK_MENU_ITEM (widget),
+ panel_toplevel_get_panel_widget (drawer->toplevel));
+ } else if (strcmp (menu->name, "properties") == 0) {
+ Drawer *drawer = menu->info->data;
+
+ panel_properties_dialog_present (drawer->toplevel);
+ } else if (strcmp (menu->name, "help") == 0) {
+ panel_show_help (screen,
+ "user-guide", "gospanel-18", NULL);
+ }
+ break;
+ case PANEL_OBJECT_MENU:
+ panel_menu_button_invoke_menu (
+ PANEL_MENU_BUTTON (menu->info->widget), menu->name);
+ break;
+ case PANEL_OBJECT_ACTION:
+ case PANEL_OBJECT_LOGOUT:
+ case PANEL_OBJECT_LOCK:
+ panel_action_button_invoke_menu (
+ PANEL_ACTION_BUTTON (menu->info->widget), menu->name);
+ break;
+ case PANEL_OBJECT_MENU_BAR:
+ panel_menu_bar_invoke_menu (
+ PANEL_MENU_BAR (menu->info->widget), menu->name);
+ break;
+
+ case PANEL_OBJECT_APPLET:
+ /*
+ * Applet's menu's are handled differently
+ */
+ break;
+ case PANEL_OBJECT_SEPARATOR:
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+applet_menu_show (GtkWidget *w,
+ AppletInfo *info)
+{
+ PanelWidget *panel_widget;
+
+ panel_widget = mate_panel_applet_get_panel_widget (info);
+
+ panel_toplevel_push_autohide_disabler (panel_widget->toplevel);
+}
+
+
+static void
+applet_menu_deactivate (GtkWidget *w,
+ AppletInfo *info)
+{
+ PanelWidget *panel_widget;
+
+ panel_widget = mate_panel_applet_get_panel_widget (info);
+
+ panel_toplevel_pop_autohide_disabler (panel_widget->toplevel);
+}
+
+AppletUserMenu *
+mate_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) == 0)
+ return menu;
+ }
+
+ return NULL;
+}
+
+void
+mate_panel_applet_add_callback (AppletInfo *info,
+ const char *callback_name,
+ const char *stock_item,
+ const char *menuitem_text,
+ CallbackEnabledFunc is_enabled_func)
+{
+ AppletUserMenu *menu;
+
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (mate_panel_applet_get_callback (info->user_menu,
+ callback_name) == 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->is_enabled_func = is_enabled_func;
+ menu->sensitive = TRUE;
+ menu->info = info;
+ menu->menuitem = NULL;
+ menu->submenu = NULL;
+
+ info->user_menu = g_list_append (info->user_menu, menu);
+
+ mate_panel_applet_recreate_menu (info);
+}
+
+static void
+setup_an_item (AppletUserMenu *menu,
+ GtkWidget *submenu,
+ int is_submenu)
+{
+ GtkWidget *image = NULL;
+
+ menu->menuitem = gtk_image_menu_item_new_with_mnemonic (menu->text);
+ if (menu->stock_item && menu->stock_item [0]) {
+ image = gtk_image_new_from_stock (menu->stock_item,
+ GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu->menuitem),
+ image);
+ }
+ gtk_widget_show (menu->menuitem);
+
+ g_signal_connect (G_OBJECT (menu->menuitem), "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &menu->menuitem);
+
+ if(submenu)
+ gtk_menu_shell_append (GTK_MENU_SHELL (submenu), menu->menuitem);
+
+ /*if an item not a submenu*/
+ if (!is_submenu) {
+ g_signal_connect (menu->menuitem, "activate",
+ G_CALLBACK (applet_callback_callback),
+ menu);
+ g_signal_connect (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 = gtk_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 = mate_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 = gtk_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);
+}
+
+GtkWidget *
+mate_panel_applet_create_menu (AppletInfo *info)
+{
+ GtkWidget *menu;
+ GtkWidget *menuitem;
+ GList *l;
+ PanelWidget *panel_widget;
+ gboolean added_anything = FALSE;
+
+ panel_widget = mate_panel_applet_get_panel_widget (info);
+
+ menu = g_object_ref_sink (gtk_menu_new ());
+
+ /* connect the show & deactivate signal, so that we can "disallow" and
+ * "re-allow" autohide when the menu is shown/deactivated.
+ */
+ g_signal_connect (menu, "show",
+ G_CALLBACK (applet_menu_show), info);
+ g_signal_connect (menu, "deactivate",
+ G_CALLBACK (applet_menu_deactivate), info);
+
+ for (l = info->user_menu; l; l = l->next) {
+ AppletUserMenu *user_menu = l->data;
+
+ if (user_menu->is_enabled_func && !user_menu->is_enabled_func ())
+ continue;
+
+ add_to_submenus (info, "", user_menu->name, user_menu,
+ menu, info->user_menu);
+
+ added_anything = TRUE;
+ }
+
+ if (!panel_lockdown_get_locked_down ()) {
+ GtkWidget *image;
+ gboolean locked;
+ gboolean lockable;
+ gboolean movable;
+ gboolean removable;
+
+ lockable = mate_panel_applet_lockable (info);
+ movable = mate_panel_applet_can_freely_move (info);
+ removable = panel_profile_id_lists_are_writable ();
+
+ locked = panel_widget_get_applet_locked (panel_widget, info->widget);
+
+ if (added_anything) {
+ menuitem = gtk_separator_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ gtk_widget_show (menuitem);
+ }
+
+ menuitem = gtk_image_menu_item_new_with_mnemonic (_("_Remove From Panel"));
+ image = gtk_image_new_from_stock (GTK_STOCK_REMOVE,
+ GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem),
+ image);
+ g_signal_connect (menuitem, "activate",
+ G_CALLBACK (applet_remove_callback), info);
+ gtk_widget_show (menuitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ gtk_widget_set_sensitive (menuitem, (!locked || lockable) && removable);
+
+ menuitem = gtk_menu_item_new_with_mnemonic (_("_Move"));
+ g_signal_connect (menuitem, "activate",
+ G_CALLBACK (move_applet_callback), info);
+ gtk_widget_show (menuitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ gtk_widget_set_sensitive (menuitem, !locked && movable);
+
+ g_assert (info->move_item == NULL);
+
+ info->move_item = menuitem;
+ g_object_add_weak_pointer (G_OBJECT (menuitem),
+ (gpointer *) &info->move_item);
+
+ menuitem = gtk_separator_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ gtk_widget_show (menuitem);
+
+ menuitem = gtk_check_menu_item_new_with_mnemonic (_("Loc_k To Panel"));
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem),
+ locked);
+ g_signal_connect (menuitem, "toggled",
+ G_CALLBACK (mate_panel_applet_lock), info);
+ gtk_widget_show (menuitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ gtk_widget_set_sensitive (menuitem, lockable);
+
+ added_anything = TRUE;
+ }
+
+ if ( ! added_anything) {
+ g_object_unref (menu);
+ return NULL;
+ }
+
+ return menu;
+}
+
+void
+mate_panel_applet_menu_set_recurse (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_get_submenu (GTK_MENU_ITEM (l->data));
+
+ if (submenu)
+ mate_panel_applet_menu_set_recurse (
+ GTK_MENU (submenu), key, data);
+ }
+
+ g_list_free (children);
+}
+
+void
+mate_panel_applet_position_menu (GtkMenu *menu,
+ int *x,
+ int *y,
+ gboolean *push_in,
+ GtkWidget *applet)
+{
+ GtkAllocation allocation;
+ GtkRequisition requisition;
+ GdkScreen *screen;
+ GtkWidget *parent;
+ int menu_x = 0;
+ int menu_y = 0;
+ int pointer_x;
+ int pointer_y;
+
+ parent = gtk_widget_get_parent (applet);
+
+ g_return_if_fail (PANEL_IS_WIDGET (parent));
+
+ screen = gtk_widget_get_screen (applet);
+
+ gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
+
+ gdk_window_get_origin (gtk_widget_get_window (applet), &menu_x, &menu_y);
+ gtk_widget_get_pointer (applet, &pointer_x, &pointer_y);
+
+ gtk_widget_get_allocation (applet, &allocation);
+
+ if (!gtk_widget_get_has_window (applet)) {
+ menu_x += allocation.x;
+ menu_y += allocation.y;
+ }
+
+ if (PANEL_WIDGET (parent)->orient == GTK_ORIENTATION_HORIZONTAL) {
+ if (gtk_widget_get_direction (GTK_WIDGET (menu)) != GTK_TEXT_DIR_RTL) {
+ if (pointer_x < allocation.width &&
+ requisition.width < pointer_x)
+ menu_x += MIN (pointer_x,
+ allocation.width - requisition.width);
+ } else {
+ menu_x += allocation.width - requisition.width;
+ if (pointer_x > 0 && pointer_x < allocation.width &&
+ pointer_x < allocation.width - requisition.width) {
+ menu_x -= MIN (allocation.width - pointer_x,
+ allocation.width - requisition.width);
+ }
+ }
+ menu_x = MIN (menu_x, gdk_screen_get_width (screen) - requisition.width);
+
+ if (menu_y > gdk_screen_get_height (screen) / 2)
+ menu_y -= requisition.height;
+ else
+ menu_y += allocation.height;
+ } else {
+ if (pointer_y < allocation.height &&
+ requisition.height < pointer_y)
+ menu_y += MIN (pointer_y, allocation.height - requisition.height);
+ menu_y = MIN (menu_y, gdk_screen_get_height (screen) - requisition.height);
+
+ if (menu_x > gdk_screen_get_width (screen) / 2)
+ menu_x -= requisition.width;
+ else
+ menu_x += allocation.width;
+ }
+
+ *x = menu_x;
+ *y = menu_y;
+ *push_in = TRUE;
+}
+
+static void
+applet_show_menu (AppletInfo *info,
+ GdkEventButton *event)
+{
+ PanelWidget *panel_widget;
+
+ g_return_if_fail (info != NULL);
+
+ panel_widget = mate_panel_applet_get_panel_widget (info);
+
+ if (info->menu == NULL)
+ info->menu = mate_panel_applet_create_menu (info);
+
+ if (info->menu == NULL)
+ return;
+
+ mate_panel_applet_menu_set_recurse (GTK_MENU (info->menu),
+ "menu_panel",
+ panel_widget);
+
+ gtk_menu_set_screen (GTK_MENU (info->menu),
+ gtk_window_get_screen (GTK_WINDOW (panel_widget->toplevel)));
+
+ if (!gtk_widget_get_realized (info->menu))
+ gtk_widget_show (info->menu);
+
+ gtk_menu_popup (GTK_MENU (info->menu),
+ NULL,
+ NULL,
+ (GtkMenuPositionFunc) mate_panel_applet_position_menu,
+ info->widget,
+ event->button,
+ event->time);
+}
+
+static gboolean
+applet_do_popup_menu (GtkWidget *widget,
+ GdkEventButton *event,
+ AppletInfo *info)
+{
+ if (mate_panel_applet_is_in_drag ())
+ return FALSE;
+
+ if (info->type == PANEL_OBJECT_APPLET)
+ return FALSE;
+
+ applet_show_menu (info, event);
+
+ 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);
+
+ return FALSE;
+}
+
+static void
+mate_panel_applet_destroy (GtkWidget *widget,
+ AppletInfo *info)
+{
+ GList *l;
+
+ g_return_if_fail (info != NULL);
+
+ info->widget = NULL;
+
+ registered_applets = g_slist_remove (registered_applets, info);
+
+ queued_position_saves =
+ g_slist_remove (queued_position_saves, info);
+
+ if (info->type == PANEL_OBJECT_DRAWER) {
+ Drawer *drawer = info->data;
+
+ if (drawer->toplevel) {
+ PanelWidget *panel_widget;
+
+ panel_widget = panel_toplevel_get_panel_widget (
+ drawer->toplevel);
+ panel_widget->master_widget = NULL;
+
+ gtk_widget_destroy (GTK_WIDGET (drawer->toplevel));
+ drawer->toplevel = NULL;
+ }
+ }
+
+ if (info->type != PANEL_OBJECT_APPLET)
+ panel_lockdown_notify_remove (G_CALLBACK (mate_panel_applet_recreate_menu),
+ info);
+
+ if (info->menu)
+ g_object_unref (info->menu);
+ info->menu = NULL;
+
+ if (info->data_destroy)
+ info->data_destroy (info->data);
+ info->data = NULL;
+
+ for (l = info->user_menu; l != NULL; l = l->next) {
+ AppletUserMenu *umenu = l->data;
+
+ g_free (umenu->name);
+ g_free (umenu->stock_item);
+ g_free (umenu->text);
+
+ g_free (umenu);
+ }
+
+ g_list_free (info->user_menu);
+ info->user_menu = NULL;
+
+ g_free (info->id);
+ info->id = NULL;
+
+ g_free (info);
+}
+
+typedef struct {
+ char *id;
+ PanelObjectType type;
+ char *toplevel_id;
+ int position;
+ guint right_stick : 1;
+ guint locked : 1;
+} MatePanelAppletToLoad;
+
+/* Each time those lists get both empty,
+ * mate_panel_applet_queue_initial_unhide_toplevels() should be called */
+static GSList *mate_panel_applets_to_load = NULL;
+static GSList *mate_panel_applets_loading = NULL;
+/* We have a timeout to always unhide toplevels after a delay, in case of some
+ * blocking applet */
+#define UNHIDE_TOPLEVELS_TIMEOUT_SECONDS 5
+static guint mate_panel_applet_unhide_toplevels_timeout = 0;
+
+static gboolean mate_panel_applet_have_load_idle = FALSE;
+
+static void
+free_applet_to_load (MatePanelAppletToLoad *applet)
+{
+ g_free (applet->id);
+ applet->id = NULL;
+
+ g_free (applet->toplevel_id);
+ applet->toplevel_id = NULL;
+
+ g_free (applet);
+}
+
+gboolean
+mate_panel_applet_on_load_queue (const char *id)
+{
+ GSList *li;
+ for (li = mate_panel_applets_to_load; li != NULL; li = li->next) {
+ MatePanelAppletToLoad *applet = li->data;
+ if (strcmp (applet->id, id) == 0)
+ return TRUE;
+ }
+ for (li = mate_panel_applets_loading; li != NULL; li = li->next) {
+ MatePanelAppletToLoad *applet = li->data;
+ if (strcmp (applet->id, id) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* This doesn't do anything if the initial unhide already happened */
+static gboolean
+mate_panel_applet_queue_initial_unhide_toplevels (gpointer user_data)
+{
+ GSList *l;
+
+ if (mate_panel_applet_unhide_toplevels_timeout != 0) {
+ g_source_remove (mate_panel_applet_unhide_toplevels_timeout);
+ mate_panel_applet_unhide_toplevels_timeout = 0;
+ }
+
+ for (l = panel_toplevel_list_toplevels (); l != NULL; l = l->next)
+ panel_toplevel_queue_initial_unhide ((PanelToplevel *) l->data);
+
+ return FALSE;
+}
+
+void
+mate_panel_applet_stop_loading (const char *id)
+{
+ MatePanelAppletToLoad *applet;
+ GSList *l;
+
+ for (l = mate_panel_applets_loading; l; l = l->next) {
+ applet = l->data;
+
+ if (strcmp (applet->id, id) == 0)
+ break;
+ }
+
+ /* this can happen if we reload an applet after it crashed,
+ * for example */
+ if (l != NULL) {
+ mate_panel_applets_loading = g_slist_delete_link (mate_panel_applets_loading, l);
+ free_applet_to_load (applet);
+ }
+
+ if (mate_panel_applets_loading == NULL && mate_panel_applets_to_load == NULL)
+ mate_panel_applet_queue_initial_unhide_toplevels (NULL);
+}
+
+static gboolean
+mate_panel_applet_load_idle_handler (gpointer dummy)
+{
+ PanelObjectType applet_type;
+ MatePanelAppletToLoad *applet = NULL;
+ PanelToplevel *toplevel = NULL;
+ PanelWidget *panel_widget;
+ GSList *l;
+
+ if (!mate_panel_applets_to_load) {
+ mate_panel_applet_have_load_idle = FALSE;
+ return FALSE;
+ }
+
+ for (l = mate_panel_applets_to_load; l; l = l->next) {
+ applet = l->data;
+
+ toplevel = panel_profile_get_toplevel_by_id (applet->toplevel_id);
+ if (toplevel)
+ break;
+ }
+
+ if (!l) {
+ /* All the remaining applets don't have a panel */
+ for (l = mate_panel_applets_to_load; l; l = l->next)
+ free_applet_to_load (l->data);
+ g_slist_free (mate_panel_applets_to_load);
+ mate_panel_applets_to_load = NULL;
+ mate_panel_applet_have_load_idle = FALSE;
+
+ if (mate_panel_applets_loading == NULL) {
+ /* unhide any potential initially hidden toplevel */
+ mate_panel_applet_queue_initial_unhide_toplevels (NULL);
+ }
+
+ return FALSE;
+ }
+
+ mate_panel_applets_to_load = g_slist_delete_link (mate_panel_applets_to_load, l);
+ mate_panel_applets_loading = g_slist_append (mate_panel_applets_loading, applet);
+
+ panel_widget = panel_toplevel_get_panel_widget (toplevel);
+
+ if (applet->right_stick) {
+ if (!panel_widget->packed)
+ applet->position = panel_widget->size - applet->position;
+ else
+ applet->position = -1;
+ }
+
+ /* We load applets asynchronously, so we specifically don't call
+ * mate_panel_applet_stop_loading() for this type. However, in case of
+ * failure during the load, we might call mate_panel_applet_stop_loading()
+ * synchronously, which will make us lose the content of the applet
+ * variable. So we save the type to be sure we always ignore the
+ * applets. */
+ applet_type = applet->type;
+
+ switch (applet_type) {
+ case PANEL_OBJECT_APPLET:
+ mate_panel_applet_frame_load_from_mateconf (
+ panel_widget,
+ applet->locked,
+ applet->position,
+ applet->id);
+ break;
+ case PANEL_OBJECT_DRAWER:
+ drawer_load_from_mateconf (panel_widget,
+ applet->locked,
+ applet->position,
+ applet->id);
+ break;
+ case PANEL_OBJECT_MENU:
+ panel_menu_button_load_from_mateconf (panel_widget,
+ applet->locked,
+ applet->position,
+ TRUE,
+ applet->id);
+ break;
+ case PANEL_OBJECT_LAUNCHER:
+ launcher_load_from_mateconf (panel_widget,
+ applet->locked,
+ applet->position,
+ applet->id);
+ break;
+ case PANEL_OBJECT_LOGOUT:
+ case PANEL_OBJECT_LOCK:
+ panel_action_button_load_compatible (
+ applet->type,
+ panel_widget,
+ applet->locked,
+ applet->position,
+ TRUE,
+ applet->id);
+ break;
+ case PANEL_OBJECT_ACTION:
+ panel_action_button_load_from_mateconf (
+ panel_widget,
+ applet->locked,
+ applet->position,
+ TRUE,
+ applet->id);
+ break;
+ case PANEL_OBJECT_MENU_BAR:
+ panel_menu_bar_load_from_mateconf (
+ panel_widget,
+ applet->locked,
+ applet->position,
+ TRUE,
+ applet->id);
+ break;
+ case PANEL_OBJECT_SEPARATOR:
+ panel_separator_load_from_mateconf (panel_widget,
+ applet->locked,
+ applet->position,
+ applet->id);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ /* Only the real applets will do a late stop_loading */
+ if (applet_type != PANEL_OBJECT_APPLET)
+ mate_panel_applet_stop_loading (applet->id);
+
+ return TRUE;
+}
+
+void
+mate_panel_applet_queue_applet_to_load (const char *id,
+ PanelObjectType type,
+ const char *toplevel_id,
+ int position,
+ gboolean right_stick,
+ gboolean locked)
+{
+ MatePanelAppletToLoad *applet;
+
+ if (!toplevel_id) {
+ g_warning ("No toplevel on which to load object '%s'\n", id);
+ return;
+ }
+
+ applet = g_new0 (MatePanelAppletToLoad, 1);
+
+ applet->id = g_strdup (id);
+ applet->type = type;
+ applet->toplevel_id = g_strdup (toplevel_id);
+ applet->position = position;
+ applet->right_stick = right_stick != FALSE;
+ applet->locked = locked != FALSE;
+
+ mate_panel_applets_to_load = g_slist_prepend (mate_panel_applets_to_load, applet);
+}
+
+static int
+mate_panel_applet_compare (const MatePanelAppletToLoad *a,
+ const MatePanelAppletToLoad *b)
+{
+ int c;
+
+ if ((c = strcmp (a->toplevel_id, b->toplevel_id)))
+ return c;
+ else if (a->right_stick != b->right_stick)
+ return b->right_stick ? -1 : 1;
+ else
+ return a->position - b->position;
+}
+
+void
+mate_panel_applet_load_queued_applets (gboolean initial_load)
+{
+ if (!mate_panel_applets_to_load) {
+ mate_panel_applet_queue_initial_unhide_toplevels (NULL);
+ return;
+ }
+
+ if (mate_panel_applets_to_load && mate_panel_applet_unhide_toplevels_timeout == 0) {
+ /* Install a timeout to make sure we don't block the
+ * unhiding because of an applet that doesn't load */
+ mate_panel_applet_unhide_toplevels_timeout =
+ g_timeout_add_seconds (UNHIDE_TOPLEVELS_TIMEOUT_SECONDS,
+ mate_panel_applet_queue_initial_unhide_toplevels,
+ NULL);
+ }
+
+ mate_panel_applets_to_load = g_slist_sort (mate_panel_applets_to_load,
+ (GCompareFunc) mate_panel_applet_compare);
+
+ if ( ! mate_panel_applet_have_load_idle) {
+ /* on panel startup, we don't care about redraws of the
+ * toplevels since they are hidden, so we give a higher
+ * priority to loading of applets */
+ if (initial_load)
+ g_idle_add_full (G_PRIORITY_HIGH_IDLE,
+ mate_panel_applet_load_idle_handler,
+ NULL, NULL);
+ else
+ g_idle_add (mate_panel_applet_load_idle_handler, NULL);
+
+ mate_panel_applet_have_load_idle = TRUE;
+ }
+}
+
+static const char* mate_panel_applet_get_toplevel_id(AppletInfo* applet)
+{
+ PanelWidget* panel_widget;
+
+ g_return_val_if_fail(applet != NULL, NULL);
+ g_return_val_if_fail(GTK_IS_WIDGET(applet->widget), NULL);
+
+ panel_widget = mate_panel_applet_get_panel_widget(applet);
+
+ if (!panel_widget)
+ {
+ return NULL;
+ }
+
+ return panel_profile_get_toplevel_id(panel_widget->toplevel);
+}
+
+static gboolean
+mate_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;
+
+ mate_panel_applet_save_position (info, info->id, TRUE);
+ }
+
+ g_slist_free (queued_position_saves);
+ queued_position_saves = NULL;
+
+ return FALSE;
+}
+
+void
+mate_panel_applet_save_position (AppletInfo *applet_info,
+ const char *id,
+ gboolean immediate)
+{
+ PanelMateConfKeyType key_type;
+ MateConfClient *client;
+ PanelWidget *panel_widget;
+ const char *key;
+ const char *toplevel_id;
+ char *old_toplevel_id;
+ gboolean right_stick;
+ gboolean locked;
+ int position;
+
+ g_return_if_fail (applet_info != NULL);
+
+ if (!immediate) {
+ if (!queued_position_source)
+ queued_position_source =
+ g_timeout_add_seconds (1,
+ (GSourceFunc) mate_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;
+ }
+
+ if (!(toplevel_id = mate_panel_applet_get_toplevel_id (applet_info)))
+ return;
+
+ client = panel_mateconf_get_client ();
+
+ key_type = applet_info->type == PANEL_OBJECT_APPLET ? PANEL_MATECONF_APPLETS : PANEL_MATECONF_OBJECTS;
+
+ panel_widget = mate_panel_applet_get_panel_widget (applet_info);
+
+ /* FIXME: Instead of getting keys, comparing and setting, there
+ should be a dirty flag */
+
+ key = panel_mateconf_full_key (key_type, id, "toplevel_id");
+ old_toplevel_id = mateconf_client_get_string (client, key, NULL);
+ if (old_toplevel_id == NULL || strcmp (old_toplevel_id, toplevel_id) != 0)
+ mateconf_client_set_string (client, key, toplevel_id, NULL);
+ g_free (old_toplevel_id);
+
+ /* Note: changing some properties of the panel that may not be locked down
+ (e.g. background) can change the state of the "panel_right_stick" and
+ "position" properties of an applet that may in fact be locked down.
+ So check if these are writable before attempting to write them */
+
+ right_stick = panel_is_applet_right_stick (applet_info->widget) ? 1 : 0;
+ key = panel_mateconf_full_key (
+ key_type, id, "panel_right_stick");
+ if (mateconf_client_key_is_writable (client, key, NULL) &&
+ (mateconf_client_get_bool (client, key, NULL) ? 1 : 0) != right_stick)
+ mateconf_client_set_bool (client, key, right_stick, NULL);
+
+ position = mate_panel_applet_get_position (applet_info);
+ if (right_stick && !panel_widget->packed)
+ position = panel_widget->size - position;
+
+ key = panel_mateconf_full_key (key_type, id, "position");
+ if (mateconf_client_key_is_writable (client, key, NULL) &&
+ mateconf_client_get_int (client, key, NULL) != position)
+ mateconf_client_set_int (client, key, position, NULL);
+
+ locked = panel_widget_get_applet_locked (panel_widget, applet_info->widget) ? 1 : 0;
+ key = panel_mateconf_full_key (key_type, id, "locked");
+ if (mateconf_client_get_bool (client, key, NULL) ? 1 : 0 != locked)
+ mateconf_client_set_bool (client, key, locked, NULL);
+}
+
+const char *
+mate_panel_applet_get_id (AppletInfo *info)
+{
+ if (!info)
+ return NULL;
+
+ return info->id;
+}
+
+const char *
+mate_panel_applet_get_id_by_widget (GtkWidget *applet_widget)
+{
+ GSList *l;
+
+ if (!applet_widget)
+ return NULL;
+
+ for (l = registered_applets; l; l = l->next) {
+ AppletInfo *info = l->data;
+
+ if (info->widget == applet_widget)
+ return info->id;
+ }
+
+ return NULL;
+}
+
+AppletInfo *
+mate_panel_applet_get_by_id (const char *id)
+{
+ GSList *l;
+
+ for (l = registered_applets; l; l = l->next) {
+ AppletInfo *info = l->data;
+
+ if (!strcmp (info->id, id))
+ return info;
+ }
+
+ return NULL;
+}
+
+GSList *
+mate_panel_applet_list_applets (void)
+{
+ return registered_applets;
+}
+
+AppletInfo *
+mate_panel_applet_get_by_type (PanelObjectType object_type, GdkScreen *screen)
+{
+ GSList *l;
+
+ for (l = registered_applets; l; l = l->next) {
+ AppletInfo *info = l->data;
+
+ if (info->type == object_type) {
+ if (screen) {
+ if (screen == gtk_widget_get_screen (info->widget))
+ return info;
+ } else
+ return info;
+ }
+ }
+
+ return NULL;
+}
+
+AppletInfo *
+mate_panel_applet_register (GtkWidget *applet,
+ gpointer data,
+ GDestroyNotify data_destroy,
+ PanelWidget *panel,
+ gboolean locked,
+ gint pos,
+ gboolean exactpos,
+ PanelObjectType type,
+ const char *id)
+{
+ AppletInfo *info;
+ const char *key;
+
+ g_return_val_if_fail (applet != NULL && panel != NULL, NULL);
+
+ if (gtk_widget_get_has_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->type = type;
+ info->widget = applet;
+ info->menu = NULL;
+ info->data = data;
+ info->data_destroy = data_destroy;
+ info->user_menu = NULL;
+ info->move_item = NULL;
+ info->id = g_strdup (id);
+
+ g_object_set_data (G_OBJECT (applet), "applet_info", info);
+
+ if (type != PANEL_OBJECT_APPLET)
+ panel_lockdown_notify_add (G_CALLBACK (mate_panel_applet_recreate_menu),
+ info);
+
+ key = panel_mateconf_full_key ((type == PANEL_OBJECT_APPLET) ?
+ PANEL_MATECONF_APPLETS : PANEL_MATECONF_OBJECTS,
+ id, "locked");
+ panel_mateconf_notify_add_while_alive (key,
+ (MateConfClientNotifyFunc) mate_panel_applet_locked_change_notify,
+ G_OBJECT (applet));
+
+ if (type == PANEL_OBJECT_DRAWER) {
+ Drawer *drawer = data;
+ PanelWidget *assoc_panel;
+
+ assoc_panel = panel_toplevel_get_panel_widget (drawer->toplevel);
+
+ g_object_set_data (G_OBJECT (applet),
+ MATE_PANEL_APPLET_ASSOC_PANEL_KEY, assoc_panel);
+ assoc_panel->master_widget = applet;
+ g_object_add_weak_pointer (
+ G_OBJECT (applet), (gpointer *) &assoc_panel->master_widget);
+ }
+
+ g_object_set_data (G_OBJECT (applet),
+ MATE_PANEL_APPLET_FORBIDDEN_PANELS, NULL);
+
+ registered_applets = g_slist_append (registered_applets, info);
+
+ if (panel_widget_add (panel, applet, locked, pos, exactpos) == -1 &&
+ panel_widget_add (panel, applet, locked, 0, TRUE) == -1) {
+ GSList *l;
+
+ for (l = panels; l; l = l->next) {
+ panel = PANEL_WIDGET (l->data);
+
+ if (panel_widget_add (panel, applet, locked, 0, TRUE) != -1)
+ break;
+ }
+
+ if (!l) {
+ g_warning (_("Cannot find an empty spot"));
+ panel_profile_delete_object (info);
+ return NULL;
+ }
+ }
+
+ if (BUTTON_IS_WIDGET (applet) ||
+ gtk_widget_get_has_window (applet)) {
+ g_signal_connect (applet, "button_press_event",
+ G_CALLBACK (applet_button_press),
+ info);
+
+ g_signal_connect (applet, "popup_menu",
+ G_CALLBACK (applet_popup_menu),
+ info);
+ }
+
+ g_signal_connect (applet, "destroy",
+ G_CALLBACK (mate_panel_applet_destroy),
+ info);
+
+ mate_panel_applet_set_dnd_enabled (info, !locked);
+
+ gtk_widget_show_all (applet);
+
+ orientation_change (info, panel);
+ size_change (info, panel);
+ back_change (info, panel);
+
+ if (type != PANEL_OBJECT_APPLET)
+ gtk_widget_grab_focus (applet);
+ else
+ gtk_widget_child_focus (applet, GTK_DIR_TAB_FORWARD);
+
+ return info;
+}
+
+int
+mate_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), MATE_PANEL_APPLET_DATA);
+
+ return applet_data->pos;
+}
+
+gboolean
+mate_panel_applet_can_freely_move (AppletInfo *applet)
+{
+ MateConfClient *client;
+ PanelMateConfKeyType key_type;
+ const char *key;
+
+ if (panel_lockdown_get_locked_down ())
+ return FALSE;
+
+ client = panel_mateconf_get_client ();
+
+ key_type = (applet->type == PANEL_OBJECT_APPLET) ? PANEL_MATECONF_APPLETS : PANEL_MATECONF_OBJECTS;
+
+ key = panel_mateconf_full_key (key_type, applet->id, "position");
+ if (!mateconf_client_key_is_writable (client, key, NULL))
+ return FALSE;
+
+ key = panel_mateconf_full_key (key_type, applet->id, "toplevel_id");
+ if (!mateconf_client_key_is_writable (client, key, NULL))
+ return FALSE;
+
+ key = panel_mateconf_full_key (key_type, applet->id, "panel_right_stick");
+ if (!mateconf_client_key_is_writable (client, key, NULL))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+mate_panel_applet_lockable (AppletInfo *applet)
+{
+ MateConfClient *client;
+ PanelMateConfKeyType key_type;
+ const char *key;
+
+ if (panel_lockdown_get_locked_down ())
+ return FALSE;
+
+ client = panel_mateconf_get_client ();
+
+ key_type = (applet->type == PANEL_OBJECT_APPLET) ? PANEL_MATECONF_APPLETS : PANEL_MATECONF_OBJECTS;
+
+ key = panel_mateconf_full_key (key_type, applet->id, "locked");
+
+ return mateconf_client_key_is_writable (client, key, NULL);
+}