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