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