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