/* Mate panel: general applet functionality * (C) 1997 the Free Software Foundation * * Authors: George Lebl * Federico Mena * Miguel de Icaza */ #include #include #include #include #include #include #include #include #include "button-widget.h" #include "drawer.h" #include "launcher.h" #include "panel-addto.h" #include "panel-config-global.h" #include "panel-applet-frame.h" #include "panel-action-button.h" #include "panel-menu-bar.h" #include "panel-separator.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" #include "panel-schemas.h" #define SMALL_ICON_SIZE 20 static GSList *registered_applets = NULL; static GSList *queued_position_saves = NULL; static guint queued_position_source = 0; static GtkCheckMenuItem *checkbox_id = NULL; static void applet_menu_show (GtkWidget *w, AppletInfo *info); static void applet_menu_deactivate (GtkWidget *w, AppletInfo *info); 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_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 checkbox_status (GtkCheckMenuItem *menuitem, AppletInfo *info) { checkbox_id = GTK_CHECK_MENU_ITEM (menuitem); } static void mate_panel_applet_lock (GtkMenuItem *menuitem, AppletInfo *info) { gboolean locked; locked = mate_panel_applet_toggle_locked (info); gtk_check_menu_item_set_active (checkbox_id, 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_signal_handlers_disconnect_by_func (info->menu, G_CALLBACK (applet_menu_show), info); g_signal_handlers_disconnect_by_func (info->menu, G_CALLBACK (applet_menu_deactivate), info); g_object_unref (info->menu); info->menu = mate_panel_applet_create_menu (info); } static void mate_panel_applet_locked_change_notify (GSettings *settings, gchar *key, GtkWidget *applet) { gboolean locked; gboolean applet_locked; AppletInfo *info; PanelWidget *panel_widget; if (applet == NULL || !GTK_IS_WIDGET (applet)) return; info = (AppletInfo *) g_object_get_data (G_OBJECT (applet), "applet_info"); if (info == NULL) return; locked = g_settings_get_boolean (settings, key); 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, NULL); else if (!strcmp (menu->name, "properties")) launcher_properties (menu->info->data); else if (g_str_has_prefix (menu->name, "launch-action_")) { const gchar *action; action = menu->name + (sizeof("launch-action_") - 1); launcher_launch (menu->info->data, action); } 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, "mate-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: 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 *icon_name, 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->gicon = panel_gicon_from_icon_name (icon_name); 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); } void mate_panel_applet_clear_user_menu (AppletInfo *info) { GList *l; for (l = info->user_menu; l != NULL; l = l->next) { AppletUserMenu *umenu = l->data; g_free (umenu->name); g_clear_object (&(umenu->gicon)); g_free (umenu->text); g_free (umenu); } g_list_free (info->user_menu); info->user_menu = NULL; } static void setup_an_item (AppletUserMenu *menu, GtkWidget *submenu, int is_submenu) { menu->menuitem = panel_image_menu_item_new_from_gicon (menu->gicon, menu->text); 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->gicon = 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 ()); gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE); /* 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 ()) { 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 = panel_image_menu_item_new_from_icon ("list-remove", _("_Remove From Panel")); 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 = panel_image_menu_item_new_from_icon (NULL, _("_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, "map", G_CALLBACK (checkbox_status), info); menuitem = panel_check_menu_item_new (menuitem); g_signal_connect (menuitem, "activate", 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; } /* Set up theme and transparency support */ GtkWidget *toplevel = gtk_widget_get_toplevel (menu); /* Fix any failures of compiz/other wm's to communicate with gtk for transparency */ GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(toplevel)); GdkVisual *visual = gdk_screen_get_rgba_visual(screen); gtk_widget_set_visual(GTK_WIDGET(toplevel), visual); /* Set menu and it's toplevel window to follow panel theme */ GtkStyleContext *context; context = gtk_widget_get_style_context (GTK_WIDGET(toplevel)); gtk_style_context_add_class(context,"gnome-panel-menu-bar"); gtk_style_context_add_class(context,"mate-panel-menu-bar"); 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); } 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_at_pointer (GTK_MENU (info->menu), NULL); } 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) { gboolean applet_locked; PanelWidget *panel_widget; panel_widget = mate_panel_applet_get_panel_widget (info); applet_locked = panel_widget_get_applet_locked (panel_widget, info->widget); if (!applet_locked) mate_panel_applet_set_dnd_enabled (info, TRUE); if (event->button == 3) return applet_do_popup_menu (widget, event, info); return FALSE; } static void mate_panel_applet_destroy (GtkWidget *widget, AppletInfo *info) { g_return_if_fail (info != NULL); g_signal_handlers_disconnect_by_data(info->settings,widget); info->widget = NULL; if (info->settings) { g_object_unref (info->settings); info->settings = 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_signal_handlers_disconnect_by_func (info->menu, G_CALLBACK (applet_menu_show), info); g_signal_handlers_disconnect_by_func (info->menu, G_CALLBACK (applet_menu_deactivate), info); g_object_unref (info->menu); } info->menu = NULL; if (info->data_destroy) info->data_destroy (info->data); info->data = NULL; mate_panel_applet_clear_user_menu (info); 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_gsettings ( panel_widget, applet->locked, applet->position, applet->id); break; case PANEL_OBJECT_DRAWER: drawer_load_from_gsettings (panel_widget, applet->locked, applet->position, applet->id); break; case PANEL_OBJECT_MENU: panel_menu_button_load_from_gsettings (panel_widget, applet->locked, applet->position, TRUE, applet->id); break; case PANEL_OBJECT_LAUNCHER: launcher_load_from_gsettings (panel_widget, applet->locked, applet->position, applet->id); break; case PANEL_OBJECT_ACTION: panel_action_button_load_from_gsettings ( panel_widget, applet->locked, applet->position, TRUE, applet->id); break; case PANEL_OBJECT_MENU_BAR: panel_menu_bar_load_from_gsettings ( panel_widget, applet->locked, applet->position, TRUE, applet->id); break; case PANEL_OBJECT_SEPARATOR: panel_separator_load_from_gsettings (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) { PanelWidget *panel_widget; 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; panel_widget = mate_panel_applet_get_panel_widget (applet_info); /* FIXME: Instead of getting keys, comparing and setting, there should be a dirty flag */ old_toplevel_id = g_settings_get_string (applet_info->settings, PANEL_OBJECT_TOPLEVEL_ID_KEY); if (old_toplevel_id == NULL || strcmp (old_toplevel_id, toplevel_id) != 0) g_settings_set_string (applet_info->settings, PANEL_OBJECT_TOPLEVEL_ID_KEY, toplevel_id); 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 */ locked = panel_widget_get_applet_locked (panel_widget, applet_info->widget) ? 1 : 0; if (g_settings_get_boolean (applet_info->settings, PANEL_OBJECT_LOCKED_KEY) ? 1 : 0 != locked) g_settings_set_boolean (applet_info->settings, PANEL_OBJECT_LOCKED_KEY, locked); if (locked) { // Until position calculations are refactored to fix the issue of the panel applets // getting reordered on resolution changes... // .. don't save position/right-stick on locked applets return; } right_stick = panel_is_applet_right_stick (applet_info->widget) ? 1 : 0; if (g_settings_is_writable (applet_info->settings, PANEL_OBJECT_PANEL_RIGHT_STICK_KEY) && (g_settings_get_boolean (applet_info->settings, PANEL_OBJECT_PANEL_RIGHT_STICK_KEY) ? 1 : 0) != right_stick) g_settings_set_boolean (applet_info->settings, PANEL_OBJECT_PANEL_RIGHT_STICK_KEY, right_stick); position = mate_panel_applet_get_position (applet_info); if (right_stick && !panel_widget->packed) position = panel_widget->size - position; if (g_settings_is_writable (applet_info->settings, PANEL_OBJECT_POSITION_KEY) && g_settings_get_int (applet_info->settings, PANEL_OBJECT_POSITION_KEY) != position) g_settings_set_int (applet_info->settings, PANEL_OBJECT_POSITION_KEY, position); } 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; gchar *path; gchar *locked_changed; 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); path = g_strdup_printf (PANEL_OBJECT_PATH "%s/", id); info->settings = g_settings_new_with_path (PANEL_OBJECT_SCHEMA, path); g_free (path); 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); locked_changed = g_strdup_printf ("changed::%s", PANEL_OBJECT_LOCKED_KEY); g_signal_connect (info->settings, locked_changed, G_CALLBACK (mate_panel_applet_locked_change_notify), G_OBJECT (applet)); g_free (locked_changed); 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) { if (panel_lockdown_get_locked_down ()) return FALSE; if (!g_settings_is_writable (applet->settings, PANEL_OBJECT_POSITION_KEY)) return FALSE; if (!g_settings_is_writable (applet->settings, PANEL_OBJECT_TOPLEVEL_ID_KEY)) return FALSE; if (!g_settings_is_writable (applet->settings, PANEL_OBJECT_PANEL_RIGHT_STICK_KEY)) return FALSE; return TRUE; } gboolean mate_panel_applet_lockable (AppletInfo *applet) { if (panel_lockdown_get_locked_down ()) return FALSE; return g_settings_is_writable (applet->settings, PANEL_OBJECT_LOCKED_KEY); }