Merged older cs.po file with newest pot file.
[gliv/czech_localization.git] / src / actions.c
blob51eeeb0d10511c7957ee7b83fccf43027388b27f
1 /*
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@yahoo.fr>
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 */
30 #include "gliv.h"
31 #include "actions.h"
32 #include "mnemonics.h"
33 #include "gliv-image.h"
34 #include "files_list.h"
35 #include "callbacks.h"
36 #include "windows.h"
37 #include "messages.h"
38 #include "next_image.h"
40 extern GlivImage *current_image;
42 typedef struct {
43 gchar *name;
44 gchar *command;
45 } action;
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;
54 gchar *quoted, *tmp;
56 for (;;) {
57 ptr = strchr(remaining, '%');
58 if (ptr == NULL) {
59 g_string_append(cmd_line, remaining);
60 return cmd_line;
63 g_string_append_len(cmd_line, remaining, ptr - remaining);
64 ptr++;
65 remaining = ptr + 1;
67 switch (*ptr) {
68 case 'd':
69 tmp = g_path_get_dirname(filename);
70 quoted = g_shell_quote(tmp);
71 g_free(tmp);
72 g_string_append(cmd_line, quoted);
73 g_free(quoted);
74 break;
76 case 'b':
77 tmp = g_path_get_basename(filename);
78 quoted = g_shell_quote(tmp);
79 g_free(tmp);
80 g_string_append(cmd_line, quoted);
81 g_free(quoted);
82 break;
84 case 'f':
85 quoted = g_shell_quote(filename);
86 g_string_append(cmd_line, quoted);
87 g_free(quoted);
88 break;
90 case '%':
91 g_string_append_c(cmd_line, '%');
92 break;
94 default:
95 g_string_append_len(cmd_line, ptr - 1, 2);
96 break;
101 static gint action_on_filename(action * a, const gchar * filename)
103 gint ret;
104 GString *cmd_line = build_command_line(a->command, filename);
106 ret = system(cmd_line->str);
107 g_string_free(cmd_line, TRUE);
109 return ret;
112 static void process_system_retval(action * a, gint retval)
114 GtkWidget *msg;
115 gchar *str;
117 if (retval == 0)
118 return;
120 if (retval < 0) {
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,
124 "%s", str);
125 } else {
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)
138 gint ret;
139 gpointer stat_data;
141 if (current_image == NULL || current_image->node == NULL)
142 return FALSE;
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);
151 return FALSE;
154 static gboolean action_every(action * a)
156 GList *node;
157 gint ret = 0;
158 gpointer stat_data;
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);
165 if (new_ret != 0)
166 ret = new_ret;
167 process_events();
170 process_system_retval(a, ret);
172 if (before == current_image)
173 reload_changed_files(stat_data);
174 else {
175 /* Don't bother checking files changes if the user changed the image */
176 g_free(stat_data);
179 return FALSE;
182 static void make_menu(GtkAccelGroup * accel_group, GtkMenuItem * menu_item,
183 GCallback cb, const gchar * menu_name)
185 const gchar *name;
186 GtkMenuItem *item;
187 GtkMenu *menu = GTK_MENU(gtk_menu_new());
188 gchar *accel_path;
189 gint i;
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());
194 push_mnemonics();
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,
206 NULL);
208 gtk_accel_map_add_entry(accel_path, 0, 0);
209 gtk_menu_item_set_accel_path(item, accel_path);
210 g_free(accel_path);
213 gtk_menu_item_set_submenu(menu_item, GTK_WIDGET(menu));
214 gtk_widget_show_all(GTK_WIDGET(menu_item));
216 pop_mnemonics();
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) {
228 /* First time */
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");
250 /*** GUI part ***/
252 #include "glade_actions.h"
254 enum {
255 COLUMN_NAME,
256 COLUMN_COMMAND,
257 N_COLUMNS
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)
266 GValue value;
268 memset(&value, 0, sizeof(GValue));
269 if (name != NULL) {
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 GtkTreeIter *find_action_by_name(const gchar * name)
288 gchar *action_name = NULL;
289 GtkTreeIter *iter = g_new(GtkTreeIter, 1);
291 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), iter)) {
292 do {
293 get_list_row(iter, &action_name, NULL);
294 if (g_str_equal(name, action_name))
295 break;
297 g_free(action_name);
298 action_name = NULL;
299 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store), iter));
302 if (action_name == NULL) {
303 gtk_tree_iter_free(iter);
304 iter = NULL;
307 g_free(action_name);
308 return iter;
311 static gboolean edit_action_dialog(gchar ** name, gchar ** command)
314 GtkWidget *dialog = create_edit_action_dialog();
315 GtkEntry *name_entry, *command_entry;
316 GtkTreeIter *duplicate = NULL;
317 gint response;
318 gboolean ok = FALSE;
320 name_entry = g_object_get_data(G_OBJECT(dialog), "name_entry");
321 command_entry = g_object_get_data(G_OBJECT(dialog), "command_entry");
323 if (*name != NULL)
324 gtk_entry_set_text(name_entry, *name);
326 if (*command != NULL)
327 gtk_entry_set_text(command_entry, *command);
329 response = run_modal_dialog(GTK_DIALOG(dialog));
330 if (response == GTK_RESPONSE_OK) {
331 gchar *new_name;
333 new_name = gtk_editable_get_chars(GTK_EDITABLE(name_entry), 0, -1);
334 if (*name == NULL || g_str_equal(new_name, *name) == FALSE) {
335 duplicate = find_action_by_name(new_name);
336 if (duplicate != NULL) {
337 GtkWidget *msg;
338 gchar *str = _("The action name '%s' is already used");
339 msg = gtk_message_dialog_new(NULL,
340 GTK_DIALOG_MODAL,
341 GTK_MESSAGE_WARNING,
342 GTK_BUTTONS_CLOSE, str, new_name);
343 run_modal_dialog(GTK_DIALOG(msg));
344 gtk_widget_destroy(msg);
345 gtk_tree_iter_free(duplicate);
349 if (duplicate == NULL) {
350 g_free(*name);
351 g_free(*command);
353 *command = gtk_editable_get_chars(GTK_EDITABLE(command_entry),
354 0, -1);
355 *name = new_name;
356 ok = TRUE;
357 } else
358 g_free(new_name);
361 gtk_widget_destroy(dialog);
362 return ok;
365 static gboolean on_add_button_clicked(void)
367 gchar *name = NULL, *command = NULL;
368 gboolean ok;
370 ok = edit_action_dialog(&name, &command);
372 if (ok && name[0] != '\0' && command[0] != '\0') {
373 GtkTreeIter new_iter;
375 if (has_selection)
376 gtk_list_store_insert_after(list_store, &new_iter, &iter);
377 else
378 gtk_list_store_append(list_store, &new_iter);
380 gtk_list_store_set(list_store, &new_iter,
381 COLUMN_NAME, name, COLUMN_COMMAND, command, -1);
384 g_free(name);
385 g_free(command);
386 return FALSE;
389 static gboolean on_property_button_clicked(void)
392 gchar *name, *command;
393 gint ok;
395 get_list_row(&iter, &name, &command);
396 ok = edit_action_dialog(&name, &command);
398 if (ok && name[0] != '\0' && command[0] != '\0')
399 gtk_list_store_set(list_store, &iter,
400 COLUMN_NAME, name, COLUMN_COMMAND, command, -1);
401 g_free(name);
402 g_free(command);
403 return FALSE;
406 static gboolean on_delete_button_clicked(void)
408 gtk_list_store_remove(list_store, &iter);
409 return FALSE;
412 static gboolean selection_changed(GtkTreeSelection * selection,
413 GtkWidget * dialog)
415 GtkWidget *property_button, *delete_button;
417 has_selection = gtk_tree_selection_get_selected(selection, NULL, &iter);
419 property_button = g_object_get_data(G_OBJECT(dialog), "property_button");
420 delete_button = g_object_get_data(G_OBJECT(dialog), "delete_button");
422 if (GTK_WIDGET_IS_SENSITIVE(property_button) != has_selection) {
423 gtk_widget_set_sensitive(property_button, has_selection);
424 gtk_widget_set_sensitive(delete_button, has_selection);
427 return FALSE;
430 #include "glade_actions.c"
432 static void add_column(GtkTreeView * tree_view, const gchar * name,
433 gint column_id)
435 GtkTreeViewColumn *column;
436 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
438 column = gtk_tree_view_column_new_with_attributes(name, renderer,
439 "text", column_id, NULL);
441 gtk_tree_view_column_set_resizable(column, TRUE);
442 gtk_tree_view_column_set_reorderable(column, TRUE);
443 gtk_tree_view_column_set_sort_column_id(column, column_id);
445 gtk_tree_view_append_column(tree_view, column);
448 static GtkListStore *make_tree_view(GtkWidget * dialog)
450 GtkTreeView *tree_view = g_object_get_data(G_OBJECT(dialog), "treeview");
451 GtkListStore *list_store;
452 GtkTreeIter iter;
453 GtkTreeSelection *selection;
454 gint i;
456 list_store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
458 for (i = 0; i < actions_array->len; i++) {
459 action *a = g_ptr_array_index(actions_array, i);
461 gtk_list_store_append(list_store, &iter);
462 gtk_list_store_set(list_store, &iter,
463 COLUMN_NAME, a->name,
464 COLUMN_COMMAND, a->command, -1);
467 gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(list_store));
469 add_column(tree_view, _("Name"), COLUMN_NAME);
470 add_column(tree_view, _("Command"), COLUMN_COMMAND);
472 selection = gtk_tree_view_get_selection(tree_view);
473 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
474 g_signal_connect(selection, "changed", G_CALLBACK(selection_changed),
475 dialog);
477 return list_store;
480 static gboolean read_store(GtkTreeModel * unused_model,
481 GtkTreePath * unused_path, GtkTreeIter * iter)
483 gchar *name, *command;
484 action *a = g_new(action, 1);
486 get_list_row(iter, &name, &command);
488 a->name = name;
489 a->command = command;
491 g_ptr_array_add(actions_array, a);
493 return FALSE;
496 static void destroy_actions(void)
498 gint i;
500 for (i = 0; i < actions_array->len; i++) {
501 action *a = g_ptr_array_index(actions_array, i);
503 g_free(a->name);
504 g_free(a->command);
505 g_free(a);
508 g_ptr_array_free(actions_array, TRUE);
509 actions_array = NULL;
512 gboolean edit_actions(void)
514 GtkWidget *dialog = create_actions_dialog();
515 gint response;
517 list_store = make_tree_view(dialog);
518 response = run_modal_dialog(GTK_DIALOG(dialog));
519 gtk_widget_destroy(dialog);
521 if (response == GTK_RESPONSE_OK) {
522 destroy_actions();
523 actions_array = g_ptr_array_new();
524 gtk_tree_model_foreach(GTK_TREE_MODEL(list_store),
525 (GtkTreeModelForeachFunc) read_store, NULL);
526 init_actions(NULL, NULL, NULL);
529 g_object_unref(list_store);
530 list_store = NULL;
531 has_selection = FALSE;
533 return FALSE;
536 /*** glivrc ***/
538 void add_action(const gchar * name, const gchar * command)
540 gint i;
541 action *a;
543 if (actions_array == NULL)
544 actions_array = g_ptr_array_new();
546 for (i = 0; i < actions_array->len; i++) {
547 a = g_ptr_array_index(actions_array, i);
549 if (g_str_equal(a->name, name)) {
550 g_free(a->command);
551 a->command = g_strdup(command);
552 return;
556 a = g_new(action, 1);
557 a->name = g_strdup(name);
558 a->command = g_strdup(command);
560 g_ptr_array_add(actions_array, a);
563 void write_actions(FILE * file)
565 gint i;
566 action *a;
568 if (actions_array == NULL)
569 return;
571 for (i = 0; i < actions_array->len; i++) {
572 a = g_ptr_array_index(actions_array, i);
574 fprintf(file, "action_name = %s\n", a->name);
575 fprintf(file, "action_command = %s\n\n", a->command);