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