Update command line options documentation
[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 [files] [collections] open collection window for command line\n"));
328                                 print_term(_("      --blank                      start with blank file list\n"));
329                                 print_term(_("      --geometry=XxY+XOFF+YOFF     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
345                                 exit(0);
346                                 }
347                         else if (!remote_do)
348                                 {
349                                 printf_term(_("invalid or ignored: %s\nUse --help for options\n"), cmd_line);
350                                 }
351
352                         g_free(cmd_all);
353                         g_free(cmd_line);
354                         i++;
355                         }
356                 g_free(base_dir);
357                 parse_out_relatives(command_line->path);
358                 parse_out_relatives(command_line->file);
359                 }
360
361         list = g_list_reverse(list);
362
363         if (!command_line->path && first_dir)
364                 {
365                 command_line->path = first_dir;
366                 first_dir = NULL;
367
368                 parse_out_relatives(command_line->path);
369                 }
370         g_free(first_dir);
371
372         if (remote_do)
373                 {
374                 if (remote_errors)
375                         {
376                         GList *work = remote_errors;
377
378                         printf_term(_("Invalid or ignored remote options: "));
379                         while (work)
380                                 {
381                                 gchar *opt = work->data;
382
383                                 printf_term("%s%s", (work == remote_errors) ? "" : ", ", opt);
384                                 work = work->next;
385                                 }
386
387                         printf_term(_("\nUse --remote-help for valid remote options.\n"));
388                         }
389
390                 remote_control(argv[0], remote_list, command_line->path, list, command_line->collection_list);
391                 }
392         g_list_free(remote_list);
393
394         if (list && list->next)
395                 {
396                 command_line->cmd_list = list;
397                 }
398         else
399                 {
400                 filelist_free(list);
401                 command_line->cmd_list = NULL;
402                 }
403
404         if (command_line->startup_blank)
405                 {
406                 g_free(command_line->path);
407                 command_line->path = NULL;
408                 g_free(command_line->file);
409                 command_line->file = NULL;
410                 filelist_free(command_line->cmd_list);
411                 command_line->cmd_list = NULL;
412                 string_list_free(command_line->collection_list);
413                 command_line->collection_list = NULL;
414                 }
415 }
416
417 static void parse_command_line_for_debug_option(gint argc, gchar *argv[])
418 {
419 #ifdef DEBUG
420         const gchar *debug_option = "--debug";
421         gint len = strlen(debug_option);
422
423         if (argc > 1)
424                 {
425                 gint i;
426
427                 for (i = 1; i < argc; i++)
428                         {
429                         const gchar *cmd_line = argv[i];
430                         if (strncmp(cmd_line, debug_option, len) == 0)
431                                 {
432                                 gint cmd_line_len = strlen(cmd_line);
433
434                                 /* we now increment the debug state for verbosity */
435                                 if (cmd_line_len == len)
436                                         debug_level_add(1);
437                                 else if (cmd_line[len] == '=' && g_ascii_isdigit(cmd_line[len+1]))
438                                         {
439                                         gint n = atoi(cmd_line + len + 1);
440                                         if (n < 0) n = 1;
441                                         debug_level_add(n);
442                                         }
443                                 }
444                         }
445                 }
446
447         DEBUG_1("debugging output enabled (level %d)", get_debug_level());
448 #endif
449 }
450
451 /*
452  *-----------------------------------------------------------------------------
453  * startup, init, and exit
454  *-----------------------------------------------------------------------------
455  */
456
457 #define RC_HISTORY_NAME "history"
458
459 static void setup_env_path(void)
460 {
461         const gchar *old_path = g_getenv("PATH");
462         gchar *path = g_strconcat(GQ_BIN_DIR, ":", old_path, NULL);
463         g_setenv("PATH", path, TRUE);
464         g_free(path);
465 }
466
467 static void keys_load(void)
468 {
469         gchar *path;
470
471         path = g_build_filename(get_rc_dir(), RC_HISTORY_NAME, NULL);
472         history_list_load(path);
473         g_free(path);
474 }
475
476 static void keys_save(void)
477 {
478         gchar *path;
479
480         path = g_build_filename(get_rc_dir(), RC_HISTORY_NAME, NULL);
481         history_list_save(path);
482         g_free(path);
483 }
484
485 static void mkdir_if_not_exists(const gchar *path)
486 {
487         if (isdir(path)) return;
488
489         log_printf(_("Creating %s dir:%s\n"), GQ_APPNAME, path);
490
491         if (!recursive_mkdir_if_not_exists(path, 0755))
492                 {
493                 log_printf(_("Could not create dir:%s\n"), path);
494                 }
495 }
496
497
498 /* We add to duplicate and modify  gtk_accel_map_print() and gtk_accel_map_save()
499  * to improve the reliability in special cases (especially when disk is full)
500  * These functions are now using secure saving stuff.
501  */
502 static void gq_accel_map_print(
503                     gpointer    data,
504                     const gchar *accel_path,
505                     guint       accel_key,
506                     GdkModifierType accel_mods,
507                     gboolean    changed)
508 {
509         GString *gstring = g_string_new(changed ? NULL : "; ");
510         SecureSaveInfo *ssi = data;
511         gchar *tmp, *name;
512
513         g_string_append(gstring, "(gtk_accel_path \"");
514
515         tmp = g_strescape(accel_path, NULL);
516         g_string_append(gstring, tmp);
517         g_free(tmp);
518
519         g_string_append(gstring, "\" \"");
520
521         name = gtk_accelerator_name(accel_key, accel_mods);
522         tmp = g_strescape(name, NULL);
523         g_free(name);
524         g_string_append(gstring, tmp);
525         g_free(tmp);
526
527         g_string_append(gstring, "\")\n");
528
529         secure_fwrite(gstring->str, sizeof(*gstring->str), gstring->len, ssi);
530
531         g_string_free(gstring, TRUE);
532 }
533
534 static gboolean gq_accel_map_save(const gchar *path)
535 {
536         gchar *pathl;
537         SecureSaveInfo *ssi;
538         GString *gstring;
539
540         pathl = path_from_utf8(path);
541         ssi = secure_open(pathl);
542         g_free(pathl);
543         if (!ssi)
544                 {
545                 log_printf(_("error saving file: %s\n"), path);
546                 return FALSE;
547                 }
548
549         gstring = g_string_new("; ");
550         if (g_get_prgname())
551                 g_string_append(gstring, g_get_prgname());
552         g_string_append(gstring, " GtkAccelMap rc-file         -*- scheme -*-\n");
553         g_string_append(gstring, "; this file is an automated accelerator map dump\n");
554         g_string_append(gstring, ";\n");
555
556         secure_fwrite(gstring->str, sizeof(*gstring->str), gstring->len, ssi);
557
558         g_string_free(gstring, TRUE);
559
560         gtk_accel_map_foreach((gpointer) ssi, gq_accel_map_print);
561
562         if (secure_close(ssi))
563                 {
564                 log_printf(_("error saving file: %s\nerror: %s\n"), path,
565                            secsave_strerror(secsave_errno));
566                 return FALSE;
567                 }
568
569         return TRUE;
570 }
571
572 static gchar *accep_map_filename(void)
573 {
574         return g_build_filename(get_rc_dir(), "accels", NULL);
575 }
576
577 static void accel_map_save(void)
578 {
579         gchar *path;
580
581         path = accep_map_filename();
582         gq_accel_map_save(path);
583         g_free(path);
584 }
585
586 static void accel_map_load(void)
587 {
588         gchar *path;
589         gchar *pathl;
590
591         path = accep_map_filename();
592         pathl = path_from_utf8(path);
593         gtk_accel_map_load(pathl);
594         g_free(pathl);
595         g_free(path);
596 }
597
598 static void gtkrc_load(void)
599 {
600         gchar *path;
601         gchar *pathl;
602
603         /* If a gtkrc file exists in the rc directory, add it to the
604          * list of files to be parsed at the end of gtk_init() */
605         path = g_build_filename(get_rc_dir(), "gtkrc", NULL);
606         pathl = path_from_utf8(path);
607         if (access(pathl, R_OK) == 0)
608                 gtk_rc_add_default_file(pathl);
609         g_free(pathl);
610         g_free(path);
611 }
612
613 static void exit_program_final(void)
614 {
615         LayoutWindow *lw = NULL;
616
617          /* make sure that external editors are loaded, we would save incomplete configuration otherwise */
618         layout_editors_reload_finish();
619
620         remote_close(remote_connection);
621
622         collect_manager_flush();
623
624         save_options(options);
625         keys_save();
626         accel_map_save();
627
628         if (layout_valid(&lw))
629                 {
630                 layout_free(lw);
631                 }
632
633         gtk_main_quit();
634 }
635
636 static GenericDialog *exit_dialog = NULL;
637
638 static void exit_confirm_cancel_cb(GenericDialog *gd, gpointer data)
639 {
640         exit_dialog = NULL;
641         generic_dialog_close(gd);
642 }
643
644 static void exit_confirm_exit_cb(GenericDialog *gd, gpointer data)
645 {
646         exit_dialog = NULL;
647         generic_dialog_close(gd);
648         exit_program_final();
649 }
650
651 static gint exit_confirm_dlg(void)
652 {
653         GtkWidget *parent;
654         LayoutWindow *lw;
655         gchar *msg;
656
657         if (exit_dialog)
658                 {
659                 gtk_window_present(GTK_WINDOW(exit_dialog->dialog));
660                 return TRUE;
661                 }
662
663         if (!collection_window_modified_exists()) return FALSE;
664
665         parent = NULL;
666         lw = NULL;
667         if (layout_valid(&lw))
668                 {
669                 parent = lw->window;
670                 }
671
672         msg = g_strdup_printf("%s - %s", GQ_APPNAME, _("exit"));
673         exit_dialog = generic_dialog_new(msg,
674                                 "exit", parent, FALSE,
675                                 exit_confirm_cancel_cb, NULL);
676         g_free(msg);
677         msg = g_strdup_printf(_("Quit %s"), GQ_APPNAME);
678         generic_dialog_add_message(exit_dialog, GTK_STOCK_DIALOG_QUESTION,
679                                    msg, _("Collections have been modified. Quit anyway?"));
680         g_free(msg);
681         generic_dialog_add_button(exit_dialog, GTK_STOCK_QUIT, NULL, exit_confirm_exit_cb, TRUE);
682
683         gtk_widget_show(exit_dialog->dialog);
684
685         return TRUE;
686 }
687
688 static void exit_program_write_metadata_cb(gint success, const gchar *dest_path, gpointer data)
689 {
690         if (success) exit_program();
691 }
692
693 void exit_program(void)
694 {
695         layout_image_full_screen_stop(NULL);
696
697         if (metadata_write_queue_confirm(FALSE, exit_program_write_metadata_cb, NULL)) return;
698
699         if (exit_confirm_dlg()) return;
700
701         exit_program_final();
702 }
703
704 /* This code is supposed to handle situation when a file mmaped by image_loader
705  * or by exif loader is truncated by some other process.
706  * This is probably not completely correct according to posix, because
707  * mmap is not in the list of calls that can be used safely in signal handler,
708  * but anyway, the handler is called in situation when the application would
709  * crash otherwise.
710  * Ideas for improvement are welcome ;)
711  */
712 /* FIXME: this probably needs some better ifdefs. Please report any compilation problems */
713
714 #if defined(SIGBUS) && defined(SA_SIGINFO)
715 static void sigbus_handler_cb(int signum, siginfo_t *info, void *context)
716 {
717         unsigned long pagesize = sysconf(_SC_PAGE_SIZE);
718         DEBUG_1("SIGBUS %p", info->si_addr);
719         mmap((void *)(((unsigned long)info->si_addr / pagesize) * pagesize), pagesize, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
720 }
721 #endif
722
723 static void setup_sigbus_handler(void)
724 {
725 #if defined(SIGBUS) && defined(SA_SIGINFO)
726         struct sigaction sigbus_action;
727         sigfillset(&sigbus_action.sa_mask);
728         sigbus_action.sa_sigaction = sigbus_handler_cb;
729         sigbus_action.sa_flags = SA_SIGINFO;
730
731         sigaction(SIGBUS, &sigbus_action, NULL);
732 #endif
733 }
734
735 gint main(gint argc, gchar *argv[])
736 {
737         CollectionData *first_collection = NULL;
738         gchar *buf;
739         CollectionData *cd = NULL;
740
741 #ifdef HAVE_GTHREAD
742 #if !GLIB_CHECK_VERSION(2,32,0)
743         g_thread_init(NULL);
744 #endif
745         gdk_threads_init();
746         gdk_threads_enter();
747
748 #endif
749
750         /* init execution time counter (debug only) */
751         init_exec_time();
752
753         /* setup locale, i18n */
754         setlocale(LC_ALL, "");
755
756 #ifdef ENABLE_NLS
757         bindtextdomain(PACKAGE, GQ_LOCALEDIR);
758         bind_textdomain_codeset(PACKAGE, "UTF-8");
759         textdomain(PACKAGE);
760 #endif
761
762         exif_init();
763
764 #ifdef HAVE_LUA
765         lua_init();
766 #endif
767
768         /* setup random seed for random slideshow */
769         srand(time(NULL));
770
771         setup_sigbus_handler();
772
773         /* register global notify functions */
774         file_data_register_notify_func(cache_notify_cb, NULL, NOTIFY_PRIORITY_HIGH);
775         file_data_register_notify_func(thumb_notify_cb, NULL, NOTIFY_PRIORITY_HIGH);
776         file_data_register_notify_func(histogram_notify_cb, NULL, NOTIFY_PRIORITY_HIGH);
777         file_data_register_notify_func(collect_manager_notify_cb, NULL, NOTIFY_PRIORITY_LOW);
778         file_data_register_notify_func(metadata_notify_cb, NULL, NOTIFY_PRIORITY_LOW);
779
780
781         gtkrc_load();
782
783         parse_command_line_for_debug_option(argc, argv);
784         DEBUG_1("%s main: gtk_init", get_exec_time());
785 #ifdef HAVE_CLUTTER
786         if (gtk_clutter_init(&argc, &argv) != CLUTTER_INIT_SUCCESS)
787                 {
788                 log_printf("Can't initialize clutter-gtk.\n");
789                 exit(1);
790                 }
791 #else
792         gtk_init(&argc, &argv);
793 #endif
794
795         if (gtk_major_version < GTK_MAJOR_VERSION ||
796             (gtk_major_version == GTK_MAJOR_VERSION && gtk_minor_version < GTK_MINOR_VERSION) )
797                 {
798                 log_printf("!!! This is a friendly warning.\n");
799                 log_printf("!!! The version of GTK+ in use now is older than when %s was compiled.\n", GQ_APPNAME);
800                 log_printf("!!!  compiled with GTK+-%d.%d\n", GTK_MAJOR_VERSION, GTK_MINOR_VERSION);
801                 log_printf("!!!   running with GTK+-%d.%d\n", gtk_major_version, gtk_minor_version);
802                 log_printf("!!! %s may quit unexpectedly with a relocation error.\n", GQ_APPNAME);
803                 }
804
805         DEBUG_1("%s main: pixbuf_inline_register_stock_icons", get_exec_time());
806         pixbuf_inline_register_stock_icons();
807
808         DEBUG_1("%s main: setting default options before commandline handling", get_exec_time());
809         options = init_options(NULL);
810         setup_default_options(options);
811
812         DEBUG_1("%s main: parse_command_line", get_exec_time());
813         parse_command_line(argc, argv);
814
815         DEBUG_1("%s main: mkdir_if_not_exists", get_exec_time());
816         /* these functions don't depend on config file */
817         mkdir_if_not_exists(get_rc_dir());
818         mkdir_if_not_exists(get_collections_dir());
819         mkdir_if_not_exists(get_thumbnails_cache_dir());
820         mkdir_if_not_exists(get_metadata_cache_dir());
821
822         setup_env_path();
823
824         keys_load();
825         accel_map_load();
826
827         /* restore session from the config file */
828
829
830         DEBUG_1("%s main: load_options", get_exec_time());
831         if (!load_options(options))
832                 {
833                 /* load_options calls these functions after it parses global options, we have to call it here if it fails */
834                 filter_add_defaults();
835                 filter_rebuild();
836                 }
837
838         /* handle missing config file and commandline additions*/
839         if (!layout_window_list)
840                 {
841                 /* broken or no config file */
842                 layout_new_from_config(NULL, NULL, TRUE);
843                 }
844
845         layout_editors_reload_start();
846
847         if (command_line->collection_list && !command_line->startup_command_line_collection)
848                 {
849                 GList *work;
850
851                 work = command_line->collection_list;
852                 while (work)
853                         {
854                         CollectWindow *cw;
855                         const gchar *path;
856
857                         path = work->data;
858                         work = work->next;
859
860                         cw = collection_window_new(path);
861                         if (!first_collection && cw) first_collection = cw->cd;
862                         }
863                 }
864
865         if (command_line->cmd_list ||
866             (command_line->startup_command_line_collection && command_line->collection_list))
867                 {
868                 GList *work;
869
870                 if (command_line->startup_command_line_collection)
871                         {
872                         CollectWindow *cw;
873
874                         cw = collection_window_new("");
875                         cd = cw->cd;
876                         }
877                 else
878                         {
879                         cd = collection_new("");        /* if we pass NULL, untitled counter is falsely increm. */
880                         }
881
882                 g_free(cd->path);
883                 cd->path = NULL;
884                 g_free(cd->name);
885                 cd->name = g_strdup(_("Command line"));
886
887                 collection_path_changed(cd);
888
889                 work = command_line->cmd_list;
890                 while (work)
891                         {
892                         collection_add(cd, (FileData *)work->data, FALSE);
893                         work = work->next;
894                         }
895
896                 work = command_line->collection_list;
897                 while (work)
898                         {
899                         collection_load(cd, (gchar *)work->data, COLLECTION_LOAD_APPEND);
900                         work = work->next;
901                         }
902
903                 if (cd->list) layout_image_set_collection(NULL, cd, cd->list->data);
904
905                 /* mem leak, we never unref this collection when !startup_command_line_collection
906                  * (the image view of the main window does not hold a ref to the collection)
907                  * this is sort of unavoidable, for if it did hold a ref, next/back
908                  * may not work as expected when closing collection windows.
909                  *
910                  * collection_unref(cd);
911                  */
912
913                 }
914         else if (first_collection)
915                 {
916                 layout_image_set_collection(NULL, first_collection,
917                                             collection_get_first(first_collection));
918                 }
919
920         buf = g_build_filename(get_rc_dir(), ".command", NULL);
921         remote_connection = remote_server_init(buf, cd);
922         g_free(buf);
923
924         DEBUG_1("%s main: gtk_main", get_exec_time());
925         gtk_main();
926 #ifdef HAVE_GTHREAD
927         gdk_threads_leave();
928 #endif
929         return 0;
930 }
931 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */