Mercurial > geeqie
comparison src/editors.c @ 1272:e0e12512cde2
read external editors from .desktop files
author | nadvornik |
---|---|
date | Sun, 01 Feb 2009 12:48:14 +0000 |
parents | 878718372aca |
children | 8fcdfacce91d |
comparison
equal
deleted
inserted
replaced
1271:4fcdbb497df3 | 1272:e0e12512cde2 |
---|---|
42 | 42 |
43 typedef struct _EditorData EditorData; | 43 typedef struct _EditorData EditorData; |
44 struct _EditorData { | 44 struct _EditorData { |
45 gint flags; | 45 gint flags; |
46 GPid pid; | 46 GPid pid; |
47 gchar *command_template; | |
48 GList *list; | 47 GList *list; |
49 gint count; | 48 gint count; |
50 gint total; | 49 gint total; |
51 gboolean stopping; | 50 gboolean stopping; |
52 EditorVerboseData *vd; | 51 EditorVerboseData *vd; |
53 EditorCallback callback; | 52 EditorCallback callback; |
54 gpointer data; | 53 gpointer data; |
54 const EditorDescription *editor; | |
55 }; | 55 }; |
56 | 56 |
57 | |
58 static Editor editor_slot_defaults[GQ_EDITOR_SLOTS] = { | |
59 { N_("The Gimp"), "gimp-remote %{%raw;*}f" }, | |
60 { N_("XV"), "xv %f" }, | |
61 { N_("Xpaint"), "xpaint %f" }, | |
62 { N_("UFraw"), "ufraw %{%raw}p" }, | |
63 { N_("Symlink"), "ln -s %p %d"}, | |
64 { NULL, NULL }, | |
65 { NULL, NULL }, | |
66 { NULL, NULL }, | |
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" }, | |
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" }, | |
69 /* special slots */ | |
70 #if 1 | |
71 /* for testing */ | |
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"), NULL }, | |
76 { N_("External New Folder command"), NULL }, | |
77 #else | |
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 }, | |
83 #endif | |
84 }; | |
85 | 57 |
86 static void editor_verbose_window_progress(EditorData *ed, const gchar *text); | 58 static void editor_verbose_window_progress(EditorData *ed, const gchar *text); |
87 static gint editor_command_next_start(EditorData *ed); | 59 static gint editor_command_next_start(EditorData *ed); |
88 static gint editor_command_next_finish(EditorData *ed, gint status); | 60 static gint editor_command_next_finish(EditorData *ed, gint status); |
89 static gint editor_command_done(EditorData *ed); | 61 static gint editor_command_done(EditorData *ed); |
62 static gint editor_command_parse(const EditorDescription *editor, GList *list, gchar **output); | |
90 | 63 |
91 /* | 64 /* |
92 *----------------------------------------------------------------------------- | 65 *----------------------------------------------------------------------------- |
93 * external editor routines | 66 * external editor routines |
94 *----------------------------------------------------------------------------- | 67 *----------------------------------------------------------------------------- |
95 */ | 68 */ |
96 | 69 |
97 void editor_set_name(gint n, gchar *name) | 70 GHashTable *editors = NULL; |
98 { | 71 |
99 if (n < 0 || n >= GQ_EDITOR_SLOTS) return; | 72 #ifdef G_KEY_FILE_DESKTOP_GROUP |
100 | 73 #define DESKTOP_GROUP G_KEY_FILE_DESKTOP_GROUP |
101 g_free(options->editor[n].name); | 74 #else |
102 | 75 #define DESKTOP_GROUP "Desktop Entry" |
103 options->editor[n].name = name ? utf8_validate_or_convert(name) : NULL; | 76 #endif |
104 } | 77 |
105 | 78 void editor_description_free(EditorDescription *editor) |
106 void editor_set_command(gint n, gchar *command) | 79 { |
107 { | 80 if (!editor) return; |
108 if (n < 0 || n >= GQ_EDITOR_SLOTS) return; | 81 |
109 | 82 g_free(editor->key); |
110 g_free(options->editor[n].command); | 83 g_free(editor->name); |
111 options->editor[n].command = command ? utf8_validate_or_convert(command) : NULL; | 84 g_free(editor->exec); |
112 } | 85 g_free(editor->menu_path); |
113 | 86 g_free(editor->hotkey); |
114 void editor_reset_defaults(void) | 87 string_list_free(editor->ext_list); |
115 { | 88 g_free(editor->icon); |
89 g_free(editor->file); | |
90 g_free(editor); | |
91 } | |
92 | |
93 static GList *editor_mime_types_to_extensions(gchar **mime_types) | |
94 { | |
95 /* FIXME: this should be rewritten to use the shared mime database, as soon as we switch to gio */ | |
96 | |
97 static const gchar *conv_table[][2] = { | |
98 {"application/x-ufraw", "%raw"}, | |
99 {"image/*", "*"}, | |
100 {"image/bmp", ".bmp"}, | |
101 {"image/gif", ".gif"}, | |
102 {"image/jpeg", ".jpeg;.jpg"}, | |
103 {"image/jpg", ".jpg;.jpeg"}, | |
104 {"image/pcx", ".pcx"}, | |
105 {"image/png", ".png"}, | |
106 {"image/svg", ".svg"}, | |
107 {"image/svg+xml", ".svg"}, | |
108 {"image/svg+xml-compressed", ".svg"}, | |
109 {"image/tiff", ".tiff;.tif"}, | |
110 {"image/x-bmp", ".bmp"}, | |
111 {"image/x-canon-crw", ".crw"}, | |
112 {"image/x-cr2", ".cr2"}, | |
113 {"image/x-dcraw", "%raw"}, | |
114 {"image/x-ico", ".ico"}, | |
115 {"image/x-mrw", ".mrw"}, | |
116 {"image/x-MS-bmp", ".bmp"}, | |
117 {"image/x-nef", ".nef"}, | |
118 {"image/x-orf", ".orf"}, | |
119 {"image/x-pcx", ".pcx"}, | |
120 {"image/xpm", ".xpm"}, | |
121 {"image/x-png", ".png"}, | |
122 {"image/x-portable-anymap", ".pam"}, | |
123 {"image/x-portable-bitmap", ".pbm"}, | |
124 {"image/x-portable-graymap", ".pgm"}, | |
125 {"image/x-portable-pixmap", ".ppm"}, | |
126 {"image/x-psd", ".psd"}, | |
127 {"image/x-raf", ".raf"}, | |
128 {"image/x-sgi", ".sgi"}, | |
129 {"image/x-tga", ".tga"}, | |
130 {"image/x-xbitmap", ".xbm"}, | |
131 {"image/x-xcf", ".xcf"}, | |
132 {"image/x-xpixmap", ".xpm"}, | |
133 {"image/x-x3f", ".x3f"}, | |
134 {NULL, NULL}}; | |
135 | |
136 gint i, j; | |
137 GList *list = NULL; | |
138 | |
139 for (i = 0; mime_types[i]; i++) | |
140 for (j = 0; conv_table[j][0]; j++) | |
141 if (strcmp(mime_types[i], conv_table[j][0]) == 0) | |
142 list = g_list_concat(list, filter_to_list(conv_table[j][1])); | |
143 | |
144 return list; | |
145 } | |
146 | |
147 static gboolean editor_read_desktop_file(const gchar *path) | |
148 { | |
149 GKeyFile *key_file; | |
150 EditorDescription *editor; | |
151 gchar *extensions; | |
152 const gchar *key = filename_from_path(path); | |
153 gchar **categories, **only_show_in, **not_show_in; | |
154 | |
155 if (g_hash_table_lookup(editors, key)) return FALSE; /* the file found earlier wins */ | |
156 | |
157 key_file = g_key_file_new(); | |
158 if (!g_key_file_load_from_file(key_file, path, 0, NULL)) | |
159 { | |
160 g_key_file_free(key_file); | |
161 return FALSE; | |
162 } | |
163 | |
164 editor = g_new0(EditorDescription, 1); | |
165 | |
166 editor->key = g_strdup(key); | |
167 editor->file = g_strdup(path); | |
168 | |
169 g_hash_table_insert(editors, editor->key, editor); | |
170 | |
171 if (g_key_file_get_boolean(key_file, DESKTOP_GROUP, "Hidden", NULL)) editor->hidden = TRUE; | |
172 | |
173 categories = g_key_file_get_string_list(key_file, DESKTOP_GROUP, "Categories", NULL, NULL); | |
174 if (categories) | |
175 { | |
176 gboolean found = FALSE; | |
177 gint i; | |
178 for (i = 0; categories[i]; i++) | |
179 /* IMHO "Graphics" is exactly the category that we are interested in, so this does not have to be configurable */ | |
180 if (strcmp(categories[i], "Graphics") == 0 || | |
181 strcmp(categories[i], "X-Geeqie") == 0) | |
182 { | |
183 found = TRUE; | |
184 break; | |
185 } | |
186 if (!found) editor->hidden = TRUE; | |
187 g_strfreev(categories); | |
188 } | |
189 else | |
190 { | |
191 editor->hidden = TRUE; | |
192 } | |
193 | |
194 only_show_in = g_key_file_get_string_list(key_file, DESKTOP_GROUP, "OnlyShowIn", NULL, NULL); | |
195 if (only_show_in) | |
196 { | |
197 gboolean found = FALSE; | |
198 gint i; | |
199 for (i = 0; only_show_in[i]; i++) | |
200 if (strcmp(only_show_in[i], "X-Geeqie") == 0) | |
201 { | |
202 found = TRUE; | |
203 break; | |
204 } | |
205 if (!found) editor->hidden = TRUE; | |
206 g_strfreev(only_show_in); | |
207 } | |
208 | |
209 not_show_in = g_key_file_get_string_list(key_file, DESKTOP_GROUP, "NotShowIn", NULL, NULL); | |
210 if (not_show_in) | |
211 { | |
212 gboolean found = FALSE; | |
213 gint i; | |
214 for (i = 0; not_show_in[i]; i++) | |
215 if (strcmp(not_show_in[i], "X-Geeqie") == 0) | |
216 { | |
217 found = TRUE; | |
218 break; | |
219 } | |
220 if (found) editor->hidden = TRUE; | |
221 g_strfreev(not_show_in); | |
222 } | |
223 | |
224 if (editor->hidden) | |
225 { | |
226 /* hidden editors will be deleted, no need to parse the rest */ | |
227 g_key_file_free(key_file); | |
228 return TRUE; | |
229 } | |
230 | |
231 editor->name = g_key_file_get_locale_string(key_file, DESKTOP_GROUP, "Name", NULL, NULL); | |
232 editor->icon = g_key_file_get_string(key_file, DESKTOP_GROUP, "Icon", NULL); | |
233 | |
234 editor->exec = g_key_file_get_string(key_file, DESKTOP_GROUP, "Exec", NULL); | |
235 | |
236 /* we take only editors that accept parameters, FIXME: the test can be improved */ | |
237 if (!strchr(editor->exec, '%')) editor->hidden = TRUE; | |
238 | |
239 editor->menu_path = g_key_file_get_string(key_file, DESKTOP_GROUP, "X-Geeqie-Menu-Path", NULL); | |
240 if (!editor->menu_path) editor->menu_path = g_strdup("EditMenu/ExternalMenu"); | |
241 | |
242 editor->hotkey = g_key_file_get_string(key_file, DESKTOP_GROUP, "X-Geeqie-Hotkey", NULL); | |
243 | |
244 extensions = g_key_file_get_string(key_file, DESKTOP_GROUP, "X-Geeqie-File-Extensions", NULL); | |
245 if (extensions) | |
246 editor->ext_list = filter_to_list(extensions); | |
247 else | |
248 { | |
249 gchar **mime_types = g_key_file_get_string_list(key_file, DESKTOP_GROUP, "MimeType", NULL, NULL); | |
250 if (mime_types) | |
251 { | |
252 editor->ext_list = editor_mime_types_to_extensions(mime_types); | |
253 g_strfreev(mime_types); | |
254 if (!editor->ext_list) editor->hidden = TRUE; | |
255 } | |
256 | |
257 } | |
258 | |
259 | |
260 if (g_key_file_get_boolean(key_file, DESKTOP_GROUP, "X-Geeqie-Keep-Fullscreen", NULL)) editor->flags |= EDITOR_KEEP_FS; | |
261 if (g_key_file_get_boolean(key_file, DESKTOP_GROUP, "X-Geeqie-Verbose", NULL)) editor->flags |= EDITOR_VERBOSE; | |
262 if (g_key_file_get_boolean(key_file, DESKTOP_GROUP, "X-Geeqie-Verbose-Multi", NULL)) editor->flags |= EDITOR_VERBOSE_MULTI; | |
263 if (g_key_file_get_boolean(key_file, DESKTOP_GROUP, "X-Geeqie-Filter", NULL)) editor->flags |= EDITOR_DEST; | |
264 if (g_key_file_get_boolean(key_file, DESKTOP_GROUP, "Terminal", NULL)) editor->flags |= EDITOR_TERMINAL; | |
265 | |
266 | |
267 editor->flags |= editor_command_parse(editor, NULL, NULL); | |
268 g_key_file_free(key_file); | |
269 | |
270 return TRUE; | |
271 } | |
272 | |
273 static gboolean editor_remove_desktop_file_cb(gpointer key, gpointer value, gpointer user_data) | |
274 { | |
275 EditorDescription *editor = value; | |
276 return editor->hidden; | |
277 } | |
278 | |
279 static void editor_read_desktop_dir(const gchar *path) | |
280 { | |
281 DIR *dp; | |
282 struct dirent *dir; | |
283 gchar *pathl; | |
284 | |
285 pathl = path_from_utf8(path); | |
286 dp = opendir(pathl); | |
287 g_free(pathl); | |
288 if (!dp) | |
289 { | |
290 /* dir not found */ | |
291 return; | |
292 } | |
293 while ((dir = readdir(dp)) != NULL) | |
294 { | |
295 gchar *namel = dir->d_name; | |
296 size_t len = strlen(namel); | |
297 | |
298 if (len > 8 && strncasecmp(namel + len - 8, ".desktop", 8) == 0) | |
299 { | |
300 gchar *name = path_to_utf8(namel); | |
301 gchar *dpath = g_build_filename(path, name, NULL); | |
302 editor_read_desktop_file(dpath); | |
303 g_free(dpath); | |
304 g_free(name); | |
305 } | |
306 } | |
307 closedir(dp); | |
308 } | |
309 | |
310 void editor_load_descriptions(void) | |
311 { | |
312 gchar *path; | |
313 gchar *xdg_data_dirs; | |
314 gchar *all_dirs; | |
315 gchar **split_dirs; | |
116 gint i; | 316 gint i; |
117 | 317 |
118 for (i = 0; i < GQ_EDITOR_SLOTS; i++) | 318 if (!editors) |
119 { | 319 { |
120 editor_set_name(i, _(editor_slot_defaults[i].name)); | 320 editors = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)editor_description_free); |
121 editor_set_command(i, _(editor_slot_defaults[i].command)); | 321 } |
122 } | 322 |
123 } | 323 xdg_data_dirs = getenv("XDG_DATA_DIRS"); |
324 if (xdg_data_dirs && xdg_data_dirs[0]) | |
325 xdg_data_dirs = path_to_utf8(xdg_data_dirs); | |
326 else | |
327 xdg_data_dirs = g_strdup("/usr/share"); | |
328 | |
329 all_dirs = g_strconcat(get_rc_dir(), ":", GQ_APP_DIR, ":", xdg_data_dirs, NULL); | |
330 | |
331 g_free(xdg_data_dirs); | |
332 | |
333 split_dirs = g_strsplit(all_dirs, ":", 0); | |
334 | |
335 g_free(all_dirs); | |
336 | |
337 for (i = 0; split_dirs[i]; i++) | |
338 { | |
339 path = g_build_filename(split_dirs[i], "applications", NULL); | |
340 editor_read_desktop_dir(path); | |
341 g_free(path); | |
342 } | |
343 | |
344 g_strfreev(split_dirs); | |
345 | |
346 g_hash_table_foreach_remove(editors, editor_remove_desktop_file_cb, NULL); | |
347 } | |
348 | |
349 static void editor_list_add_cb(gpointer key, gpointer value, gpointer data) | |
350 { | |
351 GList **listp = data; | |
352 EditorDescription *editor = value; | |
353 | |
354 /* do not show the special commands in any list, they are called explicitelly */ | |
355 if (strcmp(editor->key, CMD_COPY) == 0 || | |
356 strcmp(editor->key, CMD_MOVE) == 0 || | |
357 strcmp(editor->key, CMD_RENAME) == 0 || | |
358 strcmp(editor->key, CMD_DELETE) == 0 || | |
359 strcmp(editor->key, CMD_FOLDER) == 0) return; | |
360 | |
361 *listp = g_list_prepend(*listp, editor); | |
362 } | |
363 | |
364 GList *editor_list_get(void) | |
365 { | |
366 GList *editors_list = NULL; | |
367 g_hash_table_foreach(editors, editor_list_add_cb, &editors_list); | |
368 return editors_list; | |
369 } | |
370 | |
371 /* ------------------------------ */ | |
372 | |
124 | 373 |
125 static void editor_verbose_data_free(EditorData *ed) | 374 static void editor_verbose_data_free(EditorData *ed) |
126 { | 375 { |
127 if (!ed->vd) return; | 376 if (!ed->vd) return; |
128 g_free(ed->vd); | 377 g_free(ed->vd); |
130 } | 379 } |
131 | 380 |
132 static void editor_data_free(EditorData *ed) | 381 static void editor_data_free(EditorData *ed) |
133 { | 382 { |
134 editor_verbose_data_free(ed); | 383 editor_verbose_data_free(ed); |
135 g_free(ed->command_template); | |
136 g_free(ed); | 384 g_free(ed); |
137 } | 385 } |
138 | 386 |
139 static void editor_verbose_window_close(GenericDialog *gd, gpointer data) | 387 static void editor_verbose_window_close(GenericDialog *gd, gpointer data) |
140 { | 388 { |
279 return TRUE; | 527 return TRUE; |
280 } | 528 } |
281 | 529 |
282 typedef enum { | 530 typedef enum { |
283 PATH_FILE, | 531 PATH_FILE, |
532 PATH_FILE_URL, | |
284 PATH_DEST | 533 PATH_DEST |
285 } PathType; | 534 } PathType; |
286 | 535 |
287 | 536 |
288 static gchar *editor_command_path_parse(const FileData *fd, PathType type, const gchar *extensions) | 537 static gchar *editor_command_path_parse(const FileData *fd, PathType type, const EditorDescription *editor) |
289 { | 538 { |
290 GString *string; | 539 GString *string; |
291 gchar *pathl; | 540 gchar *pathl; |
292 const gchar *p = NULL; | 541 const gchar *p = NULL; |
293 | 542 |
294 string = g_string_new(""); | 543 string = g_string_new(""); |
295 | 544 |
296 if (type == PATH_FILE) | 545 if (type == PATH_FILE || type == PATH_FILE_URL) |
297 { | 546 { |
298 GList *ext_list = filter_to_list(extensions); | 547 GList *work = editor->ext_list; |
299 GList *work = ext_list; | |
300 | 548 |
301 if (!work) | 549 if (!work) |
302 p = fd->path; | 550 p = fd->path; |
303 else | 551 else |
304 { | 552 { |
327 break; | 575 break; |
328 } | 576 } |
329 } | 577 } |
330 if (p) break; | 578 if (p) break; |
331 } | 579 } |
332 string_list_free(ext_list); | |
333 if (!p) return NULL; | 580 if (!p) return NULL; |
334 } | 581 } |
335 } | 582 } |
336 else if (type == PATH_DEST) | 583 else if (type == PATH_DEST) |
337 { | 584 { |
352 } | 599 } |
353 string = g_string_append_c(string, *p); | 600 string = g_string_append_c(string, *p); |
354 p++; | 601 p++; |
355 } | 602 } |
356 | 603 |
604 if (type == PATH_FILE_URL) g_string_prepend(string, "file://"); | |
357 pathl = path_from_utf8(string->str); | 605 pathl = path_from_utf8(string->str); |
358 g_string_free(string, TRUE); | 606 g_string_free(string, TRUE); |
359 | 607 |
360 return pathl; | 608 return pathl; |
361 } | 609 } |
362 | 610 |
363 | 611 |
364 /* | 612 static gint editor_command_parse(const EditorDescription *editor, GList *list, gchar **output) |
365 * The supported macros for editor commands: | |
366 * | |
367 * %f first occurence replaced by quoted sequence of filenames, command is run once. | |
368 * only one occurence of this macro is supported. | |
369 * ([ls %f] results in [ls "file1" "file2" ... "lastfile"]) | |
370 * %p command is run for each filename in turn, each instance replaced with single filename. | |
371 * multiple occurences of this macro is supported for complex shell commands. | |
372 * This macro will BLOCK THE APPLICATION until it completes, since command is run once | |
373 * for every file in syncronous order. To avoid blocking add the %v macro, below. | |
374 * ([ls %p] results in [ls "file1"], [ls "file2"] ... [ls "lastfile"]) | |
375 * none if no macro is supplied, the result is equivalent to "command %f" | |
376 * ([ls] results in [ls "file1" "file2" ... "lastfile"]) | |
377 * | |
378 * Only one of the macros %f or %p may be used in a given commmand. | |
379 * | |
380 * %v must be the first two characters[1] in a command, causes a window to display | |
381 * showing the output of the command(s). | |
382 * %V same as %v except in the case of %p only displays a window for multiple files, | |
383 * operating on a single file is suppresses the output dialog. | |
384 * | |
385 * %w must be first two characters in a command, presence will disable full screen | |
386 * from exiting upon invocation. | |
387 * | |
388 * | |
389 * [1] Note: %v,%V may also be preceded by "%w". | |
390 */ | |
391 | |
392 | |
393 gint editor_command_parse(const gchar *template, GList *list, gchar **output) | |
394 { | 613 { |
395 gint flags = 0; | 614 gint flags = 0; |
396 const gchar *p = template; | 615 const gchar *p; |
397 GString *result = NULL; | 616 GString *result = NULL; |
398 gchar *extensions = NULL; | |
399 | 617 |
400 if (output) | 618 if (output) |
401 result = g_string_new(""); | 619 result = g_string_new(""); |
402 | 620 |
403 if (!template || template[0] == '\0') | 621 if (editor->exec[0] == '\0') |
404 { | 622 { |
405 flags |= EDITOR_ERROR_EMPTY; | 623 flags |= EDITOR_ERROR_EMPTY; |
406 goto err; | 624 goto err; |
407 } | 625 } |
408 | 626 |
627 p = editor->exec; | |
409 /* skip leading whitespaces if any */ | 628 /* skip leading whitespaces if any */ |
410 while (g_ascii_isspace(*p)) p++; | 629 while (g_ascii_isspace(*p)) p++; |
411 | 630 |
412 /* global flags */ | |
413 while (*p == '%') | |
414 { | |
415 switch (*++p) | |
416 { | |
417 case 'w': | |
418 flags |= EDITOR_KEEP_FS; | |
419 p++; | |
420 break; | |
421 case 'v': | |
422 flags |= EDITOR_VERBOSE; | |
423 p++; | |
424 break; | |
425 case 'V': | |
426 flags |= EDITOR_VERBOSE_MULTI; | |
427 p++; | |
428 break; | |
429 default: | |
430 flags |= EDITOR_ERROR_SYNTAX; | |
431 goto err; | |
432 } | |
433 } | |
434 | |
435 /* skip whitespaces if any */ | |
436 while (g_ascii_isspace(*p)) p++; | |
437 | |
438 /* command */ | 631 /* command */ |
439 | 632 |
440 while (*p) | 633 while (*p) |
441 { | 634 { |
442 if (*p != '%') | 635 if (*p != '%') |
443 { | 636 { |
444 if (output) result = g_string_append_c(result, *p); | 637 if (output) result = g_string_append_c(result, *p); |
445 } | 638 } |
446 else /* *p == '%' */ | 639 else /* *p == '%' */ |
447 { | 640 { |
448 extensions = NULL; | |
449 gchar *pathl = NULL; | 641 gchar *pathl = NULL; |
450 | 642 |
451 p++; | 643 p++; |
452 | |
453 /* for example "%f" or "%{.crw;.raw;.cr2}f" */ | |
454 if (*p == '{') | |
455 { | |
456 gchar *end; | |
457 | |
458 p++; | |
459 end = strchr(p, '}'); | |
460 if (!end) | |
461 { | |
462 flags |= EDITOR_ERROR_SYNTAX; | |
463 goto err; | |
464 } | |
465 | |
466 extensions = g_strndup(p, end - p); | |
467 p = end + 1; | |
468 } | |
469 | 644 |
470 switch (*p) | 645 switch (*p) |
471 { | 646 { |
472 case 'd': | 647 case 'f': /* single file */ |
473 flags |= EDITOR_DEST; | 648 case 'u': /* single url */ |
474 /* fall through */ | |
475 case 'p': | |
476 flags |= EDITOR_FOR_EACH; | 649 flags |= EDITOR_FOR_EACH; |
477 if (flags & EDITOR_SINGLE_COMMAND) | 650 if (flags & EDITOR_SINGLE_COMMAND) |
478 { | 651 { |
479 flags |= EDITOR_ERROR_INCOMPATIBLE; | 652 flags |= EDITOR_ERROR_INCOMPATIBLE; |
480 goto err; | 653 goto err; |
486 { | 659 { |
487 flags |= EDITOR_ERROR_NO_FILE; | 660 flags |= EDITOR_ERROR_NO_FILE; |
488 goto err; | 661 goto err; |
489 } | 662 } |
490 pathl = editor_command_path_parse((FileData *)list->data, | 663 pathl = editor_command_path_parse((FileData *)list->data, |
491 (flags & EDITOR_DEST) ? PATH_DEST : PATH_FILE, | 664 (*p == 'f') ? PATH_FILE : PATH_FILE_URL, |
492 extensions); | 665 editor); |
493 if (!pathl) | 666 if (!pathl) |
494 { | 667 { |
495 flags |= EDITOR_ERROR_NO_FILE; | 668 flags |= EDITOR_ERROR_NO_FILE; |
496 goto err; | 669 goto err; |
497 } | 670 } |
500 g_free(pathl); | 673 g_free(pathl); |
501 result = g_string_append_c(result, '"'); | 674 result = g_string_append_c(result, '"'); |
502 } | 675 } |
503 break; | 676 break; |
504 | 677 |
505 case 'f': | 678 case 'F': |
679 case 'U': | |
506 flags |= EDITOR_SINGLE_COMMAND; | 680 flags |= EDITOR_SINGLE_COMMAND; |
507 if (flags & (EDITOR_FOR_EACH | EDITOR_DEST)) | 681 if (flags & (EDITOR_FOR_EACH | EDITOR_DEST)) |
508 { | 682 { |
509 flags |= EDITOR_ERROR_INCOMPATIBLE; | 683 flags |= EDITOR_ERROR_INCOMPATIBLE; |
510 goto err; | 684 goto err; |
517 gboolean ok = FALSE; | 691 gboolean ok = FALSE; |
518 | 692 |
519 while (work) | 693 while (work) |
520 { | 694 { |
521 FileData *fd = work->data; | 695 FileData *fd = work->data; |
522 pathl = editor_command_path_parse(fd, PATH_FILE, extensions); | 696 pathl = editor_command_path_parse(fd, (*p == 'F') ? PATH_FILE : PATH_FILE_URL, editor); |
523 | 697 |
524 if (pathl) | 698 if (pathl) |
525 { | 699 { |
526 ok = TRUE; | 700 ok = TRUE; |
527 if (work != list) g_string_append_c(result, ' '); | 701 if (work != list) g_string_append_c(result, ' '); |
537 flags |= EDITOR_ERROR_NO_FILE; | 711 flags |= EDITOR_ERROR_NO_FILE; |
538 goto err; | 712 goto err; |
539 } | 713 } |
540 } | 714 } |
541 break; | 715 break; |
716 case 'i': | |
717 if (output) | |
718 { | |
719 result = g_string_append(result, editor->icon); | |
720 } | |
721 break; | |
722 case 'c': | |
723 if (output) | |
724 { | |
725 result = g_string_append(result, editor->name); | |
726 } | |
727 break; | |
728 case 'k': | |
729 if (output) | |
730 { | |
731 result = g_string_append(result, editor->file); | |
732 } | |
733 break; | |
542 case '%': | 734 case '%': |
543 /* %% = % escaping */ | 735 /* %% = % escaping */ |
544 if (output) result = g_string_append_c(result, *p); | 736 if (output) result = g_string_append_c(result, *p); |
737 break; | |
738 case 'd': | |
739 case 'D': | |
740 case 'n': | |
741 case 'N': | |
742 case 'v': | |
743 case 'm': | |
744 /* deprecated according to spec, ignore */ | |
545 break; | 745 break; |
546 default: | 746 default: |
547 flags |= EDITOR_ERROR_SYNTAX; | 747 flags |= EDITOR_ERROR_SYNTAX; |
548 goto err; | 748 goto err; |
549 } | 749 } |
550 if (extensions) g_free(extensions); | |
551 extensions = NULL; | |
552 } | 750 } |
553 p++; | 751 p++; |
554 } | 752 } |
555 | 753 |
556 if (output) *output = g_string_free(result, FALSE); | 754 if (output) *output = g_string_free(result, FALSE); |
561 if (output) | 759 if (output) |
562 { | 760 { |
563 g_string_free(result, TRUE); | 761 g_string_free(result, TRUE); |
564 *output = NULL; | 762 *output = NULL; |
565 } | 763 } |
566 if (extensions) g_free(extensions); | |
567 return flags; | 764 return flags; |
568 } | 765 } |
766 | |
569 | 767 |
570 static void editor_child_exit_cb (GPid pid, gint status, gpointer data) | 768 static void editor_child_exit_cb (GPid pid, gint status, gpointer data) |
571 { | 769 { |
572 EditorData *ed = data; | 770 EditorData *ed = data; |
573 g_spawn_close_pid(pid); | 771 g_spawn_close_pid(pid); |
575 | 773 |
576 editor_command_next_finish(ed, status); | 774 editor_command_next_finish(ed, status); |
577 } | 775 } |
578 | 776 |
579 | 777 |
580 static gint editor_command_one(const gchar *template, GList *list, EditorData *ed) | 778 static gint editor_command_one(const EditorDescription *editor, GList *list, EditorData *ed) |
581 { | 779 { |
582 gchar *command; | 780 gchar *command; |
583 FileData *fd = list->data; | 781 FileData *fd = list->data; |
584 GPid pid; | 782 GPid pid; |
585 gint standard_output; | 783 gint standard_output; |
586 gint standard_error; | 784 gint standard_error; |
587 gboolean ok; | 785 gboolean ok; |
588 | 786 |
589 ed->pid = -1; | 787 ed->pid = -1; |
590 ed->flags = editor_command_parse(template, list, &command); | 788 ed->flags = editor->flags | editor_command_parse(editor, list, &command); |
591 | 789 |
592 ok = !(ed->flags & EDITOR_ERROR_MASK); | 790 ok = !(ed->flags & EDITOR_ERROR_MASK); |
593 | 791 |
594 if (ok) | 792 if (ok) |
595 { | 793 { |
615 args[n++] = options->shell.path; | 813 args[n++] = options->shell.path; |
616 if (options->shell.options && *options->shell.options) | 814 if (options->shell.options && *options->shell.options) |
617 args[n++] = options->shell.options; | 815 args[n++] = options->shell.options; |
618 args[n++] = command; | 816 args[n++] = command; |
619 args[n] = NULL; | 817 args[n] = NULL; |
818 | |
819 if ((ed->flags & EDITOR_DEST) && fd->change && fd->change->dest) /* FIXME: error handling */ | |
820 { | |
821 setenv("GEEQIE_DESTINATION", fd->change->dest, TRUE); | |
822 } | |
823 else | |
824 { | |
825 unsetenv("GEEQIE_DESTINATION"); | |
826 } | |
620 | 827 |
621 ok = g_spawn_async_with_pipes(working_directory, args, NULL, | 828 ok = g_spawn_async_with_pipes(working_directory, args, NULL, |
622 G_SPAWN_DO_NOT_REAP_CHILD, /* GSpawnFlags */ | 829 G_SPAWN_DO_NOT_REAP_CHILD, /* GSpawnFlags */ |
623 NULL, NULL, | 830 NULL, NULL, |
624 &pid, | 831 &pid, |
642 { | 849 { |
643 if (!ok) | 850 if (!ok) |
644 { | 851 { |
645 gchar *buf; | 852 gchar *buf; |
646 | 853 |
647 buf = g_strdup_printf(_("Failed to run command:\n%s\n"), template); | 854 buf = g_strdup_printf(_("Failed to run command:\n%s\n"), editor->file); |
648 editor_verbose_window_fill(ed->vd, buf, strlen(buf)); | 855 editor_verbose_window_fill(ed->vd, buf, strlen(buf)); |
649 g_free(buf); | 856 g_free(buf); |
650 | 857 |
651 } | 858 } |
652 else | 859 else |
692 { | 899 { |
693 editor_verbose_window_progress(ed, (ed->flags & EDITOR_FOR_EACH) ? fd->path : _("running...")); | 900 editor_verbose_window_progress(ed, (ed->flags & EDITOR_FOR_EACH) ? fd->path : _("running...")); |
694 } | 901 } |
695 ed->count++; | 902 ed->count++; |
696 | 903 |
697 error = editor_command_one(ed->command_template, ed->list, ed); | 904 error = editor_command_one(ed->editor, ed->list, ed); |
698 if (!error && ed->vd) | 905 if (!error && ed->vd) |
699 { | 906 { |
700 gtk_widget_set_sensitive(ed->vd->button_stop, (ed->list != NULL) ); | 907 gtk_widget_set_sensitive(ed->vd->button_stop, (ed->list != NULL) ); |
701 if (ed->flags & EDITOR_FOR_EACH) | 908 if (ed->flags & EDITOR_FOR_EACH) |
702 { | 909 { |
799 void editor_skip(gpointer ed) | 1006 void editor_skip(gpointer ed) |
800 { | 1007 { |
801 editor_command_done(ed); | 1008 editor_command_done(ed); |
802 } | 1009 } |
803 | 1010 |
804 static gint editor_command_start(const gchar *template, const gchar *text, GList *list, EditorCallback cb, gpointer data) | 1011 static gint editor_command_start(const EditorDescription *editor, const gchar *text, GList *list, EditorCallback cb, gpointer data) |
805 { | 1012 { |
806 EditorData *ed; | 1013 EditorData *ed; |
807 gint flags = editor_command_parse(template, NULL, NULL); | 1014 gint flags = editor->flags; |
808 | 1015 |
809 if (flags & EDITOR_ERROR_MASK) return flags & EDITOR_ERROR_MASK; | 1016 if (flags & EDITOR_ERROR_MASK) return flags & EDITOR_ERROR_MASK; |
810 | 1017 |
811 ed = g_new0(EditorData, 1); | 1018 ed = g_new0(EditorData, 1); |
812 ed->list = filelist_copy(list); | 1019 ed->list = filelist_copy(list); |
813 ed->flags = flags; | 1020 ed->flags = flags; |
814 ed->command_template = g_strdup(template); | 1021 ed->editor = editor; |
815 ed->total = (flags & EDITOR_SINGLE_COMMAND) ? 1 : g_list_length(list); | 1022 ed->total = (flags & EDITOR_SINGLE_COMMAND) ? 1 : g_list_length(list); |
816 ed->count = 0; | 1023 ed->count = 0; |
817 ed->stopping = FALSE; | 1024 ed->stopping = FALSE; |
818 ed->callback = cb; | 1025 ed->callback = cb; |
819 ed->data = data; | 1026 ed->data = data; |
827 editor_command_next_start(ed); | 1034 editor_command_next_start(ed); |
828 /* errors from editor_command_next_start will be handled via callback */ | 1035 /* errors from editor_command_next_start will be handled via callback */ |
829 return flags & EDITOR_ERROR_MASK; | 1036 return flags & EDITOR_ERROR_MASK; |
830 } | 1037 } |
831 | 1038 |
832 gboolean is_valid_editor_command(gint n) | 1039 gboolean is_valid_editor_command(const gchar *key) |
833 { | 1040 { |
834 return (n >= 0 && n < GQ_EDITOR_SLOTS | 1041 if (!key) return FALSE; |
835 && options->editor[n].command | 1042 return g_hash_table_lookup(editors, key) != NULL; |
836 && strlen(options->editor[n].command) > 0); | 1043 } |
837 } | 1044 |
838 | 1045 gint start_editor_from_filelist_full(const gchar *key, GList *list, EditorCallback cb, gpointer data) |
839 gint start_editor_from_filelist_full(gint n, GList *list, EditorCallback cb, gpointer data) | 1046 { |
840 { | |
841 gchar *command; | |
842 gint error; | 1047 gint error; |
1048 EditorDescription *editor; | |
1049 if (!key) return FALSE; | |
1050 | |
1051 editor = g_hash_table_lookup(editors, key); | |
843 | 1052 |
844 if (!list) return FALSE; | 1053 if (!list) return FALSE; |
845 if (!is_valid_editor_command(n)) return FALSE; | 1054 if (!editor) return FALSE; |
846 | 1055 |
847 command = g_locale_from_utf8(options->editor[n].command, -1, NULL, NULL, NULL); | 1056 error = editor_command_start(editor, editor->name, list, cb, data); |
848 error = editor_command_start(command, options->editor[n].name, list, cb, data); | 1057 |
849 g_free(command); | 1058 if (error & EDITOR_ERROR_MASK) |
850 | 1059 { |
851 if (n < GQ_EDITOR_GENERIC_SLOTS && (error & EDITOR_ERROR_MASK)) | 1060 gchar *text = g_strdup_printf(_("%s\n\"%s\""), editor_get_error_str(error), editor->file); |
852 { | |
853 gchar *text = g_strdup_printf(_("%s\n#%d \"%s\":\n%s"), editor_get_error_str(error), n+1, | |
854 options->editor[n].name, options->editor[n].command); | |
855 | 1061 |
856 file_util_warning_dialog(_("Invalid editor command"), text, GTK_STOCK_DIALOG_ERROR, NULL); | 1062 file_util_warning_dialog(_("Invalid editor command"), text, GTK_STOCK_DIALOG_ERROR, NULL); |
857 g_free(text); | 1063 g_free(text); |
858 } | 1064 } |
859 | 1065 |
860 return error; | 1066 return error; |
861 } | 1067 } |
862 | 1068 |
863 gint start_editor_from_filelist(gint n, GList *list) | 1069 gint start_editor_from_filelist(const gchar *key, GList *list) |
864 { | 1070 { |
865 return start_editor_from_filelist_full(n, list, NULL, NULL); | 1071 return start_editor_from_filelist_full(key, list, NULL, NULL); |
866 } | 1072 } |
867 | 1073 |
868 gint start_editor_from_file_full(gint n, FileData *fd, EditorCallback cb, gpointer data) | 1074 gint start_editor_from_file_full(const gchar *key, FileData *fd, EditorCallback cb, gpointer data) |
869 { | 1075 { |
870 GList *list; | 1076 GList *list; |
871 gint error; | 1077 gint error; |
872 | 1078 |
873 if (!fd) return FALSE; | 1079 if (!fd) return FALSE; |
874 | 1080 |
875 list = g_list_append(NULL, fd); | 1081 list = g_list_append(NULL, fd); |
876 error = start_editor_from_filelist_full(n, list, cb, data); | 1082 error = start_editor_from_filelist_full(key, list, cb, data); |
877 g_list_free(list); | 1083 g_list_free(list); |
878 return error; | 1084 return error; |
879 } | 1085 } |
880 | 1086 |
881 gint start_editor_from_file(gint n, FileData *fd) | 1087 gint start_editor_from_file(const gchar *key, FileData *fd) |
882 { | 1088 { |
883 return start_editor_from_file_full(n, fd, NULL, NULL); | 1089 return start_editor_from_file_full(key, fd, NULL, NULL); |
884 } | 1090 } |
885 | 1091 |
886 gint editor_window_flag_set(gint n) | 1092 gint editor_window_flag_set(const gchar *key) |
887 { | 1093 { |
888 if (!is_valid_editor_command(n)) return TRUE; | 1094 EditorDescription *editor; |
889 | 1095 if (!key) return TRUE; |
890 return (editor_command_parse(options->editor[n].command, NULL, NULL) & EDITOR_KEEP_FS); | 1096 |
891 } | 1097 editor = g_hash_table_lookup(editors, key); |
892 | 1098 if (!editor) return TRUE; |
893 gint editor_is_filter(gint n) | 1099 |
894 { | 1100 return (editor->flags & EDITOR_KEEP_FS); |
895 if (!is_valid_editor_command(n)) return FALSE; | 1101 } |
896 | 1102 |
897 return (editor_command_parse(options->editor[n].command, NULL, NULL) & EDITOR_DEST); | 1103 gint editor_is_filter(const gchar *key) |
1104 { | |
1105 EditorDescription *editor; | |
1106 if (!key) return TRUE; | |
1107 | |
1108 editor = g_hash_table_lookup(editors, key); | |
1109 if (!editor) return TRUE; | |
1110 | |
1111 return (editor->flags & EDITOR_DEST); | |
898 } | 1112 } |
899 | 1113 |
900 const gchar *editor_get_error_str(gint flags) | 1114 const gchar *editor_get_error_str(gint flags) |
901 { | 1115 { |
902 if (flags & EDITOR_ERROR_EMPTY) return _("Editor template is empty."); | 1116 if (flags & EDITOR_ERROR_EMPTY) return _("Editor template is empty."); |
907 if (flags & EDITOR_ERROR_STATUS) return _("External editor returned error status."); | 1121 if (flags & EDITOR_ERROR_STATUS) return _("External editor returned error status."); |
908 if (flags & EDITOR_ERROR_SKIPPED) return _("File was skipped."); | 1122 if (flags & EDITOR_ERROR_SKIPPED) return _("File was skipped."); |
909 return _("Unknown error."); | 1123 return _("Unknown error."); |
910 } | 1124 } |
911 | 1125 |
912 const gchar *editor_get_name(gint n) | 1126 const gchar *editor_get_name(const gchar *key) |
913 { | 1127 { |
914 if (!is_valid_editor_command(n)) return NULL; | 1128 EditorDescription *editor = g_hash_table_lookup(editors, key); |
915 | 1129 |
916 if (options->editor[n].name && strlen(options->editor[n].name) > 0) | 1130 if (!editor) return NULL; |
917 return options->editor[n].name; | 1131 |
918 | 1132 return editor->name; |
919 return _("(unknown)"); | |
920 } | 1133 } |
921 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ | 1134 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ |