7 * This software is released under the GNU General Public License (GNU GPL).
8 * Please read the included file COPYING for more information.
9 * This software comes with no warranty of any kind, use at your own risk!
17 #include "ui_fileops.h"
18 #include "ui_spinner.h"
19 #include "ui_utildlg.h"
24 #define EDITOR_WINDOW_WIDTH 500
25 #define EDITOR_WINDOW_HEIGHT 300
27 #define COMMAND_SHELL "sh"
28 #define COMMAND_OPT "-c"
31 typedef struct _EditorVerboseData EditorVerboseData;
32 struct _EditorVerboseData {
36 GtkWidget *button_close;
37 GtkWidget *button_stop;
44 gchar *command_template;
49 static gchar *editor_slot_defaults[] = {
50 N_("The Gimp"), "gimp-remote -n %f",
52 N_("Xpaint"), "xpaint %f",
58 N_("Rotate jpeg clockwise"), "%vif jpegtran -rotate 90 -copy all -outfile %p_tmp %p; then mv %p_tmp %p;else rm %p_tmp;fi",
59 N_("Rotate jpeg counterclockwise"), "%vif jpegtran -rotate 270 -copy all -outfile %p_tmp %p; then mv %p_tmp %p;else rm %p_tmp;fi",
64 static void editor_verbose_window_progress(EditorVerboseData *vd, const gchar *text);
65 static gint editor_command_next(EditorVerboseData *vd);
69 *-----------------------------------------------------------------------------
70 * external editor routines
71 *-----------------------------------------------------------------------------
74 void editor_reset_defaults(void)
78 for (i = 0; i < GQVIEW_EDITOR_SLOTS; i++)
80 g_free(editor_name[i]);
81 editor_name[i] = g_strdup(_(editor_slot_defaults[i * 2]));
82 g_free(editor_command[i]);
83 editor_command[i] = g_strdup(editor_slot_defaults[i * 2 + 1]);
87 static void editor_verbose_window_close(GenericDialog *gd, gpointer data)
89 EditorVerboseData *vd = data;
91 generic_dialog_close(gd);
92 g_free(vd->command_template);
96 static void editor_verbose_window_stop(GenericDialog *gd, gpointer data)
98 EditorVerboseData *vd = data;
100 path_list_free(vd->list);
104 editor_verbose_window_progress(vd, _("stopping..."));
107 static void editor_verbose_window_enable_close(EditorVerboseData *vd)
109 vd->gd->cancel_cb = editor_verbose_window_close;
111 spinner_set_interval(vd->spinner, -1);
112 gtk_widget_set_sensitive(vd->button_stop, FALSE);
113 gtk_widget_set_sensitive(vd->button_close, TRUE);
116 static EditorVerboseData *editor_verbose_window(const gchar *template, const gchar *text)
118 EditorVerboseData *vd;
123 vd = g_new0(EditorVerboseData, 1);
126 vd->command_template = g_strdup(template);
131 vd->gd = file_util_gen_dlg(_("Edit command results"), "GQview", "editor_results",
134 buf = g_strdup_printf(_("Output of %s"), text);
135 generic_dialog_add_message(vd->gd, NULL, buf, NULL);
137 vd->button_stop = generic_dialog_add_button(vd->gd, GTK_STOCK_STOP, NULL,
138 editor_verbose_window_stop, FALSE);
139 gtk_widget_set_sensitive(vd->button_stop, FALSE);
140 vd->button_close = generic_dialog_add_button(vd->gd, GTK_STOCK_CLOSE, NULL,
141 editor_verbose_window_close, TRUE);
142 gtk_widget_set_sensitive(vd->button_close, FALSE);
144 scrolled = gtk_scrolled_window_new(NULL, NULL);
145 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
146 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
147 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
148 gtk_box_pack_start(GTK_BOX(vd->gd->vbox), scrolled, TRUE, TRUE, 5);
149 gtk_widget_show(scrolled);
151 vd->text = gtk_text_view_new();
152 gtk_text_view_set_editable(GTK_TEXT_VIEW(vd->text), FALSE);
153 gtk_widget_set_size_request(vd->text, EDITOR_WINDOW_WIDTH, EDITOR_WINDOW_HEIGHT);
154 gtk_container_add(GTK_CONTAINER(scrolled), vd->text);
155 gtk_widget_show(vd->text);
157 hbox = gtk_hbox_new(FALSE, 0);
158 gtk_box_pack_start(GTK_BOX(vd->gd->vbox), hbox, FALSE, FALSE, 0);
159 gtk_widget_show(hbox);
161 vd->progress = gtk_progress_bar_new();
162 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(vd->progress), 0.0);
163 gtk_box_pack_start(GTK_BOX(hbox), vd->progress, TRUE, TRUE, 0);
164 gtk_widget_show(vd->progress);
166 vd->spinner = spinner_new(NULL, SPINNER_SPEED);
167 gtk_box_pack_start(GTK_BOX(hbox), vd->spinner, FALSE, FALSE, 0);
168 gtk_widget_show(vd->spinner);
170 gtk_widget_show(vd->gd->dialog);
175 static void editor_verbose_window_fill(EditorVerboseData *vd, gchar *text, gint len)
177 GtkTextBuffer *buffer;
180 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vd->text));
181 gtk_text_buffer_get_iter_at_offset(buffer, &iter, -1);
182 gtk_text_buffer_insert(buffer, &iter, text, len);
185 static void editor_verbose_window_progress(EditorVerboseData *vd, const gchar *text)
189 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(vd->progress), (double)vd->count / vd->total);
192 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(vd->progress), (text) ? text : "");
195 static gboolean editor_verbose_io_cb(GIOChannel *source, GIOCondition condition, gpointer data)
197 EditorVerboseData *vd = data;
204 while (g_io_channel_read_chars(source, buf, sizeof(buf), &count, NULL) == G_IO_STATUS_NORMAL)
206 if (!g_utf8_validate(buf, count, NULL))
209 utf8 = g_locale_to_utf8(buf, count, NULL, NULL, NULL);
212 editor_verbose_window_fill(vd, utf8, -1);
217 editor_verbose_window_fill(vd, "GQview: Error converting text to valid utf8\n", -1);
222 editor_verbose_window_fill(vd, buf, count);
227 printf("Error reading from command\n");
229 if (debug) printf("Editor command HUP\n");
231 while (g_source_remove_by_user_data(vd));
234 editor_command_next(vd);
242 static int command_pipe(char *command)
248 args[0] = COMMAND_SHELL;
249 args[1] = COMMAND_OPT;
255 printf("pipe setup failed: %s\n", strerror(errno));
263 printf("fork failed: %s\n", strerror(errno));
274 execvp(args[0], args);
276 msg = g_strdup_printf("Unable to exec command:\n%s\n\n%s\n", command, strerror(errno));
277 write(1, msg, strlen(msg));
284 fcntl(fpipe[0], F_SETFL, O_NONBLOCK);
293 static gint editor_verbose_start(EditorVerboseData *vd, gchar *command)
298 fd = command_pipe(command);
303 buf = g_strdup_printf(_("Failed to run command:\n%s\n"), command);
304 editor_verbose_window_fill(vd, buf, strlen(buf));
311 channel = g_io_channel_unix_new(fd);
313 g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN,
314 editor_verbose_io_cb, vd, NULL);
315 g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_ERR,
316 editor_verbose_io_cb, vd, NULL);
317 g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_HUP,
318 editor_verbose_io_cb, vd, NULL);
319 g_io_channel_unref(channel);
324 static gint editor_command_one(const gchar *template, const gchar *path, EditorVerboseData *vd)
326 GString *result = NULL;
330 gchar path_buffer[512];
332 gint path_change = FALSE;
335 current_path = getcwd(path_buffer, sizeof(path_buffer));
337 result = g_string_new("");
338 pathl = path_from_utf8(path);
341 while ( (found = strstr(ptr, "%p")) )
343 result = g_string_append_len(result, ptr, found - ptr);
345 result = g_string_append_c(result, '"');
346 result = g_string_append(result, pathl);
347 result = g_string_append_c(result, '"');
349 result = g_string_append(result, ptr);
351 if (debug) printf("system command: %s\n", result->str);
356 base = remove_level_from_path(path);
357 if (chdir(base) == 0) path_change = TRUE;
363 result = g_string_append(result, " 2>&1");
364 ret = editor_verbose_start(vd, result->str);
368 ret = system(result->str);
371 if (path_change) chdir(current_path);
373 g_string_free(result, TRUE);
379 static gint editor_command_next(EditorVerboseData *vd)
383 editor_verbose_window_fill(vd, "\n", 1);
390 path = vd->list->data;
391 vd->list = g_list_remove(vd->list, path);
393 editor_verbose_window_progress(vd, path);
396 success = editor_command_one(vd->command_template, path, vd);
399 gtk_widget_set_sensitive(vd->button_stop, (vd->list != NULL) );
400 editor_verbose_window_fill(vd, path, strlen(path));
401 editor_verbose_window_fill(vd, "\n", 1);
405 if (success) return TRUE;
408 if (vd->count == vd->total)
414 text = _("stopped by user");
417 editor_verbose_window_progress(vd, text);
418 editor_verbose_window_enable_close(vd);
422 static void editor_command_start(const gchar *template, const gchar *text, GList *list)
424 EditorVerboseData *vd;
426 vd = editor_verbose_window(template, text);
427 vd->list = path_list_copy(list);
428 vd->total = g_list_length(list);
430 editor_command_next(vd);
433 static gint editor_line_break(const gchar *template, gchar **front, const gchar **end)
437 *front = g_strdup(template);
438 found = strstr(*front, "%f");
452 * The supported macros for editor commands:
454 * %f first occurence replaced by quoted sequence of filenames, command is run once.
455 * only one occurence of this macro is supported.
456 * ([ls %f] results in [ls "file1" "file2" ... "lastfile"])
457 * %p command is run for each filename in turn, each instance replaced with single filename.
458 * multiple occurences of this macro is supported for complex shell commands.
459 * This macro will BLOCK THE APPLICATION until it completes, since command is run once
460 * for every file in syncronous order. To avoid blocking add the %v macro, below.
461 * ([ls %p] results in [ls "file1"], [ls "file2"] ... [ls "lastfile"])
462 * none if no macro is supplied, the result is equivalent to "command %f"
463 * ([ls] results in [ls "file1" "file2" ... "lastfile"])
465 * Only one of the macros %f or %p may be used in a given commmand.
467 * %v must be the first two characters in a command, causes a window to display
468 * showing the output of the command(s).
469 * %V same as %v except in the case of %p only displays a window for multiple files,
470 * operating on a single file is suppresses the output dialog.
472 static void editor_command_run(const gchar *template, const gchar *text, GList *list)
474 gint verbose = FALSE;
475 gint for_each = FALSE;
477 if (!template || template[0] == '\0') return;
479 for_each = (strstr(template, "%p") != NULL);
481 if (strncmp(template, "%v", 2) == 0)
486 else if (strncmp(template, "%V", 2) == 0)
489 if (!for_each || list->next) verbose = TRUE;
496 editor_command_start(template, text, list);
505 gchar *path = work->data;
506 editor_command_one(template, path, NULL);
516 GString *result = NULL;
519 parser_match = editor_line_break(template, &front, &end);
520 result = g_string_new((parser_match) ? "" : " ");
525 gchar *path = work->data;
528 if (work != list) g_string_append_c(result, ' ');
529 result = g_string_append_c(result, '"');
530 pathl = path_from_utf8(path);
531 result = g_string_append(result, pathl);
533 result = g_string_append_c(result, '"');
537 result = g_string_prepend(result, front);
538 result = g_string_append(result, end);
539 if (verbose) result = g_string_append(result, " 2>&1 ");
540 result = g_string_append(result, "&");
542 if (debug) printf("system command: %s\n", result->str);
546 EditorVerboseData *vd;
548 vd = editor_verbose_window(template, text);
549 editor_verbose_window_progress(vd, _("running..."));
550 editor_verbose_start(vd, result->str);
558 g_string_free(result, TRUE);
562 void start_editor_from_path_list(gint n, GList *list)
566 if (n < 0 || n >= GQVIEW_EDITOR_SLOTS || !list ||
567 !editor_command[n] ||
568 strlen(editor_command[n]) == 0) return;
570 command = g_locale_from_utf8(editor_command[n], -1, NULL, NULL, NULL);
571 editor_command_run(command, editor_name[n], list);
575 void start_editor_from_file(gint n, const gchar *path)
581 list = g_list_append(NULL, (gchar *)path);
582 start_editor_from_path_list(n, list);