Mercurial > geeqie
annotate src/editors.c @ 274:2710d14f6a28
Fix the "continuous display" of tooltips in the collection view
(before the tooltip delay occured once, then changing icon to icon never hide the
tooltip again, now the tip is displayed shortly after the cursor moved on the icon,
but disappears when moving cursor to another icon).
Display the full path to the file when Show filename text is on (before nothing
was displayed).
When Show filename text is off, behavior is unchanged, the (short) filename is
displayed.
author | zas_ |
---|---|
date | Tue, 08 Apr 2008 21:33:29 +0000 |
parents | 68b4c8d2653e |
children | 9995c5fb202a |
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 |
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] = { |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
59 N_("The Gimp"), "gimp-remote -n %{.cr2;.crw;.nef;.raw;*}f", |
9 | 60 N_("XV"), "xv %f", |
61 N_("Xpaint"), "xpaint %f", | |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
62 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
|
63 N_("Add XMP sidecar"), "%vFILE=%{.cr2;.crw;.nef;.raw}p;XMP=`echo \"$FILE\"|sed -e 's|\\.[^.]*$|.xmp|'`; exiftool -tagsfromfile \"$FILE\" \"$XMP\"", |
9 | 64 NULL, NULL, |
65 NULL, NULL, | |
66 NULL, NULL, | |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
67 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
|
68 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
|
69 /* special slots */ |
136 | 70 #if 1 |
71 /* for testing */ | |
261 | 72 N_("External Copy command"), "%vset -x;cp %p %d", |
73 N_("External Move command"), "%vset -x;mv %p %d", | |
74 N_("External Rename command"), "%vset -x;mv %p %d", | |
75 N_("External Delete command"), "%vset -x;rm %f", | |
76 N_("External New Folder command"), NULL | |
136 | 77 #else |
261 | 78 N_("External Copy command"), NULL, |
79 N_("External Move command"), NULL, | |
80 N_("External Rename command"), NULL, | |
81 N_("External Delete command"), NULL, | |
82 N_("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 | |
254
9faf34f047b1
Make the wmclass value unique among the code by defining
zas_
parents:
238
diff
changeset
|
159 vd->gd = file_util_gen_dlg(_("Edit command results"), GQ_WMCLASS, "editor_results", |
9 | 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 { | |
196 | 247 editor_verbose_window_fill(ed->vd, "Geeqie: 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; |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
276 const gchar *p = NULL; |
123
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 { | |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
282 GList *ext_list = filter_to_list(extensions); |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
283 GList *work = ext_list; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
284 |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
285 if (!work) |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
286 p = fd->path; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
287 else |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
288 { |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
289 while(work) |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
290 { |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
291 gchar *ext = work->data; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
292 work = work->next; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
293 |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
294 if (strcmp(ext, "*") == 0 || |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
295 strcasecmp(ext, fd->extension) == 0) |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
296 { |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
297 p = fd->path; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
298 break; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
299 } |
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 GList *work2 = fd->sidecar_files; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
302 |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
303 while(work2) |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
304 { |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
305 FileData *sfd = work2->data; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
306 work2 = work2->next; |
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 if (strcasecmp(ext, sfd->extension) == 0) |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
309 { |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
310 p = sfd->path; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
311 break; |
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 } |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
314 if (p) 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 string_list_free(ext_list); |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
317 if (!p) return NULL; |
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
318 } |
138 | 319 } |
140 | 320 else if (type == PATH_DEST) |
138 | 321 { |
322 if (fd->change && fd->change->dest) | |
323 p = fd->change->dest; | |
324 else | |
325 p = ""; | |
326 } | |
123
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
327 while (*p != '\0') |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
328 { |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
329 /* must escape \, ", `, and $ to avoid problems, |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
330 * 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
|
331 */ |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
332 if (strchr("\\\"`$", *p) != NULL) |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
333 { |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
334 string = g_string_append_c(string, '\\'); |
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 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
|
337 p++; |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
338 } |
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 pathl = path_from_utf8(string->str); |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
341 g_string_free(string, TRUE); |
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 return pathl; |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
344 } |
3602a4aa7c71
Sat Dec 2 20:15:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
60
diff
changeset
|
345 |
9 | 346 |
347 /* | |
348 * The supported macros for editor commands: | |
349 * | |
350 * %f first occurence replaced by quoted sequence of filenames, command is run once. | |
351 * only one occurence of this macro is supported. | |
352 * ([ls %f] results in [ls "file1" "file2" ... "lastfile"]) | |
353 * %p command is run for each filename in turn, each instance replaced with single filename. | |
354 * multiple occurences of this macro is supported for complex shell commands. | |
355 * This macro will BLOCK THE APPLICATION until it completes, since command is run once | |
356 * for every file in syncronous order. To avoid blocking add the %v macro, below. | |
357 * ([ls %p] results in [ls "file1"], [ls "file2"] ... [ls "lastfile"]) | |
358 * none if no macro is supplied, the result is equivalent to "command %f" | |
359 * ([ls] results in [ls "file1" "file2" ... "lastfile"]) | |
360 * | |
361 * Only one of the macros %f or %p may be used in a given commmand. | |
362 * | |
60
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
363 * %v must be the first two characters[1] in a command, causes a window to display |
9 | 364 * showing the output of the command(s). |
365 * %V same as %v except in the case of %p only displays a window for multiple files, | |
366 * 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
|
367 * |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
368 * %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
|
369 * from exiting upon invocation. |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
370 * |
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 * [1] Note: %v,%V may also be preceded by "%w". |
9 | 373 */ |
140 | 374 |
375 | |
376 gint editor_command_parse(const gchar *template, GList *list, gchar **output) | |
9 | 377 { |
140 | 378 gint flags = 0; |
379 const gchar *p = template; | |
380 GString *result = NULL; | |
381 gchar *extensions = NULL; | |
238 | 382 |
140 | 383 if (output) |
384 result = g_string_new(""); | |
385 | |
386 if (!template || template[0] == '\0') | |
387 { | |
388 flags |= EDITOR_ERROR_EMPTY; | |
389 goto err; | |
390 } | |
9 | 391 |
140 | 392 |
393 /* global flags */ | |
394 while (*p == '%') | |
395 { | |
396 switch (*++p) | |
397 { | |
398 case 'w': | |
399 flags |= EDITOR_KEEP_FS; | |
400 p++; | |
401 break; | |
402 case 'v': | |
403 flags |= EDITOR_VERBOSE; | |
404 p++; | |
405 break; | |
406 case 'V': | |
407 flags |= EDITOR_VERBOSE_MULTI; | |
408 p++; | |
409 break; | |
410 } | |
411 } | |
412 | |
413 /* command */ | |
414 | |
415 while (*p) | |
416 { | |
417 if (*p != '%') | |
418 { | |
419 if (output) result = g_string_append_c(result, *p); | |
420 } | |
421 else /* *p == '%' */ | |
422 { | |
423 extensions = NULL; | |
424 gchar *pathl = NULL; | |
9 | 425 |
140 | 426 p++; |
427 | |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
428 /* for example "%f" or "%{.crw;.raw;.cr2}f" */ |
140 | 429 if (*p == '{') |
430 { | |
431 p++; | |
432 gchar *end = strchr(p, '}'); | |
433 if (!end) | |
434 { | |
435 flags |= EDITOR_ERROR_SYNTAX; | |
436 goto err; | |
437 } | |
438 | |
439 extensions = g_strndup(p, end - p); | |
440 p = end + 1; | |
441 } | |
442 | |
443 switch (*p) | |
444 { | |
445 case 'd': | |
446 flags |= EDITOR_DEST; | |
447 case 'p': | |
448 flags |= EDITOR_FOR_EACH; | |
449 if (flags & EDITOR_SINGLE_COMMAND) | |
450 { | |
451 flags |= EDITOR_ERROR_INCOMPATIBLE; | |
452 goto err; | |
453 } | |
454 if (output) | |
455 { | |
456 /* use the first file from the list */ | |
457 if (!list || !list->data) | |
458 { | |
459 flags |= EDITOR_ERROR_NO_FILE; | |
460 goto err; | |
461 } | |
462 pathl = editor_command_path_parse((FileData *)list->data, (*p == 'd') ? PATH_DEST : PATH_FILE, extensions); | |
463 if (!pathl) | |
464 { | |
465 flags |= EDITOR_ERROR_NO_FILE; | |
466 goto err; | |
467 } | |
468 result = g_string_append_c(result, '"'); | |
469 result = g_string_append(result, pathl); | |
470 g_free(pathl); | |
471 result = g_string_append_c(result, '"'); | |
472 } | |
473 break; | |
60
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
474 |
140 | 475 case 'f': |
476 flags |= EDITOR_SINGLE_COMMAND; | |
477 if (flags & (EDITOR_FOR_EACH | EDITOR_DEST)) | |
478 { | |
479 flags |= EDITOR_ERROR_INCOMPATIBLE; | |
480 goto err; | |
481 } | |
482 | |
483 if (output) | |
484 { | |
485 /* use whole list */ | |
486 GList *work = list; | |
487 gboolean ok = FALSE; | |
488 while (work) | |
489 { | |
490 FileData *fd = work->data; | |
491 pathl = editor_command_path_parse(fd, PATH_FILE, extensions); | |
492 | |
493 if (pathl) | |
494 { | |
495 ok = TRUE; | |
496 if (work != list) g_string_append_c(result, ' '); | |
497 result = g_string_append_c(result, '"'); | |
498 result = g_string_append(result, pathl); | |
499 g_free(pathl); | |
500 result = g_string_append_c(result, '"'); | |
501 } | |
502 work = work->next; | |
503 } | |
504 if (!ok) | |
505 { | |
506 flags |= EDITOR_ERROR_NO_FILE; | |
507 goto err; | |
508 } | |
509 } | |
510 break; | |
511 default: | |
512 flags |= EDITOR_ERROR_SYNTAX; | |
513 goto err; | |
514 } | |
515 if (extensions) g_free(extensions); | |
516 extensions = NULL; | |
517 } | |
518 p++; | |
9 | 519 } |
520 | |
140 | 521 if (output) *output = g_string_free(result, FALSE); |
522 return flags; | |
523 | |
524 | |
525 err: | |
526 if (output) | |
9 | 527 { |
140 | 528 g_string_free(result, TRUE); |
529 *output = NULL; | |
530 } | |
531 if (extensions) g_free(extensions); | |
532 return flags; | |
533 } | |
534 | |
535 static void editor_child_exit_cb (GPid pid, gint status, gpointer data) | |
536 { | |
537 EditorData *ed = data; | |
538 g_spawn_close_pid(pid); | |
539 ed->pid = -1; | |
540 | |
541 editor_command_next_finish(ed, status); | |
542 } | |
543 | |
544 | |
545 static gint editor_command_one(const gchar *template, GList *list, EditorData *ed) | |
546 { | |
547 gchar *command; | |
548 gchar *working_directory; | |
549 FileData *fd = list->data; | |
550 gchar *args[4]; | |
551 GPid pid; | |
552 gint standard_output; | |
553 gint standard_error; | |
554 gboolean ok; | |
555 | |
556 | |
557 ed->pid = -1; | |
558 | |
559 working_directory = remove_level_from_path(fd->path); | |
560 | |
561 ed->flags = editor_command_parse(template, list, &command); | |
562 | |
563 ok = !(ed->flags & EDITOR_ERROR_MASK); | |
564 | |
565 | |
566 args[0] = COMMAND_SHELL; | |
567 args[1] = COMMAND_OPT; | |
568 args[2] = command; | |
569 args[3] = NULL; | |
570 | |
571 if (ok) | |
572 { | |
573 ok = g_spawn_async_with_pipes(working_directory, args, NULL, | |
574 G_SPAWN_DO_NOT_REAP_CHILD, /* GSpawnFlags */ | |
575 NULL, NULL, | |
576 &pid, | |
577 NULL, | |
578 ed->vd ? &standard_output : NULL, | |
579 ed->vd ? &standard_error : NULL, | |
580 NULL); | |
581 | |
582 if (!ok) ed->flags |= EDITOR_ERROR_CANT_EXEC; | |
583 } | |
584 | |
585 if (ok) | |
586 { | |
587 g_child_watch_add(pid, editor_child_exit_cb, ed); | |
588 ed->pid = pid; | |
589 } | |
590 | |
591 | |
592 if (ed->vd) | |
593 { | |
594 | |
595 if (!ok) | |
9 | 596 { |
140 | 597 gchar *buf; |
598 | |
147
b2266996fa83
added possibility to specify prefered file type for external commands
nadvornik
parents:
140
diff
changeset
|
599 buf = g_strdup_printf(_("Failed to run command:\n%s\n"), template); |
140 | 600 editor_verbose_window_fill(ed->vd, buf, strlen(buf)); |
601 g_free(buf); | |
602 | |
603 } | |
604 else | |
605 { | |
606 | |
607 GIOChannel *channel_output; | |
608 GIOChannel *channel_error; | |
609 channel_output = g_io_channel_unix_new(standard_output); | |
610 g_io_channel_set_flags(channel_output, G_IO_FLAG_NONBLOCK, NULL); | |
611 | |
612 g_io_add_watch_full(channel_output, G_PRIORITY_HIGH, G_IO_IN | G_IO_ERR | G_IO_HUP, | |
613 editor_verbose_io_cb, ed, NULL); | |
614 g_io_channel_unref(channel_output); | |
615 | |
616 channel_error = g_io_channel_unix_new(standard_error); | |
617 g_io_channel_set_flags(channel_error, G_IO_FLAG_NONBLOCK, NULL); | |
618 | |
619 g_io_add_watch_full(channel_error, G_PRIORITY_HIGH, G_IO_IN | G_IO_ERR | G_IO_HUP, | |
620 editor_verbose_io_cb, ed, NULL); | |
621 g_io_channel_unref(channel_error); | |
622 } | |
623 } | |
624 | |
9 | 625 |
140 | 626 |
627 g_free(command); | |
628 g_free(working_directory); | |
629 | |
630 return ed->flags & EDITOR_ERROR_MASK; | |
631 } | |
632 | |
633 static gint editor_command_next_start(EditorData *ed) | |
634 { | |
635 | |
636 if (ed->vd) editor_verbose_window_fill(ed->vd, "\n", 1); | |
637 | |
638 if (ed->list && ed->count < ed->total) | |
639 { | |
640 FileData *fd; | |
641 gint error; | |
642 | |
643 fd = ed->list->data; | |
644 | |
645 if (ed->vd) | |
646 { | |
647 editor_verbose_window_progress(ed, (ed->flags & EDITOR_FOR_EACH) ? fd->path : _("running...")); | |
648 } | |
649 ed->count++; | |
650 | |
651 error = editor_command_one(ed->command_template, ed->list, ed); | |
652 if (!error && ed->vd) | |
653 { | |
654 gtk_widget_set_sensitive(ed->vd->button_stop, (ed->list != NULL) ); | |
655 if (ed->flags & EDITOR_FOR_EACH) | |
9 | 656 { |
140 | 657 editor_verbose_window_fill(ed->vd, fd->path, strlen(fd->path)); |
658 editor_verbose_window_fill(ed->vd, "\n", 1); | |
9 | 659 } |
660 } | |
140 | 661 |
662 if (!error) | |
663 return 0; | |
664 else | |
665 /* command was not started, call the finish immediately */ | |
666 return editor_command_next_finish(ed, 0); | |
667 } | |
668 | |
669 /* everything is done */ | |
237
404629011caa
Add missing return at the end of editor_command_next_start().
zas_
parents:
196
diff
changeset
|
670 return editor_command_done(ed); |
140 | 671 } |
672 | |
673 static gint editor_command_next_finish(EditorData *ed, gint status) | |
674 { | |
675 gint cont = ed->stopping ? EDITOR_CB_SKIP : EDITOR_CB_CONTINUE; | |
676 | |
677 if (status) | |
678 ed->flags |= EDITOR_ERROR_STATUS; | |
679 | |
680 if (ed->flags & EDITOR_FOR_EACH) | |
681 { | |
682 /* handle the first element from the list */ | |
683 GList *fd_element = ed->list; | |
684 ed->list = g_list_remove_link(ed->list, fd_element); | |
685 if (ed->callback) | |
686 cont = ed->callback(ed->list ? ed : NULL, ed->flags, fd_element, ed->data); | |
687 filelist_free(fd_element); | |
9 | 688 } |
689 else | |
690 { | |
140 | 691 /* handle whole list */ |
692 if (ed->callback) | |
693 cont = ed->callback(NULL, ed->flags, ed->list, ed->data); | |
694 filelist_free(ed->list); | |
695 ed->list = NULL; | |
696 } | |
9 | 697 |
140 | 698 if (cont == EDITOR_CB_SUSPEND) |
699 return ed->flags & EDITOR_ERROR_MASK; | |
700 else if (cont == EDITOR_CB_SKIP) | |
701 return editor_command_done(ed); | |
702 else | |
703 return editor_command_next_start(ed); | |
704 | |
705 } | |
9 | 706 |
140 | 707 static gint editor_command_done(EditorData *ed) |
708 { | |
709 gint flags; | |
710 const gchar *text; | |
9 | 711 |
140 | 712 if (ed->vd) |
713 { | |
714 if (ed->count == ed->total) | |
9 | 715 { |
140 | 716 text = _("done"); |
9 | 717 } |
718 else | |
719 { | |
140 | 720 text = _("stopped by user"); |
9 | 721 } |
140 | 722 editor_verbose_window_progress(ed, text); |
723 editor_verbose_window_enable_close(ed->vd); | |
724 } | |
725 | |
726 /* free the not-handled items */ | |
727 if (ed->list) | |
728 { | |
729 ed->flags |= EDITOR_ERROR_SKIPPED; | |
730 if (ed->callback) ed->callback(NULL, ed->flags, ed->list, ed->data); | |
731 filelist_free(ed->list); | |
732 ed->list = NULL; | |
733 } | |
9 | 734 |
140 | 735 ed->count = 0; |
736 | |
737 flags = ed->flags & EDITOR_ERROR_MASK; | |
738 | |
739 if (!ed->vd) editor_data_free(ed); | |
740 | |
741 return flags; | |
742 } | |
743 | |
744 void editor_resume(gpointer ed) | |
745 { | |
746 editor_command_next_start(ed); | |
747 } | |
748 void editor_skip(gpointer ed) | |
749 { | |
750 editor_command_done(ed); | |
9 | 751 } |
752 | |
140 | 753 static gint editor_command_start(const gchar *template, const gchar *text, GList *list, EditorCallback cb, gpointer data) |
754 { | |
755 EditorData *ed; | |
756 gint flags = editor_command_parse(template, NULL, NULL); | |
757 | |
758 if (flags & EDITOR_ERROR_MASK) return flags & EDITOR_ERROR_MASK; | |
759 | |
760 ed = g_new0(EditorData, 1); | |
761 ed->list = filelist_copy(list); | |
762 ed->flags = flags; | |
763 ed->command_template = g_strdup(template); | |
764 ed->total = (flags & EDITOR_SINGLE_COMMAND) ? 1 : g_list_length(list); | |
765 ed->count = 0; | |
766 ed->stopping = FALSE; | |
767 ed->callback = cb; | |
768 ed->data = data; | |
769 | |
770 if ((flags & EDITOR_VERBOSE_MULTI) && list && list->next) | |
771 flags |= EDITOR_VERBOSE; | |
772 | |
773 | |
774 if (flags & EDITOR_VERBOSE) | |
775 editor_verbose_window(ed, text); | |
776 | |
777 editor_command_next_start(ed); | |
778 /* errors from editor_command_next_start will be handled via callback */ | |
779 return flags & EDITOR_ERROR_MASK; | |
780 } | |
781 | |
782 gint start_editor_from_filelist_full(gint n, GList *list, EditorCallback cb, gpointer data) | |
9 | 783 { |
784 gchar *command; | |
140 | 785 gint error; |
9 | 786 |
787 if (n < 0 || n >= GQVIEW_EDITOR_SLOTS || !list || | |
788 !editor_command[n] || | |
134
9009856628f7
started implementation of external commands; external Delete should work
nadvornik
parents:
123
diff
changeset
|
789 strlen(editor_command[n]) == 0) return FALSE; |
9 | 790 |
791 command = g_locale_from_utf8(editor_command[n], -1, NULL, NULL, NULL); | |
140 | 792 error = editor_command_start(command, editor_name[n], list, cb, data); |
9 | 793 g_free(command); |
140 | 794 return error; |
9 | 795 } |
796 | |
140 | 797 gint start_editor_from_filelist(gint n, GList *list) |
798 { | |
799 return start_editor_from_filelist_full(n, list, NULL, NULL); | |
800 } | |
801 | |
802 | |
803 gint start_editor_from_file_full(gint n, FileData *fd, EditorCallback cb, gpointer data) | |
9 | 804 { |
805 GList *list; | |
140 | 806 gint error; |
9 | 807 |
138 | 808 if (!fd) return FALSE; |
9 | 809 |
138 | 810 list = g_list_append(NULL, fd); |
140 | 811 error = start_editor_from_filelist_full(n, list, cb, data); |
9 | 812 g_list_free(list); |
140 | 813 return error; |
9 | 814 } |
60
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
815 |
140 | 816 gint start_editor_from_file(gint n, FileData *fd) |
136 | 817 { |
140 | 818 return start_editor_from_file_full(n, fd, NULL, NULL); |
136 | 819 } |
820 | |
60
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
821 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
|
822 { |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
823 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
|
824 !editor_command[n] || |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
825 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
|
826 |
140 | 827 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
|
828 } |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
829 |
9c0c402b0ef3
Mon Jun 13 17:31:46 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
830 |
140 | 831 const gchar *editor_get_error_str(gint flags) |
832 { | |
833 if (flags & EDITOR_ERROR_EMPTY) return _("Editor template is empty."); | |
834 if (flags & EDITOR_ERROR_SYNTAX) return _("Editor template has incorrect syntax."); | |
835 if (flags & EDITOR_ERROR_INCOMPATIBLE) return _("Editor template uses incompatible macros."); | |
836 if (flags & EDITOR_ERROR_NO_FILE) return _("Can't find matching file type."); | |
837 if (flags & EDITOR_ERROR_CANT_EXEC) return _("Can't execute external editor."); | |
838 if (flags & EDITOR_ERROR_STATUS) return _("External editor returned error status."); | |
839 if (flags & EDITOR_ERROR_SKIPPED) return _("File was skipped."); | |
840 return _("Unknown error."); | |
841 } |