Fri Nov 24 21:37:01 2006 John Ellis <johne@verizon.net>
[geeqie.git] / src / main.c
1 /*
2  * GQview
3  * (C) 2006 John Ellis
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
15 #include "cache.h"
16 #include "collect.h"
17 #include "collect-io.h"
18 #include "dnd.h"
19 #include "editors.h"
20 #include "filelist.h"
21 #include "img-view.h"
22 #include "layout.h"
23 #include "layout_image.h"
24 #include "menu.h"
25 #include "pixbuf_util.h"
26 #include "preferences.h"
27 #include "rcfile.h"
28 #include "remote.h"
29 #include "similar.h"
30 #include "slideshow.h"
31 #include "utilops.h"
32 #include "ui_bookmark.h"
33 #include "ui_help.h"
34 #include "ui_fileops.h"
35 #include "ui_tabcomp.h"
36 #include "ui_utildlg.h"
37
38 #include <gdk/gdkkeysyms.h> /* for keyboard values */
39
40
41 #include <math.h>
42
43
44 static RemoteConnection *gqview_remote = NULL;
45 static CollectionData *gqview_command_collection = NULL;
46
47
48 /*
49  *-----------------------------------------------------------------------------
50  * misc (public)
51  *-----------------------------------------------------------------------------
52  */ 
53
54 void window_set_icon(GtkWidget *window, const gchar *icon, const gchar *file)
55 {
56         if (!icon && !file) icon = PIXBUF_INLINE_ICON;
57
58         if (icon)
59                 {
60                 GdkPixbuf *pixbuf;
61
62                 pixbuf = pixbuf_inline(icon);
63                 if (pixbuf)
64                         {
65                         gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
66                         g_object_unref(pixbuf);
67                         }
68                 }
69         else
70                 {
71                 gtk_window_set_icon_from_file(GTK_WINDOW(window), file, NULL);
72                 }
73 }
74
75 gint window_maximized(GtkWidget *window)
76 {
77         GdkWindowState state;
78
79         if (!window || !window->window) return FALSE;
80
81         state = gdk_window_get_state(window->window);
82         return (state & GDK_WINDOW_STATE_MAXIMIZED);
83 }
84
85 gdouble get_zoom_increment(void)
86 {
87         return ((zoom_increment != 0) ? (gdouble)zoom_increment / 10.0 : 1.0);
88 }
89
90 /*
91  *-----------------------------------------------------------------------------
92  * Open  browser with the help Documentation
93  *-----------------------------------------------------------------------------
94  */
95
96 static gchar *command_result(const gchar *binary, const gchar *command)
97 {
98         gchar *result = NULL;
99         FILE *f;
100         char buf[2048];
101         int l;
102
103         if (!binary) return NULL;
104         if (!file_in_path(binary)) return NULL;
105
106         if (!command) return g_strdup(binary);
107         if (command[0] == '!') return g_strdup(command + 1);
108
109         f = popen(command, "r");
110         if (!f) return NULL;
111
112         while ((l = fread(buf, sizeof(char), sizeof(buf), f)) > 0)
113                 {
114                 if (!result)
115                         {
116                         int n = 0;
117
118                         while (n < l && buf[n] != '\n' && buf[n] != '\r') n++;
119                         if (n > 0) result = g_strndup(buf, n);
120                         }
121                 }
122
123         pclose(f);
124
125         return result;
126 }
127
128 static void help_browser_command(const gchar *command, const gchar *path)
129 {
130         gchar *result;
131         gchar *buf;
132         gchar *begin;
133         gchar *end;
134
135         if (!command || !path) return;
136
137         if (debug) printf("Help command pre \"%s\", \"%s\"\n", command, path);
138
139         buf = g_strdup(command);
140         begin = strstr(buf, "%s");
141         if (begin)
142                 {
143                 *begin = '\0';
144                 end = begin + 2;
145                 begin = buf;
146
147                 result = g_strdup_printf("%s%s%s &", begin, path, end);
148                 }
149         else
150                 {
151                 result = g_strdup_printf("%s \"%s\" &", command, path);
152                 }
153         g_free(buf);
154
155         if (debug) printf("Help command post [%s]\n", result);
156
157         system(result);
158
159         g_free(result);
160 }
161
162 /*
163  * each set of 2 strings is one browser:
164  *   the 1st is the binary to look for in the path
165  *   the 2nd has 3 capabilities:
166  *        NULL     exec binary with html file path as command line
167  *        string   exec string and use results for command line
168  *        !string  use text following ! as command line, replacing optional %s with html file path
169 */
170 static gchar *html_browsers[] =
171 {
172         /* Redhat has a nifty htmlview script to start the user's preferred browser */
173         "htmlview",     NULL,
174         /* GNOME 2 */
175         "gconftool-2",  "gconftool-2 -g /desktop/gnome/url-handlers/http/command",
176         /* KDE */
177         "kfmclient",    "!kfmclient exec \"%s\"",
178         /* use fallbacks */
179         "firefox",      NULL,
180         "mozilla",      NULL,
181         "konqueror",    NULL,
182         "netscape",     NULL,
183         NULL,           NULL
184 };
185
186 static void help_browser_run(void)
187 {
188         gchar *result = NULL;
189         gint i;
190
191         i = 0;
192         while (!result && html_browsers[i])
193                 {
194                 result = command_result(html_browsers[i], html_browsers[i+1]);
195                 i += 2;
196                 }
197
198         if (!result)
199                 {
200                 printf("Unable to detect an installed browser.\n");
201                 return;
202                 }
203
204         help_browser_command(result, GQVIEW_HTMLDIR "/index.html");
205
206         g_free(result);
207 }
208
209 /*
210  *-----------------------------------------------------------------------------
211  * help window
212  *-----------------------------------------------------------------------------
213  */ 
214
215 static GtkWidget *help_window = NULL;
216
217 static void help_window_destroy_cb(GtkWidget *window, gpointer data)
218 {
219         help_window = NULL;
220 }
221
222 void help_window_show(const gchar *key)
223 {
224         if (key && strcmp(key, "html_contents") == 0)
225                 {
226                 help_browser_run();
227                 return;
228                 }
229
230         if (help_window)
231                 {
232                 gtk_window_present(GTK_WINDOW(help_window));
233                 if (key) help_window_set_key(help_window, key);
234                 return;
235                 }
236
237         help_window = help_window_new(_("Help - GQview"), "GQview", "help",
238                                       GQVIEW_HELPDIR "/README", key);
239         g_signal_connect(G_OBJECT(help_window), "destroy",
240                          G_CALLBACK(help_window_destroy_cb), NULL);
241 }
242
243
244 /*
245  *-----------------------------------------------------------------------------
246  * keyboard functions
247  *-----------------------------------------------------------------------------
248  */
249
250 void keyboard_scroll_calc(gint *x, gint *y, GdkEventKey *event)
251 {
252         static gint delta = 0;
253         static guint32 time_old = 0;
254         static guint keyval_old = 0;
255
256         if (event->state & GDK_CONTROL_MASK)
257                 {
258                 if (*x < 0) *x = G_MININT / 2;
259                 if (*x > 0) *x = G_MAXINT / 2;
260                 if (*y < 0) *y = G_MININT / 2;
261                 if (*y > 0) *y = G_MAXINT / 2;
262
263                 return;
264                 }
265
266         if (progressive_key_scrolling)
267                 {
268                 guint32 time_diff;
269
270                 time_diff = event->time - time_old;
271
272                 /* key pressed within 125ms ? (1/8 second) */
273                 if (time_diff > 125 || event->keyval != keyval_old) delta = 0;
274
275                 time_old = event->time;
276                 keyval_old = event->keyval;
277
278                 delta += 2;
279                 }
280         else
281                 {
282                 delta = 8;
283                 }
284
285         *x = *x * delta;
286         *y = *y * delta;
287 }
288
289
290 /*
291  *-----------------------------------------------------------------------------
292  * remote functions
293  *-----------------------------------------------------------------------------
294  */
295
296 static void gr_image_next(const gchar *text, gpointer data)
297 {
298         layout_image_next(NULL);
299 }
300
301 static void gr_image_prev(const gchar *text, gpointer data)
302 {
303         layout_image_prev(NULL);
304 }
305
306 static void gr_image_first(const gchar *text, gpointer data)
307 {
308         layout_image_first(NULL);
309 }
310
311 static void gr_image_last(const gchar *text, gpointer data)
312 {
313         layout_image_last(NULL);
314 }
315
316 static void gr_fullscreen_toggle(const gchar *text, gpointer data)
317 {
318         layout_image_full_screen_toggle(NULL);
319 }
320
321 static void gr_fullscreen_start(const gchar *text, gpointer data)
322 {
323         layout_image_full_screen_start(NULL);
324 }
325
326 static void gr_fullscreen_stop(const gchar *text, gpointer data)
327 {
328         layout_image_full_screen_stop(NULL);
329 }
330
331 static void gr_slideshow_start_rec(const gchar *text, gpointer data)
332 {
333         GList *list;
334
335         list = path_list_recursive(text);
336         if (!list) return;
337 printf("length: %d\n", g_list_length(list));
338         layout_image_slideshow_stop(NULL);
339         layout_image_slideshow_start_from_list(NULL, list);
340 }
341
342 static void gr_slideshow_toggle(const gchar *text, gpointer data)
343 {
344         layout_image_slideshow_toggle(NULL);
345 }
346
347 static void gr_slideshow_start(const gchar *text, gpointer data)
348 {
349         layout_image_slideshow_start(NULL);
350 }
351
352 static void gr_slideshow_stop(const gchar *text, gpointer data)
353 {
354         layout_image_slideshow_stop(NULL);
355 }
356
357 static void gr_slideshow_delay(const gchar *text, gpointer data)
358 {
359         gdouble n;
360
361         n = strtod(text, NULL);
362         if (n < SLIDESHOW_MIN_SECONDS || n > SLIDESHOW_MAX_SECONDS)
363                 {
364                 gchar *buf;
365
366                 buf = g_strdup_printf("Remote slideshow delay out of range (%.1f to %.1f)\n",
367                                       SLIDESHOW_MIN_SECONDS, SLIDESHOW_MAX_SECONDS);
368                 print_term(buf);
369                 g_free(buf);
370
371                 return;
372                 }
373         slideshow_delay = (gint)(n * 10.0 + 0.01);
374 }
375
376 static void gr_tools_show(const gchar *text, gpointer data)
377 {
378         gint popped;
379         gint hidden;
380
381         if (layout_tools_float_get(NULL, &popped, &hidden) && hidden)
382                 {
383                 layout_tools_float_set(NULL, popped, FALSE);
384                 }
385 }
386
387 static void gr_tools_hide(const gchar *text, gpointer data)
388 {
389         gint popped;
390         gint hidden;
391
392         if (layout_tools_float_get(NULL, &popped, &hidden) && !hidden)
393                 {
394                 layout_tools_float_set(NULL, popped, TRUE);
395                 }
396 }
397
398 static gint gr_quit_idle_cb(gpointer data)
399 {
400         exit_gqview();
401
402         return FALSE;
403 }
404
405 static void gr_quit(const gchar *text, gpointer data)
406 {
407         /* schedule exit when idle, if done from within a
408          * remote handler remote_close will crash
409          */
410         g_idle_add(gr_quit_idle_cb, NULL);
411 }
412
413 static void gr_file_load(const gchar *text, gpointer data)
414 {
415         if (isfile(text))
416                 {
417                 if (file_extension_match(text, ".gqv"))
418                         {
419                         collection_window_new(text);
420                         }
421                 else
422                         {
423                         layout_set_path(NULL, text);
424                         }
425                 }
426         else if (isdir(text))
427                 {
428                 layout_set_path(NULL, text);
429                 }
430         else
431                 {
432                 printf("remote sent filename that does not exist:\"%s\"\n", text);
433                 }
434 }
435
436 static void gr_file_view(const gchar *text, gpointer data)
437 {
438         view_window_new(text);
439 }
440
441 static void gr_list_clear(const gchar *text, gpointer data)
442 {
443         if (gqview_command_collection) collection_unref(gqview_command_collection);
444         gqview_command_collection = NULL;
445 }
446
447 static void gr_list_add(const gchar *text, gpointer data)
448 {
449         gint new = TRUE;
450
451         if (!gqview_command_collection)
452                 {
453                 CollectionData *cd;
454
455                 cd = collection_new("");
456
457                 g_free(cd->path);
458                 cd->path = NULL;
459                 g_free(cd->name);
460                 cd->name = g_strdup(_("Command line"));
461
462                 gqview_command_collection = cd;
463                 }
464         else
465                 {
466                 new = (!collection_get_first(gqview_command_collection));
467                 }
468
469         if (collection_add(gqview_command_collection, text, FALSE) && new)
470                 {
471                 layout_image_set_collection(NULL, gqview_command_collection,
472                                             collection_get_first(gqview_command_collection));
473                 }
474 }
475
476 static void gr_raise(const gchar *text, gpointer data)
477 {
478         LayoutWindow *lw = NULL;
479
480         if (layout_valid(&lw))
481                 {
482                 gtk_window_present(GTK_WINDOW(lw->window));
483                 }
484 }
485
486 typedef struct _RemoteCommandEntry RemoteCommandEntry;
487 struct _RemoteCommandEntry {
488         gchar *opt_s;
489         gchar *opt_l;
490         void (*func)(const gchar *text, gpointer data);
491         gint needs_extra;
492         gint prefer_command_line;
493         gchar *description;
494 };
495
496 static RemoteCommandEntry remote_commands[] = {
497         /* short, long                  callback,               extra, prefer,description */
498         { "-n", "--next",               gr_image_next,          FALSE, FALSE, N_("next image") },
499         { "-b", "--back",               gr_image_prev,          FALSE, FALSE, N_("previous image") },
500         { NULL, "--first",              gr_image_first,         FALSE, FALSE, N_("first image") },
501         { NULL, "--last",               gr_image_last,          FALSE, FALSE, N_("last image") },
502         { "-f", "--fullscreen",         gr_fullscreen_toggle,   FALSE, TRUE,  N_("toggle full screen") },
503         { "-fs","--fullscreen-start",   gr_fullscreen_start,    FALSE, FALSE, N_("start full screen") },
504         { "-fS","--fullscreen-stop",    gr_fullscreen_stop,     FALSE, FALSE, N_("stop full screen") },
505         { "-s", "--slideshow",          gr_slideshow_toggle,    FALSE, TRUE,  N_("toggle slide show") },
506         { "-ss","--slideshow-start",    gr_slideshow_start,     FALSE, FALSE, N_("start slide show") },
507         { "-sS","--slideshow-stop",     gr_slideshow_stop,      FALSE, FALSE, N_("stop slide show") },
508         { "-sr","--slideshow-recurse",  gr_slideshow_start_rec, TRUE,  FALSE, N_("start recursive slide show") },
509         { "-d", "--delay=",             gr_slideshow_delay,     TRUE,  FALSE, N_("set slide show delay in seconds") },
510         { "+t", "--tools-show",         gr_tools_show,          FALSE, TRUE,  N_("show tools") },
511         { "-t", "--tools-hide",         gr_tools_hide,          FALSE, TRUE,  N_("hide tools") },
512         { "-q", "--quit",               gr_quit,                FALSE, FALSE, N_("quit") },
513         { NULL, "file:",                gr_file_load,           TRUE,  FALSE, N_("open file") },
514         { NULL, "view:",                gr_file_view,           TRUE,  FALSE, N_("open file in new window") },
515         { NULL, "--list-clear",         gr_list_clear,          FALSE, FALSE, NULL },
516         { NULL, "--list-add:",          gr_list_add,            TRUE,  FALSE, NULL },
517         { NULL, "raise",                gr_raise,               FALSE, FALSE, NULL },
518         { NULL, NULL, NULL, FALSE, FALSE, NULL }
519 };
520
521 static RemoteCommandEntry *gqview_remote_command_find(const gchar *text, const gchar **offset)
522 {
523         gint match = FALSE;
524         gint i;
525
526         i = 0;
527         while (!match && remote_commands[i].func != NULL)
528                 {
529                 if (remote_commands[i].needs_extra)
530                         {
531                         if (remote_commands[i].opt_s &&
532                             strncmp(remote_commands[i].opt_s, text, strlen(remote_commands[i].opt_s)) == 0)
533                                 {
534                                 if (offset) *offset = text + strlen(remote_commands[i].opt_s);
535                                 return &remote_commands[i];
536                                 }
537                         else if (remote_commands[i].opt_l &&
538                                  strncmp(remote_commands[i].opt_l, text, strlen(remote_commands[i].opt_l)) == 0)
539                                 {
540                                 if (offset) *offset = text + strlen(remote_commands[i].opt_l);
541                                 return &remote_commands[i];
542                                 }
543                         }
544                 else
545                         {
546                         if ((remote_commands[i].opt_s && strcmp(remote_commands[i].opt_s, text) == 0) ||
547                             (remote_commands[i].opt_l && strcmp(remote_commands[i].opt_l, text) == 0))
548                                 {
549                                 if (offset) *offset = text;
550                                 return &remote_commands[i];
551                                 }
552                         }
553
554                 i++;
555                 }
556
557         return NULL;
558 }
559
560 static void gqview_remote_cb(RemoteConnection *rc, const gchar *text, gpointer data)
561 {
562         RemoteCommandEntry *entry;
563         const gchar *offset;
564
565         entry = gqview_remote_command_find(text, &offset);
566         if (entry && entry->func)
567                 {
568                 entry->func(offset, data);
569                 }
570         else
571                 {
572                 printf("unknown remote command:%s\n", text);
573                 }
574 }
575
576 static void gqview_remote_help(void)
577 {
578         gint i;
579
580         print_term(_("Remote command list:\n"));
581
582         i = 0;
583         while (remote_commands[i].func != NULL)
584                 {
585                 if (remote_commands[i].description)
586                         {
587                         gchar *buf;
588
589                         buf = g_strdup_printf("  %-3s%s %-20s %s\n",
590                                 (remote_commands[i].opt_s) ? remote_commands[i].opt_s : "",
591                                 (remote_commands[i].opt_s && remote_commands[i].opt_l) ? "," : " ",
592                                 (remote_commands[i].opt_l) ? remote_commands[i].opt_l : "",
593                                 _(remote_commands[i].description));
594
595                         print_term(buf);
596                         g_free(buf);
597                         }
598                 i++;
599                 }
600 }
601
602 static GList *gqview_remote_build_list(GList *list, int argc, char *argv[])
603 {
604         gint i;
605
606         i = 1;
607         while (i < argc)
608                 {
609                 RemoteCommandEntry *entry;
610
611                 entry = gqview_remote_command_find(argv[i], NULL);
612                 if (entry)
613                         {
614                         list = g_list_append(list, argv[i]);
615                         }
616                 i++;
617                 }
618
619         return list;
620 }
621
622 static void gqview_remote_control(const gchar *arg_exec, GList *remote_list, const gchar *path,
623                                   GList *cmd_list, GList *collection_list)
624 {
625         RemoteConnection *rc;
626         gint started = FALSE;
627         gchar *buf;
628
629         buf = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/.command", NULL);
630         rc = remote_client_open(buf);
631         if (!rc)
632                 {
633                 GString *command;
634                 GList *work;
635                 gint retry_count = 12;
636                 gint blank = FALSE;
637
638                 print_term(_("Remote GQview not running, starting..."));
639                 command = g_string_new(arg_exec);
640
641                 work = remote_list;
642                 while (work)
643                         {
644                         gchar *text;
645                         RemoteCommandEntry *entry;
646
647                         text = work->data;
648                         work = work->next;
649
650                         entry = gqview_remote_command_find(text, NULL);
651                         if (entry)
652                                 {
653                                 if (entry->prefer_command_line)
654                                         {
655                                         remote_list = g_list_remove(remote_list, text);
656                                         g_string_append(command, " ");
657                                         g_string_append(command, text);
658                                         }
659                                 if (entry->opt_l && strcmp(entry->opt_l, "file:") == 0)
660                                         {
661                                         blank = TRUE;
662                                         }
663                                 }
664                         }
665
666                 if (blank || cmd_list || path) g_string_append(command, " --blank");
667                 if (debug) g_string_append(command, " --debug");
668
669                 g_string_append(command, " &");
670                 system(command->str);
671                 g_string_free(command, TRUE);
672
673                 while (!rc && retry_count > 0)
674                         {
675                         usleep((retry_count > 10) ? 500000 : 1000000);
676                         rc = remote_client_open(buf);
677                         if (!rc) print_term(".");
678                         retry_count--;
679                         }
680
681                 print_term("\n");
682
683                 started = TRUE;
684                 }
685         g_free(buf);
686
687         if (rc)
688                 {
689                 GList *work;
690                 const gchar *prefix;
691                 gint use_path = TRUE;
692                 gint sent = FALSE;
693
694                 work = remote_list;
695                 while (work)
696                         {
697                         gchar *text;
698                         RemoteCommandEntry *entry;
699
700                         text = work->data;
701                         work = work->next;
702
703                         entry = gqview_remote_command_find(text, NULL);
704                         if (entry &&
705                             entry->opt_l &&
706                             strcmp(entry->opt_l, "file:") == 0) use_path = FALSE;
707
708                         remote_client_send(rc, text);
709
710                         sent = TRUE;
711                         }
712
713                 if (cmd_list && cmd_list->next)
714                         {
715                         prefix = "--list-add:";
716                         remote_client_send(rc, "--list-clear");
717                         }
718                 else
719                         {
720                         prefix = "file:";
721                         }
722
723                 work = cmd_list;
724                 while (work)
725                         {
726                         const gchar *name;
727                         gchar *text;
728
729                         name = work->data;
730                         work = work->next;
731
732                         text = g_strconcat(prefix, name, NULL);
733                         remote_client_send(rc, text);
734                         g_free(text);
735
736                         sent = TRUE;
737                         }
738
739                 if (path && !cmd_list && use_path)
740                         {
741                         gchar *text;
742
743                         text = g_strdup_printf("file:%s", path);
744                         remote_client_send(rc, text);
745                         g_free(text);
746
747                         sent = TRUE;
748                         }
749
750                 work = collection_list;
751                 while (work)
752                         {
753                         const gchar *name;
754                         gchar *text;
755
756                         name = work->data;
757                         work = work->next;
758
759                         text = g_strdup_printf("file:%s", name);
760                         remote_client_send(rc, text);
761                         g_free(text);
762
763                         sent = TRUE;
764                         }
765
766                 if (!started && !sent)
767                         {
768                         remote_client_send(rc, "raise");
769                         }
770                 }
771         else
772                 {
773                 print_term(_("Remote not available\n"));
774                 }
775
776         _exit(0);
777 }
778
779 /*
780  *-----------------------------------------------------------------------------
781  * command line parser (private) hehe, who needs popt anyway?
782  *-----------------------------------------------------------------------------
783  */ 
784
785 static gint startup_blank = FALSE;
786 static gint startup_full_screen = FALSE;
787 static gint startup_in_slideshow = FALSE;
788 static gint startup_command_line_collection = FALSE;
789
790
791 static void parse_command_line_add_file(const gchar *file_path, gchar **path, gchar **file,
792                                         GList **list, GList **collection_list)
793 {
794         gchar *path_parsed;
795
796         path_parsed = g_strdup(file_path);
797         parse_out_relatives(path_parsed);
798
799         if (file_extension_match(path_parsed, ".gqv"))
800                 {
801                 *collection_list = g_list_append(*collection_list, path_parsed);
802                 }
803         else
804                 {
805                 if (!*path) *path = remove_level_from_path(path_parsed);
806                 if (!*file) *file = g_strdup(path_parsed);
807                 *list = g_list_prepend(*list, path_parsed);
808                 }
809 }
810
811 static void parse_command_line_add_dir(const gchar *dir, gchar **path, gchar **file,
812                                        GList **list)
813 {
814         GList *files = NULL;
815         gchar *path_parsed;
816
817         path_parsed = g_strdup(dir);
818         parse_out_relatives(path_parsed);
819
820         if (path_list(path_parsed, &files, NULL))
821                 {
822                 GList *work;
823
824                 files = path_list_filter(files, FALSE);
825                 files = path_list_sort(files);
826
827                 work = files;
828                 while (work)
829                         {
830                         gchar *p;
831
832                         p = work->data;
833                         if (!*path) *path = remove_level_from_path(p);
834                         if (!*file) *file = g_strdup(p);
835                         *list = g_list_prepend(*list, p);
836
837                         work = work->next;
838                         }
839
840                 g_list_free(files);
841                 }
842
843         g_free(path_parsed);
844 }
845
846 static void parse_command_line_process_dir(const gchar *dir, gchar **path, gchar **file,
847                                            GList **list, gchar **first_dir)
848 {
849         
850         if (!*list && !*first_dir)
851                 {
852                 *first_dir = g_strdup(dir);
853                 }
854         else
855                 {
856                 if (*first_dir)
857                         {
858                         parse_command_line_add_dir(*first_dir, path, file, list);
859                         g_free(*first_dir);
860                         *first_dir = NULL;
861                         }
862                 parse_command_line_add_dir(dir, path, file, list);
863                 }
864 }
865
866 static void parse_command_line_process_file(const gchar *file_path, gchar **path, gchar **file,
867                                             GList **list, GList **collection_list, gchar **first_dir)
868 {
869         
870         if (*first_dir)
871                 {
872                 parse_command_line_add_dir(*first_dir, path, file, list);
873                 g_free(*first_dir);
874                 *first_dir = NULL;
875                 }
876         parse_command_line_add_file(file_path, path, file, list, collection_list);
877 }
878
879 static void parse_command_line(int argc, char *argv[], gchar **path, gchar **file,
880                                GList **cmd_list, GList **collection_list,
881                                gchar **geometry)
882 {
883         GList *list = NULL;
884         GList *remote_list = NULL;
885         gint remote_do = FALSE;
886         gchar *first_dir = NULL;
887
888         if (argc > 1)
889                 {
890                 gint i;
891                 gchar *base_dir = get_current_dir();
892                 i = 1;
893                 while (i < argc)
894                         {
895                         const gchar *cmd_line = argv[i];
896                         gchar *cmd_all = concat_dir_and_file(base_dir, cmd_line);
897
898                         if (cmd_line[0] == '/' && isdir(cmd_line))
899                                 {
900                                 parse_command_line_process_dir(cmd_line, path, file, &list, &first_dir);
901                                 }
902                         else if (isdir(cmd_all))
903                                 {
904                                 parse_command_line_process_dir(cmd_all, path, file, &list, &first_dir);
905                                 }
906                         else if (cmd_line[0] == '/' && isfile(cmd_line))
907                                 {
908                                 parse_command_line_process_file(cmd_line, path, file,
909                                                                 &list, collection_list, &first_dir);
910                                 }
911                         else if (isfile(cmd_all))
912                                 {
913                                 parse_command_line_process_file(cmd_all, path, file,
914                                                                 &list, collection_list, &first_dir);
915                                 }
916                         else if (strcmp(cmd_line, "--debug") == 0)
917                                 {
918                                 /* we now increment the debug state for verbosity */
919                                 debug++;
920                                 printf("debugging output enabled (level %d)\n", debug);
921                                 }
922                         else if (strcmp(cmd_line, "+t") == 0 ||
923                                  strcmp(cmd_line, "--with-tools") == 0)
924                                 {
925                                 tools_float = FALSE;
926                                 tools_hidden = FALSE;
927
928                                 remote_list = g_list_append(remote_list, "+t");
929                                 }
930                         else if (strcmp(cmd_line, "-t") == 0 ||
931                                  strcmp(cmd_line, "--without-tools") == 0)
932                                 {
933                                 tools_hidden = TRUE;
934
935                                 remote_list = g_list_append(remote_list, "-t");
936                                 }
937                         else if (strcmp(cmd_line, "-f") == 0 ||
938                                  strcmp(cmd_line, "--fullscreen") == 0)
939                                 {
940                                 startup_full_screen = TRUE;
941                                 }
942                         else if (strcmp(cmd_line, "-s") == 0 ||
943                                  strcmp(cmd_line, "--slideshow") == 0)
944                                 {
945                                 startup_in_slideshow = TRUE;
946                                 }
947                         else if (strcmp(cmd_line, "-l") == 0 ||
948                                  strcmp(cmd_line, "--list") == 0)
949                                 {
950                                 startup_command_line_collection = TRUE;
951                                 }
952                         else if (strncmp(cmd_line, "--geometry=", 11) == 0)
953                                 {
954                                 if (!*geometry) *geometry = g_strdup(cmd_line + 11);
955                                 }
956                         else if (strcmp(cmd_line, "-r") == 0 ||
957                                  strcmp(cmd_line, "--remote") == 0)
958                                 {
959                                 if (!remote_do)
960                                         {
961                                         remote_do = TRUE;
962                                         remote_list = gqview_remote_build_list(remote_list, argc, argv);
963                                         }
964                                 }
965                         else if (strcmp(cmd_line, "-rh") == 0 ||
966                                  strcmp(cmd_line, "--remote-help") == 0)
967                                 {
968                                 gqview_remote_help();
969                                 exit (0);
970                                 }
971                         else if (strcmp(cmd_line, "--blank") == 0)
972                                 {
973                                 startup_blank = TRUE;
974                                 }
975                         else if (strcmp(cmd_line, "-v") == 0 ||
976                                  strcmp(cmd_line, "--version") == 0)
977                                 {
978                                 printf("GQview %s\n", VERSION);
979                                 exit (0);
980                                 }
981                         else if (strcmp(cmd_line, "--alternate") == 0)
982                                 {
983                                 /* enable faster experimental algorithm */
984                                 printf("Alternate similarity algorithm enabled\n");
985                                 image_sim_alternate_set(TRUE);
986                                 }
987                         else if (strcmp(cmd_line, "-h") == 0 ||
988                                  strcmp(cmd_line, "--help") == 0)
989                                 {
990                                 printf("GQview %s\n", VERSION);
991                                 print_term(_("Usage: gqview [options] [path]\n\n"));
992                                 print_term(_("valid options are:\n"));
993                                 print_term(_("  +t, --with-tools           force show of tools\n"));
994                                 print_term(_("  -t, --without-tools        force hide of tools\n"));
995                                 print_term(_("  -f, --fullscreen           start in full screen mode\n"));
996                                 print_term(_("  -s, --slideshow            start in slideshow mode\n"));
997                                 print_term(_("  -l, --list                 open collection window for command line\n"));
998                                 print_term(_("      --geometry=GEOMETRY    set main window location\n"));
999                                 print_term(_("  -r, --remote               send following commands to open window\n"));
1000                                 print_term(_("  -rh,--remote-help          print remote command list\n"));
1001                                 print_term(_("  --debug                    turn on debug output\n"));
1002                                 print_term(_("  -v, --version              print version info\n"));
1003                                 print_term(_("  -h, --help                 show this message\n\n"));
1004                                 
1005 #if 0
1006                                 /* these options are not officially supported!
1007                                  * only for testing new features, no need to translate them */
1008                                 print_term(  "  --alternate                use alternate similarity algorithm\n");
1009 #endif
1010                                 
1011                                 exit (0);
1012                                 }
1013                         else if (!remote_do)
1014                                 {
1015                                 gchar *buf;
1016
1017                                 buf = g_strdup_printf(_("invalid or ignored: %s\nUse --help for options\n"), cmd_line);
1018                                 print_term(buf);
1019                                 g_free(buf);
1020                                 }
1021
1022                         g_free(cmd_all);
1023                         i++;
1024                         }
1025                 g_free(base_dir);
1026                 parse_out_relatives(*path);
1027                 parse_out_relatives(*file);
1028                 }
1029
1030         list = g_list_reverse(list);
1031
1032         if (!*path && first_dir)
1033                 {
1034                 *path = first_dir;
1035                 first_dir = NULL;
1036
1037                 parse_out_relatives(*path);
1038                 }
1039         g_free(first_dir);
1040
1041         if (remote_do)
1042                 {
1043                 gqview_remote_control(argv[0], remote_list, *path, list, *collection_list);
1044                 }
1045         g_list_free(remote_list);
1046
1047         if (list && list->next)
1048                 {
1049                 *cmd_list = list;
1050                 }
1051         else
1052                 {
1053                 path_list_free(list);
1054                 *cmd_list = NULL;
1055                 }
1056 }
1057
1058 /*
1059  *-----------------------------------------------------------------------------
1060  * startup, init, and exit
1061  *-----------------------------------------------------------------------------
1062  */ 
1063
1064 #define RC_HISTORY_NAME "history"
1065
1066 static void keys_load(void)
1067 {
1068         gchar *path;
1069
1070         path = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/", RC_HISTORY_NAME, NULL);
1071         history_list_load(path);
1072         g_free(path);
1073 }
1074
1075 static void keys_save(void)
1076 {
1077         gchar *path;
1078
1079         path = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/", RC_HISTORY_NAME, NULL);
1080         history_list_save(path);
1081         g_free(path);
1082 }
1083
1084 static void check_for_home_path(gchar *path)
1085 {
1086         gchar *buf;
1087
1088         buf = g_strconcat(homedir(), "/", path, NULL);
1089         if (!isdir(buf))
1090                 {
1091                 gchar *tmp;
1092
1093                 tmp = g_strdup_printf(_("Creating GQview dir:%s\n"), buf);
1094                 print_term(tmp);
1095                 g_free(tmp);
1096
1097                 if (!mkdir_utf8(buf, 0755))
1098                         {
1099                         tmp = g_strdup_printf(_("Could not create dir:%s\n"), buf);
1100                         print_term(tmp);
1101                         g_free(tmp);
1102                         }
1103                 }
1104         g_free(buf);
1105 }
1106
1107 static void setup_default_options(void)
1108 {
1109         gchar *path;
1110         gint i;
1111
1112         for (i = 0; i < GQVIEW_EDITOR_SLOTS; i++)
1113                 {
1114                 editor_name[i] = NULL;
1115                 editor_command[i] = NULL;
1116                 }
1117
1118         editor_reset_defaults();
1119
1120         bookmark_add_default(_("Home"), homedir());
1121         path = concat_dir_and_file(homedir(), "Desktop");
1122         bookmark_add_default(_("Desktop"), path);
1123         g_free(path);
1124         path = concat_dir_and_file(homedir(), GQVIEW_RC_DIR_COLLECTIONS);
1125         bookmark_add_default(_("Collections"), path);
1126         g_free(path);
1127
1128         g_free(safe_delete_path);
1129         safe_delete_path = concat_dir_and_file(homedir(), GQVIEW_RC_DIR_TRASH);
1130
1131         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
1132                 {
1133                 color_profile_input_file[i] = NULL;
1134                 color_profile_input_name[i] = NULL;
1135                 }
1136 }
1137
1138 static void exit_gqview_final(void)
1139 {
1140         gchar *path;
1141         gchar *pathl;
1142         LayoutWindow *lw = NULL;
1143
1144         remote_close(gqview_remote);
1145
1146         collect_manager_flush();
1147
1148         if (layout_valid(&lw))
1149                 {
1150                 main_window_maximized =  window_maximized(lw->window);
1151                 if (!main_window_maximized)
1152                         {
1153                         layout_geometry_get(NULL, &main_window_x, &main_window_y,
1154                                             &main_window_w, &main_window_h);
1155                         }
1156                 }
1157
1158         layout_geometry_get_dividers(NULL, &window_hdivider_pos, &window_vdivider_pos);
1159
1160         layout_views_get(NULL, &layout_view_tree, &layout_view_icons);
1161
1162         thumbnails_enabled = layout_thumb_get(NULL);
1163         layout_sort_get(NULL, &file_sort_method, &file_sort_ascending);
1164
1165         layout_geometry_get_tools(NULL, &float_window_x, &float_window_y,
1166                                   &float_window_w, &float_window_h, &float_window_divider);
1167         layout_tools_float_get(NULL, &tools_float, &tools_hidden);
1168         toolbar_hidden = layout_toolbar_hidden(NULL);
1169
1170         color_profile_enabled = layout_image_color_profile_get_use(NULL);
1171         layout_image_color_profile_get(NULL,
1172                                        &color_profile_input_type, &color_profile_screen_type,
1173                                        &color_profile_use_image);
1174
1175         save_options();
1176         keys_save();
1177
1178         path = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/accels", NULL);
1179         pathl = path_from_utf8(path);
1180         gtk_accel_map_save(pathl);
1181         g_free(pathl);
1182         g_free(path);
1183
1184         gtk_main_quit();
1185 }
1186
1187 static GenericDialog *exit_dialog = NULL;
1188
1189 static void exit_confirm_cancel_cb(GenericDialog *gd, gpointer data)
1190 {
1191         exit_dialog = NULL;
1192         generic_dialog_close(gd);
1193 }
1194
1195 static void exit_confirm_exit_cb(GenericDialog *gd, gpointer data)
1196 {
1197         exit_dialog = NULL;
1198         generic_dialog_close(gd);
1199         exit_gqview_final();
1200 }
1201
1202 static gint exit_confirm_dlg(void)
1203 {
1204         GtkWidget *parent;
1205         LayoutWindow *lw;
1206
1207         if (exit_dialog)
1208                 {
1209                 gtk_window_present(GTK_WINDOW(exit_dialog->dialog));
1210                 return TRUE;
1211                 }
1212
1213         if (!collection_window_modified_exists()) return FALSE;
1214
1215         parent = NULL;
1216         lw = NULL;
1217         if (layout_valid(&lw))
1218                 {
1219                 parent = lw->window;
1220                 }
1221
1222         exit_dialog = generic_dialog_new(_("GQview - exit"),
1223                                 "GQview", "exit", parent, FALSE,
1224                                 exit_confirm_cancel_cb, NULL);
1225         generic_dialog_add_message(exit_dialog, GTK_STOCK_DIALOG_QUESTION,
1226                                    _("Quit GQview"), _("Collections have been modified. Quit anyway?"));
1227         generic_dialog_add_button(exit_dialog, GTK_STOCK_QUIT, NULL, exit_confirm_exit_cb, TRUE);
1228
1229         gtk_widget_show(exit_dialog->dialog);
1230
1231         return TRUE;
1232 }
1233
1234 void exit_gqview(void)
1235 {
1236         layout_image_full_screen_stop(NULL);
1237
1238         if (exit_confirm_dlg()) return;
1239
1240         exit_gqview_final();
1241 }
1242
1243 int main (int argc, char *argv[])
1244 {
1245         LayoutWindow *lw;
1246         gchar *path = NULL;
1247         gchar *cmd_path = NULL;
1248         gchar *cmd_file = NULL;
1249         GList *cmd_list = NULL;
1250         GList *collection_list = NULL;
1251         CollectionData *first_collection = NULL;
1252         gchar *geometry = NULL;
1253         gchar *buf;
1254         gchar *bufl;
1255
1256         /* setup locale, i18n */
1257         gtk_set_locale();
1258         bindtextdomain(PACKAGE, GQVIEW_LOCALEDIR);
1259         bind_textdomain_codeset(PACKAGE, "UTF-8");
1260         textdomain(PACKAGE);
1261
1262         /* setup random seed for random slideshow */
1263         srand(time(NULL));
1264
1265 #if 1
1266         printf("GQview %s, This is a beta release.\n", VERSION);
1267 #endif
1268
1269         layout_order = g_strdup("123");
1270         setup_default_options();
1271         load_options();
1272
1273         parse_command_line(argc, argv, &cmd_path, &cmd_file, &cmd_list, &collection_list, &geometry);
1274
1275         gtk_init (&argc, &argv);
1276
1277         if (gtk_major_version < GTK_MAJOR_VERSION ||
1278             (gtk_major_version == GTK_MAJOR_VERSION && gtk_minor_version < GTK_MINOR_VERSION) )
1279                 {
1280                 gchar *msg;
1281                 print_term("!!! This is a friendly warning.\n");
1282                 print_term("!!! The version of GTK+ in use now is older than when GQview was compiled.\n");
1283                 msg = g_strdup_printf("!!!  compiled with GTK+-%d.%d\n", GTK_MAJOR_VERSION, GTK_MINOR_VERSION);
1284                 print_term(msg);
1285                 g_free(msg);
1286                 msg = g_strdup_printf("!!!   running with GTK+-%d.%d\n", gtk_major_version, gtk_minor_version);
1287                 print_term(msg);
1288                 g_free(msg);
1289                 print_term("!!! GQview may quit unexpectedly with a relocation error.\n");
1290                 }
1291
1292         check_for_home_path(GQVIEW_RC_DIR);
1293         check_for_home_path(GQVIEW_RC_DIR_COLLECTIONS);
1294         check_for_home_path(GQVIEW_CACHE_RC_THUMB);
1295         check_for_home_path(GQVIEW_CACHE_RC_METADATA);
1296
1297         keys_load();
1298         filter_add_defaults();
1299         filter_rebuild();
1300
1301         buf = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/accels", NULL);
1302         bufl = path_from_utf8(buf);
1303         gtk_accel_map_load(bufl);
1304         g_free(bufl);
1305         g_free(buf);
1306
1307         if (startup_blank)
1308                 {
1309                 g_free(cmd_path);
1310                 cmd_path = NULL;
1311                 g_free(cmd_file);
1312                 cmd_file = NULL;
1313                 path_list_free(cmd_list);
1314                 cmd_list = NULL;
1315                 path_list_free(collection_list);
1316                 collection_list = NULL;
1317
1318                 path = NULL;
1319                 }
1320         else if (cmd_path)
1321                 {
1322                 path = g_strdup(cmd_path);
1323                 }
1324         else if (startup_path_enable && startup_path && isdir(startup_path))
1325                 {
1326                 path = g_strdup(startup_path);
1327                 }
1328         else
1329                 {
1330                 path = get_current_dir();
1331                 }
1332
1333         lw = layout_new_with_geometry(NULL, tools_float, tools_hidden, geometry);
1334         layout_sort_set(lw, file_sort_method, file_sort_ascending);
1335
1336         if (collection_list && !startup_command_line_collection)
1337                 {
1338                 GList *work;
1339
1340                 work = collection_list;
1341                 while (work)
1342                         {
1343                         CollectWindow *cw;
1344                         const gchar *path;
1345
1346                         path = work->data;
1347                         work = work->next;
1348
1349                         cw = collection_window_new(path);
1350                         if (!first_collection && cw) first_collection = cw->cd;
1351                         }
1352                 }
1353
1354         if (cmd_list ||
1355             (startup_command_line_collection && collection_list))
1356                 {
1357                 CollectionData *cd;
1358                 GList *work;
1359
1360                 if (startup_command_line_collection)
1361                         {
1362                         CollectWindow *cw;
1363
1364                         cw = collection_window_new("");
1365                         cd = cw->cd;
1366                         }
1367                 else
1368                         {
1369                         cd = collection_new("");        /* if we pass NULL, untitled counter is falsely increm. */
1370                         gqview_command_collection = cd;
1371                         }
1372
1373                 g_free(cd->path);
1374                 cd->path = NULL;
1375                 g_free(cd->name);
1376                 cd->name = g_strdup(_("Command line"));
1377
1378                 collection_path_changed(cd);
1379
1380                 work = cmd_list;
1381                 while (work)
1382                         {
1383                         collection_add(cd, (gchar *)work->data, FALSE);
1384                         work = work->next;
1385                         }
1386
1387                 work = collection_list;
1388                 while (work)
1389                         {
1390                         collection_load(cd, (gchar *)work->data, TRUE);
1391                         work = work->next;
1392                         }
1393
1394                 layout_set_path(lw, path);
1395                 if (cd->list) layout_image_set_collection(lw, cd, cd->list->data);
1396
1397                 /* mem leak, we never unref this collection when !startup_command_line_collection
1398                  * (the image view of the main window does not hold a ref to the collection)
1399                  * this is sort of unavoidable, for if it did hold a ref, next/back
1400                  * may not work as expected when closing collection windows.
1401                  *
1402                  * collection_unref(cd);
1403                  */
1404
1405                 }
1406         else if (cmd_file)
1407                 {
1408                 layout_set_path(lw, cmd_file);
1409                 }
1410         else
1411                 {
1412                 layout_set_path(lw, path);
1413                 if (first_collection)
1414                         {
1415                         layout_image_set_collection(lw, first_collection,
1416                                                     collection_get_first(first_collection));
1417                         }
1418                 }
1419
1420         g_free(geometry);
1421         g_free(cmd_path);
1422         g_free(cmd_file);
1423         path_list_free(cmd_list);
1424         path_list_free(collection_list);
1425         g_free(path);
1426
1427         if (startup_full_screen) layout_image_full_screen_start(lw);
1428         if (startup_in_slideshow) layout_image_slideshow_start(lw);
1429
1430         buf = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/.command", NULL);
1431         gqview_remote = remote_server_open(buf);
1432         remote_server_subscribe(gqview_remote, gqview_remote_cb, NULL);
1433         g_free(buf);
1434
1435         gtk_main ();
1436         return 0;
1437 }
1438