Fix #344: Ignoring alpha channel
[geeqie.git] / src / layout.c
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "layout.h"
24
25 #include "color-man.h"
26 #include "filedata.h"
27 #include "histogram.h"
28 #include "history_list.h"
29 #include "image.h"
30 #include "image-overlay.h"
31 #include "layout_config.h"
32 #include "layout_image.h"
33 #include "layout_util.h"
34 #include "logwindow.h"
35 #include "menu.h"
36 #include "pixbuf-renderer.h"
37 #include "pixbuf_util.h"
38 #include "utilops.h"
39 #include "view_dir.h"
40 #include "view_file.h"
41 #include "ui_fileops.h"
42 #include "ui_menu.h"
43 #include "ui_misc.h"
44 #include "ui_tabcomp.h"
45 #include "window.h"
46 #include "metadata.h"
47 #include "rcfile.h"
48 #include "bar.h"
49 #include "bar_sort.h"
50 #include "preferences.h"
51 #include "shortcuts.h"
52 #ifdef HAVE_LIRC
53 #include "lirc.h"
54 #endif
55
56 #define MAINWINDOW_DEF_WIDTH 700
57 #define MAINWINDOW_DEF_HEIGHT 500
58
59 #define MAIN_WINDOW_DIV_HPOS (MAINWINDOW_DEF_WIDTH / 2)
60 #define MAIN_WINDOW_DIV_VPOS (MAINWINDOW_DEF_HEIGHT / 2)
61
62 #define TOOLWINDOW_DEF_WIDTH 260
63 #define TOOLWINDOW_DEF_HEIGHT 450
64
65 #define PROGRESS_WIDTH 150
66 #define ZOOM_LABEL_WIDTH 120
67
68 #define PANE_DIVIDER_SIZE 10
69
70
71 GList *layout_window_list = NULL;
72 LayoutWindow *current_lw = NULL;
73
74 static void layout_list_scroll_to_subpart(LayoutWindow *lw, const gchar *needle);
75
76
77 /*
78  *-----------------------------------------------------------------------------
79  * misc
80  *-----------------------------------------------------------------------------
81  */
82
83 gboolean layout_valid(LayoutWindow **lw)
84 {
85         if (*lw == NULL)
86                 {
87                 if (current_lw) *lw = current_lw;
88                 else if (layout_window_list) *lw = layout_window_list->data;
89                 return (*lw != NULL);
90                 }
91         return (g_list_find(layout_window_list, *lw) != NULL);
92 }
93
94 LayoutWindow *layout_find_by_image(ImageWindow *imd)
95 {
96         GList *work;
97
98         work = layout_window_list;
99         while (work)
100                 {
101                 LayoutWindow *lw = work->data;
102                 work = work->next;
103
104                 if (lw->image == imd) return lw;
105                 }
106
107         return NULL;
108 }
109
110 LayoutWindow *layout_find_by_image_fd(ImageWindow *imd)
111 {
112         GList *work;
113
114         work = layout_window_list;
115         while (work)
116                 {
117                 LayoutWindow *lw = work->data;
118                 work = work->next;
119
120                 if (lw->image->image_fd == imd->image_fd)
121                         return lw;
122                 }
123
124         return NULL;
125 }
126
127 LayoutWindow *layout_find_by_layout_id(const gchar *id)
128 {
129         GList *work;
130
131         if (!id || !id[0]) return NULL;
132
133         if (strcmp(id, LAYOUT_ID_CURRENT) == 0)
134                 {
135                 if (current_lw) return current_lw;
136                 if (layout_window_list) return layout_window_list->data;
137                 return NULL;
138                 }
139
140         work = layout_window_list;
141         while (work)
142                 {
143                 LayoutWindow *lw = work->data;
144                 work = work->next;
145
146                 if (lw->options.id && strcmp(id, lw->options.id) == 0)
147                         return lw;
148                 }
149
150         return NULL;
151 }
152
153 gchar *layout_get_unique_id()
154 {
155         char id[10];
156         gint i;
157
158         i = 1;
159         while (TRUE)
160                 {
161                 g_snprintf(id, sizeof(id), "lw%d", i);
162                 if (!layout_find_by_layout_id(id))
163                         {
164                         return g_strdup(id);
165                         }
166                 i++;
167                 }
168 }
169
170 static void layout_set_unique_id(LayoutWindow *lw)
171 {
172         char id[10];
173         gint i;
174         if (lw->options.id && lw->options.id[0]) return; /* id is already set */
175
176         g_free(lw->options.id);
177         lw->options.id = NULL;
178
179         if (!layout_find_by_layout_id("main"))
180                 {
181                 lw->options.id = g_strdup("main");
182                 return;
183                 }
184
185         i = 1;
186         while (TRUE)
187                 {
188                 g_snprintf(id, sizeof(id), "lw%d", i);
189                 if (!layout_find_by_layout_id(id))
190                         {
191                         lw->options.id = g_strdup(id);
192                         return;
193                         }
194                 i++;
195                 }
196 }
197
198 static gboolean layout_set_current_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
199 {
200         LayoutWindow *lw = data;
201         current_lw = lw;
202         return FALSE;
203 }
204
205 static void layout_box_folders_changed_cb(GtkWidget *widget, gpointer data)
206 {
207         LayoutWindow *lw;
208         GList *work;
209
210 /* FIXME: this is probably not the correct way to implement this */
211         work = layout_window_list;
212         while (work)
213                 {
214                 lw = work->data;
215                 lw->options.folder_window.vdivider_pos = gtk_paned_get_position(GTK_PANED(widget));
216                 work = work->next;
217                 }
218 }
219
220 /*
221  *-----------------------------------------------------------------------------
222  * menu, toolbar, and dir view
223  *-----------------------------------------------------------------------------
224  */
225
226 static void layout_path_entry_changed_cb(GtkWidget *widget, gpointer data)
227 {
228         LayoutWindow *lw = data;
229         gchar *buf;
230
231         if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) < 0) return;
232
233         buf = g_strdup(gtk_entry_get_text(GTK_ENTRY(lw->path_entry)));
234         if (!lw->dir_fd || strcmp(buf, lw->dir_fd->path) != 0)
235                 {
236                 layout_set_path(lw, buf);
237                 }
238
239         g_free(buf);
240 }
241
242 static void layout_path_entry_tab_cb(const gchar *path, gpointer data)
243 {
244         LayoutWindow *lw = data;
245         gchar *buf;
246
247         buf = g_strdup(path);
248         parse_out_relatives(buf);
249
250         if (isdir(buf))
251                 {
252                 if ((!lw->dir_fd || strcmp(lw->dir_fd->path, buf) != 0) && layout_set_path(lw, buf))
253                         {
254                         gtk_widget_grab_focus(GTK_WIDGET(lw->path_entry));
255                         gint pos = -1;
256                         /* put the G_DIR_SEPARATOR back, if we are in tab completion for a dir and result was path change */
257                         gtk_editable_insert_text(GTK_EDITABLE(lw->path_entry), G_DIR_SEPARATOR_S, -1, &pos);
258                         gtk_editable_set_position(GTK_EDITABLE(lw->path_entry),
259                                                   strlen(gtk_entry_get_text(GTK_ENTRY(lw->path_entry))));
260                         }
261                 }
262         else if (lw->dir_fd)
263                 {
264                 gchar *base = remove_level_from_path(buf);
265
266                 if (strcmp(lw->dir_fd->path, base) == 0)
267                         {
268                         layout_list_scroll_to_subpart(lw, filename_from_path(buf));
269                         }
270                 g_free(base);
271                 }
272
273         g_free(buf);
274 }
275
276 static void layout_path_entry_cb(const gchar *path, gpointer data)
277 {
278         LayoutWindow *lw = data;
279         gchar *buf;
280
281         buf = g_strdup(path);
282
283         if (!download_web_file(buf, FALSE, lw))
284                 {
285                 parse_out_relatives(buf);
286
287                 layout_set_path(lw, buf);
288                 }
289
290         g_free(buf);
291 }
292
293 static void layout_vd_select_cb(ViewDir *vd, FileData *fd, gpointer data)
294 {
295         LayoutWindow *lw = data;
296
297         layout_set_fd(lw, fd);
298 }
299
300 static void layout_path_entry_tab_append_cb(const gchar *path, gpointer data, gint n)
301 {
302         LayoutWindow *lw = data;
303
304         if (!lw || !lw->back_button) return;
305         if (!layout_valid(&lw)) return;
306
307         /* Enable back button if it makes sense */
308         gtk_widget_set_sensitive(lw->back_button, (n > 1));
309 }
310
311 static gboolean path_entry_tooltip_cb(GtkWidget *widget, gpointer data)
312 {
313         GList *box_child_list;
314         GtkComboBox *path_entry;
315         gchar *current_path;
316
317         box_child_list = gtk_container_get_children(GTK_CONTAINER(widget));
318         path_entry = box_child_list->data;
319         current_path = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(path_entry));
320         gtk_widget_set_tooltip_text(GTK_WIDGET(widget), current_path);
321
322         g_free(current_path);
323         g_list_free(box_child_list);
324
325         return FALSE;
326 }
327
328 static GtkWidget *layout_tool_setup(LayoutWindow *lw)
329 {
330         GtkWidget *box;
331         GtkWidget *box_folders;
332         GtkWidget *scd;
333         GtkWidget *menu_tool_bar;
334         GtkWidget *tabcomp;
335
336         box = gtk_vbox_new(FALSE, 0);
337
338         menu_tool_bar = layout_actions_menu_tool_bar(lw);
339         DEBUG_NAME(menu_tool_bar);
340         gtk_widget_show(menu_tool_bar);
341         gtk_box_pack_start(GTK_BOX(lw->main_box), lw->menu_tool_bar, FALSE, FALSE, 0);
342         if (lw->options.toolbar_hidden) gtk_widget_hide(lw->toolbar[TOOLBAR_MAIN]);
343
344         tabcomp = tab_completion_new_with_history(&lw->path_entry, NULL, "path_list", -1,
345                                                   layout_path_entry_cb, lw);
346         DEBUG_NAME(tabcomp);
347         tab_completion_add_tab_func(lw->path_entry, layout_path_entry_tab_cb, lw);
348         tab_completion_add_append_func(lw->path_entry, layout_path_entry_tab_append_cb, lw);
349         gtk_box_pack_start(GTK_BOX(box), tabcomp, FALSE, FALSE, 0);
350         gtk_widget_show(tabcomp);
351         gtk_widget_set_has_tooltip(GTK_WIDGET(tabcomp), TRUE);
352         g_signal_connect(G_OBJECT(tabcomp), "query_tooltip", G_CALLBACK(path_entry_tooltip_cb), lw);
353
354 #if GTK_CHECK_VERSION(3,20,0)
355         g_signal_connect(G_OBJECT(gtk_widget_get_parent(gtk_widget_get_parent(lw->path_entry))), "changed",
356                          G_CALLBACK(layout_path_entry_changed_cb), lw);
357 #else
358         g_signal_connect(G_OBJECT(gtk_widget_get_parent(lw->path_entry)), "changed",
359                          G_CALLBACK(layout_path_entry_changed_cb), lw);
360 #endif
361
362         box_folders = GTK_WIDGET(gtk_hpaned_new());
363         DEBUG_NAME(box_folders);
364         gtk_box_pack_start(GTK_BOX(box), box_folders, TRUE, TRUE, 0);
365
366         lw->vd = vd_new(lw);
367
368         vd_set_select_func(lw->vd, layout_vd_select_cb, lw);
369
370         lw->dir_view = lw->vd->widget;
371         DEBUG_NAME(lw->dir_view);
372         gtk_paned_add2(GTK_PANED(box_folders), lw->dir_view);
373         gtk_widget_show(lw->dir_view);
374
375         scd = shortcuts_new_default(lw);
376         DEBUG_NAME(scd);
377         gtk_paned_add1(GTK_PANED(box_folders), scd);
378         gtk_paned_set_position(GTK_PANED(box_folders), lw->options.folder_window.vdivider_pos);
379
380         gtk_widget_show(box_folders);
381
382         g_signal_connect(G_OBJECT(box_folders), "notify::position",
383                          G_CALLBACK(layout_box_folders_changed_cb), lw);
384
385         gtk_widget_show(box);
386
387         return box;
388 }
389
390 /*
391  *-----------------------------------------------------------------------------
392  * sort button (and menu)
393  *-----------------------------------------------------------------------------
394  */
395
396 static void layout_sort_menu_cb(GtkWidget *widget, gpointer data)
397 {
398         LayoutWindow *lw;
399         SortType type;
400
401         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
402
403         lw = submenu_item_get_data(widget);
404         if (!lw) return;
405
406         type = (SortType)GPOINTER_TO_INT(data);
407
408         if (type == SORT_EXIFTIME || type == SORT_EXIFTIMEDIGITIZED || type == SORT_RATING)
409                 {
410                 vf_read_metadata_in_idle(lw->vf);
411                 }
412         layout_sort_set(lw, type, lw->sort_ascend);
413 }
414
415 static void layout_sort_menu_ascend_cb(GtkWidget *widget, gpointer data)
416 {
417         LayoutWindow *lw = data;
418
419         layout_sort_set(lw, lw->sort_method, !lw->sort_ascend);
420 }
421
422 static void layout_sort_menu_hide_cb(GtkWidget *widget, gpointer data)
423 {
424         /* destroy the menu */
425         g_object_unref(widget);
426 }
427
428 static void layout_sort_button_press_cb(GtkWidget *widget, gpointer data)
429 {
430         LayoutWindow *lw = data;
431         GtkWidget *menu;
432         GdkEvent *event;
433         guint32 etime;
434
435         menu = submenu_add_sort(NULL, G_CALLBACK(layout_sort_menu_cb), lw, FALSE, FALSE, TRUE, lw->sort_method);
436
437         /* take ownership of menu */
438 #ifdef GTK_OBJECT_FLOATING
439         /* GTK+ < 2.10 */
440         g_object_ref(G_OBJECT(menu));
441         gtk_object_sink(GTK_OBJECT(menu));
442 #else
443         /* GTK+ >= 2.10 */
444         g_object_ref_sink(G_OBJECT(menu));
445 #endif
446
447         /* ascending option */
448         menu_item_add_divider(menu);
449         menu_item_add_check(menu, _("Ascending"), lw->sort_ascend, G_CALLBACK(layout_sort_menu_ascend_cb), lw);
450
451         g_signal_connect(G_OBJECT(menu), "selection_done",
452                          G_CALLBACK(layout_sort_menu_hide_cb), NULL);
453
454         event = gtk_get_current_event();
455         if (event)
456                 {
457                 etime = gdk_event_get_time(event);
458                 gdk_event_free(event);
459                 }
460         else
461                 {
462                 etime = 0;
463                 }
464
465         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, etime);
466 }
467
468 static GtkWidget *layout_sort_button(LayoutWindow *lw)
469 {
470         GtkWidget *button;
471
472         button = gtk_button_new_with_label(sort_type_get_text(lw->sort_method));
473         DEBUG_NAME(button);
474         g_signal_connect(G_OBJECT(button), "clicked",
475                          G_CALLBACK(layout_sort_button_press_cb), lw);
476         gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
477
478         return button;
479 }
480
481 static void layout_zoom_menu_cb(GtkWidget *widget, gpointer data)
482 {
483         ZoomMode mode;
484
485         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
486
487         mode = (ZoomMode)GPOINTER_TO_INT(data);
488         options->image.zoom_mode = mode;
489 }
490
491 static void layout_scroll_menu_cb(GtkWidget *widget, gpointer data)
492 {
493         guint scroll_type;
494
495         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
496
497         scroll_type = GPOINTER_TO_UINT(data);
498         options->image.scroll_reset_method = scroll_type;
499         image_options_sync();
500 }
501
502 static void layout_zoom_menu_hide_cb(GtkWidget *widget, gpointer data)
503 {
504         /* destroy the menu */
505         g_object_unref(widget);
506 }
507
508 static void layout_zoom_button_press_cb(GtkWidget *widget, gpointer data)
509 {
510         LayoutWindow *lw = data;
511         GtkWidget *menu;
512         GdkEvent *event;
513         guint32 etime;
514
515         menu = submenu_add_zoom(NULL, G_CALLBACK(layout_zoom_menu_cb),
516                         lw, FALSE, FALSE, TRUE, options->image.zoom_mode);
517
518         /* take ownership of menu */
519 #ifdef GTK_OBJECT_FLOATING
520         /* GTK+ < 2.10 */
521         g_object_ref(G_OBJECT(menu));
522         gtk_object_sink(GTK_OBJECT(menu));
523 #else
524         /* GTK+ >= 2.10 */
525         g_object_ref_sink(G_OBJECT(menu));
526 #endif
527
528         menu_item_add_divider(menu);
529
530         menu_item_add_radio(menu, _("Scroll to top left corner"),
531                         GUINT_TO_POINTER(SCROLL_RESET_TOPLEFT),
532                         options->image.scroll_reset_method == SCROLL_RESET_TOPLEFT,
533                         G_CALLBACK(layout_scroll_menu_cb),
534                         GUINT_TO_POINTER(SCROLL_RESET_TOPLEFT));
535         menu_item_add_radio(menu, _("Scroll to image center"),
536                         GUINT_TO_POINTER(SCROLL_RESET_CENTER),
537                         options->image.scroll_reset_method == SCROLL_RESET_CENTER,
538                         G_CALLBACK(layout_scroll_menu_cb),
539                         GUINT_TO_POINTER(SCROLL_RESET_CENTER));
540         menu_item_add_radio(menu, _("Keep the region from previous image"),
541                         GUINT_TO_POINTER(SCROLL_RESET_NOCHANGE),
542                         options->image.scroll_reset_method == SCROLL_RESET_NOCHANGE,
543                         G_CALLBACK(layout_scroll_menu_cb),
544                         GUINT_TO_POINTER(SCROLL_RESET_NOCHANGE));
545
546         g_signal_connect(G_OBJECT(menu), "selection_done",
547                          G_CALLBACK(layout_zoom_menu_hide_cb), NULL);
548
549         event = gtk_get_current_event();
550         if (event)
551                 {
552                 etime = gdk_event_get_time(event);
553                 gdk_event_free(event);
554                 }
555         else
556                 {
557                 etime = 0;
558                 }
559
560         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, etime);
561 }
562
563 static GtkWidget *layout_zoom_button(LayoutWindow *lw, GtkWidget *box, gint size, gboolean expand)
564 {
565         GtkWidget *button;
566         GtkWidget *frame;
567
568
569         frame = gtk_frame_new(NULL);
570         DEBUG_NAME(frame);
571         if (size) gtk_widget_set_size_request(frame, size, -1);
572         gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
573
574         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0);
575
576         gtk_widget_show(frame);
577
578         button = gtk_button_new_with_label("1:1");
579         g_signal_connect(G_OBJECT(button), "clicked",
580                          G_CALLBACK(layout_zoom_button_press_cb), lw);
581         gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
582
583         gtk_container_add(GTK_CONTAINER(frame), button);
584         gtk_widget_show(button);
585
586         return button;
587 }
588
589 /*
590  *-----------------------------------------------------------------------------
591  * status bar
592  *-----------------------------------------------------------------------------
593  */
594
595
596 void layout_status_update_progress(LayoutWindow *lw, gdouble val, const gchar *text)
597 {
598         static gdouble meta = 0;
599
600         if (!layout_valid(&lw)) return;
601         if (!lw->info_progress_bar) return;
602
603         /* Give priority to the loading meta data message
604          */
605         if(!g_strcmp0(text, "Loading thumbs..."))
606                 {
607                 if (meta)
608                         {
609                         return;
610                         }
611                 }
612         else
613                 {
614                 meta = val;
615                 }
616
617         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(lw->info_progress_bar), val);
618         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(lw->info_progress_bar),
619                                                                         val ? ((text) ? text : " ") : " ");
620 }
621
622 void layout_status_update_info(LayoutWindow *lw, const gchar *text)
623 {
624         gchar *buf = NULL;
625
626         if (!layout_valid(&lw)) return;
627
628         if (!text)
629                 {
630                 guint n;
631                 gint64 n_bytes = 0;
632
633                 n = layout_list_count(lw, &n_bytes);
634
635                 if (n)
636                         {
637                         guint s;
638                         gint64 s_bytes = 0;
639                         const gchar *ss;
640
641                         if (layout_image_slideshow_active(lw))
642                                 {
643                                 if (!layout_image_slideshow_paused(lw))
644                                         {
645                                         ss = _(" Slideshow");
646                                         }
647                                 else
648                                         {
649                                         ss = _(" Paused");
650                                         }
651                                 }
652                         else
653                                 {
654                                 ss = "";
655                                 }
656
657                         s = layout_selection_count(lw, &s_bytes);
658
659                         layout_bars_new_selection(lw, s);
660
661                         if (s > 0)
662                                 {
663                                 gchar *b = text_from_size_abrev(n_bytes);
664                                 gchar *sb = text_from_size_abrev(s_bytes);
665                                 buf = g_strdup_printf(_("%s, %d files (%s, %d)%s"), b, n, sb, s, ss);
666                                 g_free(b);
667                                 g_free(sb);
668                                 }
669                         else if (n > 0)
670                                 {
671                                 gchar *b = text_from_size_abrev(n_bytes);
672                                 buf = g_strdup_printf(_("%s, %d files%s"), b, n, ss);
673                                 g_free(b);
674                                 }
675                         else
676                                 {
677                                 buf = g_strdup_printf(_("%d files%s"), n, ss);
678                                 }
679
680                         text = buf;
681
682                         image_osd_update(lw->image);
683                         }
684                 else
685                         {
686                         text = "";
687                         }
688         }
689
690         if (lw->info_status) gtk_label_set_text(GTK_LABEL(lw->info_status), text);
691         g_free(buf);
692 }
693
694 void layout_status_update_image(LayoutWindow *lw)
695 {
696         guint64 n;
697         FileData *fd;
698         gint page_total;
699         gint page_num;
700
701         if (!layout_valid(&lw) || !lw->image) return;
702         if (!lw->info_zoom || !lw->info_details) return; /*called from layout_style_set */
703
704         n = layout_list_count(lw, NULL);
705
706         if (!n)
707                 {
708                 gtk_button_set_label(GTK_BUTTON(lw->info_zoom), "");
709                 gtk_label_set_text(GTK_LABEL(lw->info_details), "");
710                 }
711         else
712                 {
713                 gchar *text;
714                 gchar *b;
715
716                 text = image_zoom_get_as_text(lw->image);
717                 gtk_button_set_label(GTK_BUTTON(lw->info_zoom), text);
718                 g_free(text);
719
720                 b = image_get_fd(lw->image) ? text_from_size(image_get_fd(lw->image)->size) : g_strdup("0");
721
722                 if (lw->image->unknown)
723                         {
724                         if (image_get_path(lw->image) && !access_file(image_get_path(lw->image), R_OK))
725                                 {
726                                 text = g_strdup_printf(_("(no read permission) %s bytes"), b);
727                                 }
728                         else
729                                 {
730                                 text = g_strdup_printf(_("( ? x ? ) %s bytes"), b);
731                                 }
732                         }
733                 else
734                         {
735                         gint width, height;
736                         fd = image_get_fd(lw->image);
737                         page_total = fd->page_total;
738                         page_num = fd->page_num + 1;
739                         image_get_image_size(lw->image, &width, &height);
740
741                         if (page_total > 1)
742                                 {
743                                 text = g_strdup_printf(_("( %d x %d ) %s bytes %s%d%s%d%s"), width, height, b, "[", page_num, "/", page_total, "]");
744                                 }
745                         else
746                                 {
747                                 text = g_strdup_printf(_("( %d x %d ) %s bytes"), width, height, b);
748                                 }
749                         }
750
751                 g_signal_emit_by_name (lw->image->pr, "update-pixel");
752
753                 g_free(b);
754
755                 gtk_label_set_text(GTK_LABEL(lw->info_details), text);
756                 g_free(text);
757                 }
758         layout_util_sync_color(lw); /* update color button */
759 }
760
761 void layout_status_update_all(LayoutWindow *lw)
762 {
763         layout_status_update_progress(lw, 0.0, NULL);
764         layout_status_update_info(lw, NULL);
765         layout_status_update_image(lw);
766         layout_util_status_update_write(lw);
767 }
768
769 static GtkWidget *layout_status_label(gchar *text, GtkWidget *box, gboolean start, gint size, gboolean expand)
770 {
771         GtkWidget *label;
772         GtkWidget *frame;
773
774         frame = gtk_frame_new(NULL);
775         DEBUG_NAME(frame);
776         if (size) gtk_widget_set_size_request(frame, size, -1);
777         gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
778         if (start)
779                 {
780                 gtk_box_pack_start(GTK_BOX(box), frame, expand, expand, 0);
781                 }
782         else
783                 {
784                 gtk_box_pack_end(GTK_BOX(box), frame, expand, expand, 0);
785                 }
786         gtk_widget_show(frame);
787
788         label = gtk_label_new(text ? text : "");
789         gtk_container_add(GTK_CONTAINER(frame), label);
790         gtk_widget_show(label);
791
792         return label;
793 }
794
795 static void layout_status_setup(LayoutWindow *lw, GtkWidget *box, gboolean small_format)
796 {
797         GtkWidget *hbox;
798         GtkWidget *toolbar;
799         GtkWidget *toolbar_frame;
800
801         if (lw->info_box) return;
802
803         if (small_format)
804                 {
805                 lw->info_box = gtk_vbox_new(FALSE, 0);
806                 DEBUG_NAME(lw->info_box);
807                 }
808         else
809                 {
810                 lw->info_box = gtk_hbox_new(FALSE, 0);
811                 DEBUG_NAME(lw->info_box);
812                 }
813         gtk_box_pack_end(GTK_BOX(box), lw->info_box, FALSE, FALSE, 0);
814         gtk_widget_show(lw->info_box);
815
816         if (small_format)
817                 {
818                 hbox = gtk_hbox_new(FALSE, 0);
819                 DEBUG_NAME(hbox);
820                 gtk_box_pack_start(GTK_BOX(lw->info_box), hbox, FALSE, FALSE, 0);
821                 gtk_widget_show(hbox);
822                 }
823         else
824                 {
825                 hbox = lw->info_box;
826                 }
827         lw->info_progress_bar = gtk_progress_bar_new();
828         DEBUG_NAME(lw->info_progress_bar);
829         gtk_widget_set_size_request(lw->info_progress_bar, PROGRESS_WIDTH, -1);
830 #if GTK_CHECK_VERSION(3,0,0)
831         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(lw->info_progress_bar), "");
832         gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(lw->info_progress_bar), TRUE);
833 #endif
834         gtk_box_pack_start(GTK_BOX(hbox), lw->info_progress_bar, FALSE, FALSE, 0);
835         gtk_widget_show(lw->info_progress_bar);
836
837         lw->info_sort = layout_sort_button(lw);
838         gtk_widget_set_tooltip_text(GTK_WIDGET(lw->info_sort), _("Select sort order"));
839         gtk_box_pack_start(GTK_BOX(hbox), lw->info_sort, FALSE, FALSE, 0);
840         gtk_widget_show(lw->info_sort);
841
842         lw->info_status = layout_status_label(NULL, lw->info_box, TRUE, 0, (!small_format));
843         DEBUG_NAME(lw->info_status);
844         gtk_widget_set_tooltip_text(GTK_WIDGET(lw->info_status), _("Folder contents (files selected)"));
845
846         if (small_format)
847                 {
848                 hbox = gtk_hbox_new(FALSE, 0);
849                 DEBUG_NAME(hbox);
850                 gtk_box_pack_start(GTK_BOX(lw->info_box), hbox, FALSE, FALSE, 0);
851                 gtk_widget_show(hbox);
852                 }
853         lw->info_details = layout_status_label(NULL, hbox, TRUE, 0, TRUE);
854         DEBUG_NAME(lw->info_details);
855         gtk_widget_set_tooltip_text(GTK_WIDGET(lw->info_details), _("(Image dimensions) Image size [page n of m]"));
856         toolbar = layout_actions_toolbar(lw, TOOLBAR_STATUS);
857
858         toolbar_frame = gtk_frame_new(NULL);
859         DEBUG_NAME(toolbar_frame);
860         gtk_frame_set_shadow_type(GTK_FRAME(toolbar_frame), GTK_SHADOW_IN);
861         gtk_container_add(GTK_CONTAINER(toolbar_frame), toolbar);
862         gtk_widget_show(toolbar_frame);
863         gtk_widget_show(toolbar);
864         gtk_box_pack_end(GTK_BOX(hbox), toolbar_frame, FALSE, FALSE, 0);
865         lw->info_zoom = layout_zoom_button(lw, hbox, ZOOM_LABEL_WIDTH, TRUE);
866         gtk_widget_set_tooltip_text(GTK_WIDGET(lw->info_zoom), _("Select zoom and scroll mode"));
867         gtk_widget_show(lw->info_zoom);
868
869         if (small_format)
870                 {
871                 hbox = gtk_hbox_new(FALSE, 0);
872                 DEBUG_NAME(hbox);
873                 gtk_box_pack_start(GTK_BOX(lw->info_box), hbox, FALSE, FALSE, 0);
874                 gtk_widget_show(hbox);
875                 }
876         lw->info_pixel = layout_status_label(NULL, hbox, FALSE, 0, small_format); /* expand only in small format */
877         DEBUG_NAME(lw->info_pixel);
878         gtk_widget_set_tooltip_text(GTK_WIDGET(lw->info_pixel), _("[Pixel x,y coord]: (Pixel R,G,B value)"));
879         if (!lw->options.show_info_pixel) gtk_widget_hide(gtk_widget_get_parent(lw->info_pixel));
880 }
881
882 /*
883  *-----------------------------------------------------------------------------
884  * views
885  *-----------------------------------------------------------------------------
886  */
887
888 static GtkWidget *layout_tools_new(LayoutWindow *lw)
889 {
890         lw->dir_view = layout_tool_setup(lw);
891         return lw->dir_view;
892 }
893
894 static void layout_list_status_cb(ViewFile *vf, gpointer data)
895 {
896         LayoutWindow *lw = data;
897
898         layout_status_update_info(lw, NULL);
899 }
900
901 static void layout_list_thumb_cb(ViewFile *vf, gdouble val, const gchar *text, gpointer data)
902 {
903         LayoutWindow *lw = data;
904
905         layout_status_update_progress(lw, val, text);
906 }
907
908 static void layout_list_sync_thumb(LayoutWindow *lw)
909 {
910         if (lw->vf) vf_thumb_set(lw->vf, lw->options.show_thumbnails);
911 }
912
913 static void layout_list_sync_file_filter(LayoutWindow *lw)
914 {
915         if (lw->vf) vf_file_filter_set(lw->vf, lw->options.show_file_filter);
916 }
917
918 static GtkWidget *layout_list_new(LayoutWindow *lw)
919 {
920         lw->vf = vf_new(lw->options.file_view_type, NULL);
921         vf_set_layout(lw->vf, lw);
922
923         vf_set_status_func(lw->vf, layout_list_status_cb, lw);
924         vf_set_thumb_status_func(lw->vf, layout_list_thumb_cb, lw);
925
926         vf_marks_set(lw->vf, lw->options.show_marks);
927
928         layout_list_sync_thumb(lw);
929         layout_list_sync_file_filter(lw);
930
931         return lw->vf->widget;
932 }
933
934 static void layout_list_sync_marks(LayoutWindow *lw)
935 {
936         if (lw->vf) vf_marks_set(lw->vf, lw->options.show_marks);
937 }
938
939 static void layout_list_scroll_to_subpart(LayoutWindow *lw, const gchar *needle)
940 {
941         if (!lw) return;
942 }
943
944 GList *layout_list(LayoutWindow *lw)
945 {
946         if (!layout_valid(&lw)) return NULL;
947
948         if (lw->vf) return vf_get_list(lw->vf);
949
950         return NULL;
951 }
952
953 guint layout_list_count(LayoutWindow *lw, gint64 *bytes)
954 {
955         if (!layout_valid(&lw)) return 0;
956
957         if (lw->vf) return vf_count(lw->vf, bytes);
958
959         return 0;
960 }
961
962 FileData *layout_list_get_fd(LayoutWindow *lw, gint index)
963 {
964         if (!layout_valid(&lw)) return NULL;
965
966         if (lw->vf) return vf_index_get_data(lw->vf, index);
967
968         return NULL;
969 }
970
971 gint layout_list_get_index(LayoutWindow *lw, FileData *fd)
972 {
973         if (!layout_valid(&lw) || !fd) return -1;
974
975         if (lw->vf) return vf_index_by_fd(lw->vf, fd);
976
977         return -1;
978 }
979
980 void layout_list_sync_fd(LayoutWindow *lw, FileData *fd)
981 {
982         if (!layout_valid(&lw)) return;
983
984         if (lw->vf) vf_select_by_fd(lw->vf, fd);
985 }
986
987 static void layout_list_sync_sort(LayoutWindow *lw)
988 {
989         if (!layout_valid(&lw)) return;
990
991         if (lw->vf) vf_sort_set(lw->vf, lw->sort_method, lw->sort_ascend);
992 }
993
994 GList *layout_selection_list(LayoutWindow *lw)
995 {
996         if (!layout_valid(&lw)) return NULL;
997
998         if (layout_image_get_collection(lw, NULL))
999                 {
1000                 FileData *fd;
1001
1002                 fd = layout_image_get_fd(lw);
1003                 if (fd) return g_list_append(NULL, file_data_ref(fd));
1004                 return NULL;
1005                 }
1006
1007         if (lw->vf) return vf_selection_get_list(lw->vf);
1008
1009         return NULL;
1010 }
1011
1012 GList *layout_selection_list_by_index(LayoutWindow *lw)
1013 {
1014         if (!layout_valid(&lw)) return NULL;
1015
1016         if (lw->vf) return vf_selection_get_list_by_index(lw->vf);
1017
1018         return NULL;
1019 }
1020
1021 guint layout_selection_count(LayoutWindow *lw, gint64 *bytes)
1022 {
1023         if (!layout_valid(&lw)) return 0;
1024
1025         if (lw->vf) return vf_selection_count(lw->vf, bytes);
1026
1027         return 0;
1028 }
1029
1030 void layout_select_all(LayoutWindow *lw)
1031 {
1032         if (!layout_valid(&lw)) return;
1033
1034         if (lw->vf) vf_select_all(lw->vf);
1035 }
1036
1037 void layout_select_none(LayoutWindow *lw)
1038 {
1039         if (!layout_valid(&lw)) return;
1040
1041         if (lw->vf) vf_select_none(lw->vf);
1042 }
1043
1044 void layout_select_invert(LayoutWindow *lw)
1045 {
1046         if (!layout_valid(&lw)) return;
1047
1048         if (lw->vf) vf_select_invert(lw->vf);
1049 }
1050
1051 void layout_mark_to_selection(LayoutWindow *lw, gint mark, MarkToSelectionMode mode)
1052 {
1053         if (!layout_valid(&lw)) return;
1054
1055         if (lw->vf) vf_mark_to_selection(lw->vf, mark, mode);
1056 }
1057
1058 void layout_selection_to_mark(LayoutWindow *lw, gint mark, SelectionToMarkMode mode)
1059 {
1060         if (!layout_valid(&lw)) return;
1061
1062         if (lw->vf) vf_selection_to_mark(lw->vf, mark, mode);
1063
1064         layout_status_update_info(lw, NULL); /* osd in fullscreen mode */
1065 }
1066
1067 void layout_mark_filter_toggle(LayoutWindow *lw, gint mark)
1068 {
1069         if (!layout_valid(&lw)) return;
1070
1071         if (lw->vf) vf_mark_filter_toggle(lw->vf, mark);
1072 }
1073
1074
1075 /*
1076  *-----------------------------------------------------------------------------
1077  * access
1078  *-----------------------------------------------------------------------------
1079  */
1080
1081 const gchar *layout_get_path(LayoutWindow *lw)
1082 {
1083         if (!layout_valid(&lw)) return NULL;
1084         return lw->dir_fd ? lw->dir_fd->path : NULL;
1085 }
1086
1087 static void layout_sync_path(LayoutWindow *lw)
1088 {
1089         if (!lw->dir_fd) return;
1090
1091         if (lw->path_entry) gtk_entry_set_text(GTK_ENTRY(lw->path_entry), lw->dir_fd->path);
1092
1093         if (lw->vd) vd_set_fd(lw->vd, lw->dir_fd);
1094         if (lw->vf) vf_set_fd(lw->vf, lw->dir_fd);
1095 }
1096
1097 gboolean layout_set_path(LayoutWindow *lw, const gchar *path)
1098 {
1099         FileData *fd;
1100         gboolean ret;
1101
1102         if (!path) return FALSE;
1103
1104         fd = file_data_new_group(path);
1105         ret = layout_set_fd(lw, fd);
1106         file_data_unref(fd);
1107         return ret;
1108 }
1109
1110
1111 gboolean layout_set_fd(LayoutWindow *lw, FileData *fd)
1112 {
1113         gboolean have_file = FALSE;
1114         gboolean dir_changed = TRUE;
1115
1116         if (!layout_valid(&lw)) return FALSE;
1117
1118         if (!fd || !isname(fd->path)) return FALSE;
1119         if (lw->dir_fd && fd == lw->dir_fd)
1120                 {
1121                 return TRUE;
1122                 }
1123
1124         if (isdir(fd->path))
1125                 {
1126                 if (lw->dir_fd)
1127                         {
1128                         file_data_unregister_real_time_monitor(lw->dir_fd);
1129                         file_data_unref(lw->dir_fd);
1130                         }
1131                 lw->dir_fd = file_data_ref(fd);
1132                 file_data_register_real_time_monitor(fd);
1133                 }
1134         else
1135                 {
1136                 gchar *base;
1137
1138                 base = remove_level_from_path(fd->path);
1139                 if (lw->dir_fd && strcmp(lw->dir_fd->path, base) == 0)
1140                         {
1141                         g_free(base);
1142                         dir_changed = FALSE;
1143                         }
1144                 else if (isdir(base))
1145                         {
1146                         if (lw->dir_fd)
1147                                 {
1148                                 file_data_unregister_real_time_monitor(lw->dir_fd);
1149                                 file_data_unref(lw->dir_fd);
1150                                 }
1151                         lw->dir_fd = file_data_new_dir(base);
1152                         file_data_register_real_time_monitor(lw->dir_fd);
1153                         g_free(base);
1154                         }
1155                 else
1156                         {
1157                         g_free(base);
1158                         return FALSE;
1159                         }
1160                 if (isfile(fd->path)) have_file = TRUE;
1161                 }
1162
1163         if (lw->path_entry)
1164                 {
1165                 history_chain_append_end(lw->dir_fd->path);
1166                 tab_completion_append_to_history(lw->path_entry, lw->dir_fd->path);
1167                 }
1168         layout_sync_path(lw);
1169         layout_list_sync_sort(lw);
1170
1171         if (have_file)
1172                 {
1173                 gint row;
1174
1175                 row = layout_list_get_index(lw, fd);
1176                 if (row >= 0)
1177                         {
1178                         layout_image_set_index(lw, row);
1179                         }
1180                 else
1181                         {
1182                         layout_image_set_fd(lw, fd);
1183                         }
1184                 }
1185         else if (!options->lazy_image_sync)
1186                 {
1187                 layout_image_set_index(lw, 0);
1188                 }
1189
1190         if (options->metadata.confirm_on_dir_change && dir_changed)
1191                 metadata_write_queue_confirm(FALSE, NULL, NULL);
1192
1193         if (lw->vf && (options->read_metadata_in_idle || (lw->sort_method == SORT_EXIFTIME || lw->sort_method == SORT_EXIFTIMEDIGITIZED || lw->sort_method == SORT_RATING)))
1194                 {
1195                 vf_read_metadata_in_idle(lw->vf);
1196                 }
1197
1198         return TRUE;
1199 }
1200
1201 static void layout_refresh_lists(LayoutWindow *lw)
1202 {
1203         if (lw->vd) vd_refresh(lw->vd);
1204
1205         if (lw->vf)
1206                 {
1207                 vf_refresh(lw->vf);
1208                 vf_thumb_update(lw->vf);
1209                 }
1210 }
1211
1212 void layout_refresh(LayoutWindow *lw)
1213 {
1214         if (!layout_valid(&lw)) return;
1215
1216         DEBUG_1("layout refresh");
1217
1218         layout_refresh_lists(lw);
1219
1220         if (lw->image) layout_image_refresh(lw);
1221 }
1222
1223 void layout_thumb_set(LayoutWindow *lw, gboolean enable)
1224 {
1225         if (!layout_valid(&lw)) return;
1226
1227         if (lw->options.show_thumbnails == enable) return;
1228
1229         lw->options.show_thumbnails = enable;
1230
1231         layout_util_sync_thumb(lw);
1232         layout_list_sync_thumb(lw);
1233 }
1234
1235 void layout_file_filter_set(LayoutWindow *lw, gboolean enable)
1236 {
1237         if (!layout_valid(&lw)) return;
1238
1239         if (lw->options.show_file_filter == enable) return;
1240
1241         lw->options.show_file_filter = enable;
1242
1243         layout_util_sync_file_filter(lw);
1244         layout_list_sync_file_filter(lw);
1245 }
1246
1247 void layout_marks_set(LayoutWindow *lw, gboolean enable)
1248 {
1249         if (!layout_valid(&lw)) return;
1250
1251         if (lw->options.show_marks == enable) return;
1252
1253         lw->options.show_marks = enable;
1254
1255         layout_util_sync_marks(lw);
1256         layout_list_sync_marks(lw);
1257 }
1258
1259 gboolean layout_thumb_get(LayoutWindow *lw)
1260 {
1261         if (!layout_valid(&lw)) return FALSE;
1262
1263         return lw->options.show_thumbnails;
1264 }
1265
1266 gboolean layout_marks_get(LayoutWindow *lw)
1267 {
1268         if (!layout_valid(&lw)) return FALSE;
1269
1270         return lw->options.show_marks;
1271 }
1272
1273 void layout_sort_set(LayoutWindow *lw, SortType type, gboolean ascend)
1274 {
1275         if (!layout_valid(&lw)) return;
1276         if (lw->sort_method == type && lw->sort_ascend == ascend) return;
1277
1278         lw->sort_method = type;
1279         lw->sort_ascend = ascend;
1280
1281         if (lw->info_sort) gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(lw->info_sort))),
1282                                               sort_type_get_text(type));
1283         layout_list_sync_sort(lw);
1284 }
1285
1286 gboolean layout_sort_get(LayoutWindow *lw, SortType *type, gboolean *ascend)
1287 {
1288         if (!layout_valid(&lw)) return FALSE;
1289
1290         if (type) *type = lw->sort_method;
1291         if (ascend) *ascend = lw->sort_ascend;
1292
1293         return TRUE;
1294 }
1295
1296 gboolean layout_geometry_get(LayoutWindow *lw, gint *x, gint *y, gint *w, gint *h)
1297 {
1298         GdkWindow *window;
1299         if (!layout_valid(&lw)) return FALSE;
1300
1301         window = gtk_widget_get_window(lw->window);
1302         gdk_window_get_root_origin(window, x, y);
1303         *w = gdk_window_get_width(window);
1304         *h = gdk_window_get_height(window);
1305
1306         return TRUE;
1307 }
1308
1309 gboolean layout_geometry_get_dividers(LayoutWindow *lw, gint *h, gint *v)
1310 {
1311         GtkAllocation h_allocation;
1312         GtkAllocation v_allocation;
1313
1314         if (!layout_valid(&lw)) return FALSE;
1315
1316         if (lw->h_pane)
1317                 {
1318                 GtkWidget *child = gtk_paned_get_child1(GTK_PANED(lw->h_pane));
1319                 gtk_widget_get_allocation(child, &h_allocation);
1320                 }
1321
1322         if (lw->v_pane)
1323                 {
1324                 GtkWidget *child = gtk_paned_get_child1(GTK_PANED(lw->v_pane));
1325                 gtk_widget_get_allocation(child, &v_allocation);
1326                 }
1327
1328         if (lw->h_pane && h_allocation.x >= 0)
1329                 {
1330                 *h = h_allocation.width;
1331                 }
1332         else if (h != &lw->options.main_window.hdivider_pos)
1333                 {
1334                 *h = lw->options.main_window.hdivider_pos;
1335                 }
1336
1337         if (lw->v_pane && v_allocation.x >= 0)
1338                 {
1339                 *v = v_allocation.height;
1340                 }
1341         else if (v != &lw->options.main_window.vdivider_pos)
1342                 {
1343                 *v = lw->options.main_window.vdivider_pos;
1344                 }
1345
1346         return TRUE;
1347 }
1348
1349 void layout_views_set(LayoutWindow *lw, DirViewType dir_view_type, FileViewType file_view_type)
1350 {
1351         if (!layout_valid(&lw)) return;
1352
1353         if (lw->options.dir_view_type == dir_view_type && lw->options.file_view_type == file_view_type) return;
1354
1355         lw->options.dir_view_type = dir_view_type;
1356         lw->options.file_view_type = file_view_type;
1357
1358         layout_style_set(lw, -1, NULL);
1359 }
1360
1361 void layout_views_set_sort(LayoutWindow *lw, SortType method, gboolean ascend)
1362 {
1363         if (!layout_valid(&lw)) return;
1364
1365         if (lw->options.dir_view_list_sort.method == method && lw->options.dir_view_list_sort.ascend == ascend) return;
1366
1367         lw->options.dir_view_list_sort.method = method;
1368         lw->options.dir_view_list_sort.ascend = ascend;
1369
1370         layout_style_set(lw, -1, NULL);
1371 }
1372
1373 gboolean layout_views_get(LayoutWindow *lw, DirViewType *dir_view_type, FileViewType *file_view_type)
1374 {
1375         if (!layout_valid(&lw)) return FALSE;
1376
1377         *dir_view_type = lw->options.dir_view_type;
1378         *file_view_type = lw->options.file_view_type;
1379
1380         return TRUE;
1381 }
1382
1383 /*
1384  *-----------------------------------------------------------------------------
1385  * location utils
1386  *-----------------------------------------------------------------------------
1387  */
1388
1389 static gboolean layout_location_single(LayoutLocation l)
1390 {
1391         return (l == LAYOUT_LEFT ||
1392                 l == LAYOUT_RIGHT ||
1393                 l == LAYOUT_TOP ||
1394                 l == LAYOUT_BOTTOM);
1395 }
1396
1397 static gboolean layout_location_vertical(LayoutLocation l)
1398 {
1399         return (l & LAYOUT_TOP ||
1400                 l & LAYOUT_BOTTOM);
1401 }
1402
1403 static gboolean layout_location_first(LayoutLocation l)
1404 {
1405         return (l & LAYOUT_TOP ||
1406                 l & LAYOUT_LEFT);
1407 }
1408
1409 static LayoutLocation layout_grid_compass(LayoutWindow *lw)
1410 {
1411         if (layout_location_single(lw->dir_location)) return lw->dir_location;
1412         if (layout_location_single(lw->file_location)) return lw->file_location;
1413         return lw->image_location;
1414 }
1415
1416 static void layout_location_compute(LayoutLocation l1, LayoutLocation l2,
1417                                     GtkWidget *s1, GtkWidget *s2,
1418                                     GtkWidget **d1, GtkWidget **d2)
1419 {
1420         LayoutLocation l;
1421
1422         l = l1 & l2;    /* get common compass direction */
1423         l = l1 - l;     /* remove it */
1424
1425         if (layout_location_first(l))
1426                 {
1427                 *d1 = s1;
1428                 *d2 = s2;
1429                 }
1430         else
1431                 {
1432                 *d1 = s2;
1433                 *d2 = s1;
1434                 }
1435 }
1436
1437 /*
1438  *-----------------------------------------------------------------------------
1439  * tools window (for floating/hidden)
1440  *-----------------------------------------------------------------------------
1441  */
1442
1443 gboolean layout_geometry_get_tools(LayoutWindow *lw, gint *x, gint *y, gint *w, gint *h, gint *divider_pos)
1444 {
1445         GdkWindow *window;
1446         GtkAllocation allocation;
1447         if (!layout_valid(&lw)) return FALSE;
1448
1449         if (!lw->tools || !gtk_widget_get_visible(lw->tools))
1450                 {
1451                 /* use the stored values (sort of breaks success return value) */
1452
1453                 *divider_pos = lw->options.float_window.vdivider_pos;
1454
1455                 return FALSE;
1456                 }
1457
1458         window = gtk_widget_get_window(lw->tools);
1459         gdk_window_get_root_origin(window, x, y);
1460         *w = gdk_window_get_width(window);
1461         *h = gdk_window_get_height(window);
1462         gtk_widget_get_allocation(gtk_paned_get_child1(GTK_PANED(lw->tools_pane)), &allocation);
1463
1464         if (gtk_orientable_get_orientation(GTK_ORIENTABLE(lw->tools_pane)) == GTK_ORIENTATION_VERTICAL)
1465                 {
1466                 *divider_pos = allocation.height;
1467                 }
1468         else
1469                 {
1470                 *divider_pos = allocation.width;
1471                 }
1472
1473         return TRUE;
1474 }
1475
1476 gboolean layout_geometry_get_log_window(LayoutWindow *lw, gint *x, gint *y,
1477                                                                                                                 gint *w, gint *h)
1478 {
1479         GdkWindow *window;
1480
1481         if (!layout_valid(&lw)) return FALSE;
1482
1483         if (!lw->log_window)
1484                 {
1485                 return FALSE;
1486                 }
1487
1488         window = gtk_widget_get_window(lw->log_window);
1489         gdk_window_get_root_origin(window, x, y);
1490         *w = gdk_window_get_width(window);
1491         *h = gdk_window_get_height(window);
1492
1493         return TRUE;
1494 }
1495
1496 static void layout_tools_geometry_sync(LayoutWindow *lw)
1497 {
1498         layout_geometry_get_tools(lw, &lw->options.float_window.x, &lw->options.float_window.y,
1499                                   &lw->options.float_window.w, &lw->options.float_window.h, &lw->options.float_window.vdivider_pos);
1500 }
1501
1502 static void layout_tools_hide(LayoutWindow *lw, gboolean hide)
1503 {
1504         if (!lw->tools) return;
1505
1506         if (hide)
1507                 {
1508                 if (gtk_widget_get_visible(lw->tools))
1509                         {
1510                         layout_tools_geometry_sync(lw);
1511                         gtk_widget_hide(lw->tools);
1512                         }
1513                 }
1514         else
1515                 {
1516                 if (!gtk_widget_get_visible(lw->tools))
1517                         {
1518                         gtk_widget_show(lw->tools);
1519                         if (lw->vf) vf_refresh(lw->vf);
1520                         }
1521                 }
1522
1523         lw->options.tools_hidden = hide;
1524 }
1525
1526 static gboolean layout_tools_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer data)
1527 {
1528         LayoutWindow *lw = data;
1529
1530         layout_tools_float_toggle(lw);
1531
1532         return TRUE;
1533 }
1534
1535 static void layout_tools_setup(LayoutWindow *lw, GtkWidget *tools, GtkWidget *files)
1536 {
1537         GtkWidget *vbox;
1538         GtkWidget *w1, *w2;
1539         gboolean vertical;
1540         gboolean new_window = FALSE;
1541
1542         vertical = (layout_location_single(lw->image_location) && !layout_location_vertical(lw->image_location)) ||
1543                    (!layout_location_single(lw->image_location) && layout_location_vertical(layout_grid_compass(lw)));
1544         /* for now, tools/dir are always first in order */
1545         w1 = tools;
1546         w2 = files;
1547
1548         if (!lw->tools)
1549                 {
1550                 GdkGeometry geometry;
1551                 GdkWindowHints hints;
1552
1553                 lw->tools = window_new(GTK_WINDOW_TOPLEVEL, "tools", PIXBUF_INLINE_ICON_TOOLS, NULL, _("Tools"));
1554                 DEBUG_NAME(lw->tools);
1555                 g_signal_connect(G_OBJECT(lw->tools), "delete_event",
1556                                  G_CALLBACK(layout_tools_delete_cb), lw);
1557                 layout_keyboard_init(lw, lw->tools);
1558
1559                 if (options->save_window_positions)
1560                         {
1561                         hints = GDK_HINT_USER_POS;
1562                         }
1563                 else
1564                         {
1565                         hints = 0;
1566                         }
1567
1568                 geometry.min_width = DEFAULT_MINIMAL_WINDOW_SIZE;
1569                 geometry.min_height = DEFAULT_MINIMAL_WINDOW_SIZE;
1570                 geometry.base_width = TOOLWINDOW_DEF_WIDTH;
1571                 geometry.base_height = TOOLWINDOW_DEF_HEIGHT;
1572                 gtk_window_set_geometry_hints(GTK_WINDOW(lw->tools), NULL, &geometry,
1573                                               GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE | hints);
1574
1575
1576                 gtk_window_set_resizable(GTK_WINDOW(lw->tools), TRUE);
1577                 gtk_container_set_border_width(GTK_CONTAINER(lw->tools), 0);
1578                 gtk_container_remove(GTK_CONTAINER(lw->main_box), lw->menu_tool_bar);
1579
1580                 new_window = TRUE;
1581                 }
1582         else
1583                 {
1584                 layout_tools_geometry_sync(lw);
1585                 /* dump the contents */
1586                 gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(lw->tools)));
1587                 }
1588
1589         layout_actions_add_window(lw, lw->tools);
1590
1591         vbox = gtk_vbox_new(FALSE, 0);
1592         DEBUG_NAME(vbox);
1593         gtk_container_add(GTK_CONTAINER(lw->tools), vbox);
1594         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(lw->menu_tool_bar), FALSE, FALSE, 0);
1595         gtk_widget_show(vbox);
1596
1597         layout_status_setup(lw, vbox, TRUE);
1598
1599         if (vertical)
1600                 {
1601                 lw->tools_pane = gtk_vpaned_new();
1602                 DEBUG_NAME(lw->tools_pane);
1603                 }
1604         else
1605                 {
1606                 lw->tools_pane = gtk_hpaned_new();
1607                 DEBUG_NAME(lw->tools_pane);
1608                 }
1609         gtk_box_pack_start(GTK_BOX(vbox), lw->tools_pane, TRUE, TRUE, 0);
1610         gtk_widget_show(lw->tools_pane);
1611
1612         gtk_paned_pack1(GTK_PANED(lw->tools_pane), w1, FALSE, TRUE);
1613         gtk_paned_pack2(GTK_PANED(lw->tools_pane), w2, TRUE, TRUE);
1614
1615         gtk_widget_show(tools);
1616         gtk_widget_show(files);
1617
1618         if (new_window)
1619                 {
1620                 if (options->save_window_positions)
1621                         {
1622                         gtk_window_set_default_size(GTK_WINDOW(lw->tools), lw->options.float_window.w, lw->options.float_window.h);
1623                         gtk_window_move(GTK_WINDOW(lw->tools), lw->options.float_window.x, lw->options.float_window.y);
1624                         }
1625                 else
1626                         {
1627                         if (vertical)
1628                                 {
1629                                 gtk_window_set_default_size(GTK_WINDOW(lw->tools),
1630                                                             TOOLWINDOW_DEF_WIDTH, TOOLWINDOW_DEF_HEIGHT);
1631                                 }
1632                         else
1633                                 {
1634                                 gtk_window_set_default_size(GTK_WINDOW(lw->tools),
1635                                                             TOOLWINDOW_DEF_HEIGHT, TOOLWINDOW_DEF_WIDTH);
1636                                 }
1637                         }
1638                 }
1639
1640         if (!options->save_window_positions)
1641                 {
1642                 if (vertical)
1643                         {
1644                         lw->options.float_window.vdivider_pos = MAIN_WINDOW_DIV_VPOS;
1645                         }
1646                 else
1647                         {
1648                         lw->options.float_window.vdivider_pos = MAIN_WINDOW_DIV_HPOS;
1649                         }
1650                 }
1651
1652         gtk_paned_set_position(GTK_PANED(lw->tools_pane), lw->options.float_window.vdivider_pos);
1653 }
1654
1655 /*
1656  *-----------------------------------------------------------------------------
1657  * glue (layout arrangement)
1658  *-----------------------------------------------------------------------------
1659  */
1660
1661 static void layout_grid_compute(LayoutWindow *lw,
1662                                 GtkWidget *image, GtkWidget *tools, GtkWidget *files,
1663                                 GtkWidget **w1, GtkWidget **w2, GtkWidget **w3)
1664 {
1665         /* heh, this was fun */
1666
1667         if (layout_location_single(lw->dir_location))
1668                 {
1669                 if (layout_location_first(lw->dir_location))
1670                         {
1671                         *w1 = tools;
1672                         layout_location_compute(lw->file_location, lw->image_location, files, image, w2, w3);
1673                         }
1674                 else
1675                         {
1676                         *w3 = tools;
1677                         layout_location_compute(lw->file_location, lw->image_location, files, image, w1, w2);
1678                         }
1679                 }
1680         else if (layout_location_single(lw->file_location))
1681                 {
1682                 if (layout_location_first(lw->file_location))
1683                         {
1684                         *w1 = files;
1685                         layout_location_compute(lw->dir_location, lw->image_location, tools, image, w2, w3);
1686                         }
1687                 else
1688                         {
1689                         *w3 = files;
1690                         layout_location_compute(lw->dir_location, lw->image_location, tools, image, w1, w2);
1691                         }
1692                 }
1693         else
1694                 {
1695                 /* image */
1696                 if (layout_location_first(lw->image_location))
1697                         {
1698                         *w1 = image;
1699                         layout_location_compute(lw->file_location, lw->dir_location, files, tools, w2, w3);
1700                         }
1701                 else
1702                         {
1703                         *w3 = image;
1704                         layout_location_compute(lw->file_location, lw->dir_location, files, tools, w1, w2);
1705                         }
1706                 }
1707 }
1708
1709 void layout_split_change(LayoutWindow *lw, ImageSplitMode mode)
1710 {
1711         GtkWidget *image;
1712         gint i;
1713
1714         for (i = 0; i < MAX_SPLIT_IMAGES; i++)
1715                 {
1716                 if (lw->split_images[i])
1717                         {
1718                         gtk_widget_hide(lw->split_images[i]->widget);
1719                         if (gtk_widget_get_parent(lw->split_images[i]->widget) != lw->utility_paned)
1720                                 gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(lw->split_images[i]->widget)), lw->split_images[i]->widget);
1721                         }
1722                 }
1723         gtk_container_remove(GTK_CONTAINER(lw->utility_paned), lw->split_image_widget);
1724
1725         image = layout_image_setup_split(lw, mode);
1726
1727         gtk_paned_pack1(GTK_PANED(lw->utility_paned), image, TRUE, FALSE);
1728         gtk_widget_show(image);
1729         layout_util_sync(lw);
1730 }
1731
1732 static void layout_grid_setup(LayoutWindow *lw)
1733 {
1734         gint priority_location;
1735         GtkWidget *h;
1736         GtkWidget *v;
1737         GtkWidget *w1, *w2, *w3;
1738
1739         GtkWidget *image_sb; /* image together with sidebars in utility box */
1740         GtkWidget *tools;
1741         GtkWidget *files;
1742
1743         layout_actions_setup(lw);
1744
1745         lw->group_box = gtk_vbox_new(FALSE, 0);
1746         DEBUG_NAME(lw->group_box);
1747         gtk_box_pack_end(GTK_BOX(lw->main_box), lw->group_box, TRUE, TRUE, 0);
1748         gtk_widget_show(lw->group_box);
1749
1750         priority_location = layout_grid_compass(lw);
1751
1752         if (lw->utility_box)
1753                 {
1754                 layout_split_change(lw, lw->split_mode); /* this re-creates image frame for the new configuration */
1755                 image_sb = lw->utility_box;
1756                 DEBUG_NAME(image_sb);
1757                 }
1758         else
1759                 {
1760                 GtkWidget *image; /* image or split images together */
1761                 image = layout_image_setup_split(lw, lw->split_mode);
1762                 image_sb = layout_bars_prepare(lw, image);
1763                 DEBUG_NAME(image_sb);
1764                 }
1765
1766         tools = layout_tools_new(lw);
1767         DEBUG_NAME(tools);
1768         files = layout_list_new(lw);
1769         DEBUG_NAME(files);
1770
1771
1772         if (lw->options.tools_float || lw->options.tools_hidden)
1773                 {
1774                 gtk_box_pack_start(GTK_BOX(lw->group_box), image_sb, TRUE, TRUE, 0);
1775                 gtk_widget_show(image_sb);
1776
1777                 layout_tools_setup(lw, tools, files);
1778
1779                 image_grab_focus(lw->image);
1780
1781                 return;
1782                 }
1783         else if (lw->tools)
1784                 {
1785                 layout_tools_geometry_sync(lw);
1786                 gtk_widget_destroy(lw->tools);
1787                 lw->tools = NULL;
1788                 lw->tools_pane = NULL;
1789                 }
1790
1791         layout_status_setup(lw, lw->group_box, FALSE);
1792
1793         layout_grid_compute(lw, image_sb, tools, files, &w1, &w2, &w3);
1794
1795         v = lw->v_pane = gtk_vpaned_new();
1796         DEBUG_NAME(v);
1797
1798         h = lw->h_pane = gtk_hpaned_new();
1799         DEBUG_NAME(h);
1800
1801         if (!layout_location_vertical(priority_location))
1802                 {
1803                 GtkWidget *tmp;
1804
1805                 tmp = v;
1806                 v = h;
1807                 h = tmp;
1808                 }
1809
1810         gtk_box_pack_start(GTK_BOX(lw->group_box), v, TRUE, TRUE, 0);
1811
1812         if (!layout_location_first(priority_location))
1813                 {
1814                 gtk_paned_pack1(GTK_PANED(v), h, FALSE, TRUE);
1815                 gtk_paned_pack2(GTK_PANED(v), w3, TRUE, TRUE);
1816
1817                 gtk_paned_pack1(GTK_PANED(h), w1, FALSE, TRUE);
1818                 gtk_paned_pack2(GTK_PANED(h), w2, TRUE, TRUE);
1819                 }
1820         else
1821                 {
1822                 gtk_paned_pack1(GTK_PANED(v), w1, FALSE, TRUE);
1823                 gtk_paned_pack2(GTK_PANED(v), h, TRUE, TRUE);
1824
1825                 gtk_paned_pack1(GTK_PANED(h), w2, FALSE, TRUE);
1826                 gtk_paned_pack2(GTK_PANED(h), w3, TRUE, TRUE);
1827                 }
1828
1829         gtk_widget_show(image_sb);
1830         gtk_widget_show(tools);
1831         gtk_widget_show(files);
1832
1833         gtk_widget_show(v);
1834         gtk_widget_show(h);
1835
1836         /* fix to have image pane visible when it is left and priority widget */
1837         if (lw->options.main_window.hdivider_pos == -1 &&
1838             w1 == image_sb &&
1839             !layout_location_vertical(priority_location) &&
1840             layout_location_first(priority_location))
1841                 {
1842                 gtk_widget_set_size_request(image_sb, 200, -1);
1843                 }
1844
1845         gtk_paned_set_position(GTK_PANED(lw->h_pane), lw->options.main_window.hdivider_pos);
1846         gtk_paned_set_position(GTK_PANED(lw->v_pane), lw->options.main_window.vdivider_pos);
1847
1848         image_grab_focus(lw->image);
1849 }
1850
1851 void layout_style_set(LayoutWindow *lw, gint style, const gchar *order)
1852 {
1853         FileData *dir_fd;
1854
1855         if (!layout_valid(&lw)) return;
1856
1857         if (style != -1)
1858                 {
1859                 LayoutLocation d, f, i;
1860
1861                 layout_config_parse(style, order, &d,  &f, &i);
1862
1863                 if (lw->dir_location == d &&
1864                     lw->file_location == f &&
1865                     lw->image_location == i) return;
1866
1867                 lw->dir_location = d;
1868                 lw->file_location = f;
1869                 lw->image_location = i;
1870                 }
1871
1872         /* remember state */
1873
1874         /* layout_image_slideshow_stop(lw); slideshow should survive */
1875         layout_image_full_screen_stop(lw);
1876
1877         dir_fd = lw->dir_fd;
1878         if (dir_fd) file_data_unregister_real_time_monitor(dir_fd);
1879         lw->dir_fd = NULL;
1880
1881         layout_geometry_get_dividers(lw, &lw->options.main_window.hdivider_pos, &lw->options.main_window.vdivider_pos);
1882
1883         /* preserve utility_box (image + sidebars), menu_bar and toolbars to be reused later in layout_grid_setup */
1884         /* lw->image is preserved together with lw->utility_box */
1885         if (lw->utility_box) gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(lw->utility_box)), lw->utility_box);
1886
1887         if (lw->toolbar[TOOLBAR_STATUS]) gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(lw->toolbar[TOOLBAR_STATUS])), lw->toolbar[TOOLBAR_STATUS]);
1888
1889         if (lw->menu_tool_bar) gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(lw->menu_tool_bar)), lw->menu_tool_bar);
1890
1891         /* clear it all */
1892
1893         lw->h_pane = NULL;
1894         lw->v_pane = NULL;
1895
1896         lw->path_entry = NULL;
1897         lw->dir_view = NULL;
1898         lw->vd = NULL;
1899
1900         lw->file_view = NULL;
1901         lw->vf = NULL;
1902
1903         lw->info_box = NULL;
1904         lw->info_progress_bar = NULL;
1905         lw->info_sort = NULL;
1906         lw->info_status = NULL;
1907         lw->info_details = NULL;
1908         lw->info_pixel = NULL;
1909         lw->info_zoom = NULL;
1910
1911 /*
1912         if (lw->ui_manager) g_object_unref(lw->ui_manager);
1913         lw->ui_manager = NULL;
1914         lw->action_group = NULL;
1915         lw->action_group_editors = NULL;
1916 */
1917
1918         gtk_container_remove(GTK_CONTAINER(lw->main_box), lw->group_box);
1919         lw->group_box = NULL;
1920
1921         /* re-fill */
1922
1923         layout_grid_setup(lw);
1924         layout_tools_hide(lw, lw->options.tools_hidden);
1925
1926         layout_util_sync(lw);
1927         layout_status_update_all(lw);
1928
1929
1930         // printf("%d %d %d \n", G_OBJECT(lw->utility_box)->ref_count, G_OBJECT(lw->menu_bar)->ref_count, G_OBJECT(lw->toolbar)->ref_count);
1931
1932         /* sync */
1933
1934         if (image_get_fd(lw->image))
1935                 {
1936                 layout_set_fd(lw, image_get_fd(lw->image));
1937                 }
1938         else
1939                 {
1940                 layout_set_fd(lw, dir_fd);
1941                 }
1942         image_top_window_set_sync(lw->image, (lw->options.tools_float || lw->options.tools_hidden));
1943
1944         /* clean up */
1945
1946         file_data_unref(dir_fd);
1947 }
1948
1949 void layout_colors_update(void)
1950 {
1951         GList *work;
1952
1953         work = layout_window_list;
1954         while (work)
1955                 {
1956                 gint i;
1957                 LayoutWindow *lw = work->data;
1958                 work = work->next;
1959
1960                 if (!lw->image) continue;
1961
1962                 for (i = 0; i < MAX_SPLIT_IMAGES; i++)
1963                         {
1964                         if (!lw->split_images[i]) continue;
1965                         image_background_set_color_from_options(lw->split_images[i], !!lw->full_screen);
1966                         }
1967
1968                 image_background_set_color_from_options(lw->image, !!lw->full_screen);
1969                 }
1970 }
1971
1972 void layout_tools_float_toggle(LayoutWindow *lw)
1973 {
1974         gboolean popped;
1975
1976         if (!lw) return;
1977
1978         if (!lw->options.tools_hidden)
1979                 {
1980                 popped = !lw->options.tools_float;
1981                 }
1982         else
1983                 {
1984                 popped = TRUE;
1985                 }
1986
1987         if (lw->options.tools_float == popped)
1988                 {
1989                 if (popped && lw->options.tools_hidden)
1990                         {
1991                         layout_tools_float_set(lw, popped, FALSE);
1992                         }
1993                 }
1994         else
1995                 {
1996                 if (lw->options.tools_float)
1997                         {
1998                         layout_tools_float_set(lw, FALSE, FALSE);
1999                         }
2000                 else
2001                         {
2002                         layout_tools_float_set(lw, TRUE, FALSE);
2003                         }
2004                 }
2005 }
2006
2007 void layout_tools_hide_toggle(LayoutWindow *lw)
2008 {
2009         if (!lw) return;
2010
2011         layout_tools_float_set(lw, lw->options.tools_float, !lw->options.tools_hidden);
2012 }
2013
2014 void layout_tools_float_set(LayoutWindow *lw, gboolean popped, gboolean hidden)
2015 {
2016         if (!layout_valid(&lw)) return;
2017
2018         if (lw->options.tools_float == popped && lw->options.tools_hidden == hidden) return;
2019
2020         if (lw->options.tools_float == popped && lw->options.tools_float && lw->tools)
2021                 {
2022                 layout_tools_hide(lw, hidden);
2023                 return;
2024                 }
2025
2026         lw->options.tools_float = popped;
2027         lw->options.tools_hidden = hidden;
2028
2029         layout_style_set(lw, -1, NULL);
2030 }
2031
2032 gboolean layout_tools_float_get(LayoutWindow *lw, gboolean *popped, gboolean *hidden)
2033 {
2034         if (!layout_valid(&lw)) return FALSE;
2035
2036         *popped = lw->options.tools_float;
2037         *hidden = lw->options.tools_hidden;
2038
2039         return TRUE;
2040 }
2041
2042 void layout_toolbar_toggle(LayoutWindow *lw)
2043 {
2044         if (!layout_valid(&lw)) return;
2045         if (!lw->toolbar[TOOLBAR_MAIN]) return;
2046
2047         lw->options.toolbar_hidden = !lw->options.toolbar_hidden;
2048
2049         if (lw->options.toolbar_hidden)
2050                 {
2051                 if (gtk_widget_get_visible(lw->toolbar[TOOLBAR_MAIN])) gtk_widget_hide(lw->toolbar[TOOLBAR_MAIN]);
2052                 }
2053         else
2054                 {
2055                 if (!gtk_widget_get_visible(lw->toolbar[TOOLBAR_MAIN])) gtk_widget_show(lw->toolbar[TOOLBAR_MAIN]);
2056                 }
2057 }
2058
2059 void layout_info_pixel_set(LayoutWindow *lw, gboolean show)
2060 {
2061         GtkWidget *frame;
2062
2063         if (!layout_valid(&lw)) return;
2064         if (!lw->info_pixel) return;
2065
2066         lw->options.show_info_pixel = show;
2067
2068         frame = gtk_widget_get_parent(lw->info_pixel);
2069         if (!lw->options.show_info_pixel)
2070                 {
2071                 gtk_widget_hide(frame);
2072                 }
2073         else
2074                 {
2075                 gtk_widget_show(frame);
2076                 }
2077
2078         g_signal_emit_by_name (lw->image->pr, "update-pixel");
2079 }
2080
2081 /*
2082  *-----------------------------------------------------------------------------
2083  * configuration
2084  *-----------------------------------------------------------------------------
2085  */
2086
2087 #define CONFIG_WINDOW_DEF_WIDTH         600
2088 #define CONFIG_WINDOW_DEF_HEIGHT        400
2089
2090 typedef struct _LayoutConfig LayoutConfig;
2091 struct _LayoutConfig
2092 {
2093         LayoutWindow *lw;
2094
2095         GtkWidget *configwindow;
2096         GtkWidget *home_path_entry;
2097         GtkWidget *layout_widget;
2098
2099         LayoutOptions options;
2100 };
2101
2102 static gint layout_config_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data);
2103
2104 static void layout_config_close_cb(GtkWidget *widget, gpointer data)
2105 {
2106         LayoutConfig *lc = data;
2107
2108         gtk_widget_destroy(lc->configwindow);
2109         free_layout_options_content(&lc->options);
2110         g_free(lc);
2111 }
2112
2113 static gint layout_config_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
2114 {
2115         layout_config_close_cb(w, data);
2116         return TRUE;
2117 }
2118
2119 static void layout_config_apply_cb(GtkWidget *widget, gpointer data)
2120 {
2121         LayoutConfig *lc = data;
2122
2123         g_free(lc->options.order);
2124         lc->options.order = layout_config_get(lc->layout_widget, &lc->options.style);
2125
2126         config_entry_to_option(lc->home_path_entry, &lc->options.home_path, remove_trailing_slash);
2127
2128         layout_apply_options(lc->lw, &lc->options);
2129 }
2130
2131 static void layout_config_help_cb(GtkWidget *widget, gpointer data)
2132 {
2133         help_window_show("GuideOptionsLayout.html");
2134 }
2135
2136 static void layout_config_ok_cb(GtkWidget *widget, gpointer data)
2137 {
2138         LayoutConfig *lc = data;
2139         layout_config_apply_cb(widget, lc);
2140         layout_config_close_cb(widget, lc);
2141 }
2142
2143 static void home_path_set_current_cb(GtkWidget *widget, gpointer data)
2144 {
2145         LayoutConfig *lc = data;
2146         gtk_entry_set_text(GTK_ENTRY(lc->home_path_entry), layout_get_path(lc->lw));
2147 }
2148
2149 static void startup_path_set_current_cb(GtkWidget *widget, gpointer data)
2150 {
2151         LayoutConfig *lc = data;
2152         if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
2153                 {
2154                 return;
2155                 }
2156         lc->options.startup_path = STARTUP_PATH_CURRENT;
2157 }
2158
2159 static void startup_path_set_last_cb(GtkWidget *widget, gpointer data)
2160 {
2161         LayoutConfig *lc = data;
2162         if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
2163                 {
2164                 return;
2165                 }
2166         lc->options.startup_path = STARTUP_PATH_LAST;
2167 }
2168
2169 static void startup_path_set_home_cb(GtkWidget *widget, gpointer data)
2170 {
2171         LayoutConfig *lc = data;
2172         if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
2173                 {
2174                 return;
2175                 }
2176         lc->options.startup_path = STARTUP_PATH_HOME;
2177 }
2178
2179
2180 /*
2181 static void layout_config_save_cb(GtkWidget *widget, gpointer data)
2182 {
2183         layout_config_apply();
2184         save_options(options);
2185 }
2186 */
2187
2188 void layout_show_config_window(LayoutWindow *lw)
2189 {
2190         LayoutConfig *lc;
2191         GtkWidget *win_vbox;
2192         GtkWidget *hbox;
2193         GtkWidget *vbox;
2194         GtkWidget *button;
2195         GtkWidget *ct_button;
2196         GtkWidget *group;
2197         GtkWidget *frame;
2198         GtkWidget *tabcomp;
2199
2200         lc = g_new0(LayoutConfig, 1);
2201         lc->lw = lw;
2202         layout_sync_options_with_current_state(lw);
2203         copy_layout_options(&lc->options, &lw->options);
2204
2205         lc->configwindow = window_new(GTK_WINDOW_TOPLEVEL, "Layout", PIXBUF_INLINE_ICON_CONFIG, NULL, _("Window options and layout"));
2206         DEBUG_NAME(lc->configwindow);
2207         gtk_window_set_type_hint(GTK_WINDOW(lc->configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
2208
2209         g_signal_connect(G_OBJECT(lc->configwindow), "delete_event",
2210                          G_CALLBACK(layout_config_delete_cb), lc);
2211
2212         gtk_window_set_default_size(GTK_WINDOW(lc->configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
2213         gtk_window_set_resizable(GTK_WINDOW(lc->configwindow), TRUE);
2214         gtk_container_set_border_width(GTK_CONTAINER(lc->configwindow), PREF_PAD_BORDER);
2215
2216         win_vbox = gtk_vbox_new(FALSE, PREF_PAD_SPACE);
2217         DEBUG_NAME(win_vbox);
2218         gtk_container_add(GTK_CONTAINER(lc->configwindow), win_vbox);
2219         gtk_widget_show(win_vbox);
2220
2221         hbox = gtk_hbutton_box_new();
2222         gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
2223         gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
2224         gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
2225         gtk_widget_show(hbox);
2226
2227         button = pref_button_new(NULL, GTK_STOCK_OK, NULL, FALSE,
2228                                  G_CALLBACK(layout_config_ok_cb), lc);
2229         gtk_container_add(GTK_CONTAINER(hbox), button);
2230         gtk_widget_set_can_default(button, TRUE);
2231         gtk_widget_grab_default(button);
2232         gtk_widget_show(button);
2233
2234         ct_button = button;
2235 /*
2236         button = pref_button_new(NULL, GTK_STOCK_SAVE, NULL, FALSE,
2237                                  G_CALLBACK(layout_config_save_cb), NULL);
2238         gtk_container_add(GTK_CONTAINER(hbox), button);
2239         GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2240         gtk_widget_show(button);
2241 */
2242         button = pref_button_new(NULL, GTK_STOCK_HELP, NULL, FALSE,
2243                                  G_CALLBACK(layout_config_help_cb), lc);
2244         gtk_container_add(GTK_CONTAINER(hbox), button);
2245         gtk_widget_set_can_default(button, TRUE);
2246         gtk_widget_show(button);
2247
2248         button = pref_button_new(NULL, GTK_STOCK_APPLY, NULL, FALSE,
2249                                  G_CALLBACK(layout_config_apply_cb), lc);
2250         gtk_container_add(GTK_CONTAINER(hbox), button);
2251         gtk_widget_set_can_default(button, TRUE);
2252         gtk_widget_show(button);
2253
2254         button = pref_button_new(NULL, GTK_STOCK_CANCEL, NULL, FALSE,
2255                                  G_CALLBACK(layout_config_close_cb), lc);
2256         gtk_container_add(GTK_CONTAINER(hbox), button);
2257         gtk_widget_set_can_default(button, TRUE);
2258         gtk_widget_show(button);
2259
2260         if (!generic_dialog_get_alternative_button_order(lc->configwindow))
2261                 {
2262                 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
2263                 }
2264
2265         frame = pref_frame_new(win_vbox, TRUE, NULL, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2266         DEBUG_NAME(frame);
2267
2268         vbox = gtk_vbox_new(FALSE, PREF_PAD_SPACE);
2269         DEBUG_NAME(vbox);
2270         gtk_container_add(GTK_CONTAINER(frame), vbox);
2271         gtk_widget_show(vbox);
2272
2273
2274         group = pref_group_new(vbox, FALSE, _("General options"), GTK_ORIENTATION_VERTICAL);
2275
2276         pref_label_new(group, _("Home path (empty to use your home directory)"));
2277         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2278
2279         tabcomp = tab_completion_new(&lc->home_path_entry, lc->options.home_path, NULL, NULL, NULL, NULL);
2280         tab_completion_add_select_button(lc->home_path_entry, NULL, TRUE);
2281         gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
2282         gtk_widget_show(tabcomp);
2283
2284         button = pref_button_new(hbox, NULL, _("Use current"), FALSE,
2285                                  G_CALLBACK(home_path_set_current_cb), lc);
2286
2287         pref_checkbox_new_int(group, _("Show date in directories list view"),
2288                               lc->options.show_directory_date, &lc->options.show_directory_date);
2289
2290         group = pref_group_new(vbox, FALSE, _("Start-up directory:"), GTK_ORIENTATION_VERTICAL);
2291
2292         button = pref_radiobutton_new(group, NULL, _("No change"),
2293                                       (lc->options.startup_path == STARTUP_PATH_CURRENT),
2294                                       G_CALLBACK(startup_path_set_current_cb), lc);
2295         button = pref_radiobutton_new(group, button, _("Restore last path"),
2296                                       (lc->options.startup_path == STARTUP_PATH_LAST),
2297                                       G_CALLBACK(startup_path_set_last_cb), lc);
2298         button = pref_radiobutton_new(group, button, _("Home path"),
2299                                       (lc->options.startup_path == STARTUP_PATH_HOME),
2300                                       G_CALLBACK(startup_path_set_home_cb), lc);
2301
2302         group = pref_group_new(vbox, FALSE, _("Layout"), GTK_ORIENTATION_VERTICAL);
2303
2304         lc->layout_widget = layout_config_new();
2305         DEBUG_NAME(lc->layout_widget);
2306         layout_config_set(lc->layout_widget, lw->options.style, lw->options.order);
2307         gtk_box_pack_start(GTK_BOX(group), lc->layout_widget, TRUE, TRUE, 0);
2308
2309         gtk_widget_show(lc->layout_widget);
2310         gtk_widget_show(lc->configwindow);
2311 }
2312
2313 /*
2314  *-----------------------------------------------------------------------------
2315  * base
2316  *-----------------------------------------------------------------------------
2317  */
2318
2319 void layout_sync_options_with_current_state(LayoutWindow *lw)
2320 {
2321         Histogram *histogram;
2322         if (!layout_valid(&lw)) return;
2323
2324         lw->options.main_window.maximized =  window_maximized(lw->window);
2325         if (!lw->options.main_window.maximized)
2326                 {
2327                 layout_geometry_get(lw, &lw->options.main_window.x, &lw->options.main_window.y,
2328                                     &lw->options.main_window.w, &lw->options.main_window.h);
2329                 }
2330
2331         layout_geometry_get_dividers(lw, &lw->options.main_window.hdivider_pos, &lw->options.main_window.vdivider_pos);
2332
2333 //      layout_sort_get(NULL, &options->file_sort.method, &options->file_sort.ascending);
2334
2335         layout_geometry_get_tools(lw, &lw->options.float_window.x, &lw->options.float_window.y,
2336                                   &lw->options.float_window.w, &lw->options.float_window.h, &lw->options.float_window.vdivider_pos);
2337
2338         lw->options.image_overlay.state = image_osd_get(lw->image);
2339         histogram = image_osd_get_histogram(lw->image);
2340
2341         lw->options.image_overlay.histogram_channel = histogram->histogram_channel;
2342         lw->options.image_overlay.histogram_mode = histogram->histogram_mode;
2343
2344         g_free(lw->options.last_path);
2345         lw->options.last_path = g_strdup(layout_get_path(lw));
2346
2347         layout_geometry_get_log_window(lw, &lw->options.log_window.x, &lw->options.log_window.y,
2348                                          &lw->options.log_window.w, &lw->options.log_window.h);
2349
2350 }
2351
2352 void layout_apply_options(LayoutWindow *lw, LayoutOptions *lop)
2353 {
2354         gboolean refresh_style;
2355         gboolean refresh_lists;
2356
2357         if (!layout_valid(&lw)) return;
2358 /* FIXME: add other options too */
2359
2360         refresh_style = (lop->style != lw->options.style || strcmp(lop->order, lw->options.order) != 0);
2361         refresh_lists = (lop->show_directory_date != lw->options.show_directory_date);
2362
2363         copy_layout_options(&lw->options, lop);
2364
2365         if (refresh_style) layout_style_set(lw, lw->options.style, lw->options.order);
2366         if (refresh_lists) layout_refresh(lw);
2367 }
2368
2369 void save_layout(LayoutWindow *lw)
2370 {
2371         gchar *path;
2372         gchar *xml_name;
2373
2374         if (!g_str_has_prefix(lw->options.id, "lw") && !g_str_equal(lw->options.id, "main"))
2375                 {
2376                 xml_name = g_strdup_printf("%s.xml", lw->options.id);
2377                 path = g_build_filename(get_window_layouts_dir(), xml_name, NULL);
2378                 save_config_to_file(path, options, lw);
2379
2380                 g_free(xml_name);
2381                 g_free(path);
2382                 }
2383 }
2384
2385 void layout_close(LayoutWindow *lw)
2386 {
2387         GList *list;
2388         LayoutWindow *tmp_lw;
2389
2390         if (layout_window_list && layout_window_list->next)
2391                 {
2392                 if (g_strcmp0(lw->options.id, "main") == 0)
2393                         {
2394                         while (layout_window_list && layout_window_list->next)
2395                                 {
2396                                 list = layout_window_list;
2397                                 while (list)
2398                                         {
2399                                         tmp_lw = list->data;
2400                                         if (g_strcmp0(tmp_lw->options.id, "main") != 0)
2401                                                 {
2402                                                 save_layout(list->data);
2403                                                 layout_free(list->data);
2404                                                 break;
2405                                                 }
2406                                         list = list->next;
2407                                         }
2408                                 }
2409                         exit_program();
2410                         }
2411                 else
2412                         {
2413                         save_layout(lw);
2414                         layout_free(lw);
2415                         }
2416                 }
2417         else
2418                 {
2419                 exit_program();
2420                 }
2421 }
2422
2423 void layout_free(LayoutWindow *lw)
2424 {
2425         gint i;
2426         if (!lw) return;
2427
2428         layout_window_list = g_list_remove(layout_window_list, lw);
2429         if (current_lw == lw) current_lw = NULL;
2430
2431         if (lw->exif_window) g_signal_handlers_disconnect_matched(G_OBJECT(lw->exif_window), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, lw);
2432
2433         layout_bars_close(lw);
2434
2435         g_object_unref(lw->menu_bar);
2436         g_object_unref(lw->utility_box);
2437
2438         for (i = 0; i < TOOLBAR_COUNT; i++)
2439                 {
2440                 if (lw->toolbar[i]) g_object_unref(lw->toolbar[i]);
2441                 }
2442
2443         gtk_widget_destroy(lw->window);
2444
2445         if (lw->split_image_sizegroup) g_object_unref(lw->split_image_sizegroup);
2446
2447         file_data_unregister_notify_func(layout_image_notify_cb, lw);
2448
2449         if (lw->dir_fd)
2450                 {
2451                 file_data_unregister_real_time_monitor(lw->dir_fd);
2452                 file_data_unref(lw->dir_fd);
2453                 }
2454
2455         free_layout_options_content(&lw->options);
2456         g_free(lw);
2457 }
2458
2459 static gboolean layout_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer data)
2460 {
2461         LayoutWindow *lw = data;
2462
2463         layout_close(lw);
2464         return TRUE;
2465 }
2466
2467 LayoutWindow *layout_new(FileData *dir_fd, LayoutOptions *lop)
2468 {
2469         return layout_new_with_geometry(dir_fd, lop, NULL);
2470 }
2471
2472 gboolean release_cb(GtkWidget *widget, GdkEventButton *event, gpointer data)
2473 {
2474         return defined_mouse_buttons(widget, event, data);
2475 }
2476
2477 LayoutWindow *layout_new_with_geometry(FileData *dir_fd, LayoutOptions *lop,
2478                                        const gchar *geometry)
2479 {
2480         LayoutWindow *lw;
2481         GdkGeometry hint;
2482         GdkWindowHints hint_mask;
2483         Histogram *histogram;
2484
2485         DEBUG_1("%s layout_new: start", get_exec_time());
2486         lw = g_new0(LayoutWindow, 1);
2487
2488         if (lop)
2489                 copy_layout_options(&lw->options, lop);
2490         else
2491                 init_layout_options(&lw->options);
2492
2493         lw->sort_method = SORT_NAME;
2494         lw->sort_ascend = TRUE;
2495
2496         layout_set_unique_id(lw);
2497 //      lw->options.tools_float = popped;
2498 //      lw->options.tools_hidden = hidden;
2499 //      lw->bar_sort_enabled = options->panels.sort.enabled;
2500 //      lw->bar_enabled = options->panels.info.enabled;
2501
2502         /* default layout */
2503
2504         layout_config_parse(lw->options.style, lw->options.order,
2505                             &lw->dir_location,  &lw->file_location, &lw->image_location);
2506         if (lw->options.dir_view_type > DIRVIEW_LAST) lw->options.dir_view_type = 0;
2507         if (lw->options.file_view_type > FILEVIEW_LAST) lw->options.file_view_type = 0;
2508
2509         /* divider positions */
2510
2511         if (!options->save_window_positions)
2512                 {
2513                 lw->options.main_window.hdivider_pos = MAIN_WINDOW_DIV_HPOS;
2514                 lw->options.main_window.vdivider_pos = MAIN_WINDOW_DIV_VPOS;
2515                 lw->options.float_window.vdivider_pos = MAIN_WINDOW_DIV_VPOS;
2516                 }
2517
2518         /* window */
2519
2520         lw->window = window_new(GTK_WINDOW_TOPLEVEL, GQ_APPNAME_LC, NULL, NULL, NULL);
2521         DEBUG_NAME(lw->window);
2522         gtk_window_set_resizable(GTK_WINDOW(lw->window), TRUE);
2523         gtk_container_set_border_width(GTK_CONTAINER(lw->window), 0);
2524
2525         g_signal_connect(G_OBJECT(lw->window), "button_release_event", G_CALLBACK(release_cb), lw);
2526
2527         if (options->save_window_positions)
2528                 {
2529                 hint_mask = GDK_HINT_USER_POS;
2530                 }
2531         else
2532                 {
2533                 hint_mask = 0;
2534                 }
2535
2536         hint.min_width = 32;
2537         hint.min_height = 32;
2538         hint.base_width = 0;
2539         hint.base_height = 0;
2540         gtk_window_set_geometry_hints(GTK_WINDOW(lw->window), NULL, &hint,
2541                                       GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE | hint_mask);
2542
2543         if (options->save_window_positions)
2544                 {
2545                 gtk_window_set_default_size(GTK_WINDOW(lw->window), lw->options.main_window.w, lw->options.main_window.h);
2546 //              if (!layout_window_list)
2547 //                      {
2548                 gtk_window_move(GTK_WINDOW(lw->window), lw->options.main_window.x, lw->options.main_window.y);
2549                 if (lw->options.main_window.maximized) gtk_window_maximize(GTK_WINDOW(lw->window));
2550 //                      }
2551                 }
2552         else
2553                 {
2554                 gtk_window_set_default_size(GTK_WINDOW(lw->window), MAINWINDOW_DEF_WIDTH, MAINWINDOW_DEF_HEIGHT);
2555                 }
2556
2557         g_signal_connect(G_OBJECT(lw->window), "delete_event",
2558                          G_CALLBACK(layout_delete_cb), lw);
2559
2560         g_signal_connect(G_OBJECT(lw->window), "focus-in-event",
2561                          G_CALLBACK(layout_set_current_cb), lw);
2562
2563         layout_keyboard_init(lw, lw->window);
2564
2565 #ifdef HAVE_LIRC
2566         layout_image_lirc_init(lw);
2567 #endif
2568
2569         lw->main_box = gtk_vbox_new(FALSE, 0);
2570         DEBUG_NAME(lw->main_box);
2571         gtk_container_add(GTK_CONTAINER(lw->window), lw->main_box);
2572         gtk_widget_show(lw->main_box);
2573
2574         layout_grid_setup(lw);
2575         image_top_window_set_sync(lw->image, (lw->options.tools_float || lw->options.tools_hidden));
2576
2577         layout_util_sync(lw);
2578         layout_status_update_all(lw);
2579
2580         if (dir_fd)
2581                 {
2582                 layout_set_fd(lw, dir_fd);
2583                 }
2584         else
2585                 {
2586                 GdkPixbuf *pixbuf;
2587
2588                 pixbuf = pixbuf_inline(PIXBUF_INLINE_LOGO);
2589
2590                 /* FIXME: the zoom value set here is the value, which is then copied again and again
2591                    in "Leave Zoom at previous setting" mode. This is not ideal.  */
2592                 image_change_pixbuf(lw->image, pixbuf, 0.0, FALSE);
2593                 g_object_unref(pixbuf);
2594                 }
2595
2596         if (geometry)
2597                 {
2598                 if (!gtk_window_parse_geometry(GTK_WINDOW(lw->window), geometry))
2599                         {
2600                         log_printf("%s", _("Invalid geometry\n"));
2601                         }
2602                 }
2603
2604         gtk_widget_show(lw->window);
2605         layout_tools_hide(lw, lw->options.tools_hidden);
2606
2607         image_osd_set(lw->image, lw->options.image_overlay.state);
2608         histogram = image_osd_get_histogram(lw->image);
2609
2610         histogram->histogram_channel = lw->options.image_overlay.histogram_channel;
2611         histogram->histogram_mode = lw->options.image_overlay.histogram_mode;
2612
2613         layout_window_list = g_list_append(layout_window_list, lw);
2614
2615         file_data_register_notify_func(layout_image_notify_cb, lw, NOTIFY_PRIORITY_LOW);
2616
2617         DEBUG_1("%s layout_new: end", get_exec_time());
2618
2619         return lw;
2620 }
2621
2622 void layout_write_attributes(LayoutOptions *layout, GString *outstr, gint indent)
2623 {
2624         WRITE_NL(); WRITE_CHAR(*layout, id);
2625
2626         WRITE_NL(); WRITE_INT(*layout, style);
2627         WRITE_NL(); WRITE_CHAR(*layout, order);
2628         WRITE_NL(); WRITE_UINT(*layout, dir_view_type);
2629         WRITE_NL(); WRITE_UINT(*layout, file_view_type);
2630         WRITE_NL(); WRITE_UINT(*layout, dir_view_list_sort.method);
2631         WRITE_NL(); WRITE_BOOL(*layout, dir_view_list_sort.ascend);
2632         WRITE_NL(); WRITE_BOOL(*layout, show_marks);
2633         WRITE_NL(); WRITE_BOOL(*layout, show_file_filter);
2634         WRITE_NL(); WRITE_BOOL(*layout, show_thumbnails);
2635         WRITE_NL(); WRITE_BOOL(*layout, show_directory_date);
2636         WRITE_NL(); WRITE_CHAR(*layout, home_path);
2637         WRITE_NL(); WRITE_CHAR(*layout, last_path);
2638         WRITE_NL(); WRITE_UINT(*layout, startup_path);
2639         WRITE_SEPARATOR();
2640
2641         WRITE_NL(); WRITE_INT(*layout, main_window.x);
2642         WRITE_NL(); WRITE_INT(*layout, main_window.y);
2643         WRITE_NL(); WRITE_INT(*layout, main_window.w);
2644         WRITE_NL(); WRITE_INT(*layout, main_window.h);
2645         WRITE_NL(); WRITE_BOOL(*layout, main_window.maximized);
2646         WRITE_NL(); WRITE_INT(*layout, main_window.hdivider_pos);
2647         WRITE_NL(); WRITE_INT(*layout, main_window.vdivider_pos);
2648         WRITE_SEPARATOR();
2649
2650         WRITE_NL(); WRITE_INT(*layout, folder_window.vdivider_pos);
2651         WRITE_SEPARATOR();
2652
2653         WRITE_NL(); WRITE_INT(*layout, float_window.x);
2654         WRITE_NL(); WRITE_INT(*layout, float_window.y);
2655         WRITE_NL(); WRITE_INT(*layout, float_window.w);
2656         WRITE_NL(); WRITE_INT(*layout, float_window.h);
2657         WRITE_NL(); WRITE_INT(*layout, float_window.vdivider_pos);
2658         WRITE_SEPARATOR();
2659
2660         WRITE_NL(); WRITE_INT(*layout, properties_window.w);
2661         WRITE_NL(); WRITE_INT(*layout, properties_window.h);
2662         WRITE_SEPARATOR();
2663
2664         WRITE_NL(); WRITE_BOOL(*layout, tools_float);
2665         WRITE_NL(); WRITE_BOOL(*layout, tools_hidden);
2666         WRITE_SEPARATOR();
2667
2668         WRITE_NL(); WRITE_BOOL(*layout, toolbar_hidden);
2669         WRITE_NL(); WRITE_BOOL(*layout, show_info_pixel);
2670         WRITE_NL(); WRITE_BOOL(*layout, ignore_alpha);
2671         WRITE_SEPARATOR();
2672
2673         WRITE_NL(); WRITE_BOOL(*layout, bars_state.info);
2674         WRITE_NL(); WRITE_BOOL(*layout, bars_state.sort);
2675         WRITE_NL(); WRITE_BOOL(*layout, bars_state.tools_float);
2676         WRITE_NL(); WRITE_BOOL(*layout, bars_state.tools_hidden);
2677         WRITE_NL(); WRITE_BOOL(*layout, bars_state.hidden);
2678         WRITE_SEPARATOR();
2679
2680         WRITE_NL(); WRITE_UINT(*layout, image_overlay.state);
2681         WRITE_NL(); WRITE_INT(*layout, image_overlay.histogram_channel);
2682         WRITE_NL(); WRITE_INT(*layout, image_overlay.histogram_mode);
2683
2684         WRITE_NL(); WRITE_INT(*layout, log_window.x);
2685         WRITE_NL(); WRITE_INT(*layout, log_window.y);
2686         WRITE_NL(); WRITE_INT(*layout, log_window.w);
2687         WRITE_NL(); WRITE_INT(*layout, log_window.h);
2688
2689         WRITE_NL(); WRITE_INT(*layout, search_window.x);
2690         WRITE_NL(); WRITE_INT(*layout, search_window.y);
2691         WRITE_NL(); WRITE_INT(*layout, search_window.w);
2692         WRITE_NL(); WRITE_INT(*layout, search_window.h);
2693
2694         WRITE_NL(); WRITE_INT(*layout, dupe_window.x);
2695         WRITE_NL(); WRITE_INT(*layout, dupe_window.y);
2696         WRITE_NL(); WRITE_INT(*layout, dupe_window.w);
2697         WRITE_NL(); WRITE_INT(*layout, dupe_window.h);
2698         WRITE_SEPARATOR();
2699
2700         WRITE_NL(); WRITE_BOOL(*layout, animate);
2701 }
2702
2703
2704 void layout_write_config(LayoutWindow *lw, GString *outstr, gint indent)
2705 {
2706         layout_sync_options_with_current_state(lw);
2707         WRITE_NL(); WRITE_STRING("<layout");
2708         layout_write_attributes(&lw->options, outstr, indent + 1);
2709         WRITE_STRING(">");
2710
2711         bar_sort_write_config(lw->bar_sort, outstr, indent + 1);
2712         bar_write_config(lw->bar, outstr, indent + 1);
2713
2714         WRITE_SEPARATOR();
2715         generic_dialog_windows_write_config(outstr, indent + 1);
2716
2717         WRITE_SEPARATOR();
2718         layout_toolbar_write_config(lw, TOOLBAR_MAIN, outstr, indent + 1);
2719         layout_toolbar_write_config(lw, TOOLBAR_STATUS, outstr, indent + 1);
2720
2721         WRITE_NL(); WRITE_STRING("</layout>");
2722 }
2723
2724 void layout_load_attributes(LayoutOptions *layout, const gchar **attribute_names, const gchar **attribute_values)
2725 {
2726         gchar *id = NULL;
2727
2728         while (*attribute_names)
2729                 {
2730                 const gchar *option = *attribute_names++;
2731                 const gchar *value = *attribute_values++;
2732
2733                 /* layout options */
2734                 if (READ_CHAR_FULL("id", id)) continue;
2735
2736                 if (READ_INT(*layout, style)) continue;
2737                 if (READ_CHAR(*layout, order)) continue;
2738
2739                 if (READ_UINT(*layout, dir_view_type)) continue;
2740                 if (READ_UINT(*layout, file_view_type)) continue;
2741                 if (READ_UINT(*layout, dir_view_list_sort.method)) continue;
2742                 if (READ_BOOL(*layout, dir_view_list_sort.ascend)) continue;
2743                 if (READ_BOOL(*layout, show_marks)) continue;
2744                 if (READ_BOOL(*layout, show_file_filter)) continue;
2745                 if (READ_BOOL(*layout, show_thumbnails)) continue;
2746                 if (READ_BOOL(*layout, show_directory_date)) continue;
2747                 if (READ_CHAR(*layout, home_path)) continue;
2748                 if (READ_CHAR(*layout, last_path)) continue;
2749                 if (READ_UINT_CLAMP(*layout, startup_path, 0, STARTUP_PATH_HOME)) continue;
2750
2751                 /* window positions */
2752
2753                 if (READ_INT(*layout, main_window.x)) continue;
2754                 if (READ_INT(*layout, main_window.y)) continue;
2755                 if (READ_INT(*layout, main_window.w)) continue;
2756                 if (READ_INT(*layout, main_window.h)) continue;
2757                 if (READ_BOOL(*layout, main_window.maximized)) continue;
2758                 if (READ_INT(*layout, main_window.hdivider_pos)) continue;
2759                 if (READ_INT(*layout, main_window.vdivider_pos)) continue;
2760
2761                 if (READ_INT_CLAMP(*layout, folder_window.vdivider_pos, 1, 1000)) continue;
2762
2763                 if (READ_INT(*layout, float_window.x)) continue;
2764                 if (READ_INT(*layout, float_window.y)) continue;
2765                 if (READ_INT(*layout, float_window.w)) continue;
2766                 if (READ_INT(*layout, float_window.h)) continue;
2767                 if (READ_INT(*layout, float_window.vdivider_pos)) continue;
2768
2769                 if (READ_INT(*layout, properties_window.w)) continue;
2770                 if (READ_INT(*layout, properties_window.h)) continue;
2771
2772                 if (READ_BOOL(*layout, tools_float)) continue;
2773                 if (READ_BOOL(*layout, tools_hidden)) continue;
2774                 if (READ_BOOL(*layout, toolbar_hidden)) continue;
2775                 if (READ_BOOL(*layout, show_info_pixel)) continue;
2776                 if (READ_BOOL(*layout, ignore_alpha)) continue;
2777
2778                 if (READ_BOOL(*layout, bars_state.info)) continue;
2779                 if (READ_BOOL(*layout, bars_state.sort)) continue;
2780                 if (READ_BOOL(*layout, bars_state.tools_float)) continue;
2781                 if (READ_BOOL(*layout, bars_state.tools_hidden)) continue;
2782                 if (READ_BOOL(*layout, bars_state.hidden)) continue;
2783
2784                 if (READ_UINT(*layout, image_overlay.state)) continue;
2785                 if (READ_INT(*layout, image_overlay.histogram_channel)) continue;
2786                 if (READ_INT(*layout, image_overlay.histogram_mode)) continue;
2787
2788                 if (READ_INT(*layout, log_window.x)) continue;
2789                 if (READ_INT(*layout, log_window.y)) continue;
2790                 if (READ_INT(*layout, log_window.w)) continue;
2791                 if (READ_INT(*layout, log_window.h)) continue;
2792
2793                 if (READ_INT(*layout, search_window.x)) continue;
2794                 if (READ_INT(*layout, search_window.y)) continue;
2795                 if (READ_INT(*layout, search_window.w)) continue;
2796                 if (READ_INT(*layout, search_window.h)) continue;
2797
2798                 if (READ_INT(*layout, dupe_window.x)) continue;
2799                 if (READ_INT(*layout, dupe_window.y)) continue;
2800                 if (READ_INT(*layout, dupe_window.w)) continue;
2801                 if (READ_INT(*layout, dupe_window.h)) continue;
2802
2803                 if (READ_BOOL(*layout, animate)) continue;
2804
2805                 log_printf("unknown attribute %s = %s\n", option, value);
2806                 }
2807         if (id && strcmp(id, LAYOUT_ID_CURRENT) != 0)
2808                 {
2809                 g_free(layout->id);
2810                 layout->id = id;
2811                 }
2812         else
2813                 {
2814                 g_free(id);
2815                 }
2816 }
2817
2818 static void layout_config_startup_path(LayoutOptions *lop, gchar **path)
2819 {
2820         switch (lop->startup_path)
2821                 {
2822                 case STARTUP_PATH_LAST:
2823                         *path = (lop->last_path && isdir(lop->last_path)) ? g_strdup(lop->last_path) : get_current_dir();
2824                         break;
2825                 case STARTUP_PATH_HOME:
2826                         *path = (lop->home_path && isdir(lop->home_path)) ? g_strdup(lop->home_path) : g_strdup(homedir());
2827                         break;
2828                 default:
2829                         *path = get_current_dir();
2830                         break;
2831                 }
2832 }
2833
2834
2835 static void layout_config_commandline(LayoutOptions *lop, gchar **path)
2836 {
2837         if (command_line->startup_blank)
2838                 {
2839                 *path = NULL;
2840                 }
2841         else if (command_line->file)
2842                 {
2843                 *path = g_strdup(command_line->file);
2844                 }
2845         else if (command_line->path)
2846                 {
2847                 *path = g_strdup(command_line->path);
2848                 }
2849         else layout_config_startup_path(lop, path);
2850
2851         if (command_line->tools_show)
2852                 {
2853                 lop->tools_float = FALSE;
2854                 lop->tools_hidden = FALSE;
2855                 }
2856         else if (command_line->tools_hide)
2857                 {
2858                 lop->tools_hidden = TRUE;
2859                 }
2860 }
2861
2862 LayoutWindow *layout_new_from_config(const gchar **attribute_names, const gchar **attribute_values, gboolean use_commandline)
2863 {
2864         LayoutOptions lop;
2865         LayoutWindow *lw;
2866         gchar *path = NULL;
2867
2868         init_layout_options(&lop);
2869
2870         if (attribute_names) layout_load_attributes(&lop, attribute_names, attribute_values);
2871
2872         if (use_commandline)
2873                 {
2874                 layout_config_commandline(&lop, &path);
2875                 }
2876         else
2877                 {
2878                 layout_config_startup_path(&lop, &path);
2879                 }
2880
2881         lw = layout_new_with_geometry(NULL, &lop, use_commandline ? command_line->geometry : NULL);
2882         layout_sort_set(lw, options->file_sort.method, options->file_sort.ascending);
2883         layout_set_path(lw, path);
2884
2885         if (use_commandline && command_line->startup_full_screen) layout_image_full_screen_start(lw);
2886         if (use_commandline && command_line->startup_in_slideshow) layout_image_slideshow_start(lw);
2887         if (use_commandline && command_line->log_window_show) log_window_new(lw);
2888
2889         g_free(path);
2890         free_layout_options_content(&lop);
2891         return lw;
2892 }
2893
2894 void layout_update_from_config(LayoutWindow *lw, const gchar **attribute_names, const gchar **attribute_values)
2895 {
2896         LayoutOptions lop;
2897
2898         init_layout_options(&lop);
2899
2900         if (attribute_names) layout_load_attributes(&lop, attribute_names, attribute_values);
2901
2902         layout_apply_options(lw, &lop);
2903
2904         free_layout_options_content(&lop);
2905 }
2906
2907
2908 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */