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