From c51ef797a707f4e2c6f9688d4378f2b0e9898a66 Mon Sep 17 00:00:00 2001 From: Perberos Date: Thu, 1 Dec 2011 22:56:10 -0300 Subject: moving from https://github.com/perberos/mate-desktop-environment --- mate-panel/drawer.c | 806 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 806 insertions(+) create mode 100644 mate-panel/drawer.c (limited to 'mate-panel/drawer.c') diff --git a/mate-panel/drawer.c b/mate-panel/drawer.c new file mode 100644 index 00000000..924acb42 --- /dev/null +++ b/mate-panel/drawer.c @@ -0,0 +1,806 @@ +/* + * 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); + } +} -- cgit v1.2.1