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