2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@gmail.com>
21 /************************
22 * User-defined actions *
23 ************************/
25 #include <stdio.h> /* FILE, fprintf() */
26 #include <string.h> /* strchr(), memset() */
27 #include <stdlib.h> /* system() */
28 #include <sys/wait.h> /* WEXITSTATUS */
32 #include "mnemonics.h"
33 #include "gliv-image.h"
34 #include "files_list.h"
35 #include "callbacks.h"
38 #include "next_image.h"
40 extern GlivImage
*current_image
;
47 static GPtrArray
*actions_array
= NULL
;
49 static GString
*build_command_line(const gchar
* command
,
50 const gchar
* filename
)
52 GString
*cmd_line
= g_string_new("");
53 const gchar
*ptr
, *remaining
= command
;
57 ptr
= strchr(remaining
, '%');
59 g_string_append(cmd_line
, remaining
);
63 g_string_append_len(cmd_line
, remaining
, ptr
- remaining
);
69 tmp
= g_path_get_dirname(filename
);
70 quoted
= g_shell_quote(tmp
);
72 g_string_append(cmd_line
, quoted
);
77 tmp
= g_path_get_basename(filename
);
78 quoted
= g_shell_quote(tmp
);
80 g_string_append(cmd_line
, quoted
);
85 quoted
= g_shell_quote(filename
);
86 g_string_append(cmd_line
, quoted
);
91 g_string_append_c(cmd_line
, '%');
95 g_string_append_len(cmd_line
, ptr
- 1, 2);
101 static gint
action_on_filename(action
* a
, const gchar
* filename
)
104 GString
*cmd_line
= build_command_line(a
->command
, filename
);
106 ret
= system(cmd_line
->str
);
107 g_string_free(cmd_line
, TRUE
);
112 static void process_system_retval(action
* a
, gint retval
)
121 str
= _("Failed to create a new process");
122 msg
= gtk_message_dialog_new(NULL
, GTK_DIALOG_MODAL
,
123 GTK_MESSAGE_WARNING
, GTK_BUTTONS_CLOSE
,
126 str
= _("The action '%s' terminated with a non-null return code: %d");
127 msg
= gtk_message_dialog_new(NULL
, GTK_DIALOG_MODAL
,
128 GTK_MESSAGE_WARNING
, GTK_BUTTONS_CLOSE
,
129 str
, a
->name
, WEXITSTATUS(retval
));
132 run_modal_dialog(GTK_DIALOG(msg
));
133 gtk_widget_destroy(msg
);
136 static gboolean
action_current(action
* a
)
141 if (current_image
== NULL
|| current_image
->node
== NULL
)
144 stat_data
= stat_loaded_files(FALSE
);
146 ret
= action_on_filename(a
, current_image
->node
->data
);
147 process_system_retval(a
, ret
);
149 reload_changed_files(stat_data
);
154 static gboolean
action_every(action
* a
)
159 GlivImage
*before
= current_image
;
161 stat_data
= stat_loaded_files(FALSE
);
163 for (node
= get_list_head(); node
!= NULL
; node
= node
->next
) {
164 gint new_ret
= action_on_filename(a
, node
->data
);
170 process_system_retval(a
, ret
);
172 if (before
== current_image
)
173 reload_changed_files(stat_data
);
175 /* Don't bother checking files changes if the user changed the image */
182 static void make_menu(GtkAccelGroup
* accel_group
, GtkMenuItem
* menu_item
,
183 GCallback cb
, const gchar
* menu_name
)
187 GtkMenu
*menu
= GTK_MENU(gtk_menu_new());
191 gtk_menu_item_deselect(menu_item
);
192 gtk_menu_set_accel_group(menu
, accel_group
);
193 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), gtk_tearoff_menu_item_new());
196 for (i
= 0; i
< actions_array
->len
; i
++) {
197 action
*a
= g_ptr_array_index(actions_array
, i
);
199 name
= add_mnemonic(a
->name
);
200 item
= GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(name
));
201 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), GTK_WIDGET(item
));
203 g_signal_connect_swapped(item
, "activate", cb
, a
);
205 accel_path
= g_strconcat("<GLiv>/Commands/", menu_name
, "/", a
->name
,
208 gtk_accel_map_add_entry(accel_path
, 0, 0);
209 gtk_menu_item_set_accel_path(item
, accel_path
);
213 gtk_menu_item_set_submenu(menu_item
, GTK_WIDGET(menu
));
214 gtk_widget_show_all(GTK_WIDGET(menu_item
));
219 void init_actions(GtkAccelGroup
* the_accel_group
,
220 GtkMenuItem
* current_image_menu_item
,
221 GtkMenuItem
* every_image_menu_item
)
223 static GtkAccelGroup
*accel_group
= NULL
;
224 static GtkMenuItem
*current_image_item
= NULL
;
225 static GtkMenuItem
*every_image_item
= NULL
;
227 if (the_accel_group
!= NULL
) {
229 accel_group
= the_accel_group
;
230 add_action(_("Open in Gimp"), "gimp-remote -- %f");
233 if (current_image_menu_item
!= NULL
)
234 current_image_item
= current_image_menu_item
;
236 if (every_image_menu_item
!= NULL
)
237 every_image_item
= every_image_menu_item
;
239 if (actions_array
== NULL
)
240 actions_array
= g_ptr_array_new();
242 make_menu(accel_group
, current_image_item
, G_CALLBACK(action_current
),
243 "Action on the current image");
245 make_menu(accel_group
, every_image_item
, G_CALLBACK(action_every
),
246 "Action on every image");
252 #include "glade_actions.h"
260 static GtkListStore
*list_store
= NULL
;
261 static GtkTreeIter iter
;
262 static gboolean has_selection
= FALSE
;
264 static void get_list_row(GtkTreeIter
* where
, gchar
** name
, gchar
** command
)
268 memset(&value
, 0, sizeof(GValue
));
270 gtk_tree_model_get_value(GTK_TREE_MODEL(list_store
), where
,
271 COLUMN_NAME
, &value
);
273 *name
= g_strdup(g_value_peek_pointer(&value
));
274 g_value_unset(&value
);
277 if (command
!= NULL
) {
278 gtk_tree_model_get_value(GTK_TREE_MODEL(list_store
), where
,
279 COLUMN_COMMAND
, &value
);
281 *command
= g_strdup(g_value_peek_pointer(&value
));
282 g_value_unset(&value
);
286 static gboolean
has_action_by_name(const gchar
* name
)
288 gchar
*action_name
= NULL
;
291 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store
), &iter
)) {
293 get_list_row(&iter
, &action_name
, NULL
);
294 if (g_str_equal(name
, action_name
))
299 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store
), &iter
));
303 return action_name
!= NULL
;
306 static gboolean
edit_action_dialog(gchar
** name
, gchar
** command
)
309 GtkWidget
*dialog
= create_edit_action_dialog();
310 GtkEntry
*name_entry
, *command_entry
;
311 gboolean duplicate
= FALSE
;
315 name_entry
= g_object_get_data(G_OBJECT(dialog
), "name_entry");
316 command_entry
= g_object_get_data(G_OBJECT(dialog
), "command_entry");
319 gtk_entry_set_text(name_entry
, *name
);
321 if (*command
!= NULL
)
322 gtk_entry_set_text(command_entry
, *command
);
324 response
= run_modal_dialog(GTK_DIALOG(dialog
));
325 if (response
== GTK_RESPONSE_OK
) {
328 new_name
= gtk_editable_get_chars(GTK_EDITABLE(name_entry
), 0, -1);
329 if (*name
== NULL
|| g_str_equal(new_name
, *name
) == FALSE
) {
330 duplicate
= has_action_by_name(new_name
);
333 gchar
*str
= _("The action name '%s' is already used");
334 msg
= gtk_message_dialog_new(NULL
,
337 GTK_BUTTONS_CLOSE
, str
, new_name
);
338 run_modal_dialog(GTK_DIALOG(msg
));
339 gtk_widget_destroy(msg
);
347 *command
= gtk_editable_get_chars(GTK_EDITABLE(command_entry
),
355 gtk_widget_destroy(dialog
);
359 static gboolean
on_add_button_clicked(void)
361 gchar
*name
= NULL
, *command
= NULL
;
364 ok
= edit_action_dialog(&name
, &command
);
366 if (ok
&& name
[0] != '\0' && command
[0] != '\0') {
367 GtkTreeIter new_iter
;
370 gtk_list_store_insert_after(list_store
, &new_iter
, &iter
);
372 gtk_list_store_append(list_store
, &new_iter
);
374 gtk_list_store_set(list_store
, &new_iter
,
375 COLUMN_NAME
, name
, COLUMN_COMMAND
, command
, -1);
383 static gboolean
on_property_button_clicked(void)
386 gchar
*name
, *command
;
389 get_list_row(&iter
, &name
, &command
);
390 ok
= edit_action_dialog(&name
, &command
);
392 if (ok
&& name
[0] != '\0' && command
[0] != '\0')
393 gtk_list_store_set(list_store
, &iter
,
394 COLUMN_NAME
, name
, COLUMN_COMMAND
, command
, -1);
400 static gboolean
on_delete_button_clicked(void)
402 gtk_list_store_remove(list_store
, &iter
);
406 static gboolean
selection_changed(GtkTreeSelection
* selection
,
409 GtkWidget
*property_button
, *delete_button
;
411 has_selection
= gtk_tree_selection_get_selected(selection
, NULL
, &iter
);
413 property_button
= g_object_get_data(G_OBJECT(dialog
), "property_button");
414 delete_button
= g_object_get_data(G_OBJECT(dialog
), "delete_button");
416 if (GTK_WIDGET_IS_SENSITIVE(property_button
) != has_selection
) {
417 gtk_widget_set_sensitive(property_button
, has_selection
);
418 gtk_widget_set_sensitive(delete_button
, has_selection
);
424 #include "glade_actions.c"
426 static void add_column(GtkTreeView
* tree_view
, const gchar
* name
,
429 GtkTreeViewColumn
*column
;
430 GtkCellRenderer
*renderer
= gtk_cell_renderer_text_new();
432 column
= gtk_tree_view_column_new_with_attributes(name
, renderer
,
433 "text", column_id
, NULL
);
435 gtk_tree_view_column_set_resizable(column
, TRUE
);
436 gtk_tree_view_column_set_reorderable(column
, TRUE
);
437 gtk_tree_view_column_set_sort_column_id(column
, column_id
);
439 gtk_tree_view_append_column(tree_view
, column
);
442 static GtkListStore
*make_tree_view(GtkWidget
* dialog
)
444 GtkTreeView
*tree_view
= g_object_get_data(G_OBJECT(dialog
), "treeview");
445 GtkListStore
*list_store
;
447 GtkTreeSelection
*selection
;
450 list_store
= gtk_list_store_new(N_COLUMNS
, G_TYPE_STRING
, G_TYPE_STRING
);
452 for (i
= 0; i
< actions_array
->len
; i
++) {
453 action
*a
= g_ptr_array_index(actions_array
, i
);
455 gtk_list_store_append(list_store
, &iter
);
456 gtk_list_store_set(list_store
, &iter
,
457 COLUMN_NAME
, a
->name
,
458 COLUMN_COMMAND
, a
->command
, -1);
461 gtk_tree_view_set_model(tree_view
, GTK_TREE_MODEL(list_store
));
463 add_column(tree_view
, _("Name"), COLUMN_NAME
);
464 add_column(tree_view
, _("Command"), COLUMN_COMMAND
);
466 selection
= gtk_tree_view_get_selection(tree_view
);
467 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_SINGLE
);
468 g_signal_connect(selection
, "changed", G_CALLBACK(selection_changed
),
474 static gboolean
read_store(GtkTreeModel
* unused_model
,
475 GtkTreePath
* unused_path
, GtkTreeIter
* iter
)
477 gchar
*name
, *command
;
478 action
*a
= g_new(action
, 1);
480 get_list_row(iter
, &name
, &command
);
483 a
->command
= command
;
485 g_ptr_array_add(actions_array
, a
);
490 static void destroy_actions(void)
494 for (i
= 0; i
< actions_array
->len
; i
++) {
495 action
*a
= g_ptr_array_index(actions_array
, i
);
502 g_ptr_array_free(actions_array
, TRUE
);
503 actions_array
= NULL
;
506 gboolean
edit_actions(void)
508 GtkWidget
*dialog
= create_actions_dialog();
511 list_store
= make_tree_view(dialog
);
512 response
= run_modal_dialog(GTK_DIALOG(dialog
));
513 gtk_widget_destroy(dialog
);
515 if (response
== GTK_RESPONSE_OK
) {
517 actions_array
= g_ptr_array_new();
518 gtk_tree_model_foreach(GTK_TREE_MODEL(list_store
),
519 (GtkTreeModelForeachFunc
) read_store
, NULL
);
520 init_actions(NULL
, NULL
, NULL
);
523 g_object_unref(list_store
);
525 has_selection
= FALSE
;
532 void add_action(const gchar
* name
, const gchar
* command
)
537 if (actions_array
== NULL
)
538 actions_array
= g_ptr_array_new();
540 for (i
= 0; i
< actions_array
->len
; i
++) {
541 a
= g_ptr_array_index(actions_array
, i
);
543 if (g_str_equal(a
->name
, name
)) {
545 a
->command
= g_strdup(command
);
550 a
= g_new(action
, 1);
551 a
->name
= g_strdup(name
);
552 a
->command
= g_strdup(command
);
554 g_ptr_array_add(actions_array
, a
);
557 void write_actions(FILE * file
)
562 if (actions_array
== NULL
)
565 for (i
= 0; i
< actions_array
->len
; i
++) {
566 a
= g_ptr_array_index(actions_array
, i
);
568 fprintf(file
, "action_name = %s\n", a
->name
);
569 fprintf(file
, "action_command = %s\n\n", a
->command
);