Updating branch refs/heads/jannis/file-menu-custom-actions to d99e85d67d27ece202c0a79a0c916d21558a9e61 (commit) from 0edbd00ac4322efcf3d6eb155be8c3961a804707 (commit)
commit d99e85d67d27ece202c0a79a0c916d21558a9e61 Author: Jannis Pohlmann <jan...@xfce.org> Date: Tue Nov 8 14:34:51 2011 +0100 Generate persistent unique IDs and names for custom actions. This adds an optional <unique-id> element to the <action> element in uca.xml. When loading the UCA model from uca.xml, we check for this element in the parser. If it is not given, we generate a new ID with the following signature: <current_time.tv_sec>.<current_time.tv_usec>-<static counter value> After parsing we check if we have generated new IDs and, if so, immediately save the model back to the uca.xml file. This part is a bit of a hack but it will be a no-brainer once we've switched to xfconf, I think. The ThunarUcaEditor is extended so that it generates a unique ID for new items with the same signature as above. It does not overwrite IDs of existing custom actions. Together this allows for a persistent association of custom actions with keyboard shortcuts. plugins/thunar-uca/thunar-uca-editor.c | 41 ++++++++++ plugins/thunar-uca/thunar-uca-model.c | 123 +++++++++++++++++++++++++----- plugins/thunar-uca/thunar-uca-model.h | 5 + plugins/thunar-uca/thunar-uca-provider.c | 10 ++- plugins/thunar-uca/uca.xml.in | 4 +- 5 files changed, 159 insertions(+), 24 deletions(-) diff --git a/plugins/thunar-uca/thunar-uca-editor.c b/plugins/thunar-uca/thunar-uca-editor.c index 00add99..fa1c9f9 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 <be...@xfce.org> + * Copyright (c) 2011 Jannis Pohlmann <jan...@xfce.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -41,6 +42,9 @@ static void thunar_uca_editor_set_icon_name (ThunarUcaEditor static ThunarUcaTypes thunar_uca_editor_get_types (const ThunarUcaEditor *uca_editor); static void thunar_uca_editor_set_types (ThunarUcaEditor *uca_editor, ThunarUcaTypes types); +static const gchar *thunar_uca_editor_get_unique_id (const ThunarUcaEditor *uca_editor); +static void thunar_uca_editor_set_unique_id (ThunarUcaEditor *uca_editor, + const gchar *unique_id); static void thunar_uca_editor_command_clicked (ThunarUcaEditor *uca_editor); static void thunar_uca_editor_icon_clicked (ThunarUcaEditor *uca_editor); @@ -101,6 +105,7 @@ thunar_uca_editor_init (ThunarUcaEditor *uca_editor) GtkWidget *table; GtkWidget *hbox; GtkWidget *vbox; + gchar *unique_id; /* configure the dialog properties */ gtk_dialog_add_button (GTK_DIALOG (uca_editor), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); @@ -454,6 +459,11 @@ thunar_uca_editor_init (ThunarUcaEditor *uca_editor) gtk_misc_set_alignment (GTK_MISC (label), 0.0f, 0.0f); gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); gtk_widget_show (label); + + /* set a unique ID for new items. this will be overridden by existing items */ + unique_id = thunar_uca_model_get_unique_id (); + thunar_uca_editor_set_unique_id (uca_editor, unique_id); + g_free (unique_id); } @@ -751,6 +761,30 @@ thunar_uca_editor_set_types (ThunarUcaEditor *uca_editor, +static const gchar* +thunar_uca_editor_get_unique_id (const ThunarUcaEditor *uca_editor) +{ + g_return_val_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor), NULL); + return g_object_get_data (G_OBJECT (uca_editor->icon_button), "thunar-uca-unique-id"); +} + + + +static void +thunar_uca_editor_set_unique_id (ThunarUcaEditor *uca_editor, + const gchar *unique_id) +{ + g_return_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor)); + + /* remember the unique ID for the item */ + g_object_set_data_full (G_OBJECT (uca_editor->icon_button), + "thunar-uca-unique-id", + g_strdup (unique_id), + g_free); +} + + + /** * thunar_uca_editor_load: * @uca_editor : a #ThunarUcaEditor. @@ -771,6 +805,7 @@ thunar_uca_editor_load (ThunarUcaEditor *uca_editor, gchar *command; gchar *icon; gchar *name; + gchar *unique_id; gboolean startup_notify; g_return_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor)); @@ -785,6 +820,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_UNIQUE_ID, &unique_id, THUNAR_UCA_MODEL_COLUMN_STARTUP_NOTIFY, &startup_notify, -1); @@ -794,6 +830,9 @@ thunar_uca_editor_load (ThunarUcaEditor *uca_editor, /* setup the new icon */ thunar_uca_editor_set_icon_name (uca_editor, icon); + /* setup the unique ID */ + thunar_uca_editor_set_unique_id (uca_editor, unique_id); + /* apply the new values */ gtk_entry_set_text (GTK_ENTRY (uca_editor->description_entry), (description != NULL) ? description : ""); gtk_entry_set_text (GTK_ENTRY (uca_editor->patterns_entry), (patterns != NULL) ? patterns : ""); @@ -806,6 +845,7 @@ thunar_uca_editor_load (ThunarUcaEditor *uca_editor, g_free (patterns); g_free (command); g_free (icon); + g_free (unique_id); g_free (name); } @@ -831,6 +871,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)), + thunar_uca_editor_get_unique_id (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)), diff --git a/plugins/thunar-uca/thunar-uca-model.c b/plugins/thunar-uca/thunar-uca-model.c index 1f54bb1..ab91460 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 <be...@xfce.org> - * Copyright (c) 2009 Jannis Pohlmann <jan...@xfce.org> + * Copyright (c) 2009-2011 Jannis Pohlmann <jan...@xfce.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -35,6 +35,9 @@ #ifdef HAVE_PATHS_H #include <paths.h> #endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif #ifdef HAVE_STRING_H #include <string.h> #endif @@ -73,6 +76,7 @@ typedef enum PARSER_ACTION, PARSER_ICON, PARSER_NAME, + PARSER_UNIQUE_ID, PARSER_COMMAND, PARSER_STARTUP_NOTIFY, PARSER_PATTERNS, @@ -120,6 +124,7 @@ static gboolean thunar_uca_model_iter_parent (GtkTreeModel GtkTreeIter *child); static gboolean thunar_uca_model_load_from_file (ThunarUcaModel *uca_model, const gchar *filename, + gboolean *ids_generated, GError **error); static void thunar_uca_model_item_reset (ThunarUcaModelItem *item); static void thunar_uca_model_item_free (ThunarUcaModelItem *item); @@ -159,6 +164,7 @@ struct _ThunarUcaModel struct _ThunarUcaModelItem { gchar *name; + gchar *unique_id; gchar *description; gchar *icon; gchar *command; @@ -180,6 +186,7 @@ typedef struct GString *name; gboolean name_use; guint name_match; + GString *unique_id; GString *icon; GString *command; GString *patterns; @@ -188,6 +195,7 @@ typedef struct gboolean description_use; guint description_match; ThunarUcaTypes types; + gboolean ids_generated; } Parser; @@ -244,8 +252,9 @@ thunar_uca_model_tree_model_init (GtkTreeModelIface *iface) static void thunar_uca_model_init (ThunarUcaModel *uca_model) { - GError *error = NULL; - gchar *filename; + gboolean ids_generated = FALSE; + GError *error = NULL; + gchar *filename; /* allocate a new icon factory for our action icons * and add it to the default icon factories @@ -261,12 +270,16 @@ thunar_uca_model_init (ThunarUcaModel *uca_model) if (G_LIKELY (filename != NULL)) { /* try to load the file */ - if (!thunar_uca_model_load_from_file (uca_model, filename, &error)) + if (!thunar_uca_model_load_from_file (uca_model, filename, &ids_generated, &error)) { g_warning ("Failed to load `%s': %s", filename, error->message); g_error_free (error); } + /* save the model back to the file immediately to make unique IDs persistent */ + if (ids_generated) + thunar_uca_model_save (uca_model, NULL); + /* release the filename */ g_free (filename); } @@ -317,6 +330,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; @@ -406,6 +422,10 @@ thunar_uca_model_get_value (GtkTreeModel *tree_model, g_value_set_static_string (value, item->name ? item->name : ""); break; + case THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID: + g_value_set_static_string (value, item->unique_id ? item->unique_id : ""); + break; + case THUNAR_UCA_MODEL_COLUMN_DESCRIPTION: g_value_set_static_string (value, item->description); break; @@ -534,6 +554,7 @@ thunar_uca_model_iter_parent (GtkTreeModel *tree_model, static gboolean thunar_uca_model_load_from_file (ThunarUcaModel *uca_model, const gchar *filename, + gboolean *ids_generated, GError **error) { GMarkupParseContext *context; @@ -543,8 +564,8 @@ thunar_uca_model_load_from_file (ThunarUcaModel *uca_model, gsize content_len; g_return_val_if_fail (THUNAR_UCA_IS_MODEL (uca_model), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); g_return_val_if_fail (g_path_is_absolute (filename), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); /* read the file info memory */ if (!g_file_get_contents (filename, &content, &content_len, error)) @@ -555,11 +576,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 = 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.ids_generated = FALSE; xfce_stack_push (parser.stack, PARSER_START); /* parse the file */ @@ -567,12 +590,17 @@ thunar_uca_model_load_from_file (ThunarUcaModel *uca_model, succeed = g_markup_parse_context_parse (context, content, content_len, error) && g_markup_parse_context_end_parse (context, error); + /* tell the caller whether or not we generated new IDs */ + if (ids_generated != NULL) + *ids_generated = parser.ids_generated; + /* cleanup */ g_markup_parse_context_free (context); g_string_free (parser.description, TRUE); g_string_free (parser.patterns, TRUE); g_string_free (parser.command, TRUE); g_string_free (parser.icon, TRUE); + g_string_free (parser.unique_id, TRUE); g_string_free (parser.name, TRUE); g_free (parser.locale); xfce_stack_free (parser.stack); @@ -637,6 +665,7 @@ start_element_handler (GMarkupParseContext *context, parser->startup_notify = FALSE; g_string_truncate (parser->icon, 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); @@ -676,6 +705,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, 0); @@ -780,6 +814,7 @@ end_element_handler (GMarkupParseContext *context, { GtkTreeIter iter; Parser *parser = user_data; + gchar *unique_id; switch (xfce_stack_top (parser->stack)) { @@ -796,9 +831,21 @@ end_element_handler (GMarkupParseContext *context, case PARSER_ACTION: if (strcmp (element_name, "action") == 0) { + /* generate a unique ID if we have none yet */ + if (parser->unique_id->len == 0) + { + unique_id = thunar_uca_model_get_unique_id (); + g_string_append (parser->unique_id, unique_id); + g_free (unique_id); + + /* remember that we generated at least one new ID */ + parser->ids_generated = TRUE; + } + 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->str, parser->command->str, @@ -815,6 +862,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; @@ -900,6 +952,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, text, text_len); break; @@ -1224,6 +1280,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. @@ -1236,6 +1293,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, @@ -1257,6 +1315,8 @@ thunar_uca_model_update (ThunarUcaModel *uca_model, /* setup the new item values */ if (G_LIKELY (name != NULL && *name != '\0')) item->name = g_strdup (name); + if (G_LIKELY (unique_id != NULL && *unique_id != '\0')) + item->unique_id = g_strdup (unique_id); if (G_LIKELY (icon != NULL && *icon != '\0')) item->icon = g_strdup (icon); if (G_LIKELY (command != NULL && *command != '\0')) @@ -1339,19 +1399,19 @@ thunar_uca_model_save (ThunarUcaModel *uca_model, fp = fdopen (fd, "w"); /* write the header */ - fprintf (fp, "<?xml encoding=\"UTF-8\" version=\"1.0\"?>\n<actions>"); + fprintf (fp, "<?xml encoding=\"UTF-8\" version=\"1.0\"?>\n<actions>\n"); /* write the model items */ for (lp = uca_model->items; lp != NULL; lp = lp->next) { item = (ThunarUcaModelItem *) lp->data; - fprintf (fp, "<action>"); + fprintf (fp, "<action>\n"); patterns = g_strjoinv (";", item->patterns); - escaped = g_markup_printf_escaped ("<icon>%s</icon>" - "<name>%s</name>" - "<command>%s</command>" - "<description>%s</description>" - "<patterns>%s</patterns>", + escaped = g_markup_printf_escaped (" <icon>%s</icon>\n" + " <name>%s</name>\n" + " <command>%s</command>\n" + " <description>%s</description>\n" + " <patterns>%s</patterns>\n", (item->icon != NULL) ? item->icon : "", (item->name != NULL) ? item->name : "", (item->command != NULL) ? item->command : "", @@ -1360,21 +1420,28 @@ thunar_uca_model_save (ThunarUcaModel *uca_model, fprintf (fp, "%s", escaped); g_free (patterns); g_free (escaped); + if (item->unique_id != NULL) + { + escaped = g_markup_printf_escaped (" <unique-id>%s</unique-id>\n", + item->unique_id); + fprintf (fp, "%s", escaped); + g_free (escaped); + } if (item->startup_notify) - fprintf (fp, "<startup-notify/>"); + fprintf (fp, " <startup-notify/>\n"); if ((item->types & THUNAR_UCA_TYPE_DIRECTORIES) != 0) - fprintf (fp, "<directories/>"); + fprintf (fp, " <directories/>\n"); if ((item->types & THUNAR_UCA_TYPE_AUDIO_FILES) != 0) - fprintf (fp, "<audio-files/>"); + fprintf (fp, " <audio-files/>\n"); if ((item->types & THUNAR_UCA_TYPE_IMAGE_FILES) != 0) - fprintf (fp, "<image-files/>"); + fprintf (fp, " <image-files/>\n"); if ((item->types & THUNAR_UCA_TYPE_OTHER_FILES) != 0) - fprintf (fp, "<other-files/>"); + fprintf (fp, " <other-files/>\n"); if ((item->types & THUNAR_UCA_TYPE_TEXT_FILES) != 0) - fprintf (fp, "<text-files/>"); + fprintf (fp, " <text-files/>\n"); if ((item->types & THUNAR_UCA_TYPE_VIDEO_FILES) != 0) - fprintf (fp, "<video-files/>"); - fprintf (fp, "</action>"); + fprintf (fp, " <video-files/>\n"); + fprintf (fp, "</action>\n"); } /* write the footer and close the tmp file */ @@ -1608,3 +1675,19 @@ error: } + +gchar * +thunar_uca_model_get_unique_id (void) +{ + static gint counter = 0; + GTimeVal current_time = { 0, 0 }; + + /* get the current timestamp */ + g_get_current_time (¤t_time); + + /* produce a "<timestamp>-<counter>" string */ + return g_strdup_printf ("%ld.%ld-%d", + current_time.tv_sec, + current_time.tv_usec, + ++counter); +} diff --git a/plugins/thunar-uca/thunar-uca-model.h b/plugins/thunar-uca/thunar-uca-model.h index 9bef6df..b57fdb7 100644 --- a/plugins/thunar-uca/thunar-uca-model.h +++ b/plugins/thunar-uca/thunar-uca-model.h @@ -1,6 +1,7 @@ /* $Id$ */ /*- * Copyright (c) 2005 Benedikt Meurer <be...@xfce.org> + * Copyright (c) 2011 Jannis Pohlmann <jan...@xfce.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -38,6 +39,7 @@ typedef struct _ThunarUcaModel ThunarUcaModel; typedef enum { THUNAR_UCA_MODEL_COLUMN_NAME, + THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID, THUNAR_UCA_MODEL_COLUMN_DESCRIPTION, THUNAR_UCA_MODEL_COLUMN_ICON, THUNAR_UCA_MODEL_COLUMN_COMMAND, @@ -88,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, @@ -105,6 +108,8 @@ gboolean thunar_uca_model_parse_argv (ThunarUcaModel *uca_mod gchar ***argvp, GError **error); +gchar *thunar_uca_model_get_unique_id (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; + G_END_DECLS; #endif /* !__THUNAR_UCA_MODEL_H__ */ diff --git a/plugins/thunar-uca/thunar-uca-provider.c b/plugins/thunar-uca/thunar-uca-provider.c index 674af2f..edad6b9 100644 --- a/plugins/thunar-uca/thunar-uca-provider.c +++ b/plugins/thunar-uca/thunar-uca-provider.c @@ -1,7 +1,7 @@ /* $Id$ */ /*- * Copyright (c) 2005-2006 Benedikt Meurer <be...@xfce.org> - * Copyright (c) 2009 Jannis Pohlmann <jan...@xfce.org> + * Copyright (c) 2009-2011 Jannis Pohlmann <jan...@xfce.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -28,6 +28,8 @@ #include <libxfce4util/libxfce4util.h> #include <libxfce4ui/libxfce4ui.h> +#include <exo/exo.h> + #include <thunar-uca/thunar-uca-chooser.h> #include <thunar-uca/thunar-uca-context.h> #include <thunar-uca/thunar-uca-model.h> @@ -201,6 +203,7 @@ thunar_uca_provider_get_file_actions (ThunarxMenuProvider *menu_provider, gchar *tooltip; gchar *label; gchar *name; + gchar *unique_id; paths = thunar_uca_model_match (uca_provider->model, files); for (lp = g_list_last (paths); lp != NULL; lp = lp->prev) @@ -211,13 +214,13 @@ thunar_uca_provider_get_file_actions (ThunarxMenuProvider *menu_provider, /* determine the label, tooltip and stock-id for the item */ gtk_tree_model_get (GTK_TREE_MODEL (uca_provider->model), &iter, THUNAR_UCA_MODEL_COLUMN_NAME, &label, + THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID, &unique_id, THUNAR_UCA_MODEL_COLUMN_ICON, &icon_name, THUNAR_UCA_MODEL_COLUMN_DESCRIPTION, &tooltip, -1); /* generate a unique action name */ - /* FIXME this name is persistent only if actions are not re-ordered in the GUI */ - name = g_strdup_printf ("ThunarUca::action-%d", ++uca_provider->last_action_id); + name = g_strdup_printf ("ThunarUca::action-%s", unique_id); /* create the new action with the given parameters */ action = gtk_action_new (name, label, tooltip, NULL); @@ -251,6 +254,7 @@ thunar_uca_provider_get_file_actions (ThunarxMenuProvider *menu_provider, g_free (icon_name); g_free (tooltip); g_free (label); + g_free (unique_id); g_free (name); } diff --git a/plugins/thunar-uca/uca.xml.in b/plugins/thunar-uca/uca.xml.in index a851407..fbbd6dd 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> _______________________________________________ Xfce4-commits mailing list Xfce4-commits@xfce.org https://mail.xfce.org/mailman/listinfo/xfce4-commits