Commit ab5daf33 authored by Andre Miranda's avatar Andre Miranda

Make it possible to assign accelerators to custom actions

parent 3baffcba
......@@ -154,6 +154,7 @@ XDT_CHECK_PACKAGE([GTK], [gtk+-3.0], [3.22.0])
XDT_CHECK_PACKAGE([GDK_PIXBUF], [gdk-pixbuf-2.0], [2.14.0])
XDT_CHECK_PACKAGE([LIBXFCE4UTIL], [libxfce4util-1.0], [4.12.0])
XDT_CHECK_PACKAGE([LIBXFCE4UI], [libxfce4ui-2], [4.13.2])
XDT_CHECK_PACKAGE([LIBXFCE4KBD_PRIVATE], [libxfce4kbd-private-3], [4.12.0])
XDT_CHECK_PACKAGE([XFCONF], [libxfconf-0], [4.12.0])
dnl ******************************
......
......@@ -33,6 +33,7 @@ thunar_uca_la_CFLAGS = \
$(EXO_CFLAGS) \
$(LIBXFCE4UTIL_CFLAGS) \
$(LIBXFCE4UI_CFLAGS) \
$(LIBXFCE4KBD_PRIVATE_CFLAGS) \
$(PLATFORM_CFLAGS)
thunar_uca_la_LDFLAGS = \
......@@ -47,7 +48,8 @@ thunar_uca_la_LIBADD = \
$(top_builddir)/thunarx/libthunarx-$(THUNARX_VERSION_API).la \
$(EXO_LIBS) \
$(LIBXFCE4UTIL_LIBS) \
$(LIBXFCE4UI_LIBS)
$(LIBXFCE4UI_LIBS) \
$(LIBXFCE4KBD_PRIVATE_LIBS)
thunar_uca_la_DEPENDENCIES = \
$(top_builddir)/thunarx/libthunarx-$(THUNARX_VERSION_API).la
......
......@@ -31,20 +31,24 @@
#endif
#include <exo/exo.h>
#include <libxfce4kbd-private/xfce-shortcut-dialog.h>
#include <libxfce4ui/libxfce4ui.h>
#include <thunar-uca/thunar-uca-editor.h>
static const gchar *thunar_uca_editor_get_icon_name (const ThunarUcaEditor *uca_editor);
static void thunar_uca_editor_set_icon_name (ThunarUcaEditor *uca_editor,
const gchar *icon_name);
static ThunarUcaTypes thunar_uca_editor_get_types (const ThunarUcaEditor *uca_editor);
static void thunar_uca_editor_set_types (ThunarUcaEditor *uca_editor,
ThunarUcaTypes types);
static void thunar_uca_editor_command_clicked (ThunarUcaEditor *uca_editor);
static void thunar_uca_editor_icon_clicked (ThunarUcaEditor *uca_editor);
static void thunar_uca_editor_constructed (GObject *object);
static const gchar *thunar_uca_editor_get_icon_name (const ThunarUcaEditor *uca_editor);
static void thunar_uca_editor_set_icon_name (ThunarUcaEditor *uca_editor,
const gchar *icon_name);
static ThunarUcaTypes thunar_uca_editor_get_types (const ThunarUcaEditor *uca_editor);
static void thunar_uca_editor_set_types (ThunarUcaEditor *uca_editor,
ThunarUcaTypes types);
static void thunar_uca_editor_command_clicked (ThunarUcaEditor *uca_editor);
static void thunar_uca_editor_shortcut_clicked (ThunarUcaEditor *uca_editor);
static void thunar_uca_editor_shortcut_clear_clicked (ThunarUcaEditor *uca_editor);
static void thunar_uca_editor_icon_clicked (ThunarUcaEditor *uca_editor);
static void thunar_uca_editor_constructed (GObject *object);
......@@ -62,6 +66,7 @@ struct _ThunarUcaEditor
GtkWidget *description_entry;
GtkWidget *icon_button;
GtkWidget *command_entry;
GtkWidget *shortcut_button;
GtkWidget *sn_button;
GtkWidget *patterns_entry;
GtkWidget *directories_button;
......@@ -70,8 +75,20 @@ struct _ThunarUcaEditor
GtkWidget *text_files_button;
GtkWidget *video_files_button;
GtkWidget *other_files_button;
gchar *accel_path;
GdkModifierType accel_mods;
guint accel_key;
};
typedef struct {
gboolean in_use;
GdkModifierType mods;
guint key;
gchar *current_path;
gchar *other_path;
} ShortcutInfo;
THUNARX_DEFINE_TYPE (ThunarUcaEditor, thunar_uca_editor, GTK_TYPE_DIALOG);
......@@ -96,6 +113,7 @@ thunar_uca_editor_class_init (ThunarUcaEditorClass *klass)
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, description_entry);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, icon_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, command_entry);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, shortcut_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, sn_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, patterns_entry);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, directories_button);
......@@ -107,6 +125,8 @@ thunar_uca_editor_class_init (ThunarUcaEditorClass *klass)
gtk_widget_class_bind_template_callback(widget_class, thunar_uca_editor_icon_clicked);
gtk_widget_class_bind_template_callback(widget_class, thunar_uca_editor_command_clicked);
gtk_widget_class_bind_template_callback(widget_class, thunar_uca_editor_shortcut_clicked);
gtk_widget_class_bind_template_callback(widget_class, thunar_uca_editor_shortcut_clear_clicked);
}
......@@ -271,6 +291,129 @@ thunar_uca_editor_command_clicked (ThunarUcaEditor *uca_editor)
static void
thunar_uca_editor_shortcut_check (gpointer data,
const gchar *path,
guint key,
GdkModifierType mods,
gboolean changed)
{
ShortcutInfo *info = (ShortcutInfo*) data;
if (info->in_use)
return;
info->in_use = info->mods == mods &&
info->key == key &&
g_strcmp0 (info->current_path, path) != 0;
if (info->in_use)
info->other_path = g_strdup (path);
}
static gboolean
thunar_uca_editor_validate_shortcut (XfceShortcutDialog *dialog,
const gchar *shortcut,
ThunarUcaEditor *uca_editor)
{
GdkModifierType accel_mods;
guint accel_key;
ShortcutInfo info;
gchar *command, *message;
g_return_val_if_fail (XFCE_IS_SHORTCUT_DIALOG (dialog), FALSE);
g_return_val_if_fail (shortcut != NULL, FALSE);
/* Ignore empty shortcuts */
if (G_UNLIKELY (g_utf8_strlen (shortcut, -1) == 0))
return FALSE;
/* Ignore raw 'Return' and 'space' since that may have been used to activate the shortcut row */
if (G_UNLIKELY (g_utf8_collate (shortcut, "Return") == 0 ||
g_utf8_collate (shortcut, "space") == 0))
return FALSE;
gtk_accelerator_parse (shortcut, &accel_key, &accel_mods);
info.in_use = FALSE;
info.mods = accel_mods;
info.key = accel_key;
info.current_path = uca_editor->accel_path;
info.other_path = NULL;
gtk_accel_map_foreach_unfiltered (&info, thunar_uca_editor_shortcut_check);
if (info.in_use)
{
command = g_strrstr (info.other_path, "/");
command = command == NULL ?
info.other_path :
command + 1; /* skip leading slash */
message = g_strdup_printf (_("This shorcut is currently used by: '%s'"),
command);
xfce_dialog_show_warning (GTK_WINDOW (dialog), message,
_("Keyboard shorcut already in use"));
g_free (message);
}
g_free (info.other_path);
return !info.in_use;
}
static void
thunar_uca_editor_shortcut_clicked (ThunarUcaEditor *uca_editor)
{
GtkWidget *dialog;
gint response;
const gchar *shortcut;
GdkModifierType accel_mods;
guint accel_key;
gchar *label;
dialog = xfce_shortcut_dialog_new ("thunar",
gtk_entry_get_text (GTK_ENTRY (uca_editor->name_entry)), "");
g_signal_connect (dialog, "validate-shortcut",
G_CALLBACK (thunar_uca_editor_validate_shortcut),
uca_editor);
response = xfce_shortcut_dialog_run (XFCE_SHORTCUT_DIALOG (dialog),
gtk_widget_get_toplevel (uca_editor->shortcut_button));
if (G_LIKELY (response == GTK_RESPONSE_OK))
{
shortcut = xfce_shortcut_dialog_get_shortcut (XFCE_SHORTCUT_DIALOG (dialog));
gtk_accelerator_parse (shortcut, &accel_key, &accel_mods);
label = gtk_accelerator_get_label (accel_key, accel_mods);
gtk_button_set_label (GTK_BUTTON (uca_editor->shortcut_button), label);
uca_editor->accel_key = accel_key;
uca_editor->accel_mods = accel_mods;
g_free (label);
}
gtk_widget_destroy (dialog);
}
static void
thunar_uca_editor_shortcut_clear_clicked (ThunarUcaEditor *uca_editor)
{
uca_editor->accel_key = 0;
uca_editor->accel_mods = 0;
gtk_button_set_label (GTK_BUTTON (uca_editor->shortcut_button), _("None"));
}
static void
thunar_uca_editor_icon_clicked (ThunarUcaEditor *uca_editor)
{
......@@ -431,7 +574,10 @@ thunar_uca_editor_load (ThunarUcaEditor *uca_editor,
gchar *command;
gchar *icon_name;
gchar *name;
gchar *unique_id;
gchar *accel_label = NULL;
gboolean startup_notify;
GtkAccelKey key;
g_return_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor));
g_return_if_fail (THUNAR_UCA_IS_MODEL (uca_model));
......@@ -446,6 +592,7 @@ thunar_uca_editor_load (ThunarUcaEditor *uca_editor,
THUNAR_UCA_MODEL_COLUMN_ICON_NAME, &icon_name,
THUNAR_UCA_MODEL_COLUMN_NAME, &name,
THUNAR_UCA_MODEL_COLUMN_STARTUP_NOTIFY, &startup_notify,
THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID, &unique_id,
-1);
/* setup the new selection */
......@@ -454,11 +601,17 @@ thunar_uca_editor_load (ThunarUcaEditor *uca_editor,
/* setup the new icon */
thunar_uca_editor_set_icon_name (uca_editor, icon_name);
/* Resolve shortcut from accelerator */
uca_editor->accel_path = g_strdup_printf ("<Actions>/ThunarActions/uca-action-%s", unique_id);
if (gtk_accel_map_lookup_entry (uca_editor->accel_path, &key) && key.accel_key != 0)
accel_label = gtk_accelerator_get_label (key.accel_key, key.accel_mods);
/* 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 : "");
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_button_set_label (GTK_BUTTON (uca_editor->shortcut_button), (accel_label != NULL) ? accel_label : _("None"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (uca_editor->sn_button), startup_notify);
/* cleanup */
......@@ -467,6 +620,8 @@ thunar_uca_editor_load (ThunarUcaEditor *uca_editor,
g_free (command);
g_free (icon_name);
g_free (name);
g_free (unique_id);
g_free (accel_label);
}
......@@ -485,19 +640,34 @@ thunar_uca_editor_save (ThunarUcaEditor *uca_editor,
ThunarUcaModel *uca_model,
GtkTreeIter *iter)
{
gchar *unique_id;
GtkAccelKey key;
g_return_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor));
g_return_if_fail (THUNAR_UCA_IS_MODEL (uca_model));
g_return_if_fail (iter != NULL);
gtk_tree_model_get (GTK_TREE_MODEL (uca_model), iter,
THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID, &unique_id,
-1);
/* always clear the accelerator, it'll be updated in thunar_uca_model_update */
if (gtk_accel_map_lookup_entry (uca_editor->accel_path, &key) && key.accel_key != 0)
gtk_accel_map_change_entry (uca_editor->accel_path, 0, 0, TRUE);
thunar_uca_model_update (uca_model, iter,
gtk_entry_get_text (GTK_ENTRY (uca_editor->name_entry)),
NULL, /* don't touch the unique id */
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)),
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));
thunar_uca_editor_get_types (uca_editor),
uca_editor->accel_key,
uca_editor->accel_mods);
g_free (unique_id);
}
......@@ -154,6 +154,67 @@
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="shortcut_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Shortcut:</property>
<property name="use_underline">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="shortcut_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The shortcut for the action.</property>
<property name="hexpand">True</property>
<signal name="clicked" handler="thunar_uca_editor_shortcut_clicked" swapped="yes"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="shortcut_clear_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Clear the shortcut for this action.</property>
<signal name="clicked" handler="thunar_uca_editor_shortcut_clear_clicked" swapped="yes"/>
<child>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">edit-clear</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<style>
<class name="linked"/>
</style>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="sn_button">
<property name="label" translatable="yes">Use Startup Notification</property>
......@@ -165,7 +226,7 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
......@@ -183,7 +244,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
......@@ -198,11 +259,11 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box2">
<object class="GtkBox" id="box3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
......@@ -214,7 +275,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
<property name="top_attach">6</property>
<property name="width">2</property>
</packing>
</child>
......@@ -226,7 +287,7 @@
<property name="column_spacing">6</property>
<property name="valign">start</property>
<child>
<object class="GtkImage" id="image2">
<object class="GtkImage" id="image3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">dialog-information</property>
......@@ -441,7 +502,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
<property name="top_attach">7</property>
<property name="width">2</property>
</packing>
</child>
......@@ -615,12 +676,12 @@
</packing>
</child>
<child>
<object class="GtkBox" id="box3">
<object class="GtkBox" id="box4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">12</property>
<child>
<object class="GtkImage" id="image3">
<object class="GtkImage" id="image4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
......
......@@ -838,7 +838,8 @@ end_element_handler (GMarkupParseContext *context,
parser->command->str,
parser->startup_notify,
parser->patterns->str,
parser->types);
parser->types,
0, 0);
/* check if a new id should've been generated */
if (exo_str_is_empty (parser->unique_id->str))
......@@ -1307,11 +1308,14 @@ thunar_uca_model_update (ThunarUcaModel *uca_model,
const gchar *command,
gboolean startup_notify,
const gchar *patterns,
ThunarUcaTypes types)
ThunarUcaTypes types,
guint accel_key,
GdkModifierType accel_mods)
{
ThunarUcaModelItem *item;
GtkTreePath *path;
guint n, m;
gchar *accel_path;
g_return_if_fail (THUNAR_UCA_IS_MODEL (uca_model));
g_return_if_fail (iter->stamp == uca_model->stamp);
......@@ -1362,6 +1366,14 @@ thunar_uca_model_update (ThunarUcaModel *uca_model,
path = gtk_tree_model_get_path (GTK_TREE_MODEL (uca_model), iter);
gtk_tree_model_row_changed (GTK_TREE_MODEL (uca_model), path, iter);
gtk_tree_path_free (path);
/* update accelerator */
if (accel_key > 0)
{
accel_path = g_strdup_printf ("<Actions>/ThunarActions/uca-action-%s", item->unique_id);
gtk_accel_map_change_entry (accel_path, accel_key, accel_mods, TRUE);
g_free (accel_path);
}
}
......
......@@ -96,7 +96,9 @@ void thunar_uca_model_update (ThunarUcaModel *uca_mod
const gchar *command,
gboolean startup_notify,
const gchar *patterns,
ThunarUcaTypes types);
ThunarUcaTypes types,
guint accel_key,
GdkModifierType accel_mods);
gboolean thunar_uca_model_save (ThunarUcaModel *uca_model,
GError **error);
......
......@@ -248,6 +248,10 @@ thunar_uca_provider_get_file_menu_items (ThunarxMenuProvider *menu_provider,
g_object_ref (G_OBJECT (uca_provider)), (GClosureNotify) g_object_unref,
G_CONNECT_SWAPPED);
/* set the action path */
g_object_set_data (G_OBJECT (item), "action_path",
g_strconcat ("<Actions>/ThunarActions/", name, NULL));
/* add the menu item to the return list */
items = g_list_prepend (items, item);
......
......@@ -349,21 +349,6 @@ thunar_application_startup (GApplication *gapp)
/* initialize the application */
application->preferences = thunar_preferences_get ();
/* TODO: how do accel maps integrate with GAction/GMenu? */
/* check if we have a saved accel map */
path = xfce_resource_lookup (XFCE_RESOURCE_CONFIG, ACCEL_MAP_PATH);
if (G_LIKELY (path != NULL))
{
/* load the accel map */
gtk_accel_map_load (path);
g_free (path);
}
/* watch for changes */
application->accel_map = gtk_accel_map_get ();
g_signal_connect_swapped (G_OBJECT (application->accel_map), "changed",
G_CALLBACK (thunar_application_accel_map_changed), application);
#ifdef HAVE_GUDEV
/* establish connection with udev */
application->udev_client = g_udev_client_new (subsystems);
......@@ -381,6 +366,21 @@ thunar_application_startup (GApplication *gapp)
G_APPLICATION_CLASS (thunar_application_parent_class)->startup (gapp);
/* TODO: how do accel maps integrate with GAction/GMenu? Using GtkAction for now */
/* check if we have a saved accel map */
path = xfce_resource_lookup (XFCE_RESOURCE_CONFIG, ACCEL_MAP_PATH);
if (G_LIKELY (path != NULL))
{
/* load the accel map */
gtk_accel_map_load (path);
g_free (path);
}
/* watch for changes */
application->accel_map = gtk_accel_map_get ();
g_signal_connect_swapped (G_OBJECT (application->accel_map), "changed",
G_CALLBACK (thunar_application_accel_map_changed), application);
thunar_application_load_css ();
}
......
......@@ -87,6 +87,7 @@ thunar_menu_util_add_items_to_ui_manager (GtkUIManager *ui_manager,
GtkAction *action;
ThunarxMenu *menu;
char *subpath;
char *action_path;
GList *children;
/* add the menu items to the UI manager */
......@@ -104,6 +105,14 @@ thunar_menu_util_add_items_to_ui_manager (GtkUIManager *ui_manager,
gtk_action_get_name (GTK_ACTION (action)),
(menu != NULL) ? GTK_UI_MANAGER_MENU : GTK_UI_MANAGER_MENUITEM, FALSE);
/* TODO: Receive action path from plugin as generic data as below or create a property in ThunarxMenuItem? */
action_path = g_object_steal_data (G_OBJECT (lp->data), "action_path");
if (action_path)
{
gtk_action_set_accel_path (action, action_path);
g_free (action_path);
}
/* add submenu items if any */
if (menu != NULL) {
children = thunarx_menu_get_items (menu);
......
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