[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