[Xfce4-commits] <thunar:master> Allow keyboard shortcuts for user customizable actions (bug #1941).

Nick Schermer noreply at xfce.org
Tue Jan 15 20:40:01 CET 2013


Updating branch refs/heads/master
         to dae9c330340b7fecf0986734956b794251128831 (commit)
       from 2d40555ffa59671c1bddb15696627023a7f92937 (commit)

commit dae9c330340b7fecf0986734956b794251128831
Author: Jannis Pohlmann <jannis at xfce.org>
Date:   Tue Jan 15 20:36:15 2013 +0100

    Allow keyboard shortcuts for user customizable actions (bug #1941).
    
    Generate and store unique ids for the uca actions. Should work
    for all plugins as long they produce unique ids that are the same
    each startup.

 plugins/thunar-uca/thunar-uca-editor.c   |    2 +
 plugins/thunar-uca/thunar-uca-model.c    |   71 +++++++++++++++++-
 plugins/thunar-uca/thunar-uca-model.h    |    2 +
 plugins/thunar-uca/thunar-uca-provider.c |    5 +-
 plugins/thunar-uca/uca.xml.in            |    4 +-
 thunar/thunar-window-ui.xml              |    2 +
 thunar/thunar-window.c                   |  120 ++++++++++++++++++++++++++++++
 7 files changed, 202 insertions(+), 4 deletions(-)

diff --git a/plugins/thunar-uca/thunar-uca-editor.c b/plugins/thunar-uca/thunar-uca-editor.c
index 31ddca2..ea32a46 100644
--- a/plugins/thunar-uca/thunar-uca-editor.c
+++ b/plugins/thunar-uca/thunar-uca-editor.c
@@ -1,6 +1,7 @@
 /* $Id$ */
 /*-
  * Copyright (c) 2005-2007 Benedikt Meurer <benny at xfce.org>
+ * Copyright (c) 2011 Jannis Pohlmann <jannis at xfce.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -809,6 +810,7 @@ thunar_uca_editor_save (ThunarUcaEditor *uca_editor,
 
   thunar_uca_model_update (uca_model, iter,
                            gtk_entry_get_text (GTK_ENTRY (uca_editor->name_entry)),
+                           NULL, /* don't touch the unique id */
                            gtk_entry_get_text (GTK_ENTRY (uca_editor->description_entry)),
                            thunar_uca_editor_get_icon_name (uca_editor),
                            gtk_entry_get_text (GTK_ENTRY (uca_editor->command_entry)),
diff --git a/plugins/thunar-uca/thunar-uca-model.c b/plugins/thunar-uca/thunar-uca-model.c
index 3f7a5b6..642a344 100644
--- a/plugins/thunar-uca/thunar-uca-model.c
+++ b/plugins/thunar-uca/thunar-uca-model.c
@@ -1,7 +1,7 @@
 /* $Id$ */
 /*-
  * Copyright (c) 2005-2006 Benedikt Meurer <benny at xfce.org>
- * Copyright (c) 2009 Jannis Pohlmann <jannis at xfce.org>
+ * Copyright (c) 2009-2012 Jannis Pohlmann <jannis at xfce.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -75,6 +75,7 @@ typedef enum
   PARSER_ACTION,
   PARSER_ICON,
   PARSER_NAME,
+  PARSER_UNIQUE_ID,
   PARSER_COMMAND,
   PARSER_STARTUP_NOTIFY,
   PARSER_PATTERNS,
@@ -162,6 +163,7 @@ struct _ThunarUcaModelItem
 {
   gchar         *name;
   gchar         *description;
+  gchar         *unique_id;
   gchar         *icon_name;
   GIcon         *gicon;
   gchar         *command;
@@ -170,7 +172,7 @@ struct _ThunarUcaModelItem
   ThunarUcaTypes types;
 
   /* derived attributes */
-  gboolean       multiple_selection : 1;
+  guint          multiple_selection : 1;
 };
 
 typedef XFCE_GENERIC_STACK(ParserState) ParserStack;
@@ -183,6 +185,7 @@ typedef struct
   GString        *name;
   gboolean        name_use;
   guint           name_match;
+  GString        *unique_id;
   GString        *icon_name;
   GString        *command;
   GString        *patterns;
@@ -190,6 +193,7 @@ typedef struct
   gboolean        startup_notify;
   gboolean        description_use;
   guint           description_match;
+  gboolean        unique_id_generated;
   ThunarUcaTypes  types;
 } Parser;
 
@@ -319,6 +323,9 @@ thunar_uca_model_get_column_type (GtkTreeModel *tree_model,
     case THUNAR_UCA_MODEL_COLUMN_NAME:
       return G_TYPE_STRING;
 
+    case THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID:
+      return G_TYPE_STRING;
+
     case THUNAR_UCA_MODEL_COLUMN_DESCRIPTION:
       return G_TYPE_STRING;
 
@@ -429,6 +436,10 @@ thunar_uca_model_get_value (GtkTreeModel *tree_model,
       g_value_set_static_string (value, item->icon_name);
       break;
 
+    case THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID:
+      g_value_set_static_string (value, item->unique_id);
+      break;
+
     case THUNAR_UCA_MODEL_COLUMN_COMMAND:
       g_value_set_static_string (value, item->command);
       break;
@@ -570,11 +581,13 @@ thunar_uca_model_load_from_file (ThunarUcaModel *uca_model,
   parser.model = uca_model;
   parser.locale = g_strdup (setlocale (LC_MESSAGES, NULL));
   parser.name = g_string_new (NULL);
+  parser.unique_id = g_string_new (NULL);
   parser.icon_name = g_string_new (NULL);
   parser.command = g_string_new (NULL);
   parser.patterns = g_string_new (NULL);
   parser.description = g_string_new (NULL);
   parser.startup_notify = FALSE;
+  parser.unique_id_generated = FALSE;
   xfce_stack_push (parser.stack, PARSER_START);
 
   /* parse the file */
@@ -588,11 +601,17 @@ thunar_uca_model_load_from_file (ThunarUcaModel *uca_model,
   g_string_free (parser.patterns, TRUE);
   g_string_free (parser.command, TRUE);
   g_string_free (parser.icon_name, TRUE);
+  g_string_free (parser.unique_id, TRUE);
   g_string_free (parser.name, TRUE);
   g_free (parser.locale);
   xfce_stack_free (parser.stack);
   g_free (content);
 
+  /* save model to store new ids */
+  if (succeed
+      && parser.unique_id_generated)
+    succeed = thunar_uca_model_save (uca_model, error);
+
   return succeed;
 }
 
@@ -606,6 +625,7 @@ thunar_uca_model_item_reset (ThunarUcaModelItem *item)
   g_free (item->description);
   g_free (item->command);
   g_free (item->name);
+  g_free (item->unique_id);
   g_free (item->icon_name);
 
   if (item->gicon != NULL)
@@ -656,6 +676,7 @@ start_element_handler (GMarkupParseContext *context,
           parser->startup_notify = FALSE;
           g_string_truncate (parser->icon_name, 0);
           g_string_truncate (parser->name, 0);
+          g_string_truncate (parser->unique_id, 0);
           g_string_truncate (parser->command, 0);
           g_string_truncate (parser->patterns, 0);
           g_string_truncate (parser->description, 0);
@@ -695,6 +716,11 @@ start_element_handler (GMarkupParseContext *context,
 
           xfce_stack_push (parser->stack, PARSER_NAME);
         }
+      else if (strcmp (element_name, "unique-id") == 0)
+        {
+          g_string_truncate (parser->unique_id, 0);
+          xfce_stack_push (parser->stack, PARSER_UNIQUE_ID);
+        }
       else if (strcmp (element_name, "icon") == 0)
         {
           g_string_truncate (parser->icon_name, 0);
@@ -818,12 +844,17 @@ end_element_handler (GMarkupParseContext *context,
           thunar_uca_model_append (parser->model, &iter);
           thunar_uca_model_update (parser->model, &iter,
                                    parser->name->str,
+                                   parser->unique_id->str,
                                    parser->description->str,
                                    parser->icon_name->str,
                                    parser->command->str,
                                    parser->startup_notify,
                                    parser->patterns->str,
                                    parser->types);
+
+          /* check if a new id should've been generated */
+          if (exo_str_is_empty (parser->unique_id->str))
+            parser->unique_id_generated = TRUE;
         }
       else
         goto unknown_element;
@@ -834,6 +865,11 @@ end_element_handler (GMarkupParseContext *context,
         goto unknown_element;
       break;
 
+    case PARSER_UNIQUE_ID:
+      if (strcmp (element_name, "unique-id") != 0)
+        goto unknown_element;
+      break;
+
     case PARSER_ICON:
       if (strcmp (element_name, "icon") != 0)
         goto unknown_element;
@@ -919,6 +955,10 @@ text_handler (GMarkupParseContext *context,
         g_string_append_len (parser->name, text, text_len);
       break;
 
+    case PARSER_UNIQUE_ID:
+      g_string_append_len (parser->unique_id, text, text_len);
+      break;
+
     case PARSER_ICON:
       g_string_append_len (parser->icon_name, text, text_len);
       break;
@@ -943,6 +983,19 @@ text_handler (GMarkupParseContext *context,
 
 
 
+static gchar *
+thunar_uca_model_get_unique_id (void)
+{
+  static guint counter = 0;
+
+  /* produce a "<timestamp>-<counter>" string */
+  return g_strdup_printf ("%" G_GINT64_FORMAT "-%d",
+                          g_get_real_time (),
+                          ++counter);
+}
+
+
+
 /**
  * thunar_uca_model_get_default:
  *
@@ -1048,6 +1101,7 @@ thunar_uca_model_match (ThunarUcaModel *uca_model,
   gint                i, m, n;
 
   g_return_val_if_fail (THUNAR_UCA_IS_MODEL (uca_model), NULL);
+  g_return_val_if_fail (file_infos != NULL, NULL);
 
   /* special case to avoid overhead */
   if (G_UNLIKELY (uca_model->items == NULL))
@@ -1243,6 +1297,7 @@ thunar_uca_model_remove (ThunarUcaModel *uca_model,
  * @uca_model          : a #ThunarUcaModel.
  * @iter               : the #GtkTreeIter of the item to update.
  * @name               : the name of the item.
+ * @unique_id          : a unique ID for the item.
  * @description        : the description of the item.
  * @icon               : the icon for the item.
  * @command            : the command of the item.
@@ -1255,6 +1310,7 @@ void
 thunar_uca_model_update (ThunarUcaModel *uca_model,
                          GtkTreeIter    *iter,
                          const gchar    *name,
+                         const gchar    *unique_id,
                          const gchar    *description,
                          const gchar    *icon,
                          const gchar    *command,
@@ -1285,6 +1341,15 @@ thunar_uca_model_update (ThunarUcaModel *uca_model,
   item->types = types;
   item->startup_notify = startup_notify;
 
+  /* set the unique id once */
+  if (item->unique_id == NULL)
+    {
+      if (G_LIKELY (unique_id != NULL && *unique_id != '\0'))
+        item->unique_id = g_strdup (unique_id);
+      else
+        item->unique_id = thunar_uca_model_get_unique_id ();
+    }
+
   /* setup the patterns */
   item->patterns = g_strsplit ((patterns != NULL && *patterns != '\0') ? patterns : "*", ";", -1);
   for (m = n = 0; item->patterns[m] != NULL; ++m)
@@ -1368,11 +1433,13 @@ thunar_uca_model_save (ThunarUcaModel *uca_model,
       patterns = g_strjoinv (";", item->patterns);
       escaped = g_markup_printf_escaped ("\t<icon>%s</icon>\n"
                                          "\t<name>%s</name>\n"
+                                         "\t<unique-id>%s</unique-id>\n"
                                          "\t<command>%s</command>\n"
                                          "\t<description>%s</description>\n"
                                          "\t<patterns>%s</patterns>\n",
                                          (item->icon_name != NULL) ? item->icon_name : "",
                                          (item->name != NULL) ? item->name : "",
+                                         (item->unique_id != NULL) ? item->unique_id : "",
                                          (item->command != NULL) ? item->command : "",
                                          (item->description != NULL) ? item->description : "",
                                          patterns);
diff --git a/plugins/thunar-uca/thunar-uca-model.h b/plugins/thunar-uca/thunar-uca-model.h
index 46b3c37..fd128ff 100644
--- a/plugins/thunar-uca/thunar-uca-model.h
+++ b/plugins/thunar-uca/thunar-uca-model.h
@@ -41,6 +41,7 @@ typedef enum
   THUNAR_UCA_MODEL_COLUMN_DESCRIPTION,
   THUNAR_UCA_MODEL_COLUMN_GICON,
   THUNAR_UCA_MODEL_COLUMN_ICON_NAME,
+  THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID,
   THUNAR_UCA_MODEL_COLUMN_COMMAND,
   THUNAR_UCA_MODEL_COLUMN_STARTUP_NOTIFY,
   THUNAR_UCA_MODEL_COLUMN_PATTERNS,
@@ -89,6 +90,7 @@ void            thunar_uca_model_remove         (ThunarUcaModel         *uca_mod
 void            thunar_uca_model_update         (ThunarUcaModel         *uca_model,
                                                  GtkTreeIter            *iter,
                                                  const gchar            *name,
+                                                 const gchar            *unique_id,
                                                  const gchar            *description,
                                                  const gchar            *icon,
                                                  const gchar            *command,
diff --git a/plugins/thunar-uca/thunar-uca-provider.c b/plugins/thunar-uca/thunar-uca-provider.c
index 8072c1f..b24952e 100644
--- a/plugins/thunar-uca/thunar-uca-provider.c
+++ b/plugins/thunar-uca/thunar-uca-provider.c
@@ -199,6 +199,7 @@ thunar_uca_provider_get_file_actions (ThunarxMenuProvider *menu_provider,
   GList               *lp;
   gchar               *tooltip;
   gchar               *label;
+  gchar               *unique_id;
   gchar               *name;
   GIcon               *gicon;
 
@@ -213,10 +214,11 @@ thunar_uca_provider_get_file_actions (ThunarxMenuProvider *menu_provider,
                               THUNAR_UCA_MODEL_COLUMN_NAME, &label,
                               THUNAR_UCA_MODEL_COLUMN_GICON, &gicon,
                               THUNAR_UCA_MODEL_COLUMN_DESCRIPTION, &tooltip,
+                              THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID, &unique_id,
                               -1);
 
           /* generate a unique action name */
-          name = g_strdup_printf ("ThunarUca::action-%d", ++uca_provider->last_action_id);
+          name = g_strdup_printf ("uca-action-%s", unique_id);
 
           /* create the new action with the given parameters */
           action = gtk_action_new (name, label, tooltip, NULL);
@@ -246,6 +248,7 @@ thunar_uca_provider_get_file_actions (ThunarxMenuProvider *menu_provider,
           g_free (tooltip);
           g_free (label);
           g_free (name);
+          g_free (unique_id);
 
           if (gicon != NULL)
             g_object_unref (G_OBJECT (gicon));
diff --git a/plugins/thunar-uca/uca.xml.in b/plugins/thunar-uca/uca.xml.in
index c72a40c..65efa38 100644
--- a/plugins/thunar-uca/uca.xml.in
+++ b/plugins/thunar-uca/uca.xml.in
@@ -2,7 +2,7 @@
 <!DOCTYPE actions [
   <!ELEMENT actions (action)+>
 
-  <!ELEMENT action (icon|patterns|name|command|description|directories|audio-files|image-files|other-files|text-files|video-files)*>
+  <!ELEMENT action (icon|patterns|name|unique-id|command|description|directories|audio-files|image-files|other-files|text-files|video-files)*>
 
   <!ELEMENT icon (#PCDATA)>
   <!ELEMENT command (#PCDATA)>
@@ -11,6 +11,8 @@
   <!ELEMENT name (#PCDATA)>
   <!ATTLIST name xml:lang CDATA #IMPLIED>
 
+  <!ELEMENT unique-id (#PCDATA)>
+
   <!ELEMENT description (#PCDATA)>
   <!ATTLIST description xml:lang CDATA #IMPLIED>
 
diff --git a/thunar/thunar-window-ui.xml b/thunar/thunar-window-ui.xml
index 1e026c7..df6b505 100644
--- a/thunar/thunar-window-ui.xml
+++ b/thunar/thunar-window-ui.xml
@@ -24,6 +24,8 @@
         <placeholder name="placeholder-sendto-actions" />
       </menu>
       <separator />
+      <placeholder name="placeholder-custom-actions" />
+      <separator />
       <placeholder name="placeholder-file-properties" />
       <separator />
       <menuitem action="empty-trash" />
diff --git a/thunar/thunar-window.c b/thunar/thunar-window.c
index 078df53..7f482e1 100644
--- a/thunar/thunar-window.c
+++ b/thunar/thunar-window.c
@@ -227,6 +227,9 @@ static void     thunar_window_menu_item_selected          (GtkWidget
                                                            ThunarWindow           *window);
 static void     thunar_window_menu_item_deselected        (GtkWidget              *menu_item,
                                                            ThunarWindow           *window);
+static void     thunar_window_update_custom_actions       (ThunarView             *view,
+                                                           GParamSpec             *pspec,
+                                                           ThunarWindow           *window);
 static void     thunar_window_notify_loading              (ThunarView             *view,
                                                            GParamSpec             *pspec,
                                                            ThunarWindow           *window);
@@ -297,6 +300,10 @@ struct _ThunarWindow
   GClosure               *menu_item_selected_closure;
   GClosure               *menu_item_deselected_closure;
 
+  /* custom menu actions for the file menu */
+  GtkActionGroup         *custom_actions;
+  guint                   custom_merge_id;
+
   GtkWidget              *table;
   GtkWidget              *menubar;
   GtkWidget              *spinner;
@@ -984,6 +991,10 @@ thunar_window_finalize (GObject *object)
   g_signal_handlers_disconnect_matched (window->ui_manager, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, window);
   g_object_unref (window->ui_manager);
 
+  /* release the custom actions */
+  if (window->custom_actions != NULL)
+    g_object_unref (window->custom_actions);
+
   g_object_unref (window->action_group);
   g_object_unref (window->icon_factory);
   g_object_unref (window->launcher);
@@ -1502,6 +1513,7 @@ thunar_window_notebook_page_added (GtkWidget    *notebook,
 
   /* connect signals */
   g_signal_connect (G_OBJECT (page), "notify::loading", G_CALLBACK (thunar_window_notify_loading), window);
+  g_signal_connect (G_OBJECT (page), "notify::selected-files", G_CALLBACK (thunar_window_update_custom_actions), window);
   g_signal_connect_swapped (G_OBJECT (page), "start-open-location", G_CALLBACK (thunar_window_start_open_location), window);
   g_signal_connect_swapped (G_OBJECT (page), "change-directory", G_CALLBACK (thunar_window_set_current_directory), window);
   g_signal_connect_swapped (G_OBJECT (page), "open-new-tab", G_CALLBACK (thunar_window_notebook_insert), window);
@@ -3308,6 +3320,114 @@ thunar_window_menu_item_deselected (GtkWidget    *menu_item,
 
 
 static void
+thunar_window_update_custom_actions (ThunarView   *view,
+                                     GParamSpec   *pspec,
+                                     ThunarWindow *window)
+{
+  ThunarFile *folder;
+  GList      *selected_files;
+  GList      *actions = NULL;
+  GList      *lp;
+  GList      *providers;
+  GList      *tmp;
+
+  _thunar_return_if_fail (THUNAR_IS_VIEW (view));
+  _thunar_return_if_fail (THUNAR_IS_WINDOW (window));
+
+  /* leave if the signal is emitted from a non-active tab */
+  if (!gtk_widget_get_realized (GTK_WIDGET (window))
+      || window->view != GTK_WIDGET (view))
+    return;
+
+  /* load the menu provides from the provider factory */
+  providers = thunarx_provider_factory_list_providers (window->provider_factory,
+                                                       THUNARX_TYPE_MENU_PROVIDER);
+  if (G_LIKELY (providers != NULL))
+    {
+      /* grab a reference to the current directory of the window */
+      folder = thunar_window_get_current_directory (window);
+
+      /* get a list of selected files */
+      selected_files = thunar_component_get_selected_files (THUNAR_COMPONENT (view));
+
+      /* load the actions offered by the menu providers */
+      for (lp = providers; lp != NULL; lp = lp->next)
+        {
+          if (G_LIKELY (selected_files != NULL))
+            {
+              tmp = thunarx_menu_provider_get_file_actions (lp->data,
+                                                            GTK_WIDGET (window),
+                                                            selected_files);
+            }
+          else if (G_LIKELY (folder != NULL))
+            {
+              tmp = thunarx_menu_provider_get_folder_actions (lp->data,
+                                                              GTK_WIDGET (window),
+                                                              THUNARX_FILE_INFO (folder));
+            }
+          else
+            {
+              tmp = NULL;
+            }
+
+          actions = g_list_concat (actions, tmp);
+          g_object_unref (G_OBJECT (lp->data));
+        }
+      g_list_free (providers);
+    }
+
+  /* remove previously inserted menu actions from the UI manager */
+  if (window->custom_merge_id != 0)
+    {
+      gtk_ui_manager_remove_ui (window->ui_manager, window->custom_merge_id);
+      gtk_ui_manager_ensure_update (window->ui_manager);
+      window->custom_merge_id = 0;
+    }
+
+  /* drop any previous custom action group */
+  if (window->custom_actions != NULL)
+    {
+      gtk_ui_manager_remove_action_group (window->ui_manager, window->custom_actions);
+      g_object_unref (window->custom_actions);
+      window->custom_actions = NULL;
+    }
+
+  /* add the actions specified by the menu providers */
+  if (G_LIKELY (actions != NULL))
+    {
+      /* allocate the action group and the merge id for the custom actions */
+      window->custom_actions = gtk_action_group_new ("ThunarActions");
+      window->custom_merge_id = gtk_ui_manager_new_merge_id (window->ui_manager);
+      gtk_ui_manager_insert_action_group (window->ui_manager, window->custom_actions, -1);
+
+      /* add the actions to the UI manager */
+      for (lp = actions; lp != NULL; lp = lp->next)
+        {
+          /* add the action to the action group */
+          gtk_action_group_add_action_with_accel (window->custom_actions,
+                                                  GTK_ACTION (lp->data),
+                                                  NULL);
+
+          /* add to the file context menu */
+          gtk_ui_manager_add_ui (window->ui_manager,
+                                 window->custom_merge_id,
+                                 "/main-menu/file-menu/placeholder-custom-actions",
+                                 gtk_action_get_name (GTK_ACTION (lp->data)),
+                                 gtk_action_get_name (GTK_ACTION (lp->data)),
+                                 GTK_UI_MANAGER_MENUITEM, FALSE);
+
+          /* release the reference on the action */
+          g_object_unref (G_OBJECT (lp->data));
+        }
+
+      /* cleanup */
+      g_list_free (actions);
+    }
+}
+
+
+
+static void
 thunar_window_notify_loading (ThunarView   *view,
                               GParamSpec   *pspec,
                               ThunarWindow *window)


More information about the Xfce4-commits mailing list