Add year 2009 to copyright info everywhere.
[geeqie.git] / src / main.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  * Copyright (C) 2008 - 2009 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13
14 #include "main.h"
15
16 #include "cache.h"
17 #include "collect.h"
18 #include "collect-io.h"
19 #include "filedata.h"
20 #include "filefilter.h"
21 #include "history_list.h"
22 #include "image-overlay.h"
23 #include "layout.h"
24 #include "layout_image.h"
25 #include "options.h"
26 #include "remote.h"
27 #include "secure_save.h"
28 #include "similar.h"
29 #include "ui_fileops.h"
30 #include "ui_utildlg.h"
31 #include "cache_maint.h"
32 #include "thumb.h"
33 #include "metadata.h"
34 #include "editors.h"
35
36 #include <gdk/gdkkeysyms.h> /* for keyboard values */
37
38 #include <signal.h>
39 #include <sys/mman.h>
40
41 #include <math.h>
42 #ifdef G_OS_UNIX
43 #include <pwd.h>
44 #endif
45
46
47 static RemoteConnection *remote_connection = NULL;
48
49
50 /*
51  *-----------------------------------------------------------------------------
52  * keyboard functions
53  *-----------------------------------------------------------------------------
54  */
55
56 void keyboard_scroll_calc(gint *x, gint *y, GdkEventKey *event)
57 {
58         static gint delta = 0;
59         static guint32 time_old = 0;
60         static guint keyval_old = 0;
61
62         if (event->state & GDK_CONTROL_MASK)
63                 {
64                 if (*x < 0) *x = G_MININT / 2;
65                 if (*x > 0) *x = G_MAXINT / 2;
66                 if (*y < 0) *y = G_MININT / 2;
67                 if (*y > 0) *y = G_MAXINT / 2;
68
69                 return;
70                 }
71
72         if (options->progressive_key_scrolling)
73                 {
74                 guint32 time_diff;
75
76                 time_diff = event->time - time_old;
77
78                 /* key pressed within 125ms ? (1/8 second) */
79                 if (time_diff > 125 || event->keyval != keyval_old) delta = 0;
80
81                 time_old = event->time;
82                 keyval_old = event->keyval;
83
84                 delta += 2;
85                 }
86         else
87                 {
88                 delta = 8;
89                 }
90
91         *x = *x * delta;
92         *y = *y * delta;
93 }
94
95
96
97 /*
98  *-----------------------------------------------------------------------------
99  * command line parser (private) hehe, who needs popt anyway?
100  *-----------------------------------------------------------------------------
101  */
102
103 static gint startup_blank = FALSE;
104 static gint startup_full_screen = FALSE;
105 static gint startup_in_slideshow = FALSE;
106 static gint startup_command_line_collection = FALSE;
107
108
109 static void parse_command_line_add_file(const gchar *file_path, gchar **path, gchar **file,
110                                         GList **list, GList **collection_list)
111 {
112         gchar *path_parsed;
113
114         path_parsed = g_strdup(file_path);
115         parse_out_relatives(path_parsed);
116
117         if (file_extension_match(path_parsed, GQ_COLLECTION_EXT))
118                 {
119                 *collection_list = g_list_append(*collection_list, path_parsed);
120                 }
121         else
122                 {
123                 if (!*path) *path = remove_level_from_path(path_parsed);
124                 if (!*file) *file = g_strdup(path_parsed);
125                 *list = g_list_prepend(*list, file_data_new_simple(path_parsed));
126                 }
127 }
128
129 static void parse_command_line_add_dir(const gchar *dir, gchar **path, gchar **file,
130                                        GList **list)
131 {
132         GList *files;
133         gchar *path_parsed;
134         FileData *dir_fd;
135
136         path_parsed = g_strdup(dir);
137         parse_out_relatives(path_parsed);
138         dir_fd = file_data_new_simple(path_parsed);
139         
140
141         if (filelist_read(dir_fd, &files, NULL))
142                 {
143                 GList *work;
144
145                 files = filelist_filter(files, FALSE);
146                 files = filelist_sort_path(files);
147
148                 work = files;
149                 while (work)
150                         {
151                         FileData *fd = work->data;
152                         if (!*path) *path = remove_level_from_path(fd->path);
153                         if (!*file) *file = g_strdup(fd->path);
154                         *list = g_list_prepend(*list, fd);
155
156                         work = work->next;
157                         }
158
159                 g_list_free(files);
160                 }
161
162         g_free(path_parsed);
163         file_data_unref(dir_fd);
164 }
165
166 static void parse_command_line_process_dir(const gchar *dir, gchar **path, gchar **file,
167                                            GList **list, gchar **first_dir)
168 {
169
170         if (!*list && !*first_dir)
171                 {
172                 *first_dir = g_strdup(dir);
173                 }
174         else
175                 {
176                 if (*first_dir)
177                         {
178                         parse_command_line_add_dir(*first_dir, path, file, list);
179                         g_free(*first_dir);
180                         *first_dir = NULL;
181                         }
182                 parse_command_line_add_dir(dir, path, file, list);
183                 }
184 }
185
186 static void parse_command_line_process_file(const gchar *file_path, gchar **path, gchar **file,
187                                             GList **list, GList **collection_list, gchar **first_dir)
188 {
189
190         if (*first_dir)
191                 {
192                 parse_command_line_add_dir(*first_dir, path, file, list);
193                 g_free(*first_dir);
194                 *first_dir = NULL;
195                 }
196         parse_command_line_add_file(file_path, path, file, list, collection_list);
197 }
198
199 static void parse_command_line(gint argc, gchar *argv[], gchar **path, gchar **file,
200                                GList **cmd_list, GList **collection_list,
201                                gchar **geometry)
202 {
203         GList *list = NULL;
204         GList *remote_list = NULL;
205         GList *remote_errors = NULL;
206         gint remote_do = FALSE;
207         gchar *first_dir = NULL;
208
209         if (argc > 1)
210                 {
211                 gint i;
212                 gchar *base_dir = get_current_dir();
213                 i = 1;
214                 while (i < argc)
215                         {
216                         const gchar *cmd_line = argv[i];
217                         gchar *cmd_all = g_build_filename(base_dir, cmd_line, NULL);
218
219                         if (cmd_line[0] == G_DIR_SEPARATOR && isdir(cmd_line))
220                                 {
221                                 parse_command_line_process_dir(cmd_line, path, file, &list, &first_dir);
222                                 }
223                         else if (isdir(cmd_all))
224                                 {
225                                 parse_command_line_process_dir(cmd_all, path, file, &list, &first_dir);
226                                 }
227                         else if (cmd_line[0] == G_DIR_SEPARATOR && isfile(cmd_line))
228                                 {
229                                 parse_command_line_process_file(cmd_line, path, file,
230                                                                 &list, collection_list, &first_dir);
231                                 }
232                         else if (isfile(cmd_all))
233                                 {
234                                 parse_command_line_process_file(cmd_all, path, file,
235                                                                 &list, collection_list, &first_dir);
236                                 }
237                         else if (strncmp(cmd_line, "--debug", 7) == 0 && (cmd_line[7] == '\0' || cmd_line[7] == '='))
238                                 {
239                                 /* do nothing but do not produce warnings */
240                                 }
241                         else if (strcmp(cmd_line, "+t") == 0 ||
242                                  strcmp(cmd_line, "--with-tools") == 0)
243                                 {
244                                 options->layout.tools_float = FALSE;
245                                 options->layout.tools_hidden = FALSE;
246
247                                 remote_list = g_list_append(remote_list, "+t");
248                                 }
249                         else if (strcmp(cmd_line, "-t") == 0 ||
250                                  strcmp(cmd_line, "--without-tools") == 0)
251                                 {
252                                 options->layout.tools_hidden = TRUE;
253
254                                 remote_list = g_list_append(remote_list, "-t");
255                                 }
256                         else if (strcmp(cmd_line, "-f") == 0 ||
257                                  strcmp(cmd_line, "--fullscreen") == 0)
258                                 {
259                                 startup_full_screen = TRUE;
260                                 }
261                         else if (strcmp(cmd_line, "-s") == 0 ||
262                                  strcmp(cmd_line, "--slideshow") == 0)
263                                 {
264                                 startup_in_slideshow = TRUE;
265                                 }
266                         else if (strcmp(cmd_line, "-l") == 0 ||
267                                  strcmp(cmd_line, "--list") == 0)
268                                 {
269                                 startup_command_line_collection = TRUE;
270                                 }
271                         else if (strncmp(cmd_line, "--geometry=", 11) == 0)
272                                 {
273                                 if (!*geometry) *geometry = g_strdup(cmd_line + 11);
274                                 }
275                         else if (strcmp(cmd_line, "-r") == 0 ||
276                                  strcmp(cmd_line, "--remote") == 0)
277                                 {
278                                 if (!remote_do)
279                                         {
280                                         remote_do = TRUE;
281                                         remote_list = remote_build_list(remote_list, argc - i, &argv[i], &remote_errors);
282                                         }
283                                 }
284                         else if (strcmp(cmd_line, "-rh") == 0 ||
285                                  strcmp(cmd_line, "--remote-help") == 0)
286                                 {
287                                 remote_help();
288                                 exit(0);
289                                 }
290                         else if (strcmp(cmd_line, "--blank") == 0)
291                                 {
292                                 startup_blank = TRUE;
293                                 }
294                         else if (strcmp(cmd_line, "-v") == 0 ||
295                                  strcmp(cmd_line, "--version") == 0)
296                                 {
297                                 printf_term("%s %s\n", GQ_APPNAME, VERSION);
298                                 exit(0);
299                                 }
300                         else if (strcmp(cmd_line, "--alternate") == 0)
301                                 {
302                                 /* enable faster experimental algorithm */
303                                 log_printf("Alternate similarity algorithm enabled\n");
304                                 image_sim_alternate_set(TRUE);
305                                 }
306                         else if (strcmp(cmd_line, "-h") == 0 ||
307                                  strcmp(cmd_line, "--help") == 0)
308                                 {
309                                 printf_term("%s %s\n", GQ_APPNAME, VERSION);
310                                 printf_term(_("Usage: %s [options] [path]\n\n"), GQ_APPNAME_LC);
311                                 print_term(_("valid options are:\n"));
312                                 print_term(_("  +t, --with-tools           force show of tools\n"));
313                                 print_term(_("  -t, --without-tools        force hide of tools\n"));
314                                 print_term(_("  -f, --fullscreen           start in full screen mode\n"));
315                                 print_term(_("  -s, --slideshow            start in slideshow mode\n"));
316                                 print_term(_("  -l, --list                 open collection window for command line\n"));
317                                 print_term(_("      --geometry=GEOMETRY    set main window location\n"));
318                                 print_term(_("  -r, --remote               send following commands to open window\n"));
319                                 print_term(_("  -rh,--remote-help          print remote command list\n"));
320 #ifdef DEBUG
321                                 print_term(_("  --debug[=level]            turn on debug output\n"));
322 #endif
323                                 print_term(_("  -v, --version              print version info\n"));
324                                 print_term(_("  -h, --help                 show this message\n\n"));
325
326 #if 0
327                                 /* these options are not officially supported!
328                                  * only for testing new features, no need to translate them */
329                                 print_term(  "  --alternate                use alternate similarity algorithm\n");
330 #endif
331
332                                 exit(0);
333                                 }
334                         else if (!remote_do)
335                                 {
336                                 printf_term(_("invalid or ignored: %s\nUse --help for options\n"), cmd_line);
337                                 }
338
339                         g_free(cmd_all);
340                         i++;
341                         }
342                 g_free(base_dir);
343                 parse_out_relatives(*path);
344                 parse_out_relatives(*file);
345                 }
346
347         list = g_list_reverse(list);
348
349         if (!*path && first_dir)
350                 {
351                 *path = first_dir;
352                 first_dir = NULL;
353
354                 parse_out_relatives(*path);
355                 }
356         g_free(first_dir);
357
358         if (remote_do)
359                 {
360                 if (remote_errors)
361                         {
362                         GList *work = remote_errors;
363                         
364                         printf_term(_("Invalid or ignored remote options: "));
365                         while (work)
366                                 {
367                                 gchar *opt = work->data;
368                                                 
369                                 printf_term("%s%s", (work == remote_errors) ? "" : ", ", opt);
370                                 work = work->next;
371                                 }
372
373                         printf_term(_("\nUse --remote-help for valid remote options.\n"));
374                         }
375
376                 remote_control(argv[0], remote_list, *path, list, *collection_list);
377                 }
378         g_list_free(remote_list);
379
380         if (list && list->next)
381                 {
382                 *cmd_list = list;
383                 }
384         else
385                 {
386                 filelist_free(list);
387                 *cmd_list = NULL;
388                 }
389 }
390
391 static void parse_command_line_for_debug_option(gint argc, gchar *argv[])
392 {
393 #ifdef DEBUG
394         const gchar *debug_option = "--debug";
395         gint len = strlen(debug_option);
396
397         if (argc > 1)
398                 {
399                 gint i;
400
401                 for (i = 1; i < argc; i++)
402                         {
403                         const gchar *cmd_line = argv[i];
404                         if (strncmp(cmd_line, debug_option, len) == 0)
405                                 {
406                                 gint cmd_line_len = strlen(cmd_line);
407
408                                 /* we now increment the debug state for verbosity */
409                                 if (cmd_line_len == len)
410                                         debug_level_add(1);
411                                 else if (cmd_line[len] == '=' && g_ascii_isdigit(cmd_line[len+1]))
412                                         {
413                                         gint n = atoi(cmd_line + len + 1);
414                                         if (n < 0) n = 1;
415                                         debug_level_add(n);
416                                         }
417                                 }
418                         }
419                 }
420
421         DEBUG_1("debugging output enabled (level %d)", get_debug_level());
422 #endif
423 }
424
425 /*
426  *-----------------------------------------------------------------------------
427  * startup, init, and exit
428  *-----------------------------------------------------------------------------
429  */
430
431 #define RC_HISTORY_NAME "history"
432
433 static void keys_load(void)
434 {
435         gchar *path;
436
437         path = g_build_filename(get_rc_dir(), RC_HISTORY_NAME, NULL);
438         history_list_load(path);
439         g_free(path);
440 }
441
442 static void keys_save(void)
443 {
444         gchar *path;
445
446         path = g_build_filename(get_rc_dir(), RC_HISTORY_NAME, NULL);
447         history_list_save(path);
448         g_free(path);
449 }
450
451 static void mkdir_if_not_exists(const gchar *path)
452 {
453         if (isdir(path)) return;
454
455         log_printf(_("Creating %s dir:%s\n"), GQ_APPNAME, path);
456
457         if (!recursive_mkdir_if_not_exists(path, 0755))
458                 {
459                 log_printf(_("Could not create dir:%s\n"), path);
460                 }
461 }
462
463
464 /* We add to duplicate and modify  gtk_accel_map_print() and gtk_accel_map_save()
465  * to improve the reliability in special cases (especially when disk is full)
466  * These functions are now using secure saving stuff.
467  */
468 static void gq_accel_map_print(
469                     gpointer    data,
470                     const gchar *accel_path,
471                     guint       accel_key,
472                     GdkModifierType accel_mods,
473                     gboolean    changed)
474 {
475         GString *gstring = g_string_new(changed ? NULL : "; ");
476         SecureSaveInfo *ssi = data;
477         gchar *tmp, *name;
478
479         g_string_append(gstring, "(gtk_accel_path \"");
480
481         tmp = g_strescape(accel_path, NULL);
482         g_string_append(gstring, tmp);
483         g_free(tmp);
484
485         g_string_append(gstring, "\" \"");
486
487         name = gtk_accelerator_name(accel_key, accel_mods);
488         tmp = g_strescape(name, NULL);
489         g_free(name);
490         g_string_append(gstring, tmp);
491         g_free(tmp);
492
493         g_string_append(gstring, "\")\n");
494
495         secure_fwrite(gstring->str, sizeof(*gstring->str), gstring->len, ssi);
496
497         g_string_free(gstring, TRUE);
498 }
499
500 static gboolean gq_accel_map_save(const gchar *path)
501 {
502         gchar *pathl;
503         SecureSaveInfo *ssi;
504         GString *gstring;
505
506         pathl = path_from_utf8(path);
507         ssi = secure_open(pathl);
508         g_free(pathl);
509         if (!ssi)
510                 {
511                 log_printf(_("error saving file: %s\n"), path);
512                 return FALSE;
513                 }
514         
515         gstring = g_string_new("; ");
516         if (g_get_prgname())
517                 g_string_append(gstring, g_get_prgname());
518         g_string_append(gstring, " GtkAccelMap rc-file         -*- scheme -*-\n");
519         g_string_append(gstring, "; this file is an automated accelerator map dump\n");
520         g_string_append(gstring, ";\n");
521
522         secure_fwrite(gstring->str, sizeof(*gstring->str), gstring->len, ssi);
523
524         g_string_free(gstring, TRUE);
525
526         gtk_accel_map_foreach((gpointer) ssi, gq_accel_map_print);
527
528         if (secure_close(ssi))
529                 {
530                 log_printf(_("error saving file: %s\nerror: %s\n"), path,
531                            secsave_strerror(secsave_errno));
532                 return FALSE;
533                 }
534
535         return TRUE;
536 }
537
538 static gchar *accep_map_filename(void)
539 {
540         return g_build_filename(get_rc_dir(), "accels", NULL);
541 }
542
543 static void accel_map_save(void)
544 {
545         gchar *path;
546
547         path = accep_map_filename();
548         gq_accel_map_save(path);
549         g_free(path);
550 }
551
552 static void accel_map_load(void)
553 {
554         gchar *path;
555         gchar *pathl;
556
557         path = accep_map_filename();
558         pathl = path_from_utf8(path);
559         gtk_accel_map_load(pathl);
560         g_free(pathl);
561         g_free(path);
562 }
563
564 static void gtkrc_load(void)
565 {
566         gchar *path;
567         gchar *pathl;
568
569         /* If a gtkrc file exists in the rc directory, add it to the
570          * list of files to be parsed at the end of gtk_init() */
571         path = g_build_filename(get_rc_dir(), "gtkrc", NULL);
572         pathl = path_from_utf8(path);
573         if (access(pathl, R_OK) == 0)
574                 gtk_rc_add_default_file(pathl);
575         g_free(pathl);
576         g_free(path);
577 }
578
579 static void exit_program_final(void)
580 {
581         LayoutWindow *lw = NULL;
582
583         remote_close(remote_connection);
584
585         collect_manager_flush();
586
587         save_options(options);
588         keys_save();
589         accel_map_save();
590
591         if (layout_valid(&lw))
592                 {
593                 layout_free(lw);
594                 }
595
596         gtk_main_quit();
597 }
598
599 static GenericDialog *exit_dialog = NULL;
600
601 static void exit_confirm_cancel_cb(GenericDialog *gd, gpointer data)
602 {
603         exit_dialog = NULL;
604         generic_dialog_close(gd);
605 }
606
607 static void exit_confirm_exit_cb(GenericDialog *gd, gpointer data)
608 {
609         exit_dialog = NULL;
610         generic_dialog_close(gd);
611         exit_program_final();
612 }
613
614 static gint exit_confirm_dlg(void)
615 {
616         GtkWidget *parent;
617         LayoutWindow *lw;
618         gchar *msg;
619
620         if (exit_dialog)
621                 {
622                 gtk_window_present(GTK_WINDOW(exit_dialog->dialog));
623                 return TRUE;
624                 }
625
626         if (!collection_window_modified_exists()) return FALSE;
627
628         parent = NULL;
629         lw = NULL;
630         if (layout_valid(&lw))
631                 {
632                 parent = lw->window;
633                 }
634
635         msg = g_strdup_printf("%s - %s", GQ_APPNAME, _("exit"));
636         exit_dialog = generic_dialog_new(msg,
637                                 "exit", parent, FALSE,
638                                 exit_confirm_cancel_cb, NULL);
639         g_free(msg);
640         msg = g_strdup_printf(_("Quit %s"), GQ_APPNAME);
641         generic_dialog_add_message(exit_dialog, GTK_STOCK_DIALOG_QUESTION,
642                                    msg, _("Collections have been modified. Quit anyway?"));
643         g_free(msg);
644         generic_dialog_add_button(exit_dialog, GTK_STOCK_QUIT, NULL, exit_confirm_exit_cb, TRUE);
645
646         gtk_widget_show(exit_dialog->dialog);
647
648         return TRUE;
649 }
650
651 static void exit_program_write_metadata_cb(gint success, const gchar *dest_path, gpointer data)
652 {
653         if (success) exit_program();
654 }
655
656 void exit_program(void)
657 {
658         layout_image_full_screen_stop(NULL);
659
660         if (metadata_write_queue_confirm(exit_program_write_metadata_cb, NULL)) return;
661
662         if (exit_confirm_dlg()) return;
663
664         exit_program_final();
665 }
666
667
668
669 /* This code is supposed to handle situation when a file mmaped by image_loader 
670  * or by exif loader is truncated by some other process.
671  * This is probably not completely correct according to posix, because
672  * mmap is not in the list of calls that can be used safely in signal handler,
673  * but anyway, the handler is called in situation when the application would
674  * crash otherwise.
675  * Ideas for improvement are welcome ;)
676  */
677 /* FIXME: this probably needs some better ifdefs. Please report any compilation problems */
678
679 #ifdef SIGBUS
680 static void sigbus_handler_cb(int signum, siginfo_t *info, void *context)
681 {
682         unsigned long pagesize = sysconf(_SC_PAGE_SIZE);
683         DEBUG_1("SIGBUS %p", info->si_addr);
684         mmap((void *)(((unsigned long)info->si_addr / pagesize) * pagesize), pagesize, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
685 }
686 #endif
687
688 static void setup_sigbus_handler(void)
689 {
690 #ifdef SIGBUS
691         struct sigaction sigbus_action;
692         sigfillset(&sigbus_action.sa_mask);
693         sigbus_action.sa_sigaction = sigbus_handler_cb;
694         sigbus_action.sa_flags = SA_SIGINFO;
695
696         sigaction(SIGBUS, &sigbus_action, NULL);
697 #endif
698 }
699
700 gint main(gint argc, gchar *argv[])
701 {
702         LayoutWindow *lw;
703         gchar *path = NULL;
704         gchar *cmd_path = NULL;
705         gchar *cmd_file = NULL;
706         GList *cmd_list = NULL;
707         GList *collection_list = NULL;
708         CollectionData *first_collection = NULL;
709         gchar *geometry = NULL;
710         gchar *buf;
711         CollectionData *cd = NULL;
712
713 #ifdef HAVE_GTHREAD
714         g_thread_init (NULL);
715         gdk_threads_init();
716         gdk_threads_enter();
717 #endif
718         
719         /* init execution time counter (debug only) */
720         init_exec_time();
721
722         /* setup locale, i18n */
723         gtk_set_locale();
724
725 #ifdef ENABLE_NLS
726         bindtextdomain(PACKAGE, GQ_LOCALEDIR);
727         bind_textdomain_codeset(PACKAGE, "UTF-8");
728         textdomain(PACKAGE);
729 #endif
730
731         /* setup random seed for random slideshow */
732         srand(time(NULL));
733
734 #if 1
735         log_printf("%s %s, This is an alpha release.\n", GQ_APPNAME, VERSION);
736 #endif
737
738         setup_sigbus_handler();
739
740         /* register global notify functions */
741         file_data_register_notify_func(cache_notify_cb, NULL, NOTIFY_PRIORITY_HIGH);
742         file_data_register_notify_func(thumb_notify_cb, NULL, NOTIFY_PRIORITY_HIGH);
743         file_data_register_notify_func(collect_manager_notify_cb, NULL, NOTIFY_PRIORITY_LOW);
744
745         parse_command_line_for_debug_option(argc, argv);
746
747         options = init_options(NULL);
748         setup_default_options(options);
749         load_options(options);
750
751         gtkrc_load();
752         gtk_init(&argc, &argv);
753
754         parse_command_line(argc, argv, &cmd_path, &cmd_file, &cmd_list, &collection_list, &geometry);
755
756         if (gtk_major_version < GTK_MAJOR_VERSION ||
757             (gtk_major_version == GTK_MAJOR_VERSION && gtk_minor_version < GTK_MINOR_VERSION) )
758                 {
759                 log_printf("!!! This is a friendly warning.\n");
760                 log_printf("!!! The version of GTK+ in use now is older than when %s was compiled.\n", GQ_APPNAME);
761                 log_printf("!!!  compiled with GTK+-%d.%d\n", GTK_MAJOR_VERSION, GTK_MINOR_VERSION);
762                 log_printf("!!!   running with GTK+-%d.%d\n", gtk_major_version, gtk_minor_version);
763                 log_printf("!!! %s may quit unexpectedly with a relocation error.\n", GQ_APPNAME);
764                 }
765
766         mkdir_if_not_exists(get_rc_dir());
767         mkdir_if_not_exists(get_collections_dir());
768         mkdir_if_not_exists(get_thumbnails_cache_dir());
769         mkdir_if_not_exists(get_metadata_cache_dir());
770
771         keys_load();
772         filter_add_defaults();
773         filter_rebuild();
774
775         editor_load_descriptions();
776
777         accel_map_load();
778
779         if (startup_blank)
780                 {
781                 g_free(cmd_path);
782                 cmd_path = NULL;
783                 g_free(cmd_file);
784                 cmd_file = NULL;
785                 filelist_free(cmd_list);
786                 cmd_list = NULL;
787                 string_list_free(collection_list);
788                 collection_list = NULL;
789
790                 path = NULL;
791                 }
792         else if (cmd_path)
793                 {
794                 path = g_strdup(cmd_path);
795                 }
796         else if (options->startup.restore_path && options->startup.path && isdir(options->startup.path))
797                 {
798                 path = g_strdup(options->startup.path);
799                 }
800         else
801                 {
802                 path = get_current_dir();
803                 }
804
805         lw = layout_new_with_geometry(NULL, options->layout.tools_float, options->layout.tools_hidden, geometry);
806         layout_sort_set(lw, options->file_sort.method, options->file_sort.ascending);
807
808         if (collection_list && !startup_command_line_collection)
809                 {
810                 GList *work;
811
812                 work = collection_list;
813                 while (work)
814                         {
815                         CollectWindow *cw;
816                         const gchar *path;
817
818                         path = work->data;
819                         work = work->next;
820
821                         cw = collection_window_new(path);
822                         if (!first_collection && cw) first_collection = cw->cd;
823                         }
824                 }
825
826         if (cmd_list ||
827             (startup_command_line_collection && collection_list))
828                 {
829                 GList *work;
830
831                 if (startup_command_line_collection)
832                         {
833                         CollectWindow *cw;
834
835                         cw = collection_window_new("");
836                         cd = cw->cd;
837                         }
838                 else
839                         {
840                         cd = collection_new("");        /* if we pass NULL, untitled counter is falsely increm. */
841                         }
842
843                 g_free(cd->path);
844                 cd->path = NULL;
845                 g_free(cd->name);
846                 cd->name = g_strdup(_("Command line"));
847
848                 collection_path_changed(cd);
849
850                 work = cmd_list;
851                 while (work)
852                         {
853                         collection_add(cd, file_data_new_simple((gchar *)work->data), FALSE);
854                         work = work->next;
855                         }
856
857                 work = collection_list;
858                 while (work)
859                         {
860                         collection_load(cd, (gchar *)work->data, COLLECTION_LOAD_APPEND);
861                         work = work->next;
862                         }
863
864                 layout_set_path(lw, path);
865                 if (cd->list) layout_image_set_collection(lw, cd, cd->list->data);
866
867                 /* mem leak, we never unref this collection when !startup_command_line_collection
868                  * (the image view of the main window does not hold a ref to the collection)
869                  * this is sort of unavoidable, for if it did hold a ref, next/back
870                  * may not work as expected when closing collection windows.
871                  *
872                  * collection_unref(cd);
873                  */
874
875                 }
876         else if (cmd_file)
877                 {
878                 layout_set_path(lw, cmd_file);
879                 }
880         else
881                 {
882                 layout_set_path(lw, path);
883                 if (first_collection)
884                         {
885                         layout_image_set_collection(lw, first_collection,
886                                                     collection_get_first(first_collection));
887                         }
888                 }
889
890         image_osd_set(lw->image, options->image_overlay.common.state | (options->image_overlay.common.show_at_startup ? OSD_SHOW_INFO : OSD_SHOW_NOTHING));
891
892         g_free(geometry);
893         g_free(cmd_path);
894         g_free(cmd_file);
895         filelist_free(cmd_list);
896         string_list_free(collection_list);
897         g_free(path);
898
899         if (startup_full_screen) layout_image_full_screen_start(lw);
900         if (startup_in_slideshow) layout_image_slideshow_start(lw);
901
902         buf = g_build_filename(get_rc_dir(), ".command", NULL);
903         remote_connection = remote_server_init(buf, cd);
904         g_free(buf);
905         
906         gtk_main();
907 #ifdef HAVE_GTHREAD
908         gdk_threads_leave();
909 #endif
910         return 0;
911 }
912 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */