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