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