From fb97b62aad23bb44820713c8084785bbc147bfd7 Mon Sep 17 00:00:00 2001 From: Michael Webster Date: Sun, 27 Dec 2015 11:31:57 -0500 Subject: [PATCH] improve context menu toggle: - shows separate distinct regions of the menu item now - tooltip manifests as right-justified text next to the toggle icon --- data/icons/Makefile.am | 2 + ...tions_scalable_collapse-menu-hover-symbolic.svg | 62 +++ ...lor_actions_scalable_collapse-menu-symbolic.svg | 4 +- ...actions_scalable_expand-menu-hover-symbolic.svg | 62 +++ ...color_actions_scalable_expand-menu-symbolic.svg | 8 +- libnemo-private/Makefile.am | 2 + libnemo-private/nemo-context-menu-menu-item.c | 452 +++++++++++++++++++++ libnemo-private/nemo-context-menu-menu-item.h | 71 ++++ libnemo-private/nemo-widget-action.c | 58 ++- libnemo-private/nemo-widget-action.h | 5 + src/nemo-actions.h | 1 + src/nemo-directory-view-ui.xml | 2 +- src/nemo-view.c | 273 +++---------- 13 files changed, 765 insertions(+), 237 deletions(-) create mode 100644 data/icons/hicolor_actions_scalable_collapse-menu-hover-symbolic.svg create mode 100644 data/icons/hicolor_actions_scalable_expand-menu-hover-symbolic.svg create mode 100644 libnemo-private/nemo-context-menu-menu-item.c create mode 100644 libnemo-private/nemo-context-menu-menu-item.h diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am index b590ed9..b573ec2 100644 --- a/data/icons/Makefile.am +++ b/data/icons/Makefile.am @@ -19,7 +19,9 @@ public_icons = \ hicolor_actions_scalable_sidebar-tree-symbolic.svg \ hicolor_actions_scalable_sidebar-places-symbolic.svg \ hicolor_actions_scalable_expand-menu-symbolic.svg \ + hicolor_actions_scalable_expand-menu-hover-symbolic.svg \ hicolor_actions_scalable_collapse-menu-symbolic.svg \ + hicolor_actions_scalable_collapse-menu-hover-symbolic.svg \ hicolor_status_48x48_progress-0.png \ hicolor_status_48x48_progress-10.png \ hicolor_status_48x48_progress-20.png \ diff --git a/data/icons/hicolor_actions_scalable_collapse-menu-hover-symbolic.svg b/data/icons/hicolor_actions_scalable_collapse-menu-hover-symbolic.svg new file mode 100644 index 0000000..dce477a --- /dev/null +++ b/data/icons/hicolor_actions_scalable_collapse-menu-hover-symbolic.svg @@ -0,0 +1,62 @@ + + + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + Gnome Symbolic Icon Theme + + + diff --git a/data/icons/hicolor_actions_scalable_collapse-menu-symbolic.svg b/data/icons/hicolor_actions_scalable_collapse-menu-symbolic.svg index 6080b8e..940a236 100644 --- a/data/icons/hicolor_actions_scalable_collapse-menu-symbolic.svg +++ b/data/icons/hicolor_actions_scalable_collapse-menu-symbolic.svg @@ -31,7 +31,7 @@ id="namedview7" showgrid="false" inkscape:zoom="14.75" - inkscape:cx="-3.4237288" + inkscape:cx="-15.050847" inkscape:cy="8" inkscape:window-x="0" inkscape:window-y="25" @@ -53,7 +53,7 @@ id="title9167">Gnome Symbolic Icon Theme + transform="matrix(0.70002797,0,0,1,-24.101203,-726.0002)"> + + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + Gnome Symbolic Icon Theme + + + diff --git a/data/icons/hicolor_actions_scalable_expand-menu-symbolic.svg b/data/icons/hicolor_actions_scalable_expand-menu-symbolic.svg index 494886f..faaea15 100644 --- a/data/icons/hicolor_actions_scalable_expand-menu-symbolic.svg +++ b/data/icons/hicolor_actions_scalable_expand-menu-symbolic.svg @@ -31,8 +31,8 @@ id="namedview7" showgrid="false" inkscape:zoom="14.75" - inkscape:cx="-14.983051" - inkscape:cy="8" + inkscape:cx="-22.187982" + inkscape:cy="8.2033898" inkscape:window-x="0" inkscape:window-y="25" inkscape:window-maximized="1" @@ -53,7 +53,7 @@ id="title9167">Gnome Symbolic Icon Theme + transform="matrix(0.70002797,0,0,1,-24.101203,-726)"> + transform="matrix(0,-0.70002797,1,0,-724.5,41.601203)"> . + */ +#include + +#include "nemo-global-preferences.h" +#include "nemo-context-menu-menu-item.h" +#include "nemo-widget-action.h" + +#include + +static GtkActivatableIface *parent_activatable_iface; + +static void nemo_context_menu_menu_item_dispose (GObject *object); + +static gboolean nemo_context_menu_menu_item_enter (GtkWidget *widget, + GdkEventCrossing *event); +static gboolean nemo_context_menu_menu_item_leave (GtkWidget *widget, + GdkEventCrossing *event); +static gboolean nemo_context_menu_menu_item_motion (GtkWidget *widget, + GdkEventMotion *event); + +static gboolean nemo_context_menu_menu_item_button_press (GtkWidget *widget, + GdkEventButton *event); + +static gboolean nemo_context_menu_menu_item_button_release (GtkWidget *widget, + GdkEventButton *event); + +static void nemo_context_menu_menu_item_set_label (GtkMenuItem *menu_item, + const gchar *label); + +static void nemo_context_menu_menu_item_activatable_interface_init (GtkActivatableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (NemoContextMenuMenuItem, nemo_context_menu_menu_item, GTK_TYPE_IMAGE_MENU_ITEM, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, + nemo_context_menu_menu_item_activatable_interface_init)); + +static void +set_action_image_temporary_visibility (NemoContextMenuMenuItem *item, + gboolean visible) +{ + GtkWidget *image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (item)); + + if (!visible) { + gtk_widget_set_visible (image, FALSE); + } else { + if (gtk_image_menu_item_get_always_show_image (GTK_IMAGE_MENU_ITEM (item))) { + gtk_widget_set_visible (image, TRUE); + } + } +} + +static void +update_toggle_state (NemoContextMenuMenuItem *item, + gboolean from_event, + gboolean on_item) +{ + gboolean complex_mode = g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_CONTEXT_MENUS_SHOW_ALL_ACTIONS); + + /* const */ gchar *tip_text = complex_mode ? _("Show less actions") : + _("Show more actions"); + + gchar *markup = g_strdup_printf ("%s", tip_text); + + gtk_label_set_markup (GTK_LABEL (item->toggle_label_widget), markup); + + g_free (markup); + + if (item->on_toggle) { + set_action_image_temporary_visibility (item, FALSE); + gtk_stack_set_visible_child_name (GTK_STACK (item->stack), "toggle"); + } else { + set_action_image_temporary_visibility (item, TRUE); + gtk_stack_set_visible_child_name (GTK_STACK (item->stack), "action"); + } + + GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (item)); + + if (on_item) { + gtk_image_set_from_icon_name (GTK_IMAGE (item->toggle_widget), + complex_mode ? "collapse-menu-hover-symbolic" : "expand-menu-hover-symbolic", + GTK_ICON_SIZE_MENU); + } else { + gtk_image_set_from_icon_name (GTK_IMAGE (item->toggle_widget), + complex_mode ? "collapse-menu-symbolic" : "expand-menu-symbolic", + GTK_ICON_SIZE_MENU); + } + + GtkStateFlags default_item_state = from_event ? GTK_STATE_FLAG_PRELIGHT : gtk_style_context_get_state (context); + + gtk_style_context_set_state (context, item->on_toggle ? GTK_STATE_FLAG_NORMAL : default_item_state); + + gtk_widget_queue_draw (GTK_WIDGET (item)); +} + +static void +update_toggle_appearance_from_event (GtkWidget *widget, + gint x, + gint y, + gboolean on_item) +{ + NemoContextMenuMenuItem *item = NEMO_CONTEXT_MENU_MENU_ITEM (widget); + GtkAllocation alloc; + + gtk_widget_get_allocation (item->toggle_widget, &alloc); + + item->on_toggle = ((x >= alloc.x) && + (x <= alloc.x + alloc.width) && + (y >= alloc.y) && + (y <= alloc.y + alloc.height)); + + update_toggle_state (item, TRUE, on_item); +} + +static void +nemo_context_menu_menu_item_class_init (NemoContextMenuMenuItemClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*) klass; + GtkMenuItemClass *menu_item_class = (GtkMenuItemClass*) klass; + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gobject_class->dispose = nemo_context_menu_menu_item_dispose; + + menu_item_class->set_label = nemo_context_menu_menu_item_set_label; + + widget_class->enter_notify_event = nemo_context_menu_menu_item_enter; + widget_class->leave_notify_event = nemo_context_menu_menu_item_leave; + widget_class->motion_notify_event = nemo_context_menu_menu_item_motion; + widget_class->button_press_event = nemo_context_menu_menu_item_button_press; + widget_class->button_release_event = nemo_context_menu_menu_item_button_release; +} + +static void +nemo_context_menu_menu_item_init (NemoContextMenuMenuItem *item) +{ + item->on_toggle = FALSE; + GtkWidget *box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + + GtkWidget *stack = gtk_stack_new (); + + item->stack = stack; + + GtkWidget *label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 0.98, 0.5); + gtk_stack_add_named (GTK_STACK (stack), label, "toggle"); + item->toggle_label_widget = label; + + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_stack_add_named (GTK_STACK (stack), label, "action"); + item->label_widget = label; + + gtk_box_pack_start (GTK_BOX (box), stack, TRUE, TRUE, 0); + + GtkWidget *toggle = gtk_image_new (); + gtk_box_pack_end (GTK_BOX (box), toggle, FALSE, FALSE, 0); + item->toggle_widget = toggle; + + gtk_widget_show_all (box); + + gtk_container_add (GTK_CONTAINER (item), box); + + update_toggle_state (item, FALSE, FALSE); + + item->settings_monitor_id = g_signal_connect_swapped (nemo_preferences, + "changed::" NEMO_PREFERENCES_CONTEXT_MENUS_SHOW_ALL_ACTIONS, + G_CALLBACK (update_toggle_state), + item); +} + +static void +nemo_context_menu_menu_item_dispose (GObject *object) +{ + NemoContextMenuMenuItem *item = NEMO_CONTEXT_MENU_MENU_ITEM (object); + + if (item->settings_monitor_id > 0) { + g_signal_handler_disconnect (nemo_preferences, item->settings_monitor_id); + item->settings_monitor_id = 0; + } + + G_OBJECT_CLASS (nemo_context_menu_menu_item_parent_class)->dispose (object); +} + +static gboolean +nemo_context_menu_menu_item_enter (GtkWidget *widget, + GdkEventCrossing *event) +{ + g_return_val_if_fail (event != NULL, FALSE); + + update_toggle_appearance_from_event (widget, event->x, event->y, TRUE); + + return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent *) event); +} + +static gboolean +nemo_context_menu_menu_item_leave (GtkWidget *widget, + GdkEventCrossing *event) +{ + g_return_val_if_fail (event != NULL, FALSE); + + update_toggle_appearance_from_event (widget, event->x, event->y, FALSE); + + return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent *) event); +} + +static gboolean +nemo_context_menu_menu_item_motion (GtkWidget *widget, + GdkEventMotion *event) +{ + g_return_val_if_fail (event != NULL, FALSE); + + update_toggle_appearance_from_event (widget, event->x, event->y, TRUE); + + return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent *) event); +} + +static gboolean +nemo_context_menu_menu_item_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + NemoContextMenuMenuItem *item = NEMO_CONTEXT_MENU_MENU_ITEM (widget); + + if (event->button != GDK_BUTTON_PRIMARY) + return GDK_EVENT_PROPAGATE; + + update_toggle_appearance_from_event (widget, event->x, event->y, TRUE); + + if (item->on_toggle) { + g_settings_set_boolean (nemo_preferences, + NEMO_PREFERENCES_CONTEXT_MENUS_SHOW_ALL_ACTIONS, + !g_settings_get_boolean (nemo_preferences, + NEMO_PREFERENCES_CONTEXT_MENUS_SHOW_ALL_ACTIONS)); + return GDK_EVENT_STOP; + } + + return GDK_EVENT_PROPAGATE; +} + +static gboolean +nemo_context_menu_menu_item_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + NemoContextMenuMenuItem *item = NEMO_CONTEXT_MENU_MENU_ITEM (widget); + + if (event->button != GDK_BUTTON_PRIMARY && + event->button != GDK_BUTTON_SECONDARY) + return GDK_EVENT_PROPAGATE; + + update_toggle_appearance_from_event (widget, event->x, event->y, TRUE); + + if (item->on_toggle) { + return GDK_EVENT_STOP; + } + + return GDK_EVENT_PROPAGATE; +} + +static void +nemo_context_menu_menu_item_set_label (GtkMenuItem *menu_item, + const gchar *label) +{ + NemoContextMenuMenuItem *item = NEMO_CONTEXT_MENU_MENU_ITEM (menu_item); + + if (item->label != label) + { + g_free (item->label); + item->label = g_strdup (label); + + gtk_label_set_text_with_mnemonic (GTK_LABEL (item->label_widget), label); + + g_object_notify (G_OBJECT (menu_item), "label"); + } +} + +static gboolean +activatable_update_stock_id (GtkImageMenuItem *image_menu_item, + GtkAction *action) +{ + GtkWidget *image; + const gchar *stock_id = gtk_action_get_stock_id (action); + + image = gtk_image_menu_item_get_image (image_menu_item); + + if (GTK_IS_IMAGE (image) && + stock_id && gtk_icon_factory_lookup_default (stock_id)) { + gtk_image_set_from_stock (GTK_IMAGE (image), stock_id, GTK_ICON_SIZE_MENU); + return TRUE; + } + + return FALSE; +} + +static gboolean +activatable_update_gicon (GtkImageMenuItem *image_menu_item, + GtkAction *action) +{ + GtkWidget *image; + GIcon *icon = gtk_action_get_gicon (action); + const gchar *stock_id; + gboolean ret = FALSE; + + stock_id = gtk_action_get_stock_id (action); + + image = gtk_image_menu_item_get_image (image_menu_item); + + if (icon && GTK_IS_IMAGE (image) && + !(stock_id && gtk_icon_factory_lookup_default (stock_id))) { + gtk_image_set_from_gicon (GTK_IMAGE (image), icon, GTK_ICON_SIZE_MENU); + ret = TRUE; + } + + return ret; +} + +static void +activatable_update_icon_name (GtkImageMenuItem *image_menu_item, + GtkAction *action) +{ + GtkWidget *image; + const gchar *icon_name = gtk_action_get_icon_name (action); + + image = gtk_image_menu_item_get_image (image_menu_item); + + if (GTK_IS_IMAGE (image) && + (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY || + gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME)) { + gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, GTK_ICON_SIZE_MENU); + } +} + +static void +activatable_update_label (GtkMenuItem *menu_item, GtkAction *action) +{ + const gchar *label; + label = gtk_action_get_label (action); + nemo_context_menu_menu_item_set_label (menu_item, label); +} + +static void +nemo_context_menu_menu_item_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + if (!gtk_activatable_get_use_action_appearance (activatable)) + return; + if (strcmp (property_name, "label") == 0) + activatable_update_label (GTK_MENU_ITEM (activatable), action); + else if (strcmp (property_name, "stock-id") == 0) + activatable_update_stock_id (GTK_IMAGE_MENU_ITEM (activatable), action); + else if (strcmp (property_name, "gicon") == 0) + activatable_update_gicon (GTK_IMAGE_MENU_ITEM (activatable), action); + else if (strcmp (property_name, "icon-name") == 0) + activatable_update_icon_name (GTK_IMAGE_MENU_ITEM (activatable), action); +} + +static void +menu_item_sync_action_properties (NemoContextMenuMenuItem *menu_item, + GtkAction *action) +{ + GtkImageMenuItem *image_menu_item; + GtkActivatable *activatable; + GtkWidget *image; + gboolean use_appearance; + + image_menu_item = GTK_IMAGE_MENU_ITEM (menu_item); + + activatable = GTK_ACTIVATABLE (image_menu_item); + + if (!action) + return; + + use_appearance = gtk_activatable_get_use_action_appearance (activatable); + if (!use_appearance) + return; + + image = gtk_image_menu_item_get_image (image_menu_item); + if (image && !GTK_IS_IMAGE (image)) { + gtk_image_menu_item_set_image (image_menu_item, NULL); + image = NULL; + } + + if (!image) { + image = gtk_image_new (); + gtk_widget_show (image); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (activatable), + image); + } + + if (!activatable_update_stock_id (image_menu_item, action) && + !activatable_update_gicon (image_menu_item, action)) + activatable_update_icon_name (image_menu_item, action); + + gtk_image_menu_item_set_always_show_image (image_menu_item, + gtk_action_get_always_show_image (action)); + + activatable_update_label (GTK_MENU_ITEM (menu_item), action); +} + +static void +nemo_context_menu_menu_item_sync_action_properties (GtkActivatable *activatable, + GtkAction *action) +{ + NemoContextMenuMenuItem *context_menu_menu_item; + + context_menu_menu_item = NEMO_CONTEXT_MENU_MENU_ITEM (activatable); + + if (!action) + return; + + if (!gtk_activatable_get_use_action_appearance (activatable)) + return; + + menu_item_sync_action_properties (context_menu_menu_item, action); + + gtk_widget_show (GTK_WIDGET (context_menu_menu_item)); +} + +static void +nemo_context_menu_menu_item_activatable_interface_init (GtkActivatableIface *iface) +{ + parent_activatable_iface = g_type_interface_peek_parent (iface); + iface->update = nemo_context_menu_menu_item_update; + iface->sync_action_properties = nemo_context_menu_menu_item_sync_action_properties; +} + +/** + * nemo_context_menu_menu_item_new: + * @widget: The custom widget to use + * + * Creates a new #NemoContextMenuMenuItem. + * + * Returns: a new #NemoContextMenuMenuItem. + */ +GtkWidget * +nemo_context_menu_menu_item_new (GtkWidget *widget) +{ + return g_object_new (NEMO_TYPE_CONTEXT_MENU_MENU_ITEM, + NULL); +} diff --git a/libnemo-private/nemo-context-menu-menu-item.h b/libnemo-private/nemo-context-menu-menu-item.h new file mode 100644 index 0000000..bb032e9 --- /dev/null +++ b/libnemo-private/nemo-context-menu-menu-item.h @@ -0,0 +1,71 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __NEMO_CONTEXT_MENU_MENU_ITEM_H__ +#define __NEMO_CONTEXT_MENU_MENU_ITEM_H__ + +#include + +G_BEGIN_DECLS + +#define NEMO_TYPE_CONTEXT_MENU_MENU_ITEM (nemo_context_menu_menu_item_get_type ()) +#define NEMO_CONTEXT_MENU_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NEMO_TYPE_CONTEXT_MENU_MENU_ITEM, NemoContextMenuMenuItem)) +#define NEMO_CONTEXT_MENU_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NEMO_TYPE_CONTEXT_MENU_MENU_ITEM, NemoContextMenuMenuItemClass)) +#define NEMO_IS_CONTEXT_MENU_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NEMO_TYPE_CONTEXT_MENU_MENU_ITEM)) +#define NEMO_IS_CONTEXT_MENU_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NEMO_TYPE_CONTEXT_MENU_MENU_ITEM)) +#define NEMO_CONTEXT_MENU_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NEMO_TYPE_CONTEXT_MENU_MENU_ITEM, NemoContextMenuMenuItemClass)) + + +typedef struct _NemoContextMenuMenuItem NemoContextMenuMenuItem; +typedef struct _NemoContextMenuMenuItemClass NemoContextMenuMenuItemClass; + +struct _NemoContextMenuMenuItem +{ + GtkImageMenuItem menu_item; + + gchar *label; + GtkWidget *stack; + GtkWidget *label_widget; + GtkWidget *toggle_label_widget; + GtkWidget *toggle_widget; + + gulong settings_monitor_id; + gboolean on_toggle; +}; + +/** + * NemoContextMenuMenuItemClass: + * @parent_class: The parent class. + */ +struct _NemoContextMenuMenuItemClass +{ + GtkImageMenuItemClass parent_class; +}; + +GType nemo_context_menu_menu_item_get_type (void) G_GNUC_CONST; +GtkWidget *nemo_context_menu_menu_item_new (GtkWidget *widget); + +G_END_DECLS + +#endif /* __NEMO_CONTEXT_MENU_MENU_ITEM_H__ */ diff --git a/libnemo-private/nemo-widget-action.c b/libnemo-private/nemo-widget-action.c index 5ca13de..ceafb2a 100644 --- a/libnemo-private/nemo-widget-action.c +++ b/libnemo-private/nemo-widget-action.c @@ -19,6 +19,7 @@ #include "nemo-widget-action.h" #include "nemo-widget-menu-item.h" +#include "nemo-context-menu-menu-item.h" G_DEFINE_TYPE (NemoWidgetAction, nemo_widget_action, GTK_TYPE_ACTION); @@ -62,6 +63,7 @@ nemo_widget_action_init (NemoWidgetAction *action) action->widget_b = NULL; action->a_used = FALSE; action->b_used = FALSE; + action->is_menu_toggle = FALSE; } static void @@ -118,6 +120,22 @@ nemo_widget_action_new (const gchar *name, NULL); } +GtkAction * +nemo_widget_action_new_for_menu_toggle (const gchar *name, + const gchar *label, + const gchar *tooltip) +{ + GtkAction *ret = g_object_new (NEMO_TYPE_WIDGET_ACTION, + "name", name, + "label", label, + "tooltip", tooltip, + NULL); + + NEMO_WIDGET_ACTION (ret)->is_menu_toggle = TRUE; + + return ret; +} + static void nemo_widget_action_finalize (GObject *object) { @@ -198,29 +216,35 @@ create_menu_item (GtkAction *action) { NemoWidgetAction *widget_action; GType menu_item_type; - GtkWidget *w, *ret; - gint slot; + GtkWidget *w; + GtkWidget *ret = NULL; + gint slot = -1; widget_action = NEMO_WIDGET_ACTION (action); menu_item_type = GTK_ACTION_GET_CLASS (action)->menu_item_type; - if (!widget_action->a_used) { - w = widget_action->widget_a; - widget_action->a_used = TRUE; - slot = ACTION_SLOT_A; - } else if (!widget_action->b_used) { - w = widget_action->widget_b; - widget_action->b_used = TRUE; - slot = ACTION_SLOT_B; - } else - return NULL; + if (widget_action->is_menu_toggle) { + ret = g_object_new (NEMO_TYPE_CONTEXT_MENU_MENU_ITEM, NULL); + } else { + if (!widget_action->a_used) { + w = widget_action->widget_a; + widget_action->a_used = TRUE; + slot = ACTION_SLOT_A; + } else if (!widget_action->b_used) { + w = widget_action->widget_b; + widget_action->b_used = TRUE; + slot = ACTION_SLOT_B; + } - ret = g_object_new (menu_item_type, - "child-widget", w, - "action-slot", slot, - NULL); + if (slot != -1) + ret = g_object_new (menu_item_type, + "child-widget", w, + "action-slot", slot, + NULL); + } - gtk_activatable_set_related_action (GTK_ACTIVATABLE (ret), action); + if (ret) + gtk_activatable_set_related_action (GTK_ACTIVATABLE (ret), action); return ret; } diff --git a/libnemo-private/nemo-widget-action.h b/libnemo-private/nemo-widget-action.h index 9674ce8..a55c17f 100644 --- a/libnemo-private/nemo-widget-action.h +++ b/libnemo-private/nemo-widget-action.h @@ -44,6 +44,7 @@ struct _NemoWidgetAction { GtkWidget *widget_b; gboolean a_used; gboolean b_used; + gboolean is_menu_toggle; }; struct _NemoWidgetActionClass { @@ -59,6 +60,10 @@ GType nemo_widget_action_get_type (void); GtkAction *nemo_widget_action_new (const gchar *name, GtkWidget *widget_a, GtkWidget *widget_b); +GtkAction *nemo_widget_action_new_for_menu_toggle (const gchar *name, + const gchar *label, + const gchar *tooltip); + void nemo_widget_action_activate (NemoWidgetAction *action); GtkWidget * nemo_widget_action_get_widget_a (NemoWidgetAction *action); void nemo_widget_action_set_widget_a (NemoWidgetAction *action, GtkWidget *widget); diff --git a/src/nemo-actions.h b/src/nemo-actions.h index 22998a3..adbbc73 100644 --- a/src/nemo-actions.h +++ b/src/nemo-actions.h @@ -60,6 +60,7 @@ #define NEMO_ACTION_NEW_TAB "New Tab" #define NEMO_ACTION_OPEN "Open" +#define NEMO_ACTION_OPEN_TOGGLE "OpenToggle" #define NEMO_ACTION_OPEN_ALTERNATE "OpenAlternate" #define NEMO_ACTION_OPEN_IN_NEW_TAB "OpenInNewTab" #define NEMO_ACTION_LOCATION_OPEN_ALTERNATE "LocationOpenAlternate" diff --git a/src/nemo-directory-view-ui.xml b/src/nemo-directory-view-ui.xml index d136eb3..e30a3f6 100644 --- a/src/nemo-directory-view-ui.xml +++ b/src/nemo-directory-view-ui.xml @@ -147,7 +147,7 @@ - + diff --git a/src/nemo-view.c b/src/nemo-view.c index 19de9bd..eb44927 100644 --- a/src/nemo-view.c +++ b/src/nemo-view.c @@ -127,7 +127,7 @@ #define NEMO_VIEW_POPUP_PATH_SCRIPTS_PLACEHOLDER "/selection/Open Placeholder/Scripts/Scripts Placeholder" #define NEMO_VIEW_POPUP_PATH_ACTIONS_PLACEHOLDER "/selection/Open Placeholder/ActionsPlaceholder" #define NEMO_VIEW_POPUP_PATH_EXTENSION_ACTIONS "/selection/Extension Actions" -#define NEMO_VIEW_POPUP_PATH_OPEN "/selection/Open Placeholder/Open" +#define NEMO_VIEW_POPUP_PATH_OPEN "/selection/Open Placeholder/OpenToggle" #define NEMO_VIEW_POPUP_PATH_BACKGROUND "/background" #define NEMO_VIEW_POPUP_PATH_BACKGROUND_SCRIPTS_PLACEHOLDER "/background/Before Zoom Items/New Object Items/Scripts/Scripts Placeholder" @@ -193,12 +193,6 @@ struct NemoViewDetails GtkActionGroup *dir_action_group; guint dir_merge_id; - GtkWidget *expander_menu_widget; - GtkWidget *menu_widget_ref; - GtkWidget *expander_label_widget; - guint menu_expander_click_handler_id; - gchar *expander_tooltip_text; - gboolean supports_zooming; GList *scripts_directory_list; @@ -2686,12 +2680,6 @@ nemo_view_init (NemoView *view) /* Default to true; desktop-icon-view sets to false */ view->details->show_foreign_files = TRUE; - view->details->expander_menu_widget = NULL; - view->details->menu_widget_ref = NULL; - view->details->expander_label_widget = NULL; - view->details->menu_expander_click_handler_id = 0; - view->details->expander_tooltip_text = NULL; - view->details->non_ready_files = g_hash_table_new_full (file_and_directory_hash, file_and_directory_equal, @@ -2779,6 +2767,10 @@ nemo_view_init (NemoView *view) nemo_to_menu_preferences_changed_callback (view); + g_signal_connect_swapped (nemo_preferences, + "changed::" NEMO_PREFERENCES_CONTEXT_MENUS_SHOW_ALL_ACTIONS, + G_CALLBACK (schedule_update_menus), view); + manager = nemo_file_undo_manager_get (); g_signal_connect_object (manager, "undo-changed", G_CALLBACK (undo_manager_changed_cb), view, 0); @@ -2871,15 +2863,6 @@ nemo_view_destroy (GtkWidget *object) view = NEMO_VIEW (object); - if (view->details->expander_menu_widget != NULL) { - gtk_widget_destroy (view->details->expander_menu_widget); - } - - if (view->details->menu_expander_click_handler_id > 0) { - g_signal_handler_disconnect (view->details->menu_widget_ref, view->details->menu_expander_click_handler_id); - view->details->menu_expander_click_handler_id = 0; - } - disconnect_model_handlers (view); if (view->details->bookmarks_changed_id != 0) { @@ -2975,6 +2958,9 @@ nemo_view_finalize (GObject *object) g_signal_handlers_disconnect_by_func (nemo_preferences, nemo_to_menu_preferences_changed_callback, view); + g_signal_handlers_disconnect_by_func (nemo_preferences, + schedule_update_menus, view); + unschedule_pop_up_location_context_menu (view); if (view->details->location_popup_event != NULL) { gdk_event_free ((GdkEvent *) view->details->location_popup_event); @@ -6639,163 +6625,6 @@ update_templates_menu (NemoView *view) g_free (templates_directory_uri); } -static void ensure_expander_attached (NemoView *view, GtkWidget *menu_item); - -static void -on_expander_destroyed (GtkWidget *widget, NemoView *view) -{ - view->details->expander_menu_widget = NULL; -} - -static void -on_menu_destroyed (GtkWidget *widget, NemoView *view) -{ - view->details->menu_widget_ref = NULL; - view->details->expander_label_widget = NULL; -} - -static gboolean -pointer_in_expander_widget (GtkWidget *widget, gint x, gint y) -{ - GtkAllocation alloc; - - gtk_widget_get_allocation (widget, &alloc); - - return ((x >= alloc.x) && - (x <= alloc.x + alloc.width) && - (y >= alloc.y) && - (y <= alloc.y + alloc.height)); -} - -static gboolean -on_query_menu_tooltip (GtkWidget *widget, - gint x, - gint y, - gboolean keyboard_mode, - GtkTooltip *tooltip, - gpointer user_data) -{ - NemoView *view = NEMO_VIEW (user_data); - - if (view->details->expander_menu_widget != NULL && - pointer_in_expander_widget (view->details->expander_menu_widget, x, y)) { - gtk_tooltip_set_text (tooltip, view->details->expander_tooltip_text); - return TRUE; - } - - gtk_tooltip_set_text (tooltip, NULL); - return FALSE; -} - -static GtkWidget * -get_expander_widget_and_tooltip (NemoView *view, gchar **tooltip) -{ - if (view->details->expander_menu_widget != NULL) - gtk_widget_destroy (view->details->expander_menu_widget); - - gboolean complex_mode = g_settings_get_boolean (nemo_preferences, - NEMO_PREFERENCES_CONTEXT_MENUS_SHOW_ALL_ACTIONS); - - GtkWidget *widget = gtk_image_new_from_icon_name (complex_mode ? "collapse-menu-symbolic" : "expand-menu-symbolic", - GTK_ICON_SIZE_MENU); - - *tooltip = complex_mode ? _("Show less actions") : _("Show more actions"); - - g_signal_connect (widget, "destroy", G_CALLBACK (on_expander_destroyed), view); - - return widget; -} - -static void -attach_expander (NemoView *view, GtkWidget *widget, GtkWidget *expander) -{ - GtkWidget *parent = gtk_widget_get_parent (widget); - GtkWidget *hbox = NULL; - - if (!GTK_IS_LABEL (widget) && !GTK_IS_BOX (widget)) - return; - - if (GTK_IS_LABEL (widget)) { - g_object_ref (widget); - - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - - gtk_container_remove (GTK_CONTAINER (parent), widget); - gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0); - gtk_container_add (GTK_CONTAINER (parent), hbox); - - view->details->expander_label_widget = widget; - - g_object_unref (widget); - } - - if (GTK_IS_BOX (widget)) { - hbox = widget; - } - - gtk_box_pack_end (GTK_BOX (hbox), expander, FALSE, FALSE, 0); -} - -static gboolean -on_menu_button_released (GtkWidget *widget, GdkEvent *event, NemoView *view) -{ - if (event->button.button != GDK_BUTTON_PRIMARY) - return FALSE; - - gint ev_x = (gint) event->button.x; - gint ev_y = (gint) event->button.y; - - if (pointer_in_expander_widget (view->details->expander_menu_widget, ev_x, ev_y)) { - g_settings_set_boolean (nemo_preferences, - NEMO_PREFERENCES_CONTEXT_MENUS_SHOW_ALL_ACTIONS, - !g_settings_get_boolean (nemo_preferences, - NEMO_PREFERENCES_CONTEXT_MENUS_SHOW_ALL_ACTIONS)); - ensure_expander_attached (view, widget); - schedule_update_menus (view); - return TRUE; - } - - return FALSE; -} - -static void -ensure_expander_attached (NemoView *view, GtkWidget *menu_item) -{ - gchar *tooltip = NULL; - - GtkWidget *expander = get_expander_widget_and_tooltip (view, &tooltip); - - view->details->expander_menu_widget = expander; - - g_clear_pointer (&view->details->expander_tooltip_text, g_free); - view->details->expander_tooltip_text = g_strdup (tooltip); - - GtkWidget *child = gtk_bin_get_child (GTK_BIN (menu_item)); - - attach_expander (view, child, expander); - - gtk_widget_show_all (menu_item); - - if (view->details->menu_expander_click_handler_id == 0) { - view->details->menu_expander_click_handler_id = g_signal_connect (menu_item, - "button-release-event", - G_CALLBACK (on_menu_button_released), - view); - } - - if (view->details->menu_widget_ref == NULL) { - view->details->menu_widget_ref = menu_item; - g_signal_connect (menu_item, "destroy", G_CALLBACK (on_menu_destroyed), view); - } - - gtk_widget_set_has_tooltip (menu_item, TRUE); - - g_signal_handlers_disconnect_matched (menu_item, G_SIGNAL_MATCH_FUNC, - 0, 0, NULL, on_query_menu_tooltip, NULL); - - g_signal_connect (menu_item, "query-tooltip", G_CALLBACK (on_query_menu_tooltip), view); -} - static GtkMenu * create_popup_menu (NemoView *view, const char *popup_path) { @@ -8268,7 +8097,7 @@ static const GtkActionEntry directory_view_entries[] = { /* name, stock id */ { "Open", NULL, /* label, accelerator */ N_("_Open"), "o", /* tooltip */ N_("Open the selected item in this window"), - G_CALLBACK (action_open_callback) }, + G_CALLBACK (action_open_callback) }, /* name, stock id */ { "OpenAccel", NULL, /* label, accelerator */ "OpenAccel", "Down", /* tooltip */ NULL, @@ -8603,6 +8432,17 @@ pre_activate (NemoView *view, } } +static GtkAction * +get_expander_action (NemoView *view) +{ + GtkAction *action = nemo_widget_action_new_for_menu_toggle ("OpenToggle", N_("_Open"), + N_("Open the selected item in this window")); + + g_signal_connect (action, "activate", G_CALLBACK (action_open_callback), view); + + return action; +} + static void real_merge_menus (NemoView *view) { @@ -8620,6 +8460,9 @@ real_merge_menus (NemoView *view) directory_view_entries, G_N_ELEMENTS (directory_view_entries), view); + /* Add the special Open action with built-in menu toggle*/ + gtk_action_group_add_action (action_group, get_expander_action (view)); + tooltip = g_strdup_printf (_("Run scripts")); /* Create a script action here specially because its tooltip is dynamic */ action = gtk_action_new ("Scripts", _("_Scripts"), tooltip, NULL); @@ -9630,6 +9473,7 @@ update_complex_popup_items (NemoView *view) static void real_update_menus (NemoView *view) { + GtkWidget *menuitem; GList *selection, *l; gint selection_count; const char *tip, *label; @@ -9656,7 +9500,7 @@ real_update_menus (NemoView *view) GtkAction *action; GAppInfo *app; GIcon *app_icon; - GtkWidget *menuitem; + // GtkWidget *menuitem; gboolean next_pane_is_writable; gboolean show_properties; @@ -9711,10 +9555,6 @@ real_update_menus (NemoView *view) gtk_action_set_sensitive (action, can_create_files); gtk_action_set_visible (action, !selection_contains_recent); - action = gtk_action_group_get_action (view->details->dir_action_group, - NEMO_ACTION_OPEN); - gtk_action_set_sensitive (action, selection_count != 0); - can_open = show_app = selection_count != 0; for (l = selection; l != NULL; l = l->next) { @@ -9756,43 +9596,50 @@ real_update_menus (NemoView *view) g_object_unref (app); } - g_object_set (action, "label", - label_with_underscore ? label_with_underscore : _("_Open"), - NULL); + if (app_icon == NULL) { + app_icon = g_themed_icon_new (GTK_STOCK_OPEN); + } - if (view->details->expander_label_widget) { - gtk_label_set_text_with_mnemonic (GTK_LABEL (view->details->expander_label_widget), - label_with_underscore ? label_with_underscore : _("_Open")); - } + action = gtk_action_group_get_action (view->details->dir_action_group, + NEMO_ACTION_OPEN); + gtk_action_set_sensitive (action, selection_count != 0); - menuitem = gtk_ui_manager_get_widget ( - nemo_window_get_ui_manager (view->details->window), - NEMO_VIEW_MENU_PATH_OPEN); + g_object_set (action, "label", + label_with_underscore ? label_with_underscore : _("_Open"), + NULL); - /* Only force displaying the icon if it is an application icon */ - gtk_image_menu_item_set_always_show_image ( - GTK_IMAGE_MENU_ITEM (menuitem), app_icon != NULL); + gtk_action_set_gicon (action, app_icon); + gtk_action_set_visible (action, can_open); - menuitem = gtk_ui_manager_get_widget ( - nemo_window_get_ui_manager (view->details->window), - NEMO_VIEW_POPUP_PATH_OPEN); + action = gtk_action_group_get_action (view->details->dir_action_group, + NEMO_ACTION_OPEN_TOGGLE); + gtk_action_set_sensitive (action, selection_count != 0); - ensure_expander_attached (view, menuitem); + g_object_set (action, "label", + label_with_underscore ? label_with_underscore : _("_Open"), + NULL); - /* Only force displaying the icon if it is an application icon */ - gtk_image_menu_item_set_always_show_image ( - GTK_IMAGE_MENU_ITEM (menuitem), app_icon != NULL); + gtk_action_set_gicon (action, app_icon); + gtk_action_set_visible (action, can_open); - if (app_icon == NULL) { - app_icon = g_themed_icon_new (GTK_STOCK_OPEN); - } + g_object_unref (app_icon); + g_free (label_with_underscore); - gtk_action_set_gicon (action, app_icon); - g_object_unref (app_icon); + menuitem = gtk_ui_manager_get_widget ( + nemo_window_get_ui_manager (view->details->window), + NEMO_VIEW_MENU_PATH_OPEN); - gtk_action_set_visible (action, can_open); - - g_free (label_with_underscore); + /* Only force displaying the icon if it is an application icon */ + gtk_image_menu_item_set_always_show_image ( + GTK_IMAGE_MENU_ITEM (menuitem), app_icon != NULL); + + menuitem = gtk_ui_manager_get_widget ( + nemo_window_get_ui_manager (view->details->window), + NEMO_VIEW_POPUP_PATH_OPEN); + + /* Only force displaying the icon if it is an application icon */ + gtk_image_menu_item_set_always_show_image ( + GTK_IMAGE_MENU_ITEM (menuitem), app_icon != NULL); show_open_alternate = file_list_all_are_folders (selection) && selection_count > 0 && -- 2.5.0