[Xfce4-commits] <thunar:master> Add startup notification to the UCA plugin.

Nick Schermer noreply at xfce.org
Sun Dec 20 12:34:03 CET 2009


Updating branch refs/heads/master
         to 3072fb532daac4ae8202bcf8521faf577f128fe1 (commit)
       from 19383d0e0da7293931ae4420fca6cecbe5938259 (commit)

commit 3072fb532daac4ae8202bcf8521faf577f128fe1
Author: Nick Schermer <nick at xfce.org>
Date:   Thu Dec 17 22:08:23 2009 +0100

    Add startup notification to the UCA plugin.
    
    This makes the UCA plugin work properly with focus stealing. It
    requires a version of libxfce4ui with the
    xfce_spawn_on_screen_with_child_watch() function.

 plugins/thunar-uca/Makefile.am           |    6 ++
 plugins/thunar-uca/thunar-uca-editor.c   |   22 ++++-
 plugins/thunar-uca/thunar-uca-model.c    |   27 +++++++
 plugins/thunar-uca/thunar-uca-model.h    |    2 +
 plugins/thunar-uca/thunar-uca-provider.c |  124 +++++++++++++++++-------------
 5 files changed, 122 insertions(+), 59 deletions(-)

diff --git a/plugins/thunar-uca/Makefile.am b/plugins/thunar-uca/Makefile.am
index e9a05a4..2e37016 100644
--- a/plugins/thunar-uca/Makefile.am
+++ b/plugins/thunar-uca/Makefile.am
@@ -33,8 +33,14 @@ thunar_uca_la_SOURCES =							\
 thunar_uca_la_CFLAGS =							\
 	$(EXO_CFLAGS)							\
 	$(LIBXFCE4UTIL_CFLAGS)						\
+	$(LIBXFCE4UI_CFLAGS)						\
 	$(PLATFORM_CFLAGS)
 
+thunar_uca_la_LIBADD =							\
+	$(EXO_LIBS)							\
+	$(LIBXFCE4UTIL_LIBS)						\
+	$(LIBXFCE4UI_LIBS)
+
 thunar_uca_la_LDFLAGS =							\
 	-avoid-version							\
 	-export-dynamic							\
diff --git a/plugins/thunar-uca/thunar-uca-editor.c b/plugins/thunar-uca/thunar-uca-editor.c
index e513fbd..00add99 100644
--- a/plugins/thunar-uca/thunar-uca-editor.c
+++ b/plugins/thunar-uca/thunar-uca-editor.c
@@ -59,6 +59,7 @@ struct _ThunarUcaEditor
   GtkWidget   *description_entry;
   GtkWidget   *icon_button;
   GtkWidget   *command_entry;
+  GtkWidget   *sn_button;
   GtkWidget   *parameter_entry;
   GtkWidget   *patterns_entry;
   GtkWidget   *directories_button;
@@ -119,7 +120,7 @@ thunar_uca_editor_init (ThunarUcaEditor *uca_editor)
      Basic
    */
   label = gtk_label_new (_("Basic"));
-  table = gtk_table_new (6, 2, FALSE);
+  table = gtk_table_new (7, 2, FALSE);
   gtk_table_set_col_spacings (GTK_TABLE (table), 12);
   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
   gtk_container_set_border_width (GTK_CONTAINER (table), 12);
@@ -199,12 +200,19 @@ thunar_uca_editor_init (ThunarUcaEditor *uca_editor)
   atk_relation_set_add (relations, relation);
   g_object_unref (G_OBJECT (relation));
 
+  uca_editor->sn_button = gtk_check_button_new_with_label (_("Use Startup Notification"));
+  gtk_widget_set_tooltip_text (uca_editor->sn_button, _("Enable this option if you want a waiting cursor to be shown while the "
+                                                        "action is launched. This is also highly recommended if you have focus "
+                                                        "stealing prevention enabled in your window manager."));
+  gtk_table_attach (GTK_TABLE (table), uca_editor->sn_button, 1, 2, 3, 4, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+  gtk_widget_show (uca_editor->sn_button);
+
   label = g_object_new (GTK_TYPE_LABEL, "label", _("_Icon:"), "use-underline", TRUE, "xalign", 0.0f, NULL);
-  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
+  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5, GTK_FILL, GTK_FILL, 0, 0);
   gtk_widget_show (label);
 
   align = gtk_alignment_new (0.0f, 0.5f, 0.0f, 0.0f);
-  gtk_table_attach (GTK_TABLE (table), align, 1, 2, 3, 4, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+  gtk_table_attach (GTK_TABLE (table), align, 1, 2, 4, 5, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
   gtk_widget_show (align);
 
   uca_editor->icon_button = gtk_button_new_with_label (_("No icon"));
@@ -223,11 +231,11 @@ thunar_uca_editor_init (ThunarUcaEditor *uca_editor)
   g_object_unref (G_OBJECT (relation));
 
   align = g_object_new (GTK_TYPE_ALIGNMENT, "height-request", 12, NULL);
-  gtk_table_attach (GTK_TABLE (table), align, 0, 2, 4, 5, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+  gtk_table_attach (GTK_TABLE (table), align, 0, 2, 5, 6, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
   gtk_widget_show (align);
 
   hbox = gtk_hbox_new (FALSE, 6);
-  gtk_table_attach (GTK_TABLE (table), hbox, 0, 2, 5, 6, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+  gtk_table_attach (GTK_TABLE (table), hbox, 0, 2, 6, 7, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
   gtk_widget_show (hbox);
 
   image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DND);
@@ -763,6 +771,7 @@ thunar_uca_editor_load (ThunarUcaEditor *uca_editor,
   gchar         *command;
   gchar         *icon;
   gchar         *name;
+  gboolean       startup_notify;
 
   g_return_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor));
   g_return_if_fail (THUNAR_UCA_IS_MODEL (uca_model));
@@ -776,6 +785,7 @@ thunar_uca_editor_load (ThunarUcaEditor *uca_editor,
                       THUNAR_UCA_MODEL_COLUMN_TYPES, &types,
                       THUNAR_UCA_MODEL_COLUMN_ICON, &icon,
                       THUNAR_UCA_MODEL_COLUMN_NAME, &name,
+                      THUNAR_UCA_MODEL_COLUMN_STARTUP_NOTIFY, &startup_notify,
                       -1);
 
   /* setup the new selection */
@@ -789,6 +799,7 @@ thunar_uca_editor_load (ThunarUcaEditor *uca_editor,
   gtk_entry_set_text (GTK_ENTRY (uca_editor->patterns_entry), (patterns != NULL) ? patterns : "");
   gtk_entry_set_text (GTK_ENTRY (uca_editor->command_entry), (command != NULL) ? command : "");
   gtk_entry_set_text (GTK_ENTRY (uca_editor->name_entry), (name != NULL) ? name : "");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (uca_editor->sn_button), startup_notify);
 
   /* cleanup */
   g_free (description);
@@ -823,6 +834,7 @@ thunar_uca_editor_save (ThunarUcaEditor *uca_editor,
                            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)),
+                           gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (uca_editor->sn_button)),
                            gtk_entry_get_text (GTK_ENTRY (uca_editor->patterns_entry)),
                            thunar_uca_editor_get_types (uca_editor));
 }
diff --git a/plugins/thunar-uca/thunar-uca-model.c b/plugins/thunar-uca/thunar-uca-model.c
index fefb101..d5cb8e4 100644
--- a/plugins/thunar-uca/thunar-uca-model.c
+++ b/plugins/thunar-uca/thunar-uca-model.c
@@ -74,6 +74,7 @@ typedef enum
   PARSER_ICON,
   PARSER_NAME,
   PARSER_COMMAND,
+  PARSER_STARTUP_NOTIFY,
   PARSER_PATTERNS,
   PARSER_DESCRIPTION,
   PARSER_DIRECTORIES,
@@ -161,6 +162,7 @@ struct _ThunarUcaModelItem
   gchar         *description;
   gchar         *icon;
   gchar         *command;
+  guint          startup_notify : 1;
   gchar        **patterns;
   ThunarUcaTypes types;
 
@@ -182,6 +184,7 @@ typedef struct
   GString        *command;
   GString        *patterns;
   GString        *description;
+  gboolean        startup_notify;
   gboolean        description_use;
   guint           description_match;
   ThunarUcaTypes  types;
@@ -323,6 +326,9 @@ thunar_uca_model_get_column_type (GtkTreeModel *tree_model,
     case THUNAR_UCA_MODEL_COLUMN_COMMAND:
       return G_TYPE_STRING;
 
+    case THUNAR_UCA_MODEL_COLUMN_STARTUP_NOTIFY:
+      return G_TYPE_BOOLEAN;
+
     case THUNAR_UCA_MODEL_COLUMN_PATTERNS:
       return G_TYPE_STRING;
 
@@ -412,6 +418,10 @@ thunar_uca_model_get_value (GtkTreeModel *tree_model,
       g_value_set_static_string (value, item->command);
       break;
 
+    case THUNAR_UCA_MODEL_COLUMN_STARTUP_NOTIFY:
+      g_value_set_boolean (value, item->startup_notify);
+      break;
+
     case THUNAR_UCA_MODEL_COLUMN_PATTERNS:
       str = g_strjoinv (";", item->patterns);
       g_value_take_string (value, str);
@@ -549,6 +559,7 @@ thunar_uca_model_load_from_file (ThunarUcaModel *uca_model,
   parser.command = g_string_new (NULL);
   parser.patterns = g_string_new (NULL);
   parser.description = g_string_new (NULL);
+  parser.startup_notify = FALSE;
   xfce_stack_push (parser.stack, PARSER_START);
 
   /* parse the file */
@@ -622,6 +633,7 @@ start_element_handler (GMarkupParseContext *context,
           parser->name_match = XFCE_LOCALE_NO_MATCH;
           parser->description_match = XFCE_LOCALE_NO_MATCH;
           parser->types = 0;
+          parser->startup_notify = FALSE;
           g_string_truncate (parser->icon, 0);
           g_string_truncate (parser->name, 0);
           g_string_truncate (parser->command, 0);
@@ -707,6 +719,11 @@ start_element_handler (GMarkupParseContext *context,
 
           xfce_stack_push (parser->stack, PARSER_DESCRIPTION);
         }
+      else if (strcmp (element_name, "startup-notify") == 0)
+        {
+          parser->startup_notify = TRUE;
+          xfce_stack_push (parser->stack, PARSER_STARTUP_NOTIFY);
+        }
       else if (strcmp (element_name, "directories") == 0)
         {
           parser->types |= THUNAR_UCA_TYPE_DIRECTORIES;
@@ -784,6 +801,7 @@ end_element_handler (GMarkupParseContext *context,
                                    parser->description->str,
                                    parser->icon->str,
                                    parser->command->str,
+                                   parser->startup_notify,
                                    parser->patterns->str,
                                    parser->types);
         }
@@ -816,6 +834,11 @@ end_element_handler (GMarkupParseContext *context,
         goto unknown_element;
       break;
 
+    case PARSER_STARTUP_NOTIFY:
+      if (strcmp (element_name, "startup-notify") != 0)
+        goto unknown_element;
+      break;
+
     case PARSER_DIRECTORIES:
       if (strcmp (element_name, "directories") != 0)
         goto unknown_element;
@@ -1215,6 +1238,7 @@ thunar_uca_model_update (ThunarUcaModel *uca_model,
                          const gchar    *description,
                          const gchar    *icon,
                          const gchar    *command,
+                         gboolean        startup_notify,
                          const gchar    *patterns,
                          ThunarUcaTypes  types)
 {
@@ -1239,6 +1263,7 @@ thunar_uca_model_update (ThunarUcaModel *uca_model,
   if (G_LIKELY (description != NULL && *description != '\0'))
     item->description = g_strdup (description);
   item->types = types;
+  item->startup_notify = startup_notify;
 
   /* setup the patterns */
   item->patterns = g_strsplit ((patterns != NULL && *patterns != '\0') ? patterns : "*", ";", -1);
@@ -1334,6 +1359,8 @@ thunar_uca_model_save (ThunarUcaModel *uca_model,
       fprintf (fp, "%s", escaped);
       g_free (patterns);
       g_free (escaped);
+      if (item->startup_notify)
+        fprintf (fp, "<startup-notify/>");
       if ((item->types & THUNAR_UCA_TYPE_DIRECTORIES) != 0)
         fprintf (fp, "<directories/>");
       if ((item->types & THUNAR_UCA_TYPE_AUDIO_FILES) != 0)
diff --git a/plugins/thunar-uca/thunar-uca-model.h b/plugins/thunar-uca/thunar-uca-model.h
index d778270..9bef6df 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_ICON,
   THUNAR_UCA_MODEL_COLUMN_COMMAND,
+  THUNAR_UCA_MODEL_COLUMN_STARTUP_NOTIFY,
   THUNAR_UCA_MODEL_COLUMN_PATTERNS,
   THUNAR_UCA_MODEL_COLUMN_TYPES,
   THUNAR_UCA_MODEL_COLUMN_STOCK_LABEL,
@@ -90,6 +91,7 @@ void            thunar_uca_model_update         (ThunarUcaModel         *uca_mod
                                                  const gchar            *description,
                                                  const gchar            *icon,
                                                  const gchar            *command,
+                                                 gboolean                startup_notify,
                                                  const gchar            *patterns,
                                                  ThunarUcaTypes          types);
 
diff --git a/plugins/thunar-uca/thunar-uca-provider.c b/plugins/thunar-uca/thunar-uca-provider.c
index 3c5550a..3ac481d 100644
--- a/plugins/thunar-uca/thunar-uca-provider.c
+++ b/plugins/thunar-uca/thunar-uca-provider.c
@@ -23,9 +23,11 @@
 #include <config.h>
 #endif
 
-#include <glib/gi18n.h>
 #include <gio/gio.h>
 
+#include <libxfce4util/libxfce4util.h>
+#include <libxfce4ui/libxfce4ui.h>
+
 #include <thunar-uca/thunar-uca-chooser.h>
 #include <thunar-uca/thunar-uca-context.h>
 #include <thunar-uca/thunar-uca-model.h>
@@ -47,10 +49,10 @@ static GList *thunar_uca_provider_get_folder_actions        (ThunarxMenuProvider
                                                              ThunarxFileInfo                  *folder);
 static void   thunar_uca_provider_activated                 (ThunarUcaProvider                *uca_provider,
                                                              GtkAction                        *action);
-static void   thunar_uca_provider_child_watch               (GPid                              pid,
-                                                             gint                              status,
-                                                             gpointer                          user_data);
-static void   thunar_uca_provider_child_watch_destroy       (gpointer                          user_data);
+static void   thunar_uca_provider_child_watch               (ThunarUcaProvider                *uca_provider,
+                                                             gint                              exit_status);
+static void   thunar_uca_provider_child_watch_destroy       (gpointer                          user_data,
+                                                             GClosure                         *closure);
 
 
 
@@ -71,7 +73,7 @@ struct _ThunarUcaProvider
    * child process has terminated.
    */
   gchar          *child_watch_path;
-  gint            child_watch_id;
+  GClosure       *child_watch;
 };
 
 
@@ -133,10 +135,6 @@ thunar_uca_provider_init (ThunarUcaProvider *uca_provider)
 
   /* grab a reference on the default model */
   uca_provider->model = thunar_uca_model_get_default ();
-
-  /* initialize child watch support */
-  uca_provider->child_watch_path = NULL;
-  uca_provider->child_watch_id = -1;
 }
 
 
@@ -145,18 +143,9 @@ static void
 thunar_uca_provider_finalize (GObject *object)
 {
   ThunarUcaProvider *uca_provider = THUNAR_UCA_PROVIDER (object);
-  GSource           *source;
 
   /* give up maintaince of any pending child watch */
-  if (G_UNLIKELY (uca_provider->child_watch_id >= 0))
-    {
-      /* reset the callback function to g_spawn_close_pid() so the plugin can be
-       * safely unloaded and the child will still not become a zombie afterwards.
-       * This also resets the child_watch_id and child_watch_path properties.
-       */
-      source = g_main_context_find_source_by_id (NULL, uca_provider->child_watch_id);
-      g_source_set_callback (source, (GSourceFunc) g_spawn_close_pid, NULL, NULL);
-    }
+  thunar_uca_provider_child_watch_destroy (uca_provider, NULL);
 
   /* drop our reference on the model */
   g_object_unref (G_OBJECT (uca_provider->model));
@@ -307,7 +296,6 @@ thunar_uca_provider_activated (ThunarUcaProvider *uca_provider,
   GtkWidget           *dialog;
   GtkWidget           *window;
   gboolean             succeed;
-  GSource             *source;
   GError              *error = NULL;
   GList               *files;
   gchar              **argv;
@@ -316,7 +304,9 @@ thunar_uca_provider_activated (ThunarUcaProvider *uca_provider,
   gchar               *label;
   gchar               *uri;
   gint                 argc;
-  gint                 pid;
+  gchar               *icon_name = NULL;
+  gboolean             startup_notify;
+  GClosure            *child_watch;
 
   g_return_if_fail (THUNAR_UCA_IS_PROVIDER (uca_provider));
   g_return_if_fail (GTK_IS_ACTION (action));
@@ -340,6 +330,12 @@ thunar_uca_provider_activated (ThunarUcaProvider *uca_provider,
   succeed = thunar_uca_model_parse_argv (uca_provider->model, &iter, files, &argc, &argv, &error);
   if (G_LIKELY (succeed))
     {
+      /* get the icon name and whether startup notification is active */
+      gtk_tree_model_get (GTK_TREE_MODEL (uca_provider->model), &iter,
+                          THUNAR_UCA_MODEL_COLUMN_ICON, &icon_name,
+                          THUNAR_UCA_MODEL_COLUMN_STARTUP_NOTIFY, &startup_notify,
+                          -1);
+
       /* determine the working from the first file */
       if (G_LIKELY (files != NULL))
         {
@@ -363,36 +359,45 @@ thunar_uca_provider_activated (ThunarUcaProvider *uca_provider,
           g_free (uri);
         }
 
+      /* build closre for child watch */
+      child_watch = g_cclosure_new_swap (G_CALLBACK (thunar_uca_provider_child_watch),
+                                         uca_provider, thunar_uca_provider_child_watch_destroy);
+      g_closure_ref (child_watch);
+      g_closure_sink (child_watch);
+
       /* spawn the command on the window's screen */
-      succeed = gdk_spawn_on_screen (gtk_widget_get_screen (GTK_WIDGET (window)), working_directory,
-                                     argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
-                                     NULL, NULL, &pid, &error);
+      succeed = xfce_spawn_on_screen_with_child_watch (gtk_widget_get_screen (GTK_WIDGET (window)),
+                                                       working_directory, argv, NULL,
+                                                       G_SPAWN_SEARCH_PATH,
+                                                       startup_notify,
+                                                       gtk_get_current_event_time (),
+                                                       icon_name,
+                                                       child_watch,
+                                                       &error);
 
       /* check if we succeed */
       if (G_LIKELY (succeed))
         {
-          /* check if we already have a child watch */
-          if (G_UNLIKELY (uca_provider->child_watch_id >= 0))
-            {
-              /* reset the callback function to g_spawn_close_pid() so the plugin can be
-               * safely unloaded and the child will still not become a zombie afterwards.
-               */
-              source = g_main_context_find_source_by_id (NULL, uca_provider->child_watch_id);
-              g_source_set_callback (source, (GSourceFunc) g_spawn_close_pid, NULL, NULL);
-            }
+          /* release existing child watch */
+          thunar_uca_provider_child_watch_destroy (uca_provider, NULL);
 
-          /* schedule the new child watch */
-          uca_provider->child_watch_id = g_child_watch_add_full (G_PRIORITY_LOW, pid, thunar_uca_provider_child_watch,
-                                                                 uca_provider, thunar_uca_provider_child_watch_destroy);
+          /* set new closure */
+          uca_provider->child_watch = child_watch;
 
           /* take over ownership of the working directory as child watch path */
           uca_provider->child_watch_path = working_directory;
           working_directory = NULL;
         }
+      else
+        {
+          /* spawn failed, release watch */
+          g_closure_unref (child_watch);
+        }
 
       /* cleanup */
       g_free (working_directory);
       g_strfreev (argv);
+      g_free (icon_name);
     }
 
   /* present error message to the user */
@@ -416,14 +421,14 @@ thunar_uca_provider_activated (ThunarUcaProvider *uca_provider,
 
 
 static void
-thunar_uca_provider_child_watch (GPid     pid,
-                                 gint     status,
-                                 gpointer user_data)
+thunar_uca_provider_child_watch (ThunarUcaProvider *uca_provider,
+                                 gint               exit_status)
+
 {
-  ThunarUcaProvider *uca_provider = THUNAR_UCA_PROVIDER (user_data);
-  GFileMonitor      *monitor;
-  GError            *error = NULL;
-  GFile             *file;
+  GFileMonitor *monitor;
+  GFile        *file;
+
+  g_return_if_fail (THUNAR_UCA_IS_PROVIDER (uca_provider));
 
   GDK_THREADS_ENTER ();
 
@@ -434,7 +439,7 @@ thunar_uca_provider_child_watch (GPid     pid,
       file = g_file_new_for_path (uca_provider->child_watch_path);
 
       /* schedule a changed notification on the path */
-      monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, &error);
+      monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, NULL);
 
       if (monitor != NULL)
         {
@@ -446,8 +451,7 @@ thunar_uca_provider_child_watch (GPid     pid,
       g_object_unref (file);
     }
 
-  /* need to cleanup */
-  g_spawn_close_pid (pid);
+  thunar_uca_provider_child_watch_destroy (uca_provider, NULL);
 
   GDK_THREADS_LEAVE ();
 }
@@ -455,15 +459,27 @@ thunar_uca_provider_child_watch (GPid     pid,
 
 
 static void
-thunar_uca_provider_child_watch_destroy (gpointer user_data)
+thunar_uca_provider_child_watch_destroy (gpointer  user_data,
+                                         GClosure *closure)
 {
   ThunarUcaProvider *uca_provider = THUNAR_UCA_PROVIDER (user_data);
+  GClosure          *child_watch;
 
-  /* reset child watch id and path */
-  g_free (uca_provider->child_watch_path);
-  uca_provider->child_watch_path = NULL;
-  uca_provider->child_watch_id = -1;
-}
-
+  /* leave if the closure is not the one we're watching */
+  if (uca_provider->child_watch == closure
+      || closure == NULL)
+    {
+      /* reset child watch and path */
+      if (G_UNLIKELY (uca_provider->child_watch != NULL))
+        {
+          child_watch = uca_provider->child_watch;
+          uca_provider->child_watch = NULL;
 
+          g_closure_invalidate (child_watch);
+          g_closure_unref (child_watch);
+        }
 
+      g_free (uca_provider->child_watch_path);
+      uca_provider->child_watch_path = NULL;
+    }
+}



More information about the Xfce4-commits mailing list