show the AdobeRGB profile in the menu, indicate input profile from image
[geeqie.git] / src / layout.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  *
5  * Author: John Ellis
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12 #include "main.h"
13 #include "layout.h"
14
15 #include "image.h"
16 #include "color-man.h"
17 #include "layout_config.h"
18 #include "layout_image.h"
19 #include "layout_util.h"
20 #include "menu.h"
21 #include "pixbuf-renderer.h"
22 #include "pixbuf_util.h"
23 #include "utilops.h"
24 #include "view_dir.h"
25 #include "view_file_list.h"
26 #include "view_file_icon.h"
27 #include "ui_bookmark.h"
28 #include "ui_fileops.h"
29 #include "ui_menu.h"
30 #include "ui_misc.h"
31 #include "ui_tabcomp.h"
32 #include "histogram.h"
33
34
35 #define MAINWINDOW_DEF_WIDTH 700
36 #define MAINWINDOW_DEF_HEIGHT 500
37
38 #define MAIN_WINDOW_DIV_HPOS 270
39 #define MAIN_WINDOW_DIV_VPOS 200
40
41 #define TOOLWINDOW_DEF_WIDTH 260
42 #define TOOLWINDOW_DEF_HEIGHT 450
43 #define PROGRESS_WIDTH 150
44 #define ZOOM_LABEL_WIDTH 64
45
46 #define PANE_DIVIDER_SIZE 10
47
48
49 GList *layout_window_list = NULL;
50
51
52 static void layout_list_scroll_to_subpart(LayoutWindow *lw, const gchar *needle);
53
54
55 /*
56  *-----------------------------------------------------------------------------
57  * misc
58  *-----------------------------------------------------------------------------
59  */
60
61 gint layout_valid(LayoutWindow **lw)
62 {
63         if (*lw == NULL)
64                 {
65                 if (layout_window_list) *lw = layout_window_list->data;
66                 return (*lw != NULL);
67                 }
68
69         return (g_list_find(layout_window_list, *lw) != NULL);
70 }
71
72 LayoutWindow *layout_find_by_image(ImageWindow *imd)
73 {
74         GList *work;
75
76         work = layout_window_list;
77         while (work)
78                 {
79                 LayoutWindow *lw = work->data;
80                 work = work->next;
81
82                 if (lw->image == imd) return lw;
83                 }
84
85         return NULL;
86 }
87
88 LayoutWindow *layout_find_by_image_fd(ImageWindow *imd)
89 {
90         GList *work;
91
92         work = layout_window_list;
93         while (work)
94                 {
95                 LayoutWindow *lw = work->data;
96                 work = work->next;
97                 if (lw->image->image_fd == imd->image_fd)
98                         return lw;
99                 
100                 }
101
102         return NULL;
103 }
104
105 /*
106  *-----------------------------------------------------------------------------
107  * menu, toolbar, and dir view
108  *-----------------------------------------------------------------------------
109  */
110
111 static void layout_path_entry_changed_cb(GtkWidget *widget, gpointer data)
112 {
113         LayoutWindow *lw = data;
114         gchar *buf;
115
116         if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) < 0) return;
117
118         buf = g_strdup(gtk_entry_get_text(GTK_ENTRY(lw->path_entry)));
119         if (!buf || (lw->path && strcmp(buf, lw->path) == 0))
120                 {
121                 g_free(buf);
122                 return;
123                 }
124
125         layout_set_path(lw, buf);
126
127         g_free(buf);
128 }
129
130 static void layout_path_entry_tab_cb(const gchar *path, gpointer data)
131 {
132         LayoutWindow *lw = data;
133         gchar *buf;
134         gchar *base;
135
136         buf = g_strdup(path);
137         parse_out_relatives(buf);
138         base = remove_level_from_path(buf);
139
140         if (isdir(buf))
141                 {
142                 if ((!lw->path || strcmp(lw->path, buf) != 0) && layout_set_path(lw, buf))
143                         {
144                         gint pos = -1;
145                         /* put the '/' back, if we are in tab completion for a dir and result was path change */
146                         gtk_editable_insert_text(GTK_EDITABLE(lw->path_entry), "/", -1, &pos);
147                         gtk_editable_set_position(GTK_EDITABLE(lw->path_entry),
148                                                   strlen(gtk_entry_get_text(GTK_ENTRY(lw->path_entry))));
149                         }
150                 }
151         else if (lw->path && strcmp(lw->path, base) == 0)
152                 {
153                 layout_list_scroll_to_subpart(lw, filename_from_path(buf));
154                 }
155
156         g_free(base);
157         g_free(buf);
158 }
159
160 static void layout_path_entry_cb(const gchar *path, gpointer data)
161 {
162         LayoutWindow *lw = data;
163         gchar *buf;
164
165         buf = g_strdup(path);
166         parse_out_relatives(buf);
167
168         layout_set_path(lw, buf);
169
170         g_free(buf);
171 }
172
173 static void layout_vd_select_cb(ViewDir *vd, const gchar *path, gpointer data)
174 {
175         LayoutWindow *lw = data;
176
177         layout_set_path(lw, path);
178 }
179
180 static GtkWidget *layout_tool_setup(LayoutWindow *lw)
181 {
182         GtkWidget *box;
183         GtkWidget *menu_bar;
184         GtkWidget *tabcomp;
185
186         box = gtk_vbox_new(FALSE, 0);
187
188         menu_bar = layout_actions_menu_bar(lw);
189         gtk_box_pack_start(GTK_BOX(box), menu_bar, FALSE, FALSE, 0);
190         gtk_widget_show(menu_bar);
191
192         lw->toolbar = layout_button_bar(lw);
193         gtk_box_pack_start(GTK_BOX(box), lw->toolbar, FALSE, FALSE, 0);
194         if (!lw->toolbar_hidden) gtk_widget_show(lw->toolbar);
195
196         tabcomp = tab_completion_new_with_history(&lw->path_entry, NULL, "path_list", -1,
197                                                   layout_path_entry_cb, lw);
198         tab_completion_add_tab_func(lw->path_entry, layout_path_entry_tab_cb, lw);
199         gtk_box_pack_start(GTK_BOX(box), tabcomp, FALSE, FALSE, 0);
200         gtk_widget_show(tabcomp);
201
202         g_signal_connect(G_OBJECT(lw->path_entry->parent), "changed",
203                          G_CALLBACK(layout_path_entry_changed_cb), lw);
204
205         lw->vd = vd_new(lw->dir_view_type, lw->path);
206         vd_set_layout(lw->vd, lw);
207         vd_set_select_func(lw->vd, layout_vd_select_cb, lw);
208
209         lw->dir_view = lw->vd->widget;
210
211         gtk_box_pack_start(GTK_BOX(box), lw->dir_view, TRUE, TRUE, 0);
212         gtk_widget_show(lw->dir_view);
213
214         gtk_widget_show(box);
215
216         return box;
217 }
218
219 /*
220  *-----------------------------------------------------------------------------
221  * sort button (and menu)
222  *-----------------------------------------------------------------------------
223  */
224
225 static void layout_sort_menu_cb(GtkWidget *widget, gpointer data)
226 {
227         LayoutWindow *lw;
228         SortType type;
229
230         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
231
232         lw = submenu_item_get_data(widget);
233         if (!lw) return;
234
235         type = (SortType)GPOINTER_TO_INT(data);
236
237         layout_sort_set(lw, type, lw->sort_ascend);
238 }
239
240 static void layout_sort_menu_ascend_cb(GtkWidget *widget, gpointer data)
241 {
242         LayoutWindow *lw = data;
243
244         layout_sort_set(lw, lw->sort_method, !lw->sort_ascend);
245 }
246
247 static void layout_sort_menu_hide_cb(GtkWidget *widget, gpointer data)
248 {
249         /* destroy the menu */
250         gtk_widget_unref(GTK_WIDGET(widget));
251 }
252
253 static void layout_sort_button_press_cb(GtkWidget *widget, gpointer data)
254 {
255         LayoutWindow *lw = data;
256         GtkWidget *menu;
257         GdkEvent *event;
258         guint32 etime;
259
260         menu = submenu_add_sort(NULL, G_CALLBACK(layout_sort_menu_cb), lw, FALSE, FALSE, TRUE, lw->sort_method);
261
262         /* take ownership of menu */
263 #ifdef GTK_OBJECT_FLOATING
264         /* GTK+ < 2.10 */
265         g_object_ref(G_OBJECT(menu));
266         gtk_object_sink(GTK_OBJECT(menu));
267 #else
268         /* GTK+ >= 2.10 */
269         g_object_ref_sink(G_OBJECT(menu));
270 #endif
271
272         /* ascending option */
273         menu_item_add_divider(menu);
274         menu_item_add_check(menu, _("Ascending"), lw->sort_ascend, G_CALLBACK(layout_sort_menu_ascend_cb), lw);
275
276         g_signal_connect(G_OBJECT(menu), "selection_done",
277                          G_CALLBACK(layout_sort_menu_hide_cb), NULL);
278
279         event = gtk_get_current_event();
280         if (event)
281                 {
282                 etime = gdk_event_get_time(event);
283                 gdk_event_free(event);
284                 }
285         else
286                 {
287                 etime = 0;
288                 }
289
290         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, etime);
291 }
292
293 static GtkWidget *layout_sort_button(LayoutWindow *lw)
294 {
295         GtkWidget *button;
296
297         button = gtk_button_new_with_label(sort_type_get_text(lw->sort_method));
298         g_signal_connect(G_OBJECT(button), "clicked",
299                          G_CALLBACK(layout_sort_button_press_cb), lw);
300         gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
301
302         return button;
303 }
304
305 /*
306  *-----------------------------------------------------------------------------
307  * color profile button (and menu)
308  *-----------------------------------------------------------------------------
309  */
310
311 static void layout_color_menu_enable_cb(GtkWidget *widget, gpointer data)
312 {
313         LayoutWindow *lw = data;
314
315         layout_image_color_profile_set_use(lw, (!layout_image_color_profile_get_use(lw)));
316         layout_image_refresh(lw);
317 }
318
319 static void layout_color_menu_use_image_cb(GtkWidget *widget, gpointer data)
320 {
321         LayoutWindow *lw = data;
322         gint input, screen, use_image;
323
324         if (!layout_image_color_profile_get(lw, &input, &screen, &use_image)) return;
325         layout_image_color_profile_set(lw, input, screen, !use_image);
326         layout_image_refresh(lw);
327 }
328
329 #define COLOR_MENU_KEY "color_menu_key"
330
331 static void layout_color_menu_input_cb(GtkWidget *widget, gpointer data)
332 {
333         LayoutWindow *lw = data;
334         gint type;
335         gint input, screen, use_image;
336
337         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
338
339         type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), COLOR_MENU_KEY));
340         if (type < 0 || type > COLOR_PROFILE_INPUTS) return;
341
342         if (!layout_image_color_profile_get(lw, &input, &screen, &use_image)) return;
343         if (type == input) return;
344
345         layout_image_color_profile_set(lw, type, screen, use_image);
346         layout_image_refresh(lw);
347 }
348
349 static void layout_color_menu_screen_cb(GtkWidget *widget, gpointer data)
350 {
351         LayoutWindow *lw = data;
352         gint type;
353         gint input, screen, use_image;
354
355         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
356
357         type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), COLOR_MENU_KEY));
358         if (type < 0 || type > 1) return;
359
360         if (!layout_image_color_profile_get(lw, &input, &screen, &use_image)) return;
361         if (type == screen) return;
362
363         layout_image_color_profile_set(lw, input, type, use_image);
364         layout_image_refresh(lw);
365 }
366
367 static gchar *layout_color_name_parse(const gchar *name)
368 {
369         if (!name) return g_strdup(_("Empty"));
370         return g_strdelimit(g_strdup(name), "_", '-');
371 }
372
373 static void layout_color_button_press_cb(GtkWidget *widget, gpointer data)
374 {
375 #ifndef HAVE_LCMS
376         gchar *msg = g_strdup_printf(_("This installation of %s was not built with support for color profiles."), GQ_APPNAME);
377         file_util_warning_dialog(_("Color profiles not supported"),
378                                  msg,
379                                  GTK_STOCK_DIALOG_INFO, widget);
380         g_free(msg);
381         return;
382 #else
383         LayoutWindow *lw = data;
384         GtkWidget *menu;
385         GtkWidget *item;
386         gchar *buf;
387         gchar *front;
388         gchar *end;
389         gint active;
390         gint input = 0;
391         gint screen = 0;
392         gint use_image = 0;
393         gint from_image = 0;
394         gint i;
395
396         if (!layout_image_color_profile_get(lw, &input, &screen, &use_image)) return;
397
398         from_image = use_image && (layout_image_color_profile_get_from_image(lw) != COLOR_PROFILE_NONE);
399         menu = popup_menu_short_lived();
400
401         active = layout_image_color_profile_get_use(lw);
402         menu_item_add_check(menu, _("Use _color profiles"), active,
403                             G_CALLBACK(layout_color_menu_enable_cb), lw);
404
405         menu_item_add_divider(menu);
406
407         item = menu_item_add_check(menu, _("Use profile from _image"), use_image,
408                             G_CALLBACK(layout_color_menu_use_image_cb), lw);
409         gtk_widget_set_sensitive(item, active);
410
411         for (i = COLOR_PROFILE_SRGB; i < COLOR_PROFILE_FILE; i++)
412                 {
413                 front = g_strdup_printf(_("Input _%d:"), i);
414                 buf = g_strdup_printf("%s %s", front, i == COLOR_PROFILE_SRGB ? _("sRGB") : _("AdobeRGB compatible"));
415                 g_free(front);
416                 item = menu_item_add_radio(menu, (i == 0) ? NULL : item,
417                                    buf, (input == i),
418                                    G_CALLBACK(layout_color_menu_input_cb), lw);
419                 g_free(buf);
420                 g_object_set_data(G_OBJECT(item), COLOR_MENU_KEY, GINT_TO_POINTER(i));
421                 gtk_widget_set_sensitive(item, active && !from_image);
422                 }
423
424         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
425                 {
426                 const gchar *name;
427
428                 name = options->color_profile.input_name[i];
429                 if (!name) name = filename_from_path(options->color_profile.input_file[i]);
430
431                 front = g_strdup_printf(_("Input _%d:"), i + COLOR_PROFILE_FILE);
432                 end = layout_color_name_parse(name);
433                 buf = g_strdup_printf("%s %s", front, end);
434                 g_free(front);
435                 g_free(end);
436
437                 item = menu_item_add_radio(menu, item,
438                                            buf, (i + COLOR_PROFILE_FILE == input),
439                                            G_CALLBACK(layout_color_menu_input_cb), lw);
440                 g_free(buf);
441                 g_object_set_data(G_OBJECT(item), COLOR_MENU_KEY, GINT_TO_POINTER(i + COLOR_PROFILE_FILE));
442                 gtk_widget_set_sensitive(item, active && options->color_profile.input_file[i] && !from_image);
443                 }
444
445         menu_item_add_divider(menu);
446
447         buf = g_strdup_printf("%s sRGB", _("Screen"));
448         item = menu_item_add_radio(menu, NULL,
449                                    buf, (screen == 0),
450                                    G_CALLBACK(layout_color_menu_screen_cb), lw);
451         g_free(buf);
452         g_object_set_data(G_OBJECT(item), COLOR_MENU_KEY, GINT_TO_POINTER(0));
453         gtk_widget_set_sensitive(item, active);
454
455         item = menu_item_add_radio(menu, item,
456                                    _("_Screen profile"), (screen == 1),
457                                    G_CALLBACK(layout_color_menu_screen_cb), lw);
458         g_object_set_data(G_OBJECT(item), COLOR_MENU_KEY, GINT_TO_POINTER(1));
459         gtk_widget_set_sensitive(item, active && options->color_profile.screen_file);
460
461         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME);
462 #endif /* HAVE_LCMS */
463 }
464
465 static GtkWidget *layout_color_button(LayoutWindow *lw)
466 {
467         GtkWidget *button;
468         GtkWidget *image;
469         gint enable;
470
471         button = gtk_button_new();
472         image = gtk_image_new_from_stock(GTK_STOCK_SELECT_COLOR, GTK_ICON_SIZE_MENU);
473         gtk_container_add(GTK_CONTAINER(button), image);
474         gtk_widget_show(image);
475         g_signal_connect(G_OBJECT(button), "clicked",
476                          G_CALLBACK(layout_color_button_press_cb), lw);
477         gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
478
479 #ifdef HAVE_LCMS
480         enable = (lw->image) ? lw->image->color_profile_enable : FALSE;
481 #else
482         enable = FALSE;
483 #endif
484         gtk_widget_set_sensitive(image, enable);
485
486         return button;
487 }
488
489
490 /*
491  *-----------------------------------------------------------------------------
492  * status bar
493  *-----------------------------------------------------------------------------
494  */
495
496 void layout_status_update_progress(LayoutWindow *lw, gdouble val, const gchar *text)
497 {
498         if (!layout_valid(&lw)) return;
499         if (!lw->info_progress_bar) return;
500
501         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(lw->info_progress_bar), val);
502         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(lw->info_progress_bar), (text) ? text : " ");
503 }
504
505 void layout_status_update_info(LayoutWindow *lw, const gchar *text)
506 {
507         gchar *buf = NULL;
508
509         if (!layout_valid(&lw)) return;
510
511         if (!text)
512                 {
513                 gint n;
514                 gint64 n_bytes = 0;
515                 gint s;
516                 gint64 s_bytes = 0;
517                 const gchar *ss;
518
519                 if (layout_image_slideshow_active(lw))
520                         {
521                         if (!layout_image_slideshow_paused(lw))
522                                 {
523                                 ss = _(" Slideshow");
524                                 }
525                         else
526                                 {
527                                 ss = _(" Paused");
528                                 }
529                         }
530                 else
531                         {
532                         ss = "";
533                         }
534
535                 n = layout_list_count(lw, &n_bytes);
536                 s = layout_selection_count(lw, &s_bytes);
537
538                 layout_bars_new_selection(lw, s);
539
540                 if (s > 0)
541                         {
542                         gchar *b = text_from_size_abrev(n_bytes);
543                         gchar *sb = text_from_size_abrev(s_bytes);
544                         buf = g_strdup_printf(_("%s, %d files (%s, %d)%s"), b, n, sb, s, ss);
545                         g_free(b);
546                         g_free(sb);
547                         }
548                 else if (n > 0)
549                         {
550                         gchar *b = text_from_size_abrev(n_bytes);
551                         buf = g_strdup_printf(_("%s, %d files%s"), b, n, ss);
552                         g_free(b);
553                         }
554                 else
555                         {
556                         buf = g_strdup_printf(_("%d files%s"), n, ss);
557                         }
558
559                 text = buf;
560
561                 layout_image_overlay_update(lw);
562                 }
563
564         gtk_label_set_text(GTK_LABEL(lw->info_status), text);
565         g_free(buf);
566 }
567
568 void layout_status_update_image(LayoutWindow *lw)
569 {
570         gchar *text;
571         gchar *b;
572
573         if (!layout_valid(&lw) || !lw->image) return;
574
575         text = image_zoom_get_as_text(lw->image);
576         gtk_label_set_text(GTK_LABEL(lw->info_zoom), text);
577         g_free(text);
578
579         b = text_from_size(lw->image->size);
580
581         if (lw->image->unknown)
582                 {
583                 if (image_get_path(lw->image) && !access_file(image_get_path(lw->image), R_OK))
584                         {
585                         text = g_strdup_printf(_("(no read permission) %s bytes"), b);
586                         }
587                 else
588                         {
589                         text = g_strdup_printf(_("( ? x ? ) %s bytes"), b);
590                         }
591                 }
592         else
593                 {
594                 gint width, height;
595
596                 pixbuf_renderer_get_image_size(PIXBUF_RENDERER(lw->image->pr), &width, &height);
597                 text = g_strdup_printf(_("( %d x %d ) %s bytes"),
598                                        width, height, b);
599                 }
600
601         gtk_label_set_text(GTK_LABEL(lw->info_details), text);
602
603         g_free(b);
604         g_free(text);
605 }
606
607 void layout_status_update_all(LayoutWindow *lw)
608 {
609         layout_status_update_progress(lw, 0.0, NULL);
610         layout_status_update_info(lw, NULL);
611         layout_status_update_image(lw);
612 }
613
614 static GtkWidget *layout_status_label(gchar *text, GtkWidget *box, gint start, gint size, gint expand)
615 {
616         GtkWidget *label;
617         GtkWidget *frame;
618
619         frame = gtk_frame_new(NULL);
620         if (size) gtk_widget_set_size_request(frame, size, -1);
621         gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
622         if (start)
623                 {
624                 gtk_box_pack_start(GTK_BOX(box), frame, expand, expand, 0);
625                 }
626         else
627                 {
628                 gtk_box_pack_end(GTK_BOX(box), frame, expand, expand, 0);
629                 }
630         gtk_widget_show(frame);
631
632         label = gtk_label_new(text ? text : "");
633         gtk_container_add(GTK_CONTAINER(frame), label);
634         gtk_widget_show(label);
635
636         return label;
637 }
638
639 static void layout_status_setup(LayoutWindow *lw, GtkWidget *box, gint small_format)
640 {
641         GtkWidget *hbox;
642
643         if (lw->info_box) return;
644
645         if (small_format)
646                 {
647                 lw->info_box = gtk_vbox_new(FALSE, 0);
648                 }
649         else
650                 {
651                 lw->info_box = gtk_hbox_new(FALSE, 0);
652                 }
653         gtk_box_pack_end(GTK_BOX(box), lw->info_box, FALSE, FALSE, 0);
654         gtk_widget_show(lw->info_box);
655
656         if (small_format)
657                 {
658                 hbox = gtk_hbox_new(FALSE, 0);
659                 gtk_box_pack_start(GTK_BOX(lw->info_box), hbox, FALSE, FALSE, 0);
660                 gtk_widget_show(hbox);
661                 }
662         else
663                 {
664                 hbox = lw->info_box;
665                 }
666         lw->info_progress_bar = gtk_progress_bar_new();
667         gtk_widget_set_size_request(lw->info_progress_bar, PROGRESS_WIDTH, -1);
668         gtk_box_pack_start(GTK_BOX(hbox), lw->info_progress_bar, FALSE, FALSE, 0);
669         gtk_widget_show(lw->info_progress_bar);
670
671         lw->info_sort = layout_sort_button(lw);
672         gtk_box_pack_start(GTK_BOX(hbox), lw->info_sort, FALSE, FALSE, 0);
673         gtk_widget_show(lw->info_sort);
674
675         lw->info_color = layout_color_button(lw);
676         gtk_widget_show(lw->info_color);
677
678         if (small_format) gtk_box_pack_end(GTK_BOX(hbox), lw->info_color, FALSE, FALSE, 0);
679
680         lw->info_status = layout_status_label(NULL, lw->info_box, TRUE, 0, (!small_format));
681
682         if (small_format)
683                 {
684                 hbox = gtk_hbox_new(FALSE, 0);
685                 gtk_box_pack_start(GTK_BOX(lw->info_box), hbox, FALSE, FALSE, 0);
686                 gtk_widget_show(hbox);
687                 }
688         else
689                 {
690                 hbox = lw->info_box;
691                 }
692         lw->info_details = layout_status_label(NULL, hbox, TRUE, 0, TRUE);
693         if (!small_format) gtk_box_pack_start(GTK_BOX(hbox), lw->info_color, FALSE, FALSE, 0);
694         lw->info_zoom = layout_status_label(NULL, hbox, FALSE, ZOOM_LABEL_WIDTH, FALSE);
695 }
696
697 /*
698  *-----------------------------------------------------------------------------
699  * views
700  *-----------------------------------------------------------------------------
701  */
702
703 static GtkWidget *layout_tools_new(LayoutWindow *lw)
704 {
705         lw->dir_view = layout_tool_setup(lw);
706         return lw->dir_view;
707 }
708
709 static void layout_list_status_cb(ViewFileList *vfl, gpointer data)
710 {
711         LayoutWindow *lw = data;
712
713         layout_status_update_info(lw, NULL);
714 }
715
716 static void layout_list_thumb_cb(ViewFileList *vfl, gdouble val, const gchar *text, gpointer data)
717 {
718         LayoutWindow *lw = data;
719
720         layout_status_update_progress(lw, val, text);
721 }
722
723 static void layout_icon_status_cb(ViewFileIcon *vfi, gpointer data)
724 {
725         LayoutWindow *lw = data;
726
727         layout_status_update_info(lw, NULL);
728 }
729
730 static void layout_icon_thumb_cb(ViewFileIcon *vfi, gdouble val, const gchar *text, gpointer data)
731 {
732         LayoutWindow *lw = data;
733
734         layout_status_update_progress(lw, val, text);
735 }
736
737 static GtkWidget *layout_list_new(LayoutWindow *lw)
738 {
739         if (lw->icon_view)
740                 {
741                 lw->vfi = vficon_new(NULL);
742                 vficon_set_layout(lw->vfi, lw);
743
744                 vficon_set_status_func(lw->vfi, layout_icon_status_cb, lw);
745                 vficon_set_thumb_status_func(lw->vfi, layout_icon_thumb_cb, lw);
746                 /* FIXME vficon_marks_set(lw->vfi, lw->marks_enabled); */
747
748                 return lw->vfi->widget;
749                 }
750
751         lw->vfl = vflist_new(NULL, lw->thumbs_enabled);
752         vflist_set_layout(lw->vfl, lw);
753
754         vflist_set_status_func(lw->vfl, layout_list_status_cb, lw);
755         vflist_set_thumb_status_func(lw->vfl, layout_list_thumb_cb, lw);
756
757         vflist_marks_set(lw->vfl, lw->marks_enabled);
758         
759         return lw->vfl->widget;
760 }
761
762 static void layout_list_sync_thumb(LayoutWindow *lw)
763 {
764         if (lw->vfl) vflist_thumb_set(lw->vfl, lw->thumbs_enabled);
765 }
766
767 static void layout_list_sync_marks(LayoutWindow *lw)
768 {
769         if (lw->vfl) vflist_marks_set(lw->vfl, lw->marks_enabled);
770 }
771
772 static void layout_list_scroll_to_subpart(LayoutWindow *lw, const gchar *needle)
773 {
774         if (!lw) return;
775 #if 0
776         if (lw->vfl) vflist_scroll_to_subpart(lw->vfl, needle);
777         if (lw->vfi) vficon_scroll_to_subpart(lw->vfi, needle);
778 #endif
779 }
780
781 GList *layout_list(LayoutWindow *lw)
782 {
783         if (!layout_valid(&lw)) return NULL;
784
785         if (lw->vfl) return vflist_get_list(lw->vfl);
786         if (lw->vfi) return vficon_get_list(lw->vfi);
787
788         return NULL;
789 }
790
791 gint layout_list_count(LayoutWindow *lw, gint64 *bytes)
792 {
793         if (!layout_valid(&lw)) return 0;
794
795         if (lw->vfl) return vflist_count(lw->vfl, bytes);
796         if (lw->vfi) return vficon_count(lw->vfi, bytes);
797
798         return 0;
799 }
800
801 const gchar *layout_list_get_path(LayoutWindow *lw, gint index)
802 {
803         if (!layout_valid(&lw)) return NULL;
804
805         if (lw->vfl) return vflist_index_get_path(lw->vfl, index);
806         if (lw->vfi) return vficon_index_get_path(lw->vfi, index);
807
808         return NULL;
809 }
810
811 FileData *layout_list_get_fd(LayoutWindow *lw, gint index)
812 {
813         if (!layout_valid(&lw)) return NULL;
814
815         if (lw->vfl) return vflist_index_get_data(lw->vfl, index);
816         if (lw->vfi) return vficon_index_get_data(lw->vfi, index);
817
818         return NULL;
819 }
820
821 gint layout_list_get_index(LayoutWindow *lw, const gchar *path)
822 {
823         if (!layout_valid(&lw)) return -1;
824
825         if (lw->vfl) return vflist_index_by_path(lw->vfl, path);
826         if (lw->vfi) return vficon_index_by_path(lw->vfi, path);
827
828         return -1;
829 }
830
831 void layout_list_sync_fd(LayoutWindow *lw, FileData *fd)
832 {
833         if (!layout_valid(&lw)) return;
834
835         if (lw->vfl) vflist_select_by_fd(lw->vfl, fd);
836         if (lw->vfi) vficon_select_by_fd(lw->vfi, fd);
837 }
838
839 static void layout_list_sync_sort(LayoutWindow *lw)
840 {
841         if (!layout_valid(&lw)) return;
842
843         if (lw->vfl) vflist_sort_set(lw->vfl, lw->sort_method, lw->sort_ascend);
844         if (lw->vfi) vficon_sort_set(lw->vfi, lw->sort_method, lw->sort_ascend);
845 }
846
847 GList *layout_selection_list(LayoutWindow *lw)
848 {
849         if (!layout_valid(&lw)) return NULL;
850
851         if (layout_image_get_collection(lw, NULL))
852                 {
853                 FileData *fd;
854
855                 fd = layout_image_get_fd(lw);
856                 if (fd) return g_list_append(NULL, file_data_ref(fd));
857                 return NULL;
858                 }
859
860         if (lw->vfl) return vflist_selection_get_list(lw->vfl);
861         if (lw->vfi) return vficon_selection_get_list(lw->vfi);
862
863         return NULL;
864 }
865
866 GList *layout_selection_list_by_index(LayoutWindow *lw)
867 {
868         if (!layout_valid(&lw)) return NULL;
869
870         if (lw->vfl) return vflist_selection_get_list_by_index(lw->vfl);
871         if (lw->vfi) return vficon_selection_get_list_by_index(lw->vfi);
872
873         return NULL;
874 }
875
876 gint layout_selection_count(LayoutWindow *lw, gint64 *bytes)
877 {
878         if (!layout_valid(&lw)) return 0;
879
880         if (lw->vfl) return vflist_selection_count(lw->vfl, bytes);
881         if (lw->vfi) return vficon_selection_count(lw->vfi, bytes);
882
883         return 0;
884 }
885
886 void layout_select_all(LayoutWindow *lw)
887 {
888         if (!layout_valid(&lw)) return;
889
890         if (lw->vfl) vflist_select_all(lw->vfl);
891         if (lw->vfi) vficon_select_all(lw->vfi);
892 }
893
894 void layout_select_none(LayoutWindow *lw)
895 {
896         if (!layout_valid(&lw)) return;
897
898         if (lw->vfl) vflist_select_none(lw->vfl);
899         if (lw->vfi) vficon_select_none(lw->vfi);
900 }
901
902 void layout_mark_to_selection(LayoutWindow *lw, gint mark, MarkToSelectionMode mode)
903 {
904         if (!layout_valid(&lw)) return;
905
906         if (lw->vfl) vflist_mark_to_selection(lw->vfl, mark, mode);
907         if (lw->vfi) vficon_mark_to_selection(lw->vfi, mark, mode);
908 }
909
910 void layout_selection_to_mark(LayoutWindow *lw, gint mark, SelectionToMarkMode mode)
911 {
912         if (!layout_valid(&lw)) return;
913
914         if (lw->vfl) vflist_selection_to_mark(lw->vfl, mark, mode);
915         if (lw->vfi) vficon_selection_to_mark(lw->vfi, mark, mode);
916         
917         layout_status_update_info(lw, NULL); /* osd in fullscreen mode */
918 }
919
920 /*
921  *-----------------------------------------------------------------------------
922  * access
923  *-----------------------------------------------------------------------------
924  */
925
926 const gchar *layout_get_path(LayoutWindow *lw)
927 {
928         if (!layout_valid(&lw)) return NULL;
929         return lw->path;
930 }
931
932 static void layout_sync_path(LayoutWindow *lw)
933 {
934         if (!lw->path) return;
935
936         lw->last_time = filetime(lw->path);
937
938         gtk_entry_set_text(GTK_ENTRY(lw->path_entry), lw->path);
939         if (lw->vd) vd_set_path(lw->vd, lw->path);
940
941         if (lw->vfl) vflist_set_path(lw->vfl, lw->path);
942         if (lw->vfi) vficon_set_path(lw->vfi, lw->path);
943 }
944
945 gint layout_set_path(LayoutWindow *lw, const gchar *path)
946 {
947         gint have_file = FALSE;
948
949         if (!layout_valid(&lw)) return FALSE;
950
951         if (!path || !isname(path)) return FALSE;
952         if (lw->path && path && strcmp(path, lw->path) == 0)
953                 {
954                 return TRUE;
955                 }
956
957         if (isdir(path))
958                 {
959                 g_free(lw->path);
960                 lw->path = g_strdup(path);
961                 }
962         else
963                 {
964                 gchar *base;
965
966                 base = remove_level_from_path(path);
967                 if (lw->path && strcmp(lw->path, base) == 0)
968                         {
969                         g_free(base);
970                         }
971                 else if (isdir(base))
972                         {
973                         g_free(lw->path);
974                         lw->path = base;
975                         }
976                 else
977                         {
978                         g_free(base);
979                         return FALSE;
980                         }
981                 if (isfile(path)) have_file = TRUE;
982                 }
983
984         if (lw->path_entry) tab_completion_append_to_history(lw->path_entry, lw->path);
985         layout_sync_path(lw);
986
987         if (have_file)
988                 {
989                 gint row;
990
991                 row = layout_list_get_index(lw, path);
992                 if (row >= 0)
993                         {
994                         layout_image_set_index(lw, row);
995                         }
996                 else
997                         {
998                         layout_image_set_fd(lw, file_data_new_simple(path));
999                         }
1000                 }
1001         else if (!options->lazy_image_sync)
1002                 {
1003                 layout_image_set_index(lw, 0);
1004                 }
1005
1006         return TRUE;
1007 }
1008
1009 static void layout_refresh_lists(LayoutWindow *lw)
1010 {
1011         if (lw->path) lw->last_time = filetime(lw->path);
1012
1013         if (lw->vd) vd_refresh(lw->vd);
1014
1015         if (lw->vfl) vflist_refresh(lw->vfl);
1016         if (lw->vfi) vficon_refresh(lw->vfi);
1017 }
1018
1019 static void layout_refresh_by_time(LayoutWindow *lw)
1020 {
1021         layout_refresh_lists(lw);
1022
1023         if (lw->image && filetime(layout_image_get_path(lw)) >= lw->last_time)
1024                 {
1025                 layout_image_refresh(lw);
1026                 }
1027 }
1028
1029 void layout_refresh(LayoutWindow *lw)
1030 {
1031         if (!layout_valid(&lw)) return;
1032
1033         if (debug) printf("layout refresh\n");
1034
1035         layout_refresh_lists(lw);
1036
1037         if (lw->image) layout_image_refresh(lw);
1038 }
1039
1040 static gint layout_check_for_update_cb(gpointer data)
1041 {
1042         LayoutWindow *lw = data;
1043
1044         if (!options->update_on_time_change) return TRUE;
1045
1046         if (lw->path)
1047                 {
1048                 time_t new_time;
1049
1050                 new_time = filetime(lw->path);
1051
1052                 if (new_time > 0 && new_time > lw->last_time)
1053                         {
1054                         if (debug) printf("layout path time changed, refreshing...\n");
1055                         layout_refresh_by_time(lw);
1056                         }
1057                 }
1058
1059         return TRUE;
1060 }
1061
1062 void layout_thumb_set(LayoutWindow *lw, gint enable)
1063 {
1064         if (!layout_valid(&lw)) return;
1065
1066         if (lw->thumbs_enabled == enable) return;
1067
1068         lw->thumbs_enabled = enable;
1069
1070         layout_util_sync_thumb(lw);
1071         layout_list_sync_thumb(lw);
1072 }
1073
1074 void layout_marks_set(LayoutWindow *lw, gint enable)
1075 {
1076         if (!layout_valid(&lw)) return;
1077
1078         if (lw->marks_enabled == enable) return;
1079
1080         lw->marks_enabled = enable;
1081
1082 //      layout_util_sync_marks(lw);
1083         layout_list_sync_marks(lw);
1084 }
1085
1086 gint layout_thumb_get(LayoutWindow *lw)
1087 {
1088         if (!layout_valid(&lw)) return FALSE;
1089
1090         return lw->thumbs_enabled;
1091 }
1092
1093 void layout_sort_set(LayoutWindow *lw, SortType type, gint ascend)
1094 {
1095         if (!layout_valid(&lw)) return;
1096         if (lw->sort_method == type && lw->sort_ascend == ascend) return;
1097
1098         lw->sort_method = type;
1099         lw->sort_ascend = ascend;
1100
1101         if (lw->info_sort) gtk_label_set_text(GTK_LABEL(GTK_BIN(lw->info_sort)->child),
1102                                               sort_type_get_text(type));
1103         layout_list_sync_sort(lw);
1104 }
1105
1106 gint layout_sort_get(LayoutWindow *lw, SortType *type, gint *ascend)
1107 {
1108         if (!layout_valid(&lw)) return FALSE;
1109
1110         if (type) *type = lw->sort_method;
1111         if (ascend) *ascend = lw->sort_ascend;
1112
1113         return TRUE;
1114 }
1115
1116 gint layout_geometry_get(LayoutWindow *lw, gint *x, gint *y, gint *w, gint *h)
1117 {
1118         if (!layout_valid(&lw)) return FALSE;
1119
1120         gdk_window_get_root_origin(lw->window->window, x, y);
1121         gdk_drawable_get_size(lw->window->window, w, h);
1122
1123         return TRUE;
1124 }
1125
1126 gint layout_geometry_get_dividers(LayoutWindow *lw, gint *h, gint *v)
1127 {
1128         if (!layout_valid(&lw)) return FALSE;
1129
1130         if (lw->h_pane && GTK_PANED(lw->h_pane)->child1->allocation.x >= 0)
1131                 {
1132                 *h = GTK_PANED(lw->h_pane)->child1->allocation.width;
1133                 }
1134         else if (h != &lw->div_h)
1135                 {
1136                 *h = lw->div_h;
1137                 }
1138
1139         if (lw->v_pane && GTK_PANED(lw->v_pane)->child1->allocation.x >= 0)
1140                 {
1141                 *v = GTK_PANED(lw->v_pane)->child1->allocation.height;
1142                 }
1143         else if (v != &lw->div_v)
1144                 {
1145                 *v = lw->div_v;
1146                 }
1147
1148         return TRUE;
1149 }
1150
1151 void layout_views_set(LayoutWindow *lw, DirViewType type, gint icons)
1152 {
1153         if (!layout_valid(&lw)) return;
1154
1155         if (lw->dir_view_type == type && lw->icon_view == icons) return;
1156
1157         lw->dir_view_type = type;
1158         lw->icon_view = icons;
1159
1160         layout_style_set(lw, -1, NULL);
1161 }
1162
1163 gint layout_views_get(LayoutWindow *lw, DirViewType *type, gint *icons)
1164 {
1165         if (!layout_valid(&lw)) return FALSE;
1166
1167         *type = lw->dir_view_type;
1168         *icons = lw->icon_view;
1169
1170         return TRUE;
1171 }
1172
1173 /*
1174  *-----------------------------------------------------------------------------
1175  * location utils
1176  *-----------------------------------------------------------------------------
1177  */
1178
1179 static gint layout_location_single(LayoutLocation l)
1180 {
1181         return (l == LAYOUT_LEFT ||
1182                 l == LAYOUT_RIGHT ||
1183                 l == LAYOUT_TOP ||
1184                 l == LAYOUT_BOTTOM);
1185 }
1186
1187 static gint layout_location_vertical(LayoutLocation l)
1188 {
1189         return (l & LAYOUT_TOP ||
1190                 l & LAYOUT_BOTTOM);
1191 }
1192
1193 static gint layout_location_first(LayoutLocation l)
1194 {
1195         return (l & LAYOUT_TOP ||
1196                 l & LAYOUT_LEFT);
1197 }
1198
1199 static LayoutLocation layout_grid_compass(LayoutWindow *lw)
1200 {
1201         if (layout_location_single(lw->dir_location)) return lw->dir_location;
1202         if (layout_location_single(lw->file_location)) return lw->file_location;
1203         return lw->image_location;
1204 }
1205
1206 static void layout_location_compute(LayoutLocation l1, LayoutLocation l2,
1207                                     GtkWidget *s1, GtkWidget *s2,
1208                                     GtkWidget **d1, GtkWidget **d2)
1209 {
1210         LayoutLocation l;
1211
1212         l = l1 & l2;    /* get common compass direction */
1213         l = l1 - l;     /* remove it */
1214
1215         if (layout_location_first(l))
1216                 {
1217                 *d1 = s1;
1218                 *d2 = s2;
1219                 }
1220         else
1221                 {
1222                 *d1 = s2;
1223                 *d2 = s1;
1224                 }
1225 }
1226
1227 /*
1228  *-----------------------------------------------------------------------------
1229  * tools window (for floating/hidden)
1230  *-----------------------------------------------------------------------------
1231  */
1232
1233 gint layout_geometry_get_tools(LayoutWindow *lw, gint *x, gint *y, gint *w, gint *h, gint *divider_pos)
1234 {
1235         if (!layout_valid(&lw)) return FALSE;
1236
1237         if (!lw->tools || !GTK_WIDGET_VISIBLE(lw->tools))
1238                 {
1239                 /* use the stored values (sort of breaks success return value) */
1240
1241                 *divider_pos = lw->div_float;
1242
1243                 return FALSE;
1244                 }
1245
1246         gdk_window_get_root_origin(lw->tools->window, x, y);
1247         gdk_drawable_get_size(lw->tools->window, w, h);
1248
1249         if (GTK_IS_VPANED(lw->tools_pane))
1250                 {
1251                 *divider_pos = GTK_PANED(lw->tools_pane)->child1->allocation.height;
1252                 }
1253         else
1254                 {
1255                 *divider_pos = GTK_PANED(lw->tools_pane)->child1->allocation.width;
1256                 }
1257
1258         return TRUE;
1259 }
1260
1261 static void layout_tools_geometry_sync(LayoutWindow *lw)
1262 {
1263         layout_geometry_get_tools(lw, &options->layout.float_window.x, &options->layout.float_window.x,
1264                                   &options->layout.float_window.w, &options->layout.float_window.h, &lw->div_float);
1265 }
1266
1267 static void layout_tools_hide(LayoutWindow *lw, gint hide)
1268 {
1269         if (!lw->tools) return;
1270
1271         if (hide)
1272                 {
1273                 if (GTK_WIDGET_VISIBLE(lw->tools))
1274                         {
1275                         layout_tools_geometry_sync(lw);
1276                         gtk_widget_hide(lw->tools);
1277                         }
1278                 }
1279         else
1280                 {
1281                 if (!GTK_WIDGET_VISIBLE(lw->tools))
1282                         {
1283                         gtk_widget_show(lw->tools);
1284                         if (lw->vfi) vficon_refresh(lw->vfi);
1285                         }
1286                 }
1287
1288         lw->tools_hidden = hide;
1289 }
1290
1291 static gint layout_tools_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer data)
1292 {
1293         LayoutWindow *lw = data;
1294
1295         layout_tools_float_toggle(lw);
1296
1297         return TRUE;
1298 }
1299
1300 static void layout_tools_setup(LayoutWindow *lw, GtkWidget *tools, GtkWidget *files)
1301 {
1302         GtkWidget *vbox;
1303         GtkWidget *w1, *w2;
1304         gint vertical;
1305         gint new_window = FALSE;
1306
1307         vertical = (layout_location_single(lw->image_location) && !layout_location_vertical(lw->image_location)) ||
1308                    (!layout_location_single(lw->image_location) && layout_location_vertical(layout_grid_compass(lw)));
1309 #if 0
1310         layout_location_compute(lw->dir_location, lw->file_location,
1311                                 tools, files, &w1, &w2);
1312 #endif
1313         /* for now, tools/dir are always first in order */
1314         w1 = tools;
1315         w2 = files;
1316
1317         if (!lw->tools)
1318                 {
1319                 GdkGeometry geometry;
1320                 GdkWindowHints hints;
1321
1322                 lw->tools = window_new(GTK_WINDOW_TOPLEVEL, "tools", PIXBUF_INLINE_ICON_TOOLS, NULL, _("Tools"));
1323                 g_signal_connect(G_OBJECT(lw->tools), "delete_event",
1324                                  G_CALLBACK(layout_tools_delete_cb), lw);
1325                 layout_keyboard_init(lw, lw->tools);
1326
1327                 if (options->layout.save_window_positions)
1328                         {
1329                         hints = GDK_HINT_USER_POS;
1330                         }
1331                 else
1332                         {
1333                         hints = 0;
1334                         }
1335
1336                 geometry.min_width = 32;
1337                 geometry.min_height = 32;
1338                 geometry.base_width = TOOLWINDOW_DEF_WIDTH;
1339                 geometry.base_height = TOOLWINDOW_DEF_HEIGHT;
1340                 gtk_window_set_geometry_hints(GTK_WINDOW(lw->tools), NULL, &geometry,
1341                                               GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE | hints);
1342
1343
1344                 gtk_window_set_resizable(GTK_WINDOW(lw->tools), TRUE);
1345                 gtk_container_set_border_width(GTK_CONTAINER(lw->tools), 0);
1346         
1347                 new_window = TRUE;
1348                 }
1349         else
1350                 {
1351                 layout_tools_geometry_sync(lw);
1352                 /* dump the contents */
1353                 gtk_widget_destroy(GTK_BIN(lw->tools)->child);
1354                 }
1355
1356         layout_actions_add_window(lw, lw->tools);
1357
1358         vbox = gtk_vbox_new(FALSE, 0);
1359         gtk_container_add(GTK_CONTAINER(lw->tools), vbox);
1360         gtk_widget_show(vbox);
1361
1362         layout_status_setup(lw, vbox, TRUE);
1363
1364         if (vertical)
1365                 {
1366                 lw->tools_pane = gtk_vpaned_new();
1367                 }
1368         else
1369                 {
1370                 lw->tools_pane = gtk_hpaned_new();
1371                 }
1372         gtk_box_pack_start(GTK_BOX(vbox), lw->tools_pane, TRUE, TRUE, 0);
1373         gtk_widget_show(lw->tools_pane);
1374
1375         gtk_paned_pack1(GTK_PANED(lw->tools_pane), w1, FALSE, TRUE);
1376         gtk_paned_pack2(GTK_PANED(lw->tools_pane), w2, TRUE, TRUE);
1377
1378         gtk_widget_show(tools);
1379         gtk_widget_show(files);
1380
1381         if (new_window)
1382                 {
1383                 if (options->layout.save_window_positions)
1384                         {
1385                         gtk_window_set_default_size(GTK_WINDOW(lw->tools), options->layout.float_window.w, options->layout.float_window.h);
1386                         gtk_window_move(GTK_WINDOW(lw->tools), options->layout.float_window.x, options->layout.float_window.y);
1387                         }
1388                 else
1389                         {
1390                         if (vertical)
1391                                 {
1392                                 gtk_window_set_default_size(GTK_WINDOW(lw->tools),
1393                                                             TOOLWINDOW_DEF_WIDTH, TOOLWINDOW_DEF_HEIGHT);
1394                                 }
1395                         else
1396                                 {
1397                                 gtk_window_set_default_size(GTK_WINDOW(lw->tools),
1398                                                             TOOLWINDOW_DEF_HEIGHT, TOOLWINDOW_DEF_WIDTH);
1399                                 }
1400                         }
1401                 }
1402
1403         if (!options->layout.save_window_positions)
1404                 {
1405                 if (vertical)
1406                         {
1407                         lw->div_float = MAIN_WINDOW_DIV_VPOS;
1408                         }
1409                 else
1410                         {
1411                         lw->div_float = MAIN_WINDOW_DIV_HPOS;
1412                         }
1413                 }
1414
1415         gtk_paned_set_position(GTK_PANED(lw->tools_pane), lw->div_float);
1416 }
1417
1418 /*
1419  *-----------------------------------------------------------------------------
1420  * glue (layout arrangement)
1421  *-----------------------------------------------------------------------------
1422  */
1423
1424 static void layout_grid_compute(LayoutWindow *lw,
1425                                 GtkWidget *image, GtkWidget *tools, GtkWidget *files,
1426                                 GtkWidget **w1, GtkWidget **w2, GtkWidget **w3)
1427 {
1428         /* heh, this was fun */
1429
1430         if (layout_location_single(lw->dir_location))
1431                 {
1432                 if (layout_location_first(lw->dir_location))
1433                         {
1434                         *w1 = tools;
1435                         layout_location_compute(lw->file_location, lw->image_location, files, image, w2, w3);
1436                         }
1437                 else
1438                         {
1439                         *w3 = tools;
1440                         layout_location_compute(lw->file_location, lw->image_location, files, image, w1, w2);
1441                         }
1442                 }
1443         else if (layout_location_single(lw->file_location))
1444                 {
1445                 if (layout_location_first(lw->file_location))
1446                         {
1447                         *w1 = files;
1448                         layout_location_compute(lw->dir_location, lw->image_location, tools, image, w2, w3);
1449                         }
1450                 else
1451                         {
1452                         *w3 = files;
1453                         layout_location_compute(lw->dir_location, lw->image_location, tools, image, w1, w2);
1454                         }
1455                 }
1456         else
1457                 {
1458                 /* image */
1459                 if (layout_location_first(lw->image_location))
1460                         {
1461                         *w1 = image;
1462                         layout_location_compute(lw->file_location, lw->dir_location, files, tools, w2, w3);
1463                         }
1464                 else
1465                         {
1466                         *w3 = image;
1467                         layout_location_compute(lw->file_location, lw->dir_location, files, tools, w1, w2);
1468                         }
1469                 }
1470 }
1471
1472 void layout_split_change(LayoutWindow *lw, ImageSplitMode mode)
1473 {
1474         GtkWidget *image;
1475         gint i;
1476
1477         for (i = 0; i < MAX_SPLIT_IMAGES; i++)
1478                 {
1479                 if (lw->split_images[i])
1480                         {
1481                         gtk_widget_hide(lw->split_images[i]->widget);
1482                         if (lw->split_images[i]->widget->parent != lw->utility_box) 
1483                                 gtk_container_remove(GTK_CONTAINER(lw->split_images[i]->widget->parent), lw->split_images[i]->widget);
1484                         }
1485                 }
1486         gtk_container_remove(GTK_CONTAINER(lw->utility_box), lw->split_image_widget);
1487
1488         image = layout_image_setup_split(lw, mode);
1489
1490         gtk_box_pack_start(GTK_BOX(lw->utility_box), image, TRUE, TRUE, 0);
1491         gtk_box_reorder_child(GTK_BOX(lw->utility_box), image, 0);
1492         gtk_widget_show(image);
1493 }
1494
1495 static void layout_grid_setup(LayoutWindow *lw)
1496 {
1497         gint priority_location;
1498         GtkWidget *h;
1499         GtkWidget *v;
1500         GtkWidget *w1, *w2, *w3;
1501
1502         GtkWidget *image;
1503         GtkWidget *tools;
1504         GtkWidget *files;
1505
1506         layout_actions_setup(lw);
1507         layout_actions_add_window(lw, lw->window);
1508
1509         lw->group_box = gtk_vbox_new(FALSE, 0);
1510         gtk_box_pack_start(GTK_BOX(lw->main_box), lw->group_box, TRUE, TRUE, 0);
1511         gtk_widget_show(lw->group_box);
1512
1513         priority_location = layout_grid_compass(lw);
1514
1515         image = layout_image_setup_split(lw, lw->split_mode);
1516         
1517         tools = layout_tools_new(lw);
1518         files = layout_list_new(lw);
1519
1520         image = layout_bars_prepare(lw, image);
1521
1522         if (lw->tools_float || lw->tools_hidden)
1523                 {
1524                 gtk_box_pack_start(GTK_BOX(lw->group_box), image, TRUE, TRUE, 0);
1525                 gtk_widget_show(image);
1526
1527                 layout_tools_setup(lw, tools, files);
1528
1529                 gtk_widget_grab_focus(lw->image->widget);
1530
1531                 return;
1532                 }
1533         else if (lw->tools)
1534                 {
1535                 layout_tools_geometry_sync(lw);
1536                 gtk_widget_destroy(lw->tools);
1537                 lw->tools = NULL;
1538                 lw->tools_pane = NULL;
1539                 }
1540
1541         layout_status_setup(lw, lw->group_box, FALSE);
1542
1543         layout_grid_compute(lw, image, tools, files, &w1, &w2, &w3);
1544
1545         v = lw->v_pane = gtk_vpaned_new();
1546
1547         h = lw->h_pane = gtk_hpaned_new();
1548
1549         if (!layout_location_vertical(priority_location))
1550                 {
1551                 GtkWidget *tmp;
1552
1553                 tmp = v;
1554                 v = h;
1555                 h = tmp;
1556                 }
1557
1558         gtk_box_pack_start(GTK_BOX(lw->group_box), v, TRUE, TRUE, 0);
1559
1560         if (!layout_location_first(priority_location))
1561                 {
1562                 gtk_paned_pack1(GTK_PANED(v), h, FALSE, TRUE);
1563                 gtk_paned_pack2(GTK_PANED(v), w3, TRUE, TRUE);
1564
1565                 gtk_paned_pack1(GTK_PANED(h), w1, FALSE, TRUE);
1566                 gtk_paned_pack2(GTK_PANED(h), w2, TRUE, TRUE);
1567                 }
1568         else
1569                 {
1570                 gtk_paned_pack1(GTK_PANED(v), w1, FALSE, TRUE);
1571                 gtk_paned_pack2(GTK_PANED(v), h, TRUE, TRUE);
1572
1573                 gtk_paned_pack1(GTK_PANED(h), w2, FALSE, TRUE);
1574                 gtk_paned_pack2(GTK_PANED(h), w3, TRUE, TRUE);
1575                 }
1576
1577         gtk_widget_show(image);
1578         gtk_widget_show(tools);
1579         gtk_widget_show(files);
1580
1581         gtk_widget_show(v);
1582         gtk_widget_show(h);
1583
1584         /* fix to have image pane visible when it is left and priority widget */
1585         if (lw->div_h == -1 &&
1586             w1 == image &&
1587             !layout_location_vertical(priority_location) &&
1588             layout_location_first(priority_location))
1589                 {
1590                 gtk_widget_set_size_request(image, 200, -1);
1591                 }
1592
1593         gtk_paned_set_position(GTK_PANED(lw->h_pane), lw->div_h);
1594         gtk_paned_set_position(GTK_PANED(lw->v_pane), lw->div_v);
1595
1596         gtk_widget_grab_focus(lw->image->widget);
1597 }
1598
1599 void layout_style_set(LayoutWindow *lw, gint style, const gchar *order)
1600 {
1601         gchar *path;
1602         gint i;
1603
1604         if (!layout_valid(&lw)) return;
1605
1606         if (style != -1)
1607                 {
1608                 LayoutLocation d, f, i;
1609
1610                 layout_config_parse(style, order, &d,  &f, &i);
1611
1612                 if (lw->dir_location == d &&
1613                     lw->file_location == f &&
1614                     lw->image_location == i) return;
1615
1616                 lw->dir_location = d;
1617                 lw->file_location = f;
1618                 lw->image_location = i;
1619                 }
1620
1621         /* remember state */
1622
1623         layout_image_slideshow_stop(lw);
1624         layout_image_full_screen_stop(lw);
1625
1626         path = lw->path;
1627         lw->path = NULL;
1628         lw->image = NULL;
1629         lw->utility_box = NULL;
1630
1631         layout_geometry_get_dividers(lw, &lw->div_h, &lw->div_v);
1632
1633         /* clear it all */
1634
1635         for (i = 0; i < MAX_SPLIT_IMAGES; i++)
1636                 {
1637                 if (lw->split_images[i])
1638                         {
1639                         gtk_widget_hide(lw->split_images[i]->widget);
1640                         gtk_container_remove(GTK_CONTAINER(lw->split_images[i]->widget->parent), lw->split_images[i]->widget);
1641                         }
1642                 }
1643
1644         lw->h_pane = NULL;
1645         lw->v_pane = NULL;
1646
1647         lw->toolbar = NULL;
1648         lw->thumb_button = NULL;
1649         lw->path_entry = NULL;
1650         lw->dir_view = NULL;
1651         lw->vd = NULL;
1652
1653         lw->file_view = NULL;
1654         lw->vfl = NULL;
1655         lw->vfi = NULL;
1656
1657         lw->info_box = NULL;
1658         lw->info_progress_bar = NULL;
1659         lw->info_sort = NULL;
1660         lw->info_color = NULL;
1661         lw->info_status = NULL;
1662         lw->info_details = NULL;
1663         lw->info_zoom = NULL;
1664
1665         if (lw->ui_manager) g_object_unref(lw->ui_manager);
1666         lw->ui_manager = NULL;
1667         lw->action_group = NULL;
1668
1669         gtk_container_remove(GTK_CONTAINER(lw->main_box), lw->group_box);
1670         lw->group_box = NULL;
1671
1672         /* re-fill */
1673
1674         layout_grid_setup(lw);
1675         layout_tools_hide(lw, lw->tools_hidden);
1676
1677         layout_list_sync_sort(lw);
1678         layout_util_sync(lw);
1679         layout_status_update_all(lw);
1680
1681         /* sync */
1682
1683         if (image_get_path(lw->image))
1684                 {
1685                 layout_set_path(lw, image_get_path(lw->image));
1686                 }
1687         else
1688                 {
1689                 layout_set_path(lw, path);
1690                 }
1691         image_top_window_set_sync(lw->image, (lw->tools_float || lw->tools_hidden));
1692
1693         /* clean up */
1694
1695         g_free(path);
1696 }
1697
1698 void layout_styles_update(void)
1699 {
1700         GList *work;
1701
1702         work = layout_window_list;
1703         while (work)
1704                 {
1705                 LayoutWindow *lw = work->data;
1706                 work = work->next;
1707
1708                 layout_style_set(lw, options->layout.style, options->layout.order);
1709                 }
1710 }
1711
1712 void layout_colors_update(void)
1713 {
1714         GList *work;
1715
1716         work = layout_window_list;
1717         while (work)
1718                 {
1719                 LayoutWindow *lw = work->data;
1720                 work = work->next;
1721                 
1722                 if (!lw->image) continue;
1723                 image_background_set_color(lw->image, options->image.use_custom_border_color ? &options->image.border_color : NULL);
1724                 }
1725 }
1726
1727 void layout_tools_float_toggle(LayoutWindow *lw)
1728 {
1729         gint popped;
1730
1731         if (!lw) return;
1732
1733         if (!lw->tools_hidden)
1734                 {
1735                 popped = !lw->tools_float;
1736                 }
1737         else
1738                 {
1739                 popped = TRUE;
1740                 }
1741
1742         if (lw->tools_float == popped)
1743                 {
1744                 if (popped && lw->tools_hidden)
1745                         {
1746                         layout_tools_float_set(lw, popped, FALSE);
1747                         }
1748                 }
1749         else
1750                 {
1751                 if (lw->tools_float)
1752                         {
1753                         layout_tools_float_set(lw, FALSE, FALSE);
1754                         }
1755                 else
1756                         {
1757                         layout_tools_float_set(lw, TRUE, FALSE);
1758                         }
1759                 }
1760 }
1761
1762 void layout_tools_hide_toggle(LayoutWindow *lw)
1763 {
1764         if (!lw) return;
1765
1766         layout_tools_float_set(lw, lw->tools_float, !lw->tools_hidden);
1767 }
1768
1769 void layout_tools_float_set(LayoutWindow *lw, gint popped, gint hidden)
1770 {
1771         if (!layout_valid(&lw)) return;
1772
1773         if (lw->tools_float == popped && lw->tools_hidden == hidden) return;
1774
1775         if (lw->tools_float == popped && lw->tools_float && lw->tools)
1776                 {
1777                 layout_tools_hide(lw, hidden);
1778                 return;
1779                 }
1780
1781         lw->tools_float = popped;
1782         lw->tools_hidden = hidden;
1783
1784         layout_style_set(lw, -1, NULL);
1785 }
1786
1787 gint layout_tools_float_get(LayoutWindow *lw, gint *popped, gint *hidden)
1788 {
1789         if (!layout_valid(&lw)) return FALSE;
1790
1791         *popped = lw->tools_float;
1792         *hidden = lw->tools_hidden;
1793
1794         return TRUE;
1795 }
1796
1797 void layout_toolbar_toggle(LayoutWindow *lw)
1798 {
1799         if (!layout_valid(&lw)) return;
1800         if (!lw->toolbar) return;
1801
1802         lw->toolbar_hidden = !lw->toolbar_hidden;
1803
1804         if (lw->toolbar_hidden)
1805                 {
1806                 if (GTK_WIDGET_VISIBLE(lw->toolbar)) gtk_widget_hide(lw->toolbar);
1807                 }
1808         else
1809                 {
1810                 if (!GTK_WIDGET_VISIBLE(lw->toolbar)) gtk_widget_show(lw->toolbar);
1811                 }
1812 }
1813
1814 gint layout_toolbar_hidden(LayoutWindow *lw)
1815 {
1816         if (!layout_valid(&lw)) return TRUE;
1817
1818         return lw->toolbar_hidden;
1819 }
1820
1821 /*
1822  *-----------------------------------------------------------------------------
1823  * base
1824  *-----------------------------------------------------------------------------
1825  */
1826
1827 void layout_close(LayoutWindow *lw)
1828 {
1829         if (layout_window_list && layout_window_list->next)
1830                 {
1831                 layout_free(lw);
1832                 }
1833         else
1834                 {
1835                 exit_program();
1836                 }
1837 }
1838
1839 void layout_free(LayoutWindow *lw)
1840 {
1841         if (!lw) return;
1842
1843         layout_window_list = g_list_remove(layout_window_list, lw);
1844
1845         if (lw->last_time_id != -1)
1846                 {
1847                 g_source_remove(lw->last_time_id);
1848                 }
1849
1850         layout_bars_close(lw);
1851
1852         gtk_widget_destroy(lw->window);
1853         
1854         histogram_free(lw->histogram);
1855
1856         g_free(lw->path);
1857
1858         g_free(lw);
1859 }
1860
1861 static gint layout_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer data)
1862 {
1863         LayoutWindow *lw = data;
1864
1865         layout_close(lw);
1866         return TRUE;
1867 }
1868
1869 LayoutWindow *layout_new(const gchar *path, gint popped, gint hidden)
1870 {
1871         return layout_new_with_geometry(path, popped, hidden, NULL);
1872 }
1873
1874 LayoutWindow *layout_new_with_geometry(const gchar *path, gint popped, gint hidden,
1875                                        const gchar *geometry)
1876 {
1877         LayoutWindow *lw;
1878         GdkGeometry hint;
1879         GdkWindowHints hint_mask;
1880
1881         lw = g_new0(LayoutWindow, 1);
1882
1883         lw->thumbs_enabled = options->layout.show_thumbnails;
1884         lw->sort_method = SORT_NAME;
1885         lw->sort_ascend = TRUE;
1886
1887         lw->tools_float = popped;
1888         lw->tools_hidden = hidden;
1889
1890         lw->toolbar_hidden = options->layout.toolbar_hidden;
1891
1892         lw->utility_box = NULL;
1893         lw->bar_sort = NULL;
1894         lw->bar_sort_enabled = FALSE;
1895         lw->bar_exif = NULL;
1896         lw->bar_exif_enabled = FALSE;
1897         lw->bar_exif_size = -1;
1898         lw->bar_exif_advanced = FALSE;
1899
1900         lw->histogram_enabled = FALSE;
1901
1902         /* default layout */
1903
1904         layout_config_parse(options->layout.style, options->layout.order,
1905                             &lw->dir_location,  &lw->file_location, &lw->image_location);
1906         lw->dir_view_type = options->layout.dir_view_type;
1907         lw->icon_view = options->layout.view_as_icons;
1908
1909         /* divider positions */
1910
1911         if (options->layout.save_window_positions)
1912                 {
1913                 lw->div_h = options->layout.main_window.hdivider_pos;
1914                 lw->div_v = options->layout.main_window.vdivider_pos;
1915                 lw->div_float = options->layout.float_window.vdivider_pos;
1916                 }
1917         else
1918                 {
1919                 lw->div_h = MAIN_WINDOW_DIV_HPOS;
1920                 lw->div_v = MAIN_WINDOW_DIV_VPOS;
1921                 lw->div_float = MAIN_WINDOW_DIV_VPOS;
1922                 }
1923
1924         /* window */
1925
1926         lw->window = window_new(GTK_WINDOW_TOPLEVEL, GQ_WMCLASS, NULL, NULL, NULL);
1927         gtk_window_set_resizable(GTK_WINDOW(lw->window), TRUE);
1928         gtk_container_set_border_width(GTK_CONTAINER(lw->window), 0);
1929
1930         if (options->layout.save_window_positions)
1931                 {
1932                 hint_mask = GDK_HINT_USER_POS;
1933                 }
1934         else
1935                 {
1936                 hint_mask = 0;
1937                 }
1938
1939         hint.min_width = 32;
1940         hint.min_height = 32;
1941         hint.base_width = 0;
1942         hint.base_height = 0;
1943         gtk_window_set_geometry_hints(GTK_WINDOW(lw->window), NULL, &hint,
1944                                       GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE | hint_mask);
1945
1946         if (options->layout.save_window_positions)
1947                 {
1948                 gtk_window_set_default_size(GTK_WINDOW(lw->window), options->layout.main_window.w, options->layout.main_window.h);
1949                 if (!layout_window_list)
1950                         {
1951                         gtk_window_move(GTK_WINDOW(lw->window), options->layout.main_window.x, options->layout.main_window.y);
1952                         if (options->layout.main_window.maximized) gtk_window_maximize(GTK_WINDOW(lw->window));
1953                         }
1954                 }
1955         else
1956                 {
1957                 gtk_window_set_default_size(GTK_WINDOW(lw->window), MAINWINDOW_DEF_WIDTH, MAINWINDOW_DEF_HEIGHT);
1958                 }
1959
1960         g_signal_connect(G_OBJECT(lw->window), "delete_event",
1961                          G_CALLBACK(layout_delete_cb), lw);
1962
1963         layout_keyboard_init(lw, lw->window);
1964
1965         lw->main_box = gtk_vbox_new(FALSE, 0);
1966         gtk_container_add(GTK_CONTAINER(lw->window), lw->main_box);
1967         gtk_widget_show(lw->main_box);
1968
1969         layout_grid_setup(lw);
1970         image_top_window_set_sync(lw->image, (lw->tools_float || lw->tools_hidden));
1971
1972         layout_util_sync(lw);
1973         layout_status_update_all(lw);
1974
1975         if (path)
1976                 {
1977                 layout_set_path(lw, path);
1978                 }
1979         else
1980                 {
1981                 GdkPixbuf *pixbuf;
1982
1983                 pixbuf = pixbuf_inline(PIXBUF_INLINE_LOGO);
1984                 image_change_pixbuf(lw->image, pixbuf, 1.0);
1985                 gdk_pixbuf_unref(pixbuf);
1986                 }
1987
1988         /* set up the time stat timeout */
1989         lw->last_time = 0;
1990         lw->last_time_id = g_timeout_add(5000, layout_check_for_update_cb, lw);
1991
1992         if (geometry)
1993                 {
1994                 if (!gtk_window_parse_geometry(GTK_WINDOW(lw->window), geometry))
1995                         {
1996                         print_term(_("Invalid geometry\n"));
1997                         }
1998                 }
1999
2000         gtk_widget_show(lw->window);
2001         layout_tools_hide(lw, lw->tools_hidden);
2002
2003         layout_window_list = g_list_append(layout_window_list, lw);
2004
2005         return lw;
2006 }
2007
2008 /*
2009  *-----------------------------------------------------------------------------
2010  * maintenance (for rename, move, remove)
2011  *-----------------------------------------------------------------------------
2012  */
2013
2014 static void layout_real_time_update(LayoutWindow *lw)
2015 {
2016         /* this resets the last time stamp of path so that a refresh does not occur
2017          * from an internal file operation.
2018          */
2019
2020         if (lw->path) lw->last_time = filetime(lw->path);
2021 }
2022
2023 static void layout_real_renamed(LayoutWindow *lw, FileData *fd)
2024 {
2025         gint update = FALSE;
2026
2027         if (lw->image) layout_image_maint_renamed(lw, fd);
2028
2029         if (lw->vfl) update |= vflist_maint_renamed(lw->vfl, fd);
2030         if (lw->vfi) update |= vficon_maint_renamed(lw->vfi, fd);
2031
2032         if (update) layout_real_time_update(lw);
2033 }
2034
2035 static void layout_real_removed(LayoutWindow *lw, FileData *fd, GList *ignore_list)
2036 {
2037         gint update = FALSE;
2038
2039         if (lw->image) layout_image_maint_removed(lw, fd);
2040
2041         if (lw->vfl) update |= vflist_maint_removed(lw->vfl, fd, ignore_list);
2042         if (lw->vfi) update |= vficon_maint_removed(lw->vfi, fd, ignore_list);
2043
2044         if (update) layout_real_time_update(lw);
2045 }
2046
2047 static void layout_real_moved(LayoutWindow *lw, FileData *fd, GList *ignore_list)
2048 {
2049         gint update = FALSE;
2050
2051         if (lw->image) layout_image_maint_moved(lw, fd);
2052
2053         if (lw->vfl) update |= vflist_maint_moved(lw->vfl, fd, ignore_list);
2054         if (lw->vfi) update |= vficon_maint_moved(lw->vfi, fd, ignore_list);
2055
2056         if (update) layout_real_time_update(lw);
2057 }
2058
2059 void layout_maint_renamed(FileData *fd)
2060 {
2061         GList *work = layout_window_list;
2062         while (work)
2063                 {
2064                 LayoutWindow *lw = work->data;
2065                 work = work->next;
2066
2067                 layout_real_renamed(lw, fd);
2068                 }
2069 }
2070
2071 void layout_maint_removed(FileData *fd, GList *ignore_list)
2072 {
2073         GList *work = layout_window_list;
2074         while (work)
2075                 {
2076                 LayoutWindow *lw = work->data;
2077                 work = work->next;
2078
2079                 layout_real_removed(lw, fd, ignore_list);
2080                 }
2081 }
2082
2083 void layout_maint_moved(FileData *fd, GList *ignore_list)
2084 {
2085         GList *work = layout_window_list;
2086         while (work)
2087                 {
2088                 LayoutWindow *lw = work->data;
2089                 work = work->next;
2090
2091                 layout_real_moved(lw, fd, ignore_list);
2092                 }
2093 }