diff --git a/.gitignore b/.gitignore index 3c440e6..bdf777d 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,4 @@ /nemo-2.8.1.tar.gz /nemo-2.8.2.tar.gz /nemo-2.8.5.tar.gz +/nemo-2.8.6.tar.gz diff --git a/0001-improve-context-menu-toggle.patch b/0001-improve-context-menu-toggle.patch new file mode 100644 index 0000000..dd5f390 --- /dev/null +++ b/0001-improve-context-menu-toggle.patch @@ -0,0 +1,1311 @@ +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 + diff --git a/nemo.spec b/nemo.spec index d25bddf..b5f5ba8 100644 --- a/nemo.spec +++ b/nemo.spec @@ -2,7 +2,7 @@ Name: nemo Summary: File manager for Cinnamon -Version: 2.8.5 +Version: 2.8.6 Release: 1%{?dist} License: GPLv2+ and LGPLv2+ Group: User Interface/Desktops @@ -12,6 +12,7 @@ URL: https://github.com/linuxmint/nemo #Source0: http://leigh123linux.fedorapeople.org/pub/nemo/source/nemo-%%{version}.git%%{_internal_version}.tar.gz Source0: http://leigh123linux.fedorapeople.org/pub/nemo/source/nemo-%{version}.tar.gz Source1: nemo-fedora.gschema.override +Patch0: 0001-improve-context-menu-toggle.patch Requires: redhat-menus Requires: gvfs @@ -163,6 +164,10 @@ fi %{_datadir}/gir-1.0/*.gir %changelog +* Tue Dec 29 2015 Leigh Scott - 2.8.6-1 +- update to 2.8.6 release +- patch to fix bz 1287917 & 1288324 + * Sat Nov 21 2015 Leigh Scott - 2.8.5-1 - update to 2.8.5 release @@ -171,7 +176,7 @@ fi * Mon Nov 09 2015 Leigh Scott - 2.8.2-1 - update to 2.8.2 release - + * Mon Oct 26 2015 Leigh Scott - 2.8.1-1 - update to 2.8.1 release diff --git a/sources b/sources index 986207a..57a951b 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -d79ff3cd4ac36222435e2b42ff443016 nemo-2.8.5.tar.gz +a397cefe09d0bd89766dfcd26f5e332f nemo-2.8.6.tar.gz