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