Commit 3072fb53 authored by Nick Schermer's avatar Nick Schermer

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.
parent 19383d0e
......@@ -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 \
......
......@@ -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));
}
......
......@@ -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)
......
......@@ -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);
......
......@@ -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;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment