/* * MATE panel drawer module. * (C) 1997 The Free Software Foundation * * Authors: Miguel de Icaza * Federico Mena * George Lebl */ #include #include #include #include #include #include #include #include #include #include "drawer.h" #include "applet.h" #include "button-widget.h" #include "panel-config-global.h" #include "panel-mateconf.h" #include "panel-profile.h" #include "panel-util.h" #include "xstuff.h" #include "panel-globals.h" #include "panel-lockdown.h" #include "panel-icon-names.h" static void drawer_click (GtkWidget *w, Drawer *drawer) { if (!panel_toplevel_get_is_hidden (drawer->toplevel)) panel_toplevel_hide (drawer->toplevel, FALSE, -1); else panel_toplevel_unhide (drawer->toplevel); } static void toplevel_destroyed (GtkWidget *widget, Drawer *drawer) { drawer->toplevel = NULL; gtk_widget_destroy (drawer->button); } static void destroy_drawer (GtkWidget *widget, Drawer *drawer) { MateConfClient *client; int i; client = panel_mateconf_get_client (); for (i = 0; i < PANEL_DRAWER_N_LISTENERS; i++) { if (drawer->listeners [i]) mateconf_client_notify_remove (client, drawer->listeners [i]); drawer->listeners [i] = 0; } if (drawer->toplevel) gtk_widget_destroy (GTK_WIDGET (drawer->toplevel)); drawer->toplevel = NULL; if (drawer->close_timeout_id) g_source_remove (drawer->close_timeout_id); drawer->close_timeout_id = 0; } static void drawer_focus_panel_widget (Drawer *drawer, GtkDirectionType direction) { PanelWidget *panel_widget; panel_widget = panel_toplevel_get_panel_widget (drawer->toplevel); gtk_window_present (GTK_WINDOW (drawer->toplevel)); gtk_container_set_focus_child (GTK_CONTAINER (panel_widget), NULL); gtk_widget_child_focus (GTK_WIDGET (panel_widget), direction); } static gboolean key_press_drawer (GtkWidget *widget, GdkEventKey *event, Drawer *drawer) { gboolean retval = TRUE; GtkOrientation orient; if (event->state & gtk_accelerator_get_default_mod_mask ()) return FALSE; orient = PANEL_WIDGET (gtk_widget_get_parent (drawer->button))->orient; switch (event->keyval) { case GDK_Up: case GDK_KP_Up: if (orient == GTK_ORIENTATION_HORIZONTAL) { if (!panel_toplevel_get_is_hidden (drawer->toplevel)) drawer_focus_panel_widget (drawer, GTK_DIR_TAB_BACKWARD); } else { /* let default focus movement happen */ retval = FALSE; } break; case GDK_Left: case GDK_KP_Left: if (orient == GTK_ORIENTATION_VERTICAL) { if (!panel_toplevel_get_is_hidden (drawer->toplevel)) drawer_focus_panel_widget (drawer, GTK_DIR_TAB_BACKWARD); } else { /* let default focus movement happen */ retval = FALSE; } break; case GDK_Down: case GDK_KP_Down: if (orient == GTK_ORIENTATION_HORIZONTAL) { if (!panel_toplevel_get_is_hidden (drawer->toplevel)) drawer_focus_panel_widget (drawer, GTK_DIR_TAB_FORWARD); } else { /* let default focus movement happen */ retval = FALSE; } break; case GDK_Right: case GDK_KP_Right: if (orient == GTK_ORIENTATION_VERTICAL) { if (!panel_toplevel_get_is_hidden (drawer->toplevel)) drawer_focus_panel_widget (drawer, GTK_DIR_TAB_FORWARD); } else { /* let default focus movement happen */ retval = FALSE; } break; case GDK_Escape: panel_toplevel_hide (drawer->toplevel, FALSE, -1); break; default: retval = FALSE; break; } return retval; } /* * This function implements Esc moving focus from the drawer to the drawer * icon and closing the drawer and Shift+Esc moving focus from the drawer * to the drawer icon without closing the drawer when focus is in the drawer. */ static gboolean key_press_drawer_widget (GtkWidget *widget, GdkEventKey *event, Drawer *drawer) { PanelWidget *panel_widget; if (event->keyval != GDK_Escape) return FALSE; panel_widget = panel_toplevel_get_panel_widget (drawer->toplevel); gtk_window_present (GTK_WINDOW (panel_widget->toplevel)); if ((event->state & gtk_accelerator_get_default_mod_mask ()) == GDK_SHIFT_MASK || panel_toplevel_get_is_hidden (drawer->toplevel)) return TRUE; panel_toplevel_hide (drawer->toplevel, FALSE, -1); return TRUE; } static void drag_data_received_cb (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint info, guint time_, Drawer *drawer) { PanelWidget *panel_widget; if (!panel_check_dnd_target_data (widget, context, &info, NULL)) { gtk_drag_finish (context, FALSE, FALSE, time_); return; } panel_widget = panel_toplevel_get_panel_widget (drawer->toplevel); panel_receive_dnd_data ( panel_widget, info, -1, selection_data, context, time_); } static gboolean drag_motion_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time_, Drawer *drawer) { PanelWidget *panel_widget; guint info = 0; if (!panel_check_dnd_target_data (widget, context, &info, NULL)) return FALSE; panel_widget = panel_toplevel_get_panel_widget (drawer->toplevel); if (!panel_check_drop_forbidden (panel_widget, context, info, time_)) return FALSE; if (drawer->close_timeout_id) g_source_remove (drawer->close_timeout_id); drawer->close_timeout_id = 0; button_widget_set_dnd_highlight (BUTTON_WIDGET (widget), TRUE); if (panel_toplevel_get_is_hidden (drawer->toplevel)) { panel_toplevel_unhide (drawer->toplevel); drawer->opened_for_drag = TRUE; } return TRUE; } static gboolean close_drawer_in_idle (gpointer data) { Drawer *drawer = (Drawer *) data; drawer->close_timeout_id = 0; if (drawer->opened_for_drag) { panel_toplevel_hide (drawer->toplevel, FALSE, -1); drawer->opened_for_drag = FALSE; } return FALSE; } static void queue_drawer_close_for_drag (Drawer *drawer) { if (!drawer->close_timeout_id) drawer->close_timeout_id = g_timeout_add_seconds (1, close_drawer_in_idle, drawer); } static void drag_leave_cb (GtkWidget *widget, GdkDragContext *context, guint time_, Drawer *drawer) { queue_drawer_close_for_drag (drawer); button_widget_set_dnd_highlight (BUTTON_WIDGET (widget), FALSE); } static gboolean drag_drop_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time_, Drawer *drawer) { GdkAtom atom = NULL; if (!panel_check_dnd_target_data (widget, context, NULL, &atom)) return FALSE; gtk_drag_get_data (widget, context, atom, time_); return TRUE; } static void drag_data_get_cb (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time, Drawer *drawer) { char *foo; foo = g_strdup_printf ("DRAWER:%d", panel_find_applet_index (widget)); gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), 8, (guchar *)foo, strlen (foo)); g_free (foo); } static void set_tooltip_and_name (Drawer *drawer, const char *tooltip) { g_return_if_fail (drawer != NULL); g_return_if_fail (tooltip != NULL); if (tooltip && !tooltip [0]) tooltip = NULL; panel_toplevel_set_name (drawer->toplevel, tooltip); panel_util_set_tooltip_text (drawer->button, tooltip); } static Drawer * create_drawer_applet (PanelToplevel *toplevel, PanelToplevel *parent_toplevel, const char *tooltip, const char *custom_icon, gboolean use_custom_icon, PanelOrientation orientation) { Drawer *drawer; AtkObject *atk_obj; drawer = g_new0 (Drawer, 1); drawer->toplevel = toplevel; if (!use_custom_icon || !custom_icon || !custom_icon [0]) { drawer->button = button_widget_new (PANEL_ICON_DRAWER, TRUE, orientation); } else { drawer->button = button_widget_new (custom_icon, TRUE, orientation); } if (!drawer->button) { g_free (drawer); return NULL; } atk_obj = gtk_widget_get_accessible (drawer->button); atk_object_set_name (atk_obj, _("Drawer")); set_tooltip_and_name (drawer, tooltip); gtk_drag_dest_set (drawer->button, 0, NULL, 0, 0); g_signal_connect (drawer->button, "drag_data_get", G_CALLBACK (drag_data_get_cb), drawer); g_signal_connect (drawer->button, "drag_data_received", G_CALLBACK (drag_data_received_cb), drawer); g_signal_connect (drawer->button, "drag_motion", G_CALLBACK (drag_motion_cb), drawer); g_signal_connect (drawer->button, "drag_leave", G_CALLBACK (drag_leave_cb), drawer); g_signal_connect (drawer->button, "drag_drop", G_CALLBACK (drag_drop_cb), drawer); g_signal_connect (drawer->button, "clicked", G_CALLBACK (drawer_click), drawer); g_signal_connect (drawer->button, "destroy", G_CALLBACK (destroy_drawer), drawer); g_signal_connect (drawer->button, "key_press_event", G_CALLBACK (key_press_drawer), drawer); g_signal_connect (toplevel, "destroy", G_CALLBACK (toplevel_destroyed), drawer); gtk_widget_show (drawer->button); g_signal_connect (drawer->toplevel, "key_press_event", G_CALLBACK (key_press_drawer_widget), drawer); panel_toplevel_attach_to_widget ( toplevel, parent_toplevel, GTK_WIDGET (drawer->button)); return drawer; } static PanelToplevel * create_drawer_toplevel (const char *drawer_id) { PanelToplevel *toplevel; MateConfClient *client; const char *key; char *toplevel_id; client = panel_mateconf_get_client (); toplevel_id = panel_profile_find_new_id (PANEL_MATECONF_TOPLEVELS); toplevel = panel_profile_load_toplevel (client, PANEL_CONFIG_DIR, PANEL_MATECONF_TOPLEVELS, toplevel_id); if (!toplevel) { g_free (toplevel_id); return NULL; } key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, drawer_id, "attached_toplevel_id"); mateconf_client_set_string (client, key, toplevel_id, NULL); g_free (toplevel_id); panel_profile_set_toplevel_enable_buttons (toplevel, TRUE); panel_profile_set_toplevel_enable_arrows (toplevel, TRUE); return toplevel; } static void drawer_button_size_allocated (GtkWidget *widget, GtkAllocation *alloc, Drawer *drawer) { if (!gtk_widget_get_realized (widget)) return; gtk_widget_queue_resize (GTK_WIDGET (drawer->toplevel)); g_object_set_data (G_OBJECT (widget), "allocated", GINT_TO_POINTER (TRUE)); } static void panel_drawer_use_custom_icon_changed (MateConfClient *client, guint cnxn_id, MateConfEntry *entry, Drawer *drawer) { gboolean use_custom_icon; char *custom_icon = NULL; if (!entry->value || entry->value->type != MATECONF_VALUE_BOOL) return; use_custom_icon = mateconf_value_get_bool (entry->value); if (use_custom_icon) { const char *key; key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, drawer->info->id, "custom_icon"); custom_icon = mateconf_client_get_string (client, key, NULL); } button_widget_set_icon_name (BUTTON_WIDGET (drawer->button), custom_icon); g_free (custom_icon); } static void panel_drawer_custom_icon_changed (MateConfClient *client, guint cnxn_id, MateConfEntry *entry, Drawer *drawer) { const char *custom_icon; if (!entry->value || entry->value->type != MATECONF_VALUE_STRING) return; custom_icon = mateconf_value_get_string (entry->value); if (custom_icon && custom_icon [0]) { const char *key; gboolean use_custom_icon; key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, drawer->info->id, "use_custom_icon"); use_custom_icon = mateconf_client_get_bool (client, key, NULL); if (use_custom_icon) button_widget_set_icon_name (BUTTON_WIDGET (drawer->button), custom_icon); } } static void panel_drawer_tooltip_changed (MateConfClient *client, guint cnxn_id, MateConfEntry *entry, Drawer *drawer) { if (!entry->value || entry->value->type != MATECONF_VALUE_STRING) return; set_tooltip_and_name (drawer, mateconf_value_get_string (entry->value)); } static void panel_drawer_connect_to_mateconf (Drawer *drawer) { MateConfClient *client; const char *key; int i = 0; client = panel_mateconf_get_client (); key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, drawer->info->id, "use_custom_icon"); drawer->listeners [i++] = mateconf_client_notify_add (client, key, (MateConfClientNotifyFunc) panel_drawer_use_custom_icon_changed, drawer, NULL, NULL); key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, drawer->info->id, "custom_icon"); drawer->listeners [i++] = mateconf_client_notify_add (client, key, (MateConfClientNotifyFunc) panel_drawer_custom_icon_changed, drawer, NULL, NULL); key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, drawer->info->id, "tooltip"); drawer->listeners [i++] = mateconf_client_notify_add (client, key, (MateConfClientNotifyFunc) panel_drawer_tooltip_changed, drawer, NULL, NULL); g_assert (i == PANEL_DRAWER_N_LISTENERS); } static gboolean drawer_changes_enabled (void) { return !panel_lockdown_get_locked_down (); } static void load_drawer_applet (char *toplevel_id, const char *custom_icon, gboolean use_custom_icon, const char *tooltip, PanelToplevel *parent_toplevel, gboolean locked, int pos, gboolean exactpos, const char *id) { PanelOrientation orientation; PanelToplevel *toplevel = NULL; Drawer *drawer = NULL; PanelWidget *panel_widget; orientation = panel_toplevel_get_orientation (parent_toplevel); if (toplevel_id) toplevel = panel_profile_get_toplevel_by_id (toplevel_id); if (!toplevel) toplevel = create_drawer_toplevel (id); if (toplevel) { panel_toplevel_hide (toplevel, FALSE, -1); drawer = create_drawer_applet (toplevel, parent_toplevel, tooltip, custom_icon, use_custom_icon, orientation); } if (!drawer) return; panel_widget = panel_toplevel_get_panel_widget (parent_toplevel); drawer->info = mate_panel_applet_register (drawer->button, drawer, (GDestroyNotify) g_free, panel_widget, locked, pos, exactpos, PANEL_OBJECT_DRAWER, id); if (!drawer->info) { gtk_widget_destroy (GTK_WIDGET (toplevel)); return; } g_signal_connect_after (drawer->button, "size_allocate", G_CALLBACK (drawer_button_size_allocated), drawer); panel_widget_add_forbidden (panel_toplevel_get_panel_widget (drawer->toplevel)); panel_widget_set_applet_expandable (panel_widget, GTK_WIDGET (drawer->button), FALSE, TRUE); panel_widget_set_applet_size_constrained (panel_widget, GTK_WIDGET (drawer->button), TRUE); mate_panel_applet_add_callback (drawer->info, "add", GTK_STOCK_ADD, _("_Add to Drawer..."), drawer_changes_enabled); mate_panel_applet_add_callback (drawer->info, "properties", GTK_STOCK_PROPERTIES, _("_Properties"), drawer_changes_enabled); mate_panel_applet_add_callback (drawer->info, "help", GTK_STOCK_HELP, _("_Help"), NULL); panel_drawer_connect_to_mateconf (drawer); } static void panel_drawer_prepare (const char *drawer_id, const char *custom_icon, gboolean use_custom_icon, const char *tooltip, char **attached_toplevel_id) { MateConfClient *client; const char *key; client = panel_mateconf_get_client (); if (tooltip) { key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, drawer_id, "tooltip"); mateconf_client_set_string (client, key, tooltip, NULL); } key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, drawer_id, "use_custom_icon"); mateconf_client_set_bool (client, key, use_custom_icon, NULL); if (custom_icon) { key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, drawer_id, "custom_icon"); mateconf_client_set_string (client, key, custom_icon, NULL); } if (attached_toplevel_id) { char *toplevel_id; char *toplevel_dir; toplevel_id = panel_profile_find_new_id (PANEL_MATECONF_TOPLEVELS); toplevel_dir = g_strdup_printf (PANEL_CONFIG_DIR "/toplevels/%s", toplevel_id); panel_mateconf_associate_schemas_in_dir (client, toplevel_dir, PANEL_SCHEMAS_DIR "/toplevels"); key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, drawer_id, "attached_toplevel_id"); mateconf_client_set_string (client, key, toplevel_id, NULL); key = panel_mateconf_full_key (PANEL_MATECONF_TOPLEVELS, toplevel_id, "enable_buttons"); mateconf_client_set_bool (client, key, TRUE, NULL); key = panel_mateconf_full_key (PANEL_MATECONF_TOPLEVELS, toplevel_id, "enable_arrows"); mateconf_client_set_bool (client, key, TRUE, NULL); *attached_toplevel_id = toplevel_id; } } void panel_drawer_create (PanelToplevel *toplevel, int position, const char *custom_icon, gboolean use_custom_icon, const char *tooltip) { char *id; id = panel_profile_prepare_object (PANEL_OBJECT_DRAWER, toplevel, position, FALSE); panel_drawer_prepare (id, custom_icon, use_custom_icon, tooltip, NULL); panel_profile_add_to_list (PANEL_MATECONF_OBJECTS, id); g_free (id); } char * panel_drawer_create_with_id (const char *toplevel_id, int position, const char *custom_icon, gboolean use_custom_icon, const char *tooltip) { char *id; char *attached_toplevel_id = NULL; id = panel_profile_prepare_object_with_id (PANEL_OBJECT_DRAWER, toplevel_id, position, FALSE); panel_drawer_prepare (id, custom_icon, use_custom_icon, tooltip, &attached_toplevel_id); panel_profile_add_to_list (PANEL_MATECONF_OBJECTS, id); g_free (id); return attached_toplevel_id; } void drawer_load_from_mateconf (PanelWidget *panel_widget, gboolean locked, gint position, const char *id) { MateConfClient *client; const char *key; gboolean use_custom_icon; char *toplevel_id; char *custom_icon; char *tooltip; g_return_if_fail (panel_widget != NULL); g_return_if_fail (id != NULL); client = panel_mateconf_get_client (); key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, id, "attached_toplevel_id"); toplevel_id = mateconf_client_get_string (client, key, NULL); panel_profile_load_toplevel (client, PANEL_CONFIG_DIR, PANEL_MATECONF_TOPLEVELS, toplevel_id); key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, id, "use_custom_icon"); use_custom_icon = mateconf_client_get_bool (client, key, NULL); key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, id, "custom_icon"); custom_icon = mateconf_client_get_string (client, key, NULL); key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, id, "tooltip"); tooltip = mateconf_client_get_string (client, key, NULL); load_drawer_applet (toplevel_id, custom_icon, use_custom_icon, tooltip, panel_widget->toplevel, locked, position, TRUE, id); g_free (toplevel_id); g_free (custom_icon); g_free (tooltip); } void panel_drawer_set_dnd_enabled (Drawer *drawer, gboolean dnd_enabled) { if (dnd_enabled) { static GtkTargetEntry dnd_targets[] = { { "application/x-mate-panel-applet-internal", 0, 0 } }; gtk_widget_set_has_window (drawer->button, TRUE); gtk_drag_source_set (drawer->button, GDK_BUTTON1_MASK, dnd_targets, 1, GDK_ACTION_MOVE); //FIXME: we're forgetting the use_custom_icon case, here gtk_drag_source_set_icon_name (drawer->button, button_widget_get_icon_name (BUTTON_WIDGET (drawer->button))); gtk_widget_set_has_window (drawer->button, FALSE); } else gtk_drag_source_unset (drawer->button); } static void drawer_deletion_response (GtkWidget *dialog, int response, Drawer *drawer) { if (response == GTK_RESPONSE_OK) panel_profile_delete_object (drawer->info); gtk_widget_destroy (dialog); } void drawer_query_deletion (Drawer *drawer) { GtkWidget *dialog; if (drawer->toplevel) { PanelWidget *panel_widget; panel_widget = panel_toplevel_get_panel_widget ( drawer->toplevel); if (!panel_global_config_get_confirm_panel_remove () || !g_list_length (panel_widget->applet_list)) { panel_profile_delete_object (drawer->info); return; } dialog = panel_deletion_dialog (drawer->toplevel); g_signal_connect (dialog, "response", G_CALLBACK (drawer_deletion_response), drawer); g_signal_connect_object (drawer->toplevel, "destroy", G_CALLBACK (gtk_widget_destroy), dialog, G_CONNECT_SWAPPED); gtk_widget_show_all (dialog); } }