Mercurial > geeqie.yaz
annotate src/editors.c @ 669:dd5d7fe9458f
Improve editors a bit:
- allow whitespaces before and after %v, %V, %w
- allow % escaping using %% (mandatory to use shell commands than contain % characters)
- display a dialog on execution if a syntax error is detected (only for generic editors)
- update README editors section
author | zas_ |
---|---|
date | Fri, 16 May 2008 08:37:07 +0000 |
parents | 905688aa2317 |
children | a484500de88d |
rev | line source |
---|---|
9 | 1 /* |
196 | 2 * Geeqie |
123
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
3 * (C) 2006 John Ellis |
475 | 4 * Copyright (C) 2008 The Geeqie Team |
9 | 5 * |
6 * Author: John Ellis | |
7 * | |
8 * This software is released under the GNU General Public License (GNU GPL). | |
9 * Please read the included file COPYING for more information. | |
10 * This software comes with no warranty of any kind, use at your own risk! | |
11 */ | |
12 | |
13 | |
281 | 14 #include "main.h" |
9 | 15 #include "editors.h" |
16 | |
669 | 17 #include "debug.h" |
18 #include "filedata.h" | |
19 #include "filefilter.h" | |
9 | 20 #include "utilops.h" |
21 #include "ui_fileops.h" | |
22 #include "ui_spinner.h" | |
23 #include "ui_utildlg.h" | |
24 | |
25 #include <errno.h> | |
26 | |
27 | |
28 #define EDITOR_WINDOW_WIDTH 500 | |
29 #define EDITOR_WINDOW_HEIGHT 300 | |
30 | |
140 | 31 #define COMMAND_SHELL "/bin/sh" |
9 | 32 #define COMMAND_OPT "-c" |
33 | |
34 | |
35 typedef struct _EditorVerboseData EditorVerboseData; | |
36 struct _EditorVerboseData { | |
37 GenericDialog *gd; | |
38 GtkWidget *button_close; | |
39 GtkWidget *button_stop; | |
40 GtkWidget *text; | |
41 GtkWidget *progress; | |
42 GtkWidget *spinner; | |
140 | 43 }; |
44 | |
45 typedef struct _EditorData EditorData; | |
46 struct _EditorData { | |
47 gint flags; | |
48 GPid pid; | |
49 gchar *command_template; | |
50 GList *list; | |
9 | 51 gint count; |
52 gint total; | |
140 | 53 gboolean stopping; |
54 EditorVerboseData *vd; | |
55 EditorCallback callback; | |
56 gpointer data; | |
9 | 57 }; |
58 | |
59 | |
283 | 60 static gchar *editor_slot_defaults[GQ_EDITOR_SLOTS * 2] = { |
416 | 61 N_("The Gimp"), "gimp-remote %{.cr2;.crw;.nef;.raw;*}f", |
9 | 62 N_("XV"), "xv %f", |
63 N_("Xpaint"), "xpaint %f", | |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
64 N_("UFraw"), "ufraw %{.cr2;.crw;.nef;.raw}p", |
190
c2923efebfdc
whitelist of files that can have an xmp sidecar, sample external command
nadvornik
parents:
147
diff
changeset
|
65 N_("Add XMP sidecar"), "%vFILE=%{.cr2;.crw;.nef;.raw}p;XMP=`echo \"$FILE\"|sed -e 's|\\.[^.]*$|.xmp|'`; exiftool -tagsfromfile \"$FILE\" \"$XMP\"", |
9 | 66 NULL, NULL, |
67 NULL, NULL, | |
68 NULL, NULL, | |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
69 N_("Rotate jpeg clockwise"), "%vif jpegtran -rotate 90 -copy all -outfile %{.jpg;.jpeg}p_tmp %{.jpg;.jpeg}p; then mv %{.jpg;.jpeg}p_tmp %{.jpg;.jpeg}p;else rm %{.jpg;.jpeg}p_tmp;fi", |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
70 N_("Rotate jpeg counterclockwise"), "%vif jpegtran -rotate 270 -copy all -outfile %{.jpg;.jpeg}p_tmp %{.jpg;.jpeg}p; then mv %{.jpg;.jpeg}p_tmp %{.jpg;.jpeg}p;else rm %{.jpg;.jpeg}p_tmp;fi", |
134
9009856628f7
started implementation of external commands; external Delete should work
nadvornik
parents:
123
diff
changeset
|
71 /* special slots */ |
136 | 72 #if 1 |
73 /* for testing */ | |
261 | 74 N_("External Copy command"), "%vset -x;cp %p %d", |
75 N_("External Move command"), "%vset -x;mv %p %d", | |
76 N_("External Rename command"), "%vset -x;mv %p %d", | |
440
3d16af3b133a
rm as external delete command is too dangerous, even for testing
nadvornik
parents:
416
diff
changeset
|
77 N_("External Delete command"), NULL, |
261 | 78 N_("External New Folder command"), NULL |
136 | 79 #else |
261 | 80 N_("External Copy command"), NULL, |
81 N_("External Move command"), NULL, | |
82 N_("External Rename command"), NULL, | |
83 N_("External Delete command"), NULL, | |
84 N_("External New Folder command"), NULL | |
136 | 85 #endif |
9 | 86 }; |
87 | |
140 | 88 static void editor_verbose_window_progress(EditorData *ed, const gchar *text); |
89 static gint editor_command_next_start(EditorData *ed); | |
90 static gint editor_command_next_finish(EditorData *ed, gint status); | |
91 static gint editor_command_done(EditorData *ed); | |
9 | 92 |
93 /* | |
94 *----------------------------------------------------------------------------- | |
95 * external editor routines | |
96 *----------------------------------------------------------------------------- | |
97 */ | |
98 | |
99 void editor_reset_defaults(void) | |
100 { | |
101 gint i; | |
102 | |
283 | 103 for (i = 0; i < GQ_EDITOR_SLOTS; i++) |
9 | 104 { |
318 | 105 g_free(options->editor_name[i]); |
106 options->editor_name[i] = g_strdup(_(editor_slot_defaults[i * 2])); | |
107 g_free(options->editor_command[i]); | |
108 options->editor_command[i] = g_strdup(editor_slot_defaults[i * 2 + 1]); | |
9 | 109 } |
110 } | |
111 | |
140 | 112 static void editor_verbose_data_free(EditorData *ed) |
113 { | |
114 if (!ed->vd) return; | |
115 g_free(ed->vd); | |
116 ed->vd = NULL; | |
117 } | |
118 | |
119 static void editor_data_free(EditorData *ed) | |
120 { | |
121 editor_verbose_data_free(ed); | |
122 g_free(ed->command_template); | |
123 g_free(ed); | |
124 } | |
125 | |
9 | 126 static void editor_verbose_window_close(GenericDialog *gd, gpointer data) |
127 { | |
140 | 128 EditorData *ed = data; |
9 | 129 |
130 generic_dialog_close(gd); | |
140 | 131 editor_verbose_data_free(ed); |
132 if (ed->pid == -1) editor_data_free(ed); /* the process has already terminated */ | |
9 | 133 } |
134 | |
135 static void editor_verbose_window_stop(GenericDialog *gd, gpointer data) | |
136 { | |
140 | 137 EditorData *ed = data; |
138 ed->stopping = TRUE; | |
139 ed->count = 0; | |
140 editor_verbose_window_progress(ed, _("stopping...")); | |
9 | 141 } |
142 | |
143 static void editor_verbose_window_enable_close(EditorVerboseData *vd) | |
144 { | |
145 vd->gd->cancel_cb = editor_verbose_window_close; | |
146 | |
147 spinner_set_interval(vd->spinner, -1); | |
148 gtk_widget_set_sensitive(vd->button_stop, FALSE); | |
149 gtk_widget_set_sensitive(vd->button_close, TRUE); | |
150 } | |
151 | |
140 | 152 static EditorVerboseData *editor_verbose_window(EditorData *ed, const gchar *text) |
9 | 153 { |
154 EditorVerboseData *vd; | |
155 GtkWidget *scrolled; | |
156 GtkWidget *hbox; | |
157 gchar *buf; | |
158 | |
159 vd = g_new0(EditorVerboseData, 1); | |
160 | |
254
9faf34f047b1
Make the wmclass value unique among the code by defining
zas_
parents:
238
diff
changeset
|
161 vd->gd = file_util_gen_dlg(_("Edit command results"), GQ_WMCLASS, "editor_results", |
9 | 162 NULL, FALSE, |
140 | 163 NULL, ed); |
9 | 164 buf = g_strdup_printf(_("Output of %s"), text); |
165 generic_dialog_add_message(vd->gd, NULL, buf, NULL); | |
166 g_free(buf); | |
167 vd->button_stop = generic_dialog_add_button(vd->gd, GTK_STOCK_STOP, NULL, | |
168 editor_verbose_window_stop, FALSE); | |
169 gtk_widget_set_sensitive(vd->button_stop, FALSE); | |
170 vd->button_close = generic_dialog_add_button(vd->gd, GTK_STOCK_CLOSE, NULL, | |
171 editor_verbose_window_close, TRUE); | |
172 gtk_widget_set_sensitive(vd->button_close, FALSE); | |
173 | |
174 scrolled = gtk_scrolled_window_new(NULL, NULL); | |
175 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); | |
176 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), | |
177 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
178 gtk_box_pack_start(GTK_BOX(vd->gd->vbox), scrolled, TRUE, TRUE, 5); | |
179 gtk_widget_show(scrolled); | |
180 | |
181 vd->text = gtk_text_view_new(); | |
182 gtk_text_view_set_editable(GTK_TEXT_VIEW(vd->text), FALSE); | |
183 gtk_widget_set_size_request(vd->text, EDITOR_WINDOW_WIDTH, EDITOR_WINDOW_HEIGHT); | |
184 gtk_container_add(GTK_CONTAINER(scrolled), vd->text); | |
185 gtk_widget_show(vd->text); | |
186 | |
187 hbox = gtk_hbox_new(FALSE, 0); | |
188 gtk_box_pack_start(GTK_BOX(vd->gd->vbox), hbox, FALSE, FALSE, 0); | |
189 gtk_widget_show(hbox); | |
190 | |
191 vd->progress = gtk_progress_bar_new(); | |
192 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(vd->progress), 0.0); | |
193 gtk_box_pack_start(GTK_BOX(hbox), vd->progress, TRUE, TRUE, 0); | |
194 gtk_widget_show(vd->progress); | |
195 | |
196 vd->spinner = spinner_new(NULL, SPINNER_SPEED); | |
197 gtk_box_pack_start(GTK_BOX(hbox), vd->spinner, FALSE, FALSE, 0); | |
198 gtk_widget_show(vd->spinner); | |
442 | 199 |
9 | 200 gtk_widget_show(vd->gd->dialog); |
201 | |
140 | 202 ed->vd = vd; |
9 | 203 return vd; |
204 } | |
205 | |
206 static void editor_verbose_window_fill(EditorVerboseData *vd, gchar *text, gint len) | |
207 { | |
208 GtkTextBuffer *buffer; | |
209 GtkTextIter iter; | |
210 | |
211 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vd->text)); | |
212 gtk_text_buffer_get_iter_at_offset(buffer, &iter, -1); | |
213 gtk_text_buffer_insert(buffer, &iter, text, len); | |
214 } | |
215 | |
140 | 216 static void editor_verbose_window_progress(EditorData *ed, const gchar *text) |
9 | 217 { |
140 | 218 if (!ed->vd) return; |
219 | |
220 if (ed->total) | |
9 | 221 { |
140 | 222 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ed->vd->progress), (double)ed->count / ed->total); |
9 | 223 } |
224 | |
140 | 225 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ed->vd->progress), (text) ? text : ""); |
9 | 226 } |
227 | |
228 static gboolean editor_verbose_io_cb(GIOChannel *source, GIOCondition condition, gpointer data) | |
229 { | |
140 | 230 EditorData *ed = data; |
9 | 231 gchar buf[512]; |
232 gsize count; | |
233 | |
140 | 234 if (condition & G_IO_IN) |
9 | 235 { |
140 | 236 while (g_io_channel_read_chars(source, buf, sizeof(buf), &count, NULL) == G_IO_STATUS_NORMAL) |
237 { | |
238 if (!g_utf8_validate(buf, count, NULL)) | |
9 | 239 { |
140 | 240 gchar *utf8; |
444 | 241 |
140 | 242 utf8 = g_locale_to_utf8(buf, count, NULL, NULL, NULL); |
243 if (utf8) | |
9 | 244 { |
140 | 245 editor_verbose_window_fill(ed->vd, utf8, -1); |
246 g_free(utf8); | |
9 | 247 } |
248 else | |
249 { | |
288
d1f74154463e
Replace occurences of Geeqie / geeqie by constants defined in main.h.
zas_
parents:
283
diff
changeset
|
250 editor_verbose_window_fill(ed->vd, "Error converting text to valid utf8\n", -1); |
9 | 251 } |
252 } | |
140 | 253 else |
254 { | |
255 editor_verbose_window_fill(ed->vd, buf, count); | |
256 } | |
257 } | |
9 | 258 } |
259 | |
140 | 260 if (condition & (G_IO_ERR | G_IO_HUP)) |
9 | 261 { |
140 | 262 g_io_channel_shutdown(source, TRUE, NULL); |
9 | 263 return FALSE; |
264 } | |
265 | |
266 return TRUE; | |
267 } | |
268 | |
138 | 269 typedef enum { |
270 PATH_FILE, | |
140 | 271 PATH_DEST |
138 | 272 } PathType; |
273 | |
274 | |
140 | 275 static gchar *editor_command_path_parse(const FileData *fd, PathType type, const gchar *extensions) |
123
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
276 { |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
277 GString *string; |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
278 gchar *pathl; |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
279 const gchar *p = NULL; |
123
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
280 |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
281 string = g_string_new(""); |
442 | 282 |
138 | 283 if (type == PATH_FILE) |
284 { | |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
285 GList *ext_list = filter_to_list(extensions); |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
286 GList *work = ext_list; |
442 | 287 |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
288 if (!work) |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
289 p = fd->path; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
290 else |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
291 { |
516 | 292 while (work) |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
293 { |
444 | 294 GList *work2; |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
295 gchar *ext = work->data; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
296 work = work->next; |
442 | 297 |
298 if (strcmp(ext, "*") == 0 || | |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
299 strcasecmp(ext, fd->extension) == 0) |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
300 { |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
301 p = fd->path; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
302 break; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
303 } |
442 | 304 |
444 | 305 work2 = fd->sidecar_files; |
516 | 306 while (work2) |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
307 { |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
308 FileData *sfd = work2->data; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
309 work2 = work2->next; |
442 | 310 |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
311 if (strcasecmp(ext, sfd->extension) == 0) |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
312 { |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
313 p = sfd->path; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
314 break; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
315 } |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
316 } |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
317 if (p) break; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
318 } |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
319 string_list_free(ext_list); |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
320 if (!p) return NULL; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
321 } |
138 | 322 } |
140 | 323 else if (type == PATH_DEST) |
138 | 324 { |
325 if (fd->change && fd->change->dest) | |
326 p = fd->change->dest; | |
327 else | |
328 p = ""; | |
329 } | |
444 | 330 |
123
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
331 while (*p != '\0') |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
332 { |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
333 /* must escape \, ", `, and $ to avoid problems, |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
334 * we assume system shell supports bash-like escaping |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
335 */ |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
336 if (strchr("\\\"`$", *p) != NULL) |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
337 { |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
338 string = g_string_append_c(string, '\\'); |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
339 } |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
340 string = g_string_append_c(string, *p); |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
341 p++; |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
342 } |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
343 |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
344 pathl = path_from_utf8(string->str); |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
345 g_string_free(string, TRUE); |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
346 |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
347 return pathl; |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
348 } |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
349 |
9 | 350 |
351 /* | |
352 * The supported macros for editor commands: | |
353 * | |
354 * %f first occurence replaced by quoted sequence of filenames, command is run once. | |
355 * only one occurence of this macro is supported. | |
356 * ([ls %f] results in [ls "file1" "file2" ... "lastfile"]) | |
357 * %p command is run for each filename in turn, each instance replaced with single filename. | |
358 * multiple occurences of this macro is supported for complex shell commands. | |
359 * This macro will BLOCK THE APPLICATION until it completes, since command is run once | |
360 * for every file in syncronous order. To avoid blocking add the %v macro, below. | |
361 * ([ls %p] results in [ls "file1"], [ls "file2"] ... [ls "lastfile"]) | |
362 * none if no macro is supplied, the result is equivalent to "command %f" | |
363 * ([ls] results in [ls "file1" "file2" ... "lastfile"]) | |
364 * | |
365 * Only one of the macros %f or %p may be used in a given commmand. | |
366 * | |
60
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
367 * %v must be the first two characters[1] in a command, causes a window to display |
9 | 368 * showing the output of the command(s). |
369 * %V same as %v except in the case of %p only displays a window for multiple files, | |
370 * operating on a single file is suppresses the output dialog. | |
60
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
371 * |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
372 * %w must be first two characters in a command, presence will disable full screen |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
373 * from exiting upon invocation. |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
374 * |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
375 * |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
376 * [1] Note: %v,%V may also be preceded by "%w". |
9 | 377 */ |
140 | 378 |
379 | |
380 gint editor_command_parse(const gchar *template, GList *list, gchar **output) | |
9 | 381 { |
140 | 382 gint flags = 0; |
383 const gchar *p = template; | |
384 GString *result = NULL; | |
385 gchar *extensions = NULL; | |
442 | 386 |
140 | 387 if (output) |
388 result = g_string_new(""); | |
389 | |
442 | 390 if (!template || template[0] == '\0') |
140 | 391 { |
392 flags |= EDITOR_ERROR_EMPTY; | |
393 goto err; | |
394 } | |
669 | 395 |
396 /* skip leading whitespaces if any */ | |
397 while (g_ascii_isspace(*p)) p++; | |
442 | 398 |
140 | 399 /* global flags */ |
400 while (*p == '%') | |
401 { | |
402 switch (*++p) | |
403 { | |
404 case 'w': | |
405 flags |= EDITOR_KEEP_FS; | |
406 p++; | |
407 break; | |
408 case 'v': | |
409 flags |= EDITOR_VERBOSE; | |
410 p++; | |
411 break; | |
412 case 'V': | |
413 flags |= EDITOR_VERBOSE_MULTI; | |
414 p++; | |
415 break; | |
669 | 416 default: |
417 flags |= EDITOR_ERROR_SYNTAX; | |
418 goto err; | |
140 | 419 } |
420 } | |
442 | 421 |
669 | 422 /* skip whitespaces if any */ |
423 while (g_ascii_isspace(*p)) p++; | |
424 | |
140 | 425 /* command */ |
442 | 426 |
140 | 427 while (*p) |
428 { | |
429 if (*p != '%') | |
430 { | |
431 if (output) result = g_string_append_c(result, *p); | |
432 } | |
433 else /* *p == '%' */ | |
434 { | |
435 extensions = NULL; | |
436 gchar *pathl = NULL; | |
9 | 437 |
140 | 438 p++; |
442 | 439 |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
440 /* for example "%f" or "%{.crw;.raw;.cr2}f" */ |
140 | 441 if (*p == '{') |
442 { | |
444 | 443 gchar *end; |
444 | |
140 | 445 p++; |
444 | 446 end = strchr(p, '}'); |
140 | 447 if (!end) |
448 { | |
449 flags |= EDITOR_ERROR_SYNTAX; | |
450 goto err; | |
451 } | |
442 | 452 |
140 | 453 extensions = g_strndup(p, end - p); |
454 p = end + 1; | |
455 } | |
442 | 456 |
457 switch (*p) | |
140 | 458 { |
459 case 'd': | |
460 flags |= EDITOR_DEST; | |
669 | 461 /* fall through */ |
140 | 462 case 'p': |
463 flags |= EDITOR_FOR_EACH; | |
464 if (flags & EDITOR_SINGLE_COMMAND) | |
465 { | |
466 flags |= EDITOR_ERROR_INCOMPATIBLE; | |
467 goto err; | |
468 } | |
469 if (output) | |
470 { | |
471 /* use the first file from the list */ | |
442 | 472 if (!list || !list->data) |
140 | 473 { |
474 flags |= EDITOR_ERROR_NO_FILE; | |
475 goto err; | |
476 } | |
669 | 477 pathl = editor_command_path_parse((FileData *)list->data, |
478 (flags & EDITOR_DEST) ? PATH_DEST : PATH_FILE, | |
479 extensions); | |
442 | 480 if (!pathl) |
140 | 481 { |
482 flags |= EDITOR_ERROR_NO_FILE; | |
483 goto err; | |
484 } | |
485 result = g_string_append_c(result, '"'); | |
486 result = g_string_append(result, pathl); | |
487 g_free(pathl); | |
488 result = g_string_append_c(result, '"'); | |
489 } | |
442 | 490 break; |
60
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
491 |
140 | 492 case 'f': |
493 flags |= EDITOR_SINGLE_COMMAND; | |
494 if (flags & (EDITOR_FOR_EACH | EDITOR_DEST)) | |
495 { | |
496 flags |= EDITOR_ERROR_INCOMPATIBLE; | |
497 goto err; | |
498 } | |
499 | |
500 if (output) | |
501 { | |
502 /* use whole list */ | |
503 GList *work = list; | |
504 gboolean ok = FALSE; | |
444 | 505 |
140 | 506 while (work) |
507 { | |
508 FileData *fd = work->data; | |
509 pathl = editor_command_path_parse(fd, PATH_FILE, extensions); | |
510 | |
511 if (pathl) | |
512 { | |
513 ok = TRUE; | |
514 if (work != list) g_string_append_c(result, ' '); | |
515 result = g_string_append_c(result, '"'); | |
516 result = g_string_append(result, pathl); | |
517 g_free(pathl); | |
518 result = g_string_append_c(result, '"'); | |
519 } | |
520 work = work->next; | |
521 } | |
442 | 522 if (!ok) |
140 | 523 { |
524 flags |= EDITOR_ERROR_NO_FILE; | |
525 goto err; | |
526 } | |
527 } | |
442 | 528 break; |
669 | 529 case '%': |
530 /* %% = % escaping */ | |
531 if (output) result = g_string_append_c(result, *p); | |
532 break; | |
140 | 533 default: |
534 flags |= EDITOR_ERROR_SYNTAX; | |
535 goto err; | |
536 } | |
537 if (extensions) g_free(extensions); | |
538 extensions = NULL; | |
539 } | |
540 p++; | |
9 | 541 } |
542 | |
140 | 543 if (output) *output = g_string_free(result, FALSE); |
544 return flags; | |
545 | |
442 | 546 |
140 | 547 err: |
442 | 548 if (output) |
9 | 549 { |
140 | 550 g_string_free(result, TRUE); |
551 *output = NULL; | |
552 } | |
553 if (extensions) g_free(extensions); | |
554 return flags; | |
555 } | |
556 | |
557 static void editor_child_exit_cb (GPid pid, gint status, gpointer data) | |
558 { | |
559 EditorData *ed = data; | |
560 g_spawn_close_pid(pid); | |
561 ed->pid = -1; | |
442 | 562 |
140 | 563 editor_command_next_finish(ed, status); |
564 } | |
565 | |
566 | |
567 static gint editor_command_one(const gchar *template, GList *list, EditorData *ed) | |
568 { | |
569 gchar *command; | |
570 FileData *fd = list->data; | |
571 GPid pid; | |
442 | 572 gint standard_output; |
573 gint standard_error; | |
140 | 574 gboolean ok; |
575 | |
576 ed->pid = -1; | |
577 ed->flags = editor_command_parse(template, list, &command); | |
578 | |
579 ok = !(ed->flags & EDITOR_ERROR_MASK); | |
580 | |
581 if (ok) | |
582 { | |
443 | 583 gchar *working_directory; |
584 gchar *args[4]; | |
585 | |
586 working_directory = remove_level_from_path(fd->path); | |
587 args[0] = COMMAND_SHELL; | |
588 args[1] = COMMAND_OPT; | |
589 args[2] = command; | |
590 args[3] = NULL; | |
591 | |
442 | 592 ok = g_spawn_async_with_pipes(working_directory, args, NULL, |
140 | 593 G_SPAWN_DO_NOT_REAP_CHILD, /* GSpawnFlags */ |
442 | 594 NULL, NULL, |
595 &pid, | |
596 NULL, | |
597 ed->vd ? &standard_output : NULL, | |
598 ed->vd ? &standard_error : NULL, | |
140 | 599 NULL); |
443 | 600 |
601 g_free(working_directory); | |
442 | 602 |
140 | 603 if (!ok) ed->flags |= EDITOR_ERROR_CANT_EXEC; |
604 } | |
605 | |
442 | 606 if (ok) |
140 | 607 { |
608 g_child_watch_add(pid, editor_child_exit_cb, ed); | |
609 ed->pid = pid; | |
610 } | |
442 | 611 |
140 | 612 if (ed->vd) |
613 { | |
614 if (!ok) | |
9 | 615 { |
140 | 616 gchar *buf; |
617 | |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
618 buf = g_strdup_printf(_("Failed to run command:\n%s\n"), template); |
140 | 619 editor_verbose_window_fill(ed->vd, buf, strlen(buf)); |
620 g_free(buf); | |
621 | |
622 } | |
442 | 623 else |
140 | 624 { |
625 GIOChannel *channel_output; | |
626 GIOChannel *channel_error; | |
444 | 627 |
140 | 628 channel_output = g_io_channel_unix_new(standard_output); |
629 g_io_channel_set_flags(channel_output, G_IO_FLAG_NONBLOCK, NULL); | |
630 | |
631 g_io_add_watch_full(channel_output, G_PRIORITY_HIGH, G_IO_IN | G_IO_ERR | G_IO_HUP, | |
632 editor_verbose_io_cb, ed, NULL); | |
633 g_io_channel_unref(channel_output); | |
634 | |
635 channel_error = g_io_channel_unix_new(standard_error); | |
636 g_io_channel_set_flags(channel_error, G_IO_FLAG_NONBLOCK, NULL); | |
637 | |
638 g_io_add_watch_full(channel_error, G_PRIORITY_HIGH, G_IO_IN | G_IO_ERR | G_IO_HUP, | |
639 editor_verbose_io_cb, ed, NULL); | |
640 g_io_channel_unref(channel_error); | |
641 } | |
642 } | |
442 | 643 |
140 | 644 g_free(command); |
645 | |
646 return ed->flags & EDITOR_ERROR_MASK; | |
647 } | |
648 | |
649 static gint editor_command_next_start(EditorData *ed) | |
650 { | |
651 if (ed->vd) editor_verbose_window_fill(ed->vd, "\n", 1); | |
652 | |
653 if (ed->list && ed->count < ed->total) | |
654 { | |
655 FileData *fd; | |
656 gint error; | |
657 | |
658 fd = ed->list->data; | |
659 | |
660 if (ed->vd) | |
661 { | |
662 editor_verbose_window_progress(ed, (ed->flags & EDITOR_FOR_EACH) ? fd->path : _("running...")); | |
663 } | |
664 ed->count++; | |
665 | |
666 error = editor_command_one(ed->command_template, ed->list, ed); | |
667 if (!error && ed->vd) | |
668 { | |
669 gtk_widget_set_sensitive(ed->vd->button_stop, (ed->list != NULL) ); | |
670 if (ed->flags & EDITOR_FOR_EACH) | |
9 | 671 { |
140 | 672 editor_verbose_window_fill(ed->vd, fd->path, strlen(fd->path)); |
673 editor_verbose_window_fill(ed->vd, "\n", 1); | |
9 | 674 } |
675 } | |
140 | 676 |
442 | 677 if (!error) |
140 | 678 return 0; |
679 else | |
680 /* command was not started, call the finish immediately */ | |
681 return editor_command_next_finish(ed, 0); | |
682 } | |
442 | 683 |
140 | 684 /* everything is done */ |
237
404629011caa
Add missing return at the end of editor_command_next_start().
zas_
parents:
196
diff
changeset
|
685 return editor_command_done(ed); |
140 | 686 } |
687 | |
688 static gint editor_command_next_finish(EditorData *ed, gint status) | |
689 { | |
690 gint cont = ed->stopping ? EDITOR_CB_SKIP : EDITOR_CB_CONTINUE; | |
691 | |
692 if (status) | |
693 ed->flags |= EDITOR_ERROR_STATUS; | |
694 | |
695 if (ed->flags & EDITOR_FOR_EACH) | |
696 { | |
697 /* handle the first element from the list */ | |
698 GList *fd_element = ed->list; | |
444 | 699 |
140 | 700 ed->list = g_list_remove_link(ed->list, fd_element); |
701 if (ed->callback) | |
702 cont = ed->callback(ed->list ? ed : NULL, ed->flags, fd_element, ed->data); | |
703 filelist_free(fd_element); | |
9 | 704 } |
705 else | |
706 { | |
140 | 707 /* handle whole list */ |
708 if (ed->callback) | |
709 cont = ed->callback(NULL, ed->flags, ed->list, ed->data); | |
710 filelist_free(ed->list); | |
711 ed->list = NULL; | |
712 } | |
9 | 713 |
140 | 714 if (cont == EDITOR_CB_SUSPEND) |
715 return ed->flags & EDITOR_ERROR_MASK; | |
716 else if (cont == EDITOR_CB_SKIP) | |
717 return editor_command_done(ed); | |
718 else | |
719 return editor_command_next_start(ed); | |
720 } | |
9 | 721 |
140 | 722 static gint editor_command_done(EditorData *ed) |
723 { | |
724 gint flags; | |
9 | 725 |
140 | 726 if (ed->vd) |
727 { | |
444 | 728 const gchar *text; |
729 | |
140 | 730 if (ed->count == ed->total) |
9 | 731 { |
140 | 732 text = _("done"); |
9 | 733 } |
734 else | |
735 { | |
140 | 736 text = _("stopped by user"); |
9 | 737 } |
140 | 738 editor_verbose_window_progress(ed, text); |
739 editor_verbose_window_enable_close(ed->vd); | |
740 } | |
741 | |
742 /* free the not-handled items */ | |
743 if (ed->list) | |
744 { | |
745 ed->flags |= EDITOR_ERROR_SKIPPED; | |
746 if (ed->callback) ed->callback(NULL, ed->flags, ed->list, ed->data); | |
747 filelist_free(ed->list); | |
748 ed->list = NULL; | |
749 } | |
9 | 750 |
140 | 751 ed->count = 0; |
752 | |
753 flags = ed->flags & EDITOR_ERROR_MASK; | |
754 | |
755 if (!ed->vd) editor_data_free(ed); | |
756 | |
757 return flags; | |
758 } | |
759 | |
760 void editor_resume(gpointer ed) | |
761 { | |
762 editor_command_next_start(ed); | |
763 } | |
443 | 764 |
140 | 765 void editor_skip(gpointer ed) |
766 { | |
442 | 767 editor_command_done(ed); |
9 | 768 } |
769 | |
140 | 770 static gint editor_command_start(const gchar *template, const gchar *text, GList *list, EditorCallback cb, gpointer data) |
771 { | |
772 EditorData *ed; | |
773 gint flags = editor_command_parse(template, NULL, NULL); | |
442 | 774 |
140 | 775 if (flags & EDITOR_ERROR_MASK) return flags & EDITOR_ERROR_MASK; |
776 | |
777 ed = g_new0(EditorData, 1); | |
778 ed->list = filelist_copy(list); | |
779 ed->flags = flags; | |
780 ed->command_template = g_strdup(template); | |
781 ed->total = (flags & EDITOR_SINGLE_COMMAND) ? 1 : g_list_length(list); | |
782 ed->count = 0; | |
783 ed->stopping = FALSE; | |
784 ed->callback = cb; | |
785 ed->data = data; | |
442 | 786 |
140 | 787 if ((flags & EDITOR_VERBOSE_MULTI) && list && list->next) |
788 flags |= EDITOR_VERBOSE; | |
442 | 789 |
140 | 790 if (flags & EDITOR_VERBOSE) |
791 editor_verbose_window(ed, text); | |
442 | 792 |
793 editor_command_next_start(ed); | |
140 | 794 /* errors from editor_command_next_start will be handled via callback */ |
795 return flags & EDITOR_ERROR_MASK; | |
796 } | |
797 | |
444 | 798 static gint is_valid_editor_command(gint n) |
799 { | |
800 return (n >= 0 && n < GQ_EDITOR_SLOTS | |
801 && options->editor_command[n] | |
802 && strlen(options->editor_command[n]) > 0); | |
803 } | |
804 | |
140 | 805 gint start_editor_from_filelist_full(gint n, GList *list, EditorCallback cb, gpointer data) |
9 | 806 { |
807 gchar *command; | |
140 | 808 gint error; |
9 | 809 |
444 | 810 if (!list) return FALSE; |
811 if (!is_valid_editor_command(n)) return FALSE; | |
9 | 812 |
318 | 813 command = g_locale_from_utf8(options->editor_command[n], -1, NULL, NULL, NULL); |
814 error = editor_command_start(command, options->editor_name[n], list, cb, data); | |
9 | 815 g_free(command); |
669 | 816 |
817 if (n < GQ_EDITOR_GENERIC_SLOTS && (error & EDITOR_ERROR_SYNTAX)) | |
818 { | |
819 gchar *text = g_strdup_printf(_("Syntax error in the editor template \"%s\":\n%s"), | |
820 options->editor_name[n], options->editor_command[n]); | |
821 | |
822 file_util_warning_dialog(_("Invalid editor command"), text, GTK_STOCK_DIALOG_ERROR, NULL); | |
823 g_free(text); | |
824 } | |
825 | |
140 | 826 return error; |
9 | 827 } |
828 | |
140 | 829 gint start_editor_from_filelist(gint n, GList *list) |
830 { | |
831 return start_editor_from_filelist_full(n, list, NULL, NULL); | |
832 } | |
833 | |
834 gint start_editor_from_file_full(gint n, FileData *fd, EditorCallback cb, gpointer data) | |
9 | 835 { |
836 GList *list; | |
140 | 837 gint error; |
9 | 838 |
138 | 839 if (!fd) return FALSE; |
9 | 840 |
138 | 841 list = g_list_append(NULL, fd); |
140 | 842 error = start_editor_from_filelist_full(n, list, cb, data); |
9 | 843 g_list_free(list); |
140 | 844 return error; |
9 | 845 } |
60
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
846 |
140 | 847 gint start_editor_from_file(gint n, FileData *fd) |
136 | 848 { |
140 | 849 return start_editor_from_file_full(n, fd, NULL, NULL); |
136 | 850 } |
851 | |
60
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
852 gint editor_window_flag_set(gint n) |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
853 { |
444 | 854 if (!is_valid_editor_command(n)) return TRUE; |
60
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
855 |
318 | 856 return (editor_command_parse(options->editor_command[n], NULL, NULL) & EDITOR_KEEP_FS); |
60
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
857 } |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
858 |
140 | 859 const gchar *editor_get_error_str(gint flags) |
860 { | |
861 if (flags & EDITOR_ERROR_EMPTY) return _("Editor template is empty."); | |
862 if (flags & EDITOR_ERROR_SYNTAX) return _("Editor template has incorrect syntax."); | |
863 if (flags & EDITOR_ERROR_INCOMPATIBLE) return _("Editor template uses incompatible macros."); | |
864 if (flags & EDITOR_ERROR_NO_FILE) return _("Can't find matching file type."); | |
865 if (flags & EDITOR_ERROR_CANT_EXEC) return _("Can't execute external editor."); | |
866 if (flags & EDITOR_ERROR_STATUS) return _("External editor returned error status."); | |
867 if (flags & EDITOR_ERROR_SKIPPED) return _("File was skipped."); | |
868 return _("Unknown error."); | |
869 } |