2
* Copyright (C) 2011 Nick Schermer <nick@xfce.org>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License along
15
* with this program; if not, write to the Free Software Foundation, Inc.,
16
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27
#include <libxfce4util/libxfce4util.h>
28
#include <libxfce4ui/libxfce4ui.h>
29
#include <xfconf/xfconf.h>
31
#include <src/appfinder-actions.h>
32
#include <src/appfinder-private.h>
36
static void xfce_appfinder_actions_finalize (GObject *object);
37
static void xfce_appfinder_actions_free (XfceAppfinderAction *action);
38
static void xfce_appfinder_actions_load (XfceAppfinderActions *actions,
40
static void xfce_appfinder_actions_save (XfceAppfinderActions *actions,
41
gboolean save_actions);
42
static void xfce_appfinder_actions_changed (XfconfChannel *channel,
43
const gchar *prop_name,
45
XfceAppfinderActions *actions);
49
struct _XfceAppfinderActionsClass
51
GObjectClass __parent__;
54
struct _XfceAppfinderActions
58
XfconfChannel *channel;
59
gulong property_watch_id;
68
XFCE_APPFINDER_ACTION_TYPE_PREFIX,
69
XFCE_APPFINDER_ACTION_TYPE_REGEX
73
struct _XfceAppfinderAction
75
AppfinderActionType type;
87
G_DEFINE_TYPE (XfceAppfinderActions, xfce_appfinder_actions, G_TYPE_OBJECT)
92
xfce_appfinder_actions_class_init (XfceAppfinderActionsClass *klass)
94
GObjectClass *gobject_class;
96
gobject_class = G_OBJECT_CLASS (klass);
97
gobject_class->finalize = xfce_appfinder_actions_finalize;
103
xfce_appfinder_actions_init (XfceAppfinderActions *actions)
105
actions->channel = xfconf_channel_get ("xfce4-appfinder");
107
xfce_appfinder_actions_load (actions, FALSE);
109
actions->property_watch_id =
110
g_signal_connect (G_OBJECT (actions->channel), "property-changed",
111
G_CALLBACK (xfce_appfinder_actions_changed), actions);
117
xfce_appfinder_actions_finalize (GObject *object)
119
XfceAppfinderActions *actions = XFCE_APPFINDER_ACTIONS (object);
121
if (actions->reload_idle_id != 0)
122
g_source_remove (actions->reload_idle_id);
124
g_signal_handler_disconnect (actions->channel, actions->property_watch_id);
126
g_slist_foreach (actions->actions, (GFunc) xfce_appfinder_actions_free, NULL);
127
g_slist_free (actions->actions);
129
(*G_OBJECT_CLASS (xfce_appfinder_actions_parent_class)->finalize) (object);
135
xfce_appfinder_actions_free (XfceAppfinderAction *action)
137
g_free (action->pattern);
138
g_free (action->command);
140
if (action->regex != NULL)
141
g_regex_unref (action->regex);
143
g_slice_free (XfceAppfinderAction, action);
149
xfce_appfinder_actions_load_defaults (XfceAppfinderActions *actions)
152
XfceAppfinderAction *action;
153
XfceAppfinderAction defaults[] =
155
/* default actions, sorted */
156
{ XFCE_APPFINDER_ACTION_TYPE_REGEX, 0,
157
"^(file|http|https):\\/\\/(.*)$",
161
{ XFCE_APPFINDER_ACTION_TYPE_PREFIX, 0,
163
"exo-open --launch TerminalEmulator %s",
166
{ XFCE_APPFINDER_ACTION_TYPE_PREFIX, 0,
168
"exo-open --launch WebBrowser http://en.wikipedia.org/wiki/%s",
171
{ XFCE_APPFINDER_ACTION_TYPE_PREFIX, 0,
173
"exo-open --launch TerminalEmulator man %s",
178
APPFINDER_DEBUG ("loaded default actions");
180
for (i = 0; i < G_N_ELEMENTS (defaults); i++)
182
action = g_slice_new0 (XfceAppfinderAction);
183
action->type = defaults[i].type;
184
action->unique_id = i + 1;
185
action->pattern = g_strdup (defaults[i].pattern);
186
action->command = g_strdup (defaults[i].command);
187
action->save = defaults[i].save;
189
actions->actions = g_slist_prepend (actions->actions, action);
196
xfce_appfinder_actions_sort (gconstpointer a,
199
const XfceAppfinderAction *action_a = a;
200
const XfceAppfinderAction *action_b = b;
202
if (action_a->type != action_b->type)
203
return action_a->type == XFCE_APPFINDER_ACTION_TYPE_PREFIX ? -1 : 1;
205
/* reverse the order so prefixes are properly matched */
206
return -g_strcmp0 (action_a->pattern, action_b->pattern);
212
xfce_appfinder_actions_load (XfceAppfinderActions *actions,
215
XfceAppfinderAction *action;
218
gchar *pattern, *command;
224
GSList *li, *old_actions = NULL;
226
appfinder_return_if_fail (XFCE_IS_APPFINDER_ACTIONS (actions));
227
appfinder_return_if_fail (steal || actions->actions == NULL);
231
old_actions = actions->actions;
232
actions->actions = NULL;
235
if (xfconf_channel_has_property (actions->channel, "/actions"))
237
array = xfconf_channel_get_arrayv (actions->channel, "/actions");
238
if (G_LIKELY (array != NULL))
240
for (i = 0; i < array->len; i++)
242
value = g_ptr_array_index (array, i);
243
appfinder_assert (value != NULL);
244
unique_id = g_value_get_int (value);
246
/* look for the id in the old actions */
247
for (li = old_actions; li != NULL; li = li->next)
250
if (action->unique_id == unique_id)
256
/* use the old action */
257
old_actions = g_slist_delete_link (old_actions, li);
258
actions->actions = g_slist_prepend (actions->actions, action);
263
/* no usable actions was found, create a new one */
264
g_snprintf (prop, sizeof (prop), "/actions/action-%d/type", unique_id);
265
type = xfconf_channel_get_int (actions->channel, prop, XFCE_APPFINDER_ACTION_TYPE_PREFIX);
266
if (type < XFCE_APPFINDER_ACTION_TYPE_PREFIX
267
|| type > XFCE_APPFINDER_ACTION_TYPE_REGEX)
270
g_snprintf (prop, sizeof (prop), "/actions/action-%d/pattern", unique_id);
271
pattern = xfconf_channel_get_string (actions->channel, prop, NULL);
273
g_snprintf (prop, sizeof (prop), "/actions/action-%d/command", unique_id);
274
command = xfconf_channel_get_string (actions->channel, prop, NULL);
276
g_snprintf (prop, sizeof (prop), "/actions/action-%d/save", unique_id);
277
save = xfconf_channel_get_bool (actions->channel, prop, FALSE);
279
action = g_slice_new0 (XfceAppfinderAction);
281
action->unique_id = unique_id;
282
action->pattern = pattern;
283
action->command = command;
286
actions->actions = g_slist_prepend (actions->actions, action);
289
xfconf_array_free (array);
294
xfce_appfinder_actions_load_defaults (actions);
295
xfce_appfinder_actions_save (actions, TRUE);
298
if (old_actions != NULL)
300
g_slist_foreach (old_actions, (GFunc) xfce_appfinder_actions_free, NULL);
301
g_slist_free (old_actions);
304
actions->actions = g_slist_sort (actions->actions, xfce_appfinder_actions_sort);
306
APPFINDER_DEBUG ("loaded %d actions", g_slist_length (actions->actions));
312
xfce_appfinder_actions_reload_idle (gpointer data)
314
xfce_appfinder_actions_load (XFCE_APPFINDER_ACTIONS (data), TRUE);
321
xfce_appfinder_actions_reload_idle_destroyed (gpointer data)
323
XFCE_APPFINDER_ACTIONS (data)->reload_idle_id = 0;
329
xfce_appfinder_actions_save (XfceAppfinderActions *actions,
330
gboolean save_actions)
332
XfceAppfinderAction *action;
338
if (actions->property_watch_id > 0)
339
g_signal_handler_block (actions->channel, actions->property_watch_id);
341
array = g_ptr_array_new ();
343
for (li = actions->actions; li != NULL; li = li->next)
345
value = g_new0 (GValue, 1);
346
g_value_init (value, G_TYPE_INT);
349
g_value_set_int (value, action->unique_id);
350
g_ptr_array_add (array, value);
354
g_snprintf (prop, sizeof (prop), "/actions/action-%d/type", action->unique_id);
355
xfconf_channel_set_int (actions->channel, prop, action->type);
357
g_snprintf (prop, sizeof (prop), "/actions/action-%d/pattern", action->unique_id);
358
xfconf_channel_set_string (actions->channel, prop, action->pattern);
360
g_snprintf (prop, sizeof (prop), "/actions/action-%d/command", action->unique_id);
361
xfconf_channel_set_string (actions->channel, prop, action->command);
363
g_snprintf (prop, sizeof (prop), "/actions/action-%d/save", action->unique_id);
364
xfconf_channel_set_bool (actions->channel, prop, action->save);
368
xfconf_channel_set_arrayv (actions->channel, "/actions", array);
370
xfconf_array_free (array);
372
if (actions->property_watch_id > 0)
373
g_signal_handler_unblock (actions->channel, actions->property_watch_id);
380
xfce_appfinder_actions_changed (XfconfChannel *channel,
381
const gchar *prop_name,
383
XfceAppfinderActions *actions)
388
XfceAppfinderAction *action;
390
if (prop_name == NULL)
393
if (strcmp (prop_name, "/actions") == 0)
395
if (actions->reload_idle_id == 0)
397
actions->reload_idle_id = g_idle_add_full (G_PRIORITY_LOW,
398
xfce_appfinder_actions_reload_idle, actions,
399
xfce_appfinder_actions_reload_idle_destroyed);
402
else if (sscanf (prop_name, "/actions/action-%d/%30s",
403
&unique_id, field) == 2)
405
for (li = actions->actions; li != NULL; li = li->next)
409
if (action->unique_id == unique_id)
411
if (strcmp (field, "type") == 0
412
&& G_VALUE_HOLDS_INT (value))
414
action->type = g_value_get_int (value);
416
else if (strcmp (field, "pattern") == 0
417
&& G_VALUE_HOLDS_STRING (value))
419
g_free (action->pattern);
420
action->pattern = g_value_dup_string (value);
422
if (action->regex != NULL)
424
g_regex_unref (action->regex);
425
action->regex = NULL;
428
else if (strcmp (field, "command") == 0
429
&& G_VALUE_HOLDS_STRING (value))
431
g_free (action->command);
432
action->command = g_value_dup_string (value);
434
else if (strcmp (field, "save") == 0
435
&& G_VALUE_HOLDS_BOOLEAN (value))
437
action->save = g_value_get_boolean (value);
449
xfce_appfinder_actions_expand_command (XfceAppfinderAction *action,
457
if (G_UNLIKELY (action->command == NULL))
460
string = g_string_sized_new (128);
461
len = strlen (action->pattern);
463
for (p = action->command; *p != '\0'; ++p)
465
if (G_UNLIKELY (p[0] == '%' && p[1] != '\0'))
470
trim = g_strdup (text + len);
471
g_string_append (string, g_strchug (trim));
476
g_string_append (string, text);
480
g_string_append_c (string, '%');
486
g_string_append_c (string, *p);
490
return g_string_free (string, FALSE);
495
XfceAppfinderActions *
496
xfce_appfinder_actions_get (void)
498
static XfceAppfinderActions *actions = NULL;
500
if (G_LIKELY (actions != NULL))
502
g_object_ref (G_OBJECT (actions));
506
actions = g_object_new (XFCE_TYPE_APPFINDER_ACTIONS, NULL);
507
g_object_add_weak_pointer (G_OBJECT (actions), (gpointer) &actions);
508
appfinder_refcount_debug_add (G_OBJECT (actions), "actions");
517
xfce_appfinder_actions_get_unique_id (XfceAppfinderActions *actions)
519
static gint unique_id = 1;
521
XfceAppfinderAction *action;
523
appfinder_return_val_if_fail (XFCE_IS_APPFINDER_ACTIONS (actions), -1);
525
for (; unique_id < G_MAXINT; unique_id++)
527
for (li = actions->actions; li != NULL; li = li->next)
530
if (action->unique_id == unique_id)
544
xfce_appfinder_actions_execute (XfceAppfinderActions *actions,
550
XfceAppfinderAction *action;
553
GMatchInfo *match_info = NULL;
554
gboolean found = FALSE;
556
appfinder_return_val_if_fail (error != NULL && *error == NULL, NULL);
557
appfinder_return_val_if_fail (XFCE_IS_APPFINDER_ACTIONS (actions), NULL);
558
appfinder_return_val_if_fail (text != NULL, NULL);
560
for (li = actions->actions; li != NULL; li = li->next)
564
/* skip empty actions */
565
if (!IS_STRING (action->pattern)
566
|| !IS_STRING (action->command))
569
switch (action->type)
571
case XFCE_APPFINDER_ACTION_TYPE_PREFIX:
572
if (g_str_has_prefix (text, action->pattern))
574
cmd = xfce_appfinder_actions_expand_command (action, text);
579
case XFCE_APPFINDER_ACTION_TYPE_REGEX:
580
if (action->regex == NULL)
582
/* allocate the regex */
583
action->regex = g_regex_new (action->pattern, 0, 0, &err);
584
if (action->regex == NULL)
586
g_message ("Failed to create regex for \"%s\": %s",
587
action->pattern, err->message);
594
if (g_regex_match (action->regex, text, 0, &match_info))
596
/* expand the command string */
597
cmd = g_match_info_expand_references (match_info, action->command, &err);
598
if (G_UNLIKELY (err != NULL))
607
if (match_info != NULL)
608
g_match_info_free (match_info);
615
if (save_cmd != NULL)
616
*save_cmd = action->save;
621
appfinder_assert ((cmd != NULL) == found);