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