9edbb550069e7b6d4398564f832f96ca1dde8358
[geeqie.git] / src / layout_image.c
1 /*
2  * GQview
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 "gqview.h"
13 #include "layout_image.h"
14
15 #include "collect.h"
16 #include "dnd.h"
17 #include "editors.h"
18 #include "filelist.h"
19 #include "fullscreen.h"
20 #include "image.h"
21 #include "image-overlay.h"
22 #include "img-view.h"
23 #include "info.h"
24 #include "layout.h"
25 #include "layout_util.h"
26 #include "menu.h"
27 #include "pixbuf_util.h"
28 #include "utilops.h"
29 #include "slideshow.h"
30 #include "ui_bookmark.h"
31 #include "ui_fileops.h"
32 #include "ui_menu.h"
33
34 #include <gdk/gdkkeysyms.h> /* for keyboard values */
35
36
37 static GtkWidget *layout_image_pop_menu(LayoutWindow *lw);
38 static void layout_image_set_buttons(LayoutWindow *lw);
39
40 /*
41  *----------------------------------------------------------------------------
42  * full screen overlay
43  *----------------------------------------------------------------------------
44  */
45
46 static void layout_image_overlay_set(LayoutWindow *lw, gint enable)
47 {
48         lw->full_screen_overlay_on = enable;
49
50         if (!lw->full_screen) return;
51
52         image_osd_set(lw->image, enable, enable);
53 }
54
55 void layout_image_overlay_update(LayoutWindow *lw)
56 {
57         if (!lw || !lw->full_screen) return;
58
59         image_osd_update(lw->image);
60 }
61
62 /*
63  *----------------------------------------------------------------------------
64  * full screen
65  *----------------------------------------------------------------------------
66  */
67
68 static void layout_image_fullscreen_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
69 {
70         LayoutWindow *lw = data;
71
72         if (!lw->full_screen) return;
73
74         gdk_window_get_origin(lw->full_screen->imd->pr->window, x, y);
75         popup_menu_position_clamp(menu, x, y, 0);
76 }
77
78 static void layout_image_full_screen_menu_popup(LayoutWindow *lw)
79 {
80         GtkWidget *menu;
81
82         menu = layout_image_pop_menu(lw);
83         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, layout_image_fullscreen_menu_pos_cb, lw, 0, GDK_CURRENT_TIME);
84 }
85
86 static gint layout_image_full_screen_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
87 {
88         LayoutWindow *lw = data;
89         gint stop_signal;
90         gint x = 0;
91         gint y = 0;
92
93         stop_signal = TRUE;
94         switch (event->keyval)
95                 {
96                 case GDK_Left: case GDK_KP_Left:
97                         x -= 1;
98                         break;
99                 case GDK_Right: case GDK_KP_Right:
100                         x += 1;
101                         break;
102                 case GDK_Up: case GDK_KP_Up:
103                         y -= 1;
104                         break;
105                 case GDK_Down: case GDK_KP_Down:
106                         y += 1;
107                         break;
108                 default:
109                         stop_signal = FALSE;
110                         break;
111                 }
112
113         if (x != 0 || y!= 0)
114                 {
115                 if (event->state & GDK_SHIFT_MASK)
116                         {
117                         x *= 3;
118                         y *= 3;
119                         }
120
121                 keyboard_scroll_calc(&x, &y, event);
122                 layout_image_scroll(lw, x, y);
123                 }
124
125         if (stop_signal) return stop_signal;
126
127         if (event->state & GDK_CONTROL_MASK)
128                 {
129                 gint n = -1;
130
131                 stop_signal = TRUE;
132                 switch (event->keyval)
133                         {
134                         case '1':
135                                 n = 0;
136                                 break;
137                         case '2':
138                                 n = 1;
139                                 break;
140                         case '3':
141                                 n = 2;
142                                 break;
143                         case '4':
144                                 n = 3;
145                                 break;
146                         case '5':
147                                 n = 4;
148                                 break;
149                         case '6':
150                                 n = 5;
151                                 break;
152                         case '7':
153                                 n = 6;
154                                 break;
155                         case '8':
156                                 n = 7;
157                                 break;
158                         case '9':
159                                 n = 8;
160                                 break;
161                         case '0':
162                                 n = 9;
163                                 break;
164                         case 'C': case 'c':
165                                 file_util_copy(layout_image_get_path(lw), NULL, NULL, widget);
166                                 break;
167                         case 'M': case 'm':
168                                 file_util_move(layout_image_get_path(lw), NULL, NULL, widget);
169                                 break;
170                         case 'R': case 'r':
171                                 file_util_rename(layout_image_get_path(lw), NULL, widget);
172                                 break;
173                         case 'D': case 'd':
174                                 file_util_delete(layout_image_get_path(lw), NULL, widget);
175                                 break;
176                         case 'P': case 'p':
177                                 info_window_new(layout_image_get_path(lw), NULL);
178                                 break;
179                         case 'Q': case 'q':
180                                 exit_gqview();
181                                 return FALSE;
182                                 break;
183                         default:
184                                 stop_signal = FALSE;
185                                 break;
186                         }
187                 if (n != -1)
188                         {
189                         if (!editor_window_flag_set(n))
190                                 {
191                                 layout_image_full_screen_stop(lw);
192                                 }
193                         start_editor_from_file(n, layout_image_get_path(lw));
194                         }
195                 }
196         else if (event->state & GDK_SHIFT_MASK)
197                 {
198                 stop_signal = TRUE;
199                 switch (event->keyval)
200                         {
201                         case 'R': case 'r':
202                                 layout_image_alter(lw, ALTER_ROTATE_180);
203                                 break;
204                         case 'M': case 'm':
205                                 layout_image_alter(lw, ALTER_MIRROR);
206                                 break;
207                         case 'F': case 'f':
208                                 layout_image_alter(lw, ALTER_FLIP);
209                                 break;
210                         case 'G': case 'g':
211                                 layout_image_alter(lw, ALTER_DESATURATE);
212                                 break;
213                         default:
214                                 stop_signal = FALSE;
215                                 break;
216                         }
217                 }
218         else
219                 {
220                 stop_signal = TRUE;
221                 switch (event->keyval)
222                         {
223                         case '+': case '=': case GDK_KP_Add:
224                                 layout_image_zoom_adjust(lw, get_zoom_increment());
225                                 break;
226                         case '-': case GDK_KP_Subtract:
227                                 layout_image_zoom_adjust(lw, -get_zoom_increment());
228                                 break;
229                         case 'X': case 'x': case GDK_KP_Multiply:
230                                 layout_image_zoom_set(lw, 0.0);
231                                 break;
232                         case 'Z': case 'z': case GDK_KP_Divide:
233                         case '1':
234                                 layout_image_zoom_set(lw, 1.0);
235                                 break;
236                         case '2':
237                                 layout_image_zoom_set(lw, 2.0);
238                                 break;
239                         case '3':
240                                 layout_image_zoom_set(lw, 3.0);
241                                 break;
242                         case '4':
243                                 layout_image_zoom_set(lw, 4.0);
244                                 break;
245                         case '7':
246                                 layout_image_zoom_set(lw, -4.0);
247                                 break;
248                         case '8':
249                                 layout_image_zoom_set(lw, -3.0);
250                                 break;
251                         case '9':
252                                 layout_image_zoom_set(lw, -2.0);
253                                 break;
254                         case 'W': case 'w':
255                                 layout_image_zoom_set_fill_geometry(lw, FALSE);
256                                 break;
257                         case 'H': case 'h':
258                                 layout_image_zoom_set_fill_geometry(lw, TRUE);
259                                 break;
260                         case GDK_Page_Up: case GDK_KP_Page_Up:
261                         case GDK_BackSpace:
262                         case 'B': case 'b':
263                                 layout_image_prev(lw);
264                                 break;
265                         case GDK_Page_Down: case GDK_KP_Page_Down:
266                         case GDK_space:
267                         case 'N': case 'n':
268                                 layout_image_next(lw);
269                                 break;
270                         case GDK_Home: case GDK_KP_Home:
271                                 layout_image_first(lw);
272                                 break;
273                         case GDK_End: case GDK_KP_End:
274                                 layout_image_last(lw);
275                                 break;
276                         case ']':
277                                 layout_image_alter(lw, ALTER_ROTATE_90);
278                                 break;
279                         case '[':
280                                 layout_image_alter(lw, ALTER_ROTATE_90_CC);
281                                 break;
282                         case GDK_Delete: case GDK_KP_Delete:
283                                 if (enable_delete_key)
284                                         {
285                                         file_util_delete(layout_image_get_path(lw), NULL, widget);
286                                         }
287                                 break;
288                         case GDK_Escape:
289                                 layout_image_full_screen_stop(lw);
290                                 break;
291                         case 'R': case 'r':
292                                 layout_refresh(lw);
293                                 break;
294                         case 'S': case 's':
295                                 layout_image_slideshow_toggle(lw);
296                                 break;
297                         case 'P': case 'p':
298                                 layout_image_slideshow_pause_toggle(lw);
299                                 break;
300                         case 'F': case 'f':
301                         case 'V': case 'v':
302                         case GDK_F11:
303                                 layout_image_full_screen_stop(lw);
304                                 break;
305                         case GDK_Menu:
306                         case GDK_F10:
307                                 layout_image_full_screen_menu_popup(lw);
308                                 break;
309                         case 'I': case 'i':
310                                 layout_image_overlay_set(lw, !(lw->full_screen_overlay_on));
311                                 break;
312                         default:
313                                 stop_signal = FALSE;
314                                 break;
315                         }
316                 }
317
318         return stop_signal;
319 }
320
321 static void layout_image_full_screen_stop_func(FullScreenData *fs, gpointer data)
322 {
323         LayoutWindow *lw = data;
324
325         /* restore image window */
326         lw->image = fs->normal_imd;
327
328         if (lw->slideshow)
329                 {
330                 lw->slideshow->imd = lw->image;
331                 }
332
333         lw->full_screen = NULL;
334 }
335
336 void layout_image_full_screen_start(LayoutWindow *lw)
337 {
338         if (!layout_valid(&lw)) return;
339
340         if (lw->full_screen) return;
341
342         lw->full_screen = fullscreen_start(lw->window, lw->image,
343                                            layout_image_full_screen_stop_func, lw);
344
345         /* set to new image window */
346         lw->image = lw->full_screen->imd;
347
348         if (lw->slideshow)
349                 {
350                 lw->slideshow->imd = lw->image;
351                 }
352
353         layout_image_set_buttons(lw);
354
355         g_signal_connect(G_OBJECT(lw->full_screen->window), "key_press_event",
356                          G_CALLBACK(layout_image_full_screen_key_press_cb), lw);
357
358 #if 0
359         gtk_widget_set_sensitive(lw->window, FALSE);
360         if (lw->tools) gtk_widget_set_sensitive(lw->tools, FALSE);
361 #endif
362
363         layout_image_overlay_set(lw, lw->full_screen_overlay_on);
364 }
365
366 void layout_image_full_screen_stop(LayoutWindow *lw)
367 {
368         if (!layout_valid(&lw)) return;
369         if (!lw->full_screen) return;
370
371         fullscreen_stop(lw->full_screen);
372
373 #if 0
374         gtk_widget_set_sensitive(lw->window, TRUE);
375         if (lw->tools) gtk_widget_set_sensitive(lw->tools, TRUE);
376 #endif
377 }
378
379 void layout_image_full_screen_toggle(LayoutWindow *lw)
380 {
381         if (!layout_valid(&lw)) return;
382         if (lw->full_screen)
383                 {
384                 layout_image_full_screen_stop(lw);
385                 }
386         else
387                 {
388                 layout_image_full_screen_start(lw);
389                 }
390 }
391
392 gint layout_image_full_screen_active(LayoutWindow *lw)
393 {
394         if (!layout_valid(&lw)) return FALSE;
395
396         return (lw->full_screen != NULL);
397 }
398
399 /*
400  *----------------------------------------------------------------------------
401  * slideshow
402  *----------------------------------------------------------------------------
403  */
404
405 static void layout_image_slideshow_next(LayoutWindow *lw)
406 {
407         if (lw->slideshow) slideshow_next(lw->slideshow);
408 }
409
410 static void layout_image_slideshow_prev(LayoutWindow *lw)
411 {
412         if (lw->slideshow) slideshow_prev(lw->slideshow);
413 }
414
415 static void layout_image_slideshow_stop_func(SlideShowData *ss, gpointer data)
416 {
417         LayoutWindow *lw = data;
418
419         lw->slideshow = NULL;
420         layout_status_update_info(lw, NULL);
421 }
422
423 void layout_image_slideshow_start(LayoutWindow *lw)
424 {
425         CollectionData *cd;
426         CollectInfo *info;
427
428         if (!layout_valid(&lw)) return;
429         if (lw->slideshow) return;
430
431         cd = image_get_collection(lw->image, &info);
432
433         if (cd && info)
434                 {
435                 lw->slideshow = slideshow_start_from_collection(lw->image, cd,
436                                 layout_image_slideshow_stop_func, lw, info);
437                 }
438         else
439                 {
440                 lw->slideshow = slideshow_start(lw->image, lw,
441                                 layout_list_get_index(lw, layout_image_get_path(lw)),
442                                 layout_image_slideshow_stop_func, lw);
443                 }
444
445         layout_status_update_info(lw, NULL);
446 }
447
448 /* note that slideshow will take ownership of the list, do not free it */
449 void layout_image_slideshow_start_from_list(LayoutWindow *lw, GList *list)
450 {
451         if (!layout_valid(&lw)) return;
452
453         if (lw->slideshow || !list)
454                 {
455                 path_list_free(list);
456                 return;
457                 }
458
459         lw->slideshow = slideshow_start_from_path_list(lw->image, list,
460                                                        layout_image_slideshow_stop_func, lw);
461
462         layout_status_update_info(lw, NULL);
463 }
464
465 void layout_image_slideshow_stop(LayoutWindow *lw)
466 {
467         if (!layout_valid(&lw)) return;
468
469         if (!lw->slideshow) return;
470
471         slideshow_free(lw->slideshow);
472         /* the stop_func sets lw->slideshow to NULL for us */
473 }
474
475 void layout_image_slideshow_toggle(LayoutWindow *lw)
476 {
477         if (!layout_valid(&lw)) return;
478
479         if (lw->slideshow)
480                 {
481                 layout_image_slideshow_stop(lw);
482                 }
483         else
484                 {
485                 layout_image_slideshow_start(lw);
486                 }
487 }
488
489 gint layout_image_slideshow_active(LayoutWindow *lw)
490 {
491         if (!layout_valid(&lw)) return FALSE;
492
493         return (lw->slideshow != NULL);
494 }
495
496 gint layout_image_slideshow_pause_toggle(LayoutWindow *lw)
497 {
498         gint ret;
499
500         if (!layout_valid(&lw)) return FALSE;
501
502         ret = slideshow_pause_toggle(lw->slideshow);
503
504         layout_status_update_info(lw, NULL);
505
506         return ret;
507 }
508
509 gint layout_image_slideshow_paused(LayoutWindow *lw)
510 {
511         if (!layout_valid(&lw)) return FALSE;
512
513         return (slideshow_paused(lw->slideshow));
514 }
515
516 static gint layout_image_slideshow_continue_check(LayoutWindow *lw)
517 {
518         if (!lw->slideshow) return FALSE;
519
520         if (!slideshow_should_continue(lw->slideshow))
521                 {
522                 layout_image_slideshow_stop(lw);
523                 return FALSE;
524                 }
525
526         return TRUE;
527 }
528
529 /*
530  *----------------------------------------------------------------------------
531  * pop-up menus
532  *----------------------------------------------------------------------------
533  */
534
535 static void li_pop_menu_zoom_in_cb(GtkWidget *widget, gpointer data)
536 {
537         LayoutWindow *lw = data;
538
539         image_zoom_adjust(lw->image, get_zoom_increment());
540 }
541
542 static void li_pop_menu_zoom_out_cb(GtkWidget *widget, gpointer data)
543 {
544         LayoutWindow *lw = data;
545         image_zoom_adjust(lw->image, -get_zoom_increment());
546 }
547
548 static void li_pop_menu_zoom_1_1_cb(GtkWidget *widget, gpointer data)
549 {
550         LayoutWindow *lw = data;
551
552         image_zoom_set(lw->image, 1.0);
553 }
554
555 static void li_pop_menu_zoom_fit_cb(GtkWidget *widget, gpointer data)
556 {
557         LayoutWindow *lw = data;
558
559         image_zoom_set(lw->image, 0.0);
560 }
561
562 static void li_pop_menu_edit_cb(GtkWidget *widget, gpointer data)
563 {
564         LayoutWindow *lw;
565         gint n;
566
567         lw = submenu_item_get_data(widget);
568         n = GPOINTER_TO_INT(data);
569
570         if (!editor_window_flag_set(n))
571                 {
572                 layout_image_full_screen_stop(lw);
573                 }
574         start_editor_from_file(n, layout_image_get_path(lw));
575 }
576
577 static void li_pop_menu_wallpaper_cb(GtkWidget *widget, gpointer data)
578 {
579         LayoutWindow *lw = data;
580
581         layout_image_to_root(lw);
582 }
583
584 static void li_pop_menu_alter_cb(GtkWidget *widget, gpointer data)
585 {
586         LayoutWindow *lw = data;
587         AlterType type;
588
589         lw = submenu_item_get_data(widget);
590         type = (AlterType)GPOINTER_TO_INT(data);
591
592         image_alter(lw->image, type);
593 }
594
595 static void li_pop_menu_info_cb(GtkWidget *widget, gpointer data)
596 {
597         LayoutWindow *lw = data;
598
599         info_window_new(layout_image_get_path(lw), NULL);
600 }
601
602 static void li_pop_menu_new_cb(GtkWidget *widget, gpointer data)
603 {
604         LayoutWindow *lw = data;
605
606         view_window_new(layout_image_get_path(lw));
607 }
608
609 static GtkWidget *li_pop_menu_click_parent(GtkWidget *widget, LayoutWindow *lw)
610 {
611         GtkWidget *menu;
612         GtkWidget *parent;
613
614         menu = gtk_widget_get_toplevel(widget);
615         if (!menu) return NULL;
616
617         parent = g_object_get_data(G_OBJECT(menu), "click_parent");
618
619         if (!parent && lw->full_screen)
620                 {
621                 parent = lw->full_screen->imd->widget;
622                 }
623
624         return parent;
625 }
626
627 static void li_pop_menu_copy_cb(GtkWidget *widget, gpointer data)
628 {
629         LayoutWindow *lw = data;
630
631         file_util_copy(layout_image_get_path(lw), NULL, NULL,
632                        li_pop_menu_click_parent(widget, lw));
633 }
634
635 static void li_pop_menu_move_cb(GtkWidget *widget, gpointer data)
636 {
637         LayoutWindow *lw = data;
638
639         file_util_move(layout_image_get_path(lw), NULL, NULL,
640                        li_pop_menu_click_parent(widget, lw));
641 }
642
643 static void li_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
644 {
645         LayoutWindow *lw = data;
646
647         file_util_rename(layout_image_get_path(lw), NULL,
648                          li_pop_menu_click_parent(widget, lw));
649 }
650
651 static void li_pop_menu_delete_cb(GtkWidget *widget, gpointer data)
652 {
653         LayoutWindow *lw = data;
654
655         file_util_delete(layout_image_get_path(lw), NULL,
656                          li_pop_menu_click_parent(widget, lw));
657 }
658
659 static void li_pop_menu_slide_start_cb(GtkWidget *widget, gpointer data)
660 {
661         LayoutWindow *lw = data;
662
663         layout_image_slideshow_start(lw);
664 }
665
666 static void li_pop_menu_slide_stop_cb(GtkWidget *widget, gpointer data)
667 {
668         LayoutWindow *lw = data;
669
670         layout_image_slideshow_stop(lw);
671 }
672
673 static void li_pop_menu_slide_pause_cb(GtkWidget *widget, gpointer data)
674 {
675         LayoutWindow *lw = data;
676
677         layout_image_slideshow_pause_toggle(lw);
678 }
679
680 static void li_pop_menu_full_screen_cb(GtkWidget *widget, gpointer data)
681 {
682         LayoutWindow *lw = data;
683
684         layout_image_full_screen_toggle(lw);
685 }
686
687 static void li_pop_menu_hide_cb(GtkWidget *widget, gpointer data)
688 {
689         LayoutWindow *lw = data;
690
691         layout_tools_hide_toggle(lw);
692 }
693
694 static GtkWidget *layout_image_pop_menu(LayoutWindow *lw)
695 {
696         GtkWidget *menu;
697         GtkWidget *item;
698         GtkWidget *submenu;
699         const gchar *path;
700         gint fullscreen;
701
702         path = layout_image_get_path(lw);
703         fullscreen = layout_image_full_screen_active(lw);
704
705         menu = popup_menu_short_lived();
706
707         menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN, G_CALLBACK(li_pop_menu_zoom_in_cb), lw);
708         menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT, G_CALLBACK(li_pop_menu_zoom_out_cb), lw);
709         menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100, G_CALLBACK(li_pop_menu_zoom_1_1_cb), lw);
710         menu_item_add_stock(menu, _("Fit image to _window"), GTK_STOCK_ZOOM_FIT, G_CALLBACK(li_pop_menu_zoom_fit_cb), lw);
711         menu_item_add_divider(menu);
712
713         submenu = submenu_add_edit(menu, &item, G_CALLBACK(li_pop_menu_edit_cb), lw);
714         if (!path) gtk_widget_set_sensitive(item, FALSE);
715         menu_item_add_divider(submenu);
716         menu_item_add(submenu, _("Set as _wallpaper"), G_CALLBACK(li_pop_menu_wallpaper_cb), lw);
717
718         item = submenu_add_alter(menu, G_CALLBACK(li_pop_menu_alter_cb), lw);
719
720         item = menu_item_add_stock(menu, _("_Properties"), GTK_STOCK_PROPERTIES, G_CALLBACK(li_pop_menu_info_cb), lw);
721         if (!path) gtk_widget_set_sensitive(item, FALSE);
722
723         item = menu_item_add_stock(menu, _("View in _new window"), GTK_STOCK_NEW, G_CALLBACK(li_pop_menu_new_cb), lw);
724         if (!path || fullscreen) gtk_widget_set_sensitive(item, FALSE);
725
726         menu_item_add_divider(menu);
727
728         item = menu_item_add_stock(menu, _("_Copy..."), GTK_STOCK_COPY, G_CALLBACK(li_pop_menu_copy_cb), lw);
729         if (!path) gtk_widget_set_sensitive(item, FALSE);
730         item = menu_item_add(menu, _("_Move..."), G_CALLBACK(li_pop_menu_move_cb), lw);
731         if (!path) gtk_widget_set_sensitive(item, FALSE);
732         item = menu_item_add(menu, _("_Rename..."), G_CALLBACK(li_pop_menu_rename_cb), lw);
733         if (!path) gtk_widget_set_sensitive(item, FALSE);
734         item = menu_item_add_stock(menu, _("_Delete..."), GTK_STOCK_DELETE, G_CALLBACK(li_pop_menu_delete_cb), lw);
735         if (!path) gtk_widget_set_sensitive(item, FALSE);
736
737         menu_item_add_divider(menu);
738
739         if (layout_image_slideshow_active(lw))
740                 {
741                 menu_item_add(menu, _("_Stop slideshow"), G_CALLBACK(li_pop_menu_slide_stop_cb), lw);
742                 if (layout_image_slideshow_paused(lw))
743                         {
744                         item = menu_item_add(menu, _("Continue slides_how"),
745                                              G_CALLBACK(li_pop_menu_slide_pause_cb), lw);
746                         }
747                 else
748                         {
749                         item = menu_item_add(menu, _("Pause slides_how"),
750                                              G_CALLBACK(li_pop_menu_slide_pause_cb), lw);
751                         }
752                 }
753         else
754                 {
755                 menu_item_add(menu, _("_Start slideshow"), G_CALLBACK(li_pop_menu_slide_start_cb), lw);
756                 item = menu_item_add(menu, _("Pause slides_how"), G_CALLBACK(li_pop_menu_slide_pause_cb), lw);
757                 gtk_widget_set_sensitive(item, FALSE);
758                 }
759
760         if (!fullscreen)
761                 {
762                 menu_item_add(menu, _("_Full screen"), G_CALLBACK(li_pop_menu_full_screen_cb), lw);
763                 }
764         else
765                 {
766                 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(li_pop_menu_full_screen_cb), lw);
767                 }
768
769         menu_item_add_divider(menu);
770
771         item = menu_item_add_check(menu, _("Hide file _list"), lw->tools_hidden,
772                                    G_CALLBACK(li_pop_menu_hide_cb), lw);
773         if (fullscreen) gtk_widget_set_sensitive(item, FALSE);
774
775         return menu;
776 }
777
778 static void layout_image_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
779 {
780         LayoutWindow *lw = data;
781
782         gdk_window_get_origin(lw->image->pr->window, x, y);
783         popup_menu_position_clamp(menu, x, y, 0);
784 }
785
786 void layout_image_menu_popup(LayoutWindow *lw)
787 {
788         GtkWidget *menu;
789
790         menu = layout_image_pop_menu(lw);
791         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, layout_image_menu_pos_cb, lw, 0, GDK_CURRENT_TIME);
792 }
793
794 /*
795  *----------------------------------------------------------------------------
796  * dnd
797  *----------------------------------------------------------------------------
798  */
799
800 static void layout_image_dnd_receive(GtkWidget *widget, GdkDragContext *context,
801                                      gint x, gint y,
802                                      GtkSelectionData *selection_data, guint info,
803                                      guint time, gpointer data)
804 {
805         LayoutWindow *lw = data;
806
807         if (info == TARGET_URI_LIST || info == TARGET_APP_COLLECTION_MEMBER)
808                 {
809                 CollectionData *source;
810                 GList *list;
811                 GList *info_list;
812
813                 if (info == TARGET_URI_LIST)
814                         {
815                         list = uri_list_from_text((gchar *)selection_data->data, TRUE);
816                         source = NULL;
817                         info_list = NULL;
818                         }
819                 else
820                         {
821                         source = collection_from_dnd_data((gchar *)selection_data->data, &list, &info_list);
822                         }
823
824                 if (list)
825                         {
826                         gchar *path;
827
828                         path = list->data;
829
830                         if (isfile(path))
831                                 {
832                                 gchar *base;
833                                 gint row;
834
835                                 base = remove_level_from_path(path);
836                                 if (strcmp(base, layout_get_path(lw)) != 0)
837                                         {
838                                         layout_set_path(lw, base);
839                                         }
840                                 g_free(base);
841
842                                 row = layout_list_get_index(lw, path);
843                                 if (source && info_list)
844                                         {
845                                         layout_image_set_collection(lw, source, info_list->data);
846                                         }
847                                 else if (row == -1)
848                                         {
849                                         layout_image_set_path(lw, path);
850                                         }
851                                 else
852                                         {
853                                         layout_image_set_index(lw, row);
854                                         }
855                                 }
856                         else if (isdir(path))
857                                 {
858                                 layout_set_path(lw, path);
859                                 layout_image_set_path(lw, NULL);
860                                 }
861                         }
862
863                 path_list_free(list);
864                 g_list_free(info_list);
865                 }
866 }
867
868 static void layout_image_dnd_get(GtkWidget *widget, GdkDragContext *context,
869                                  GtkSelectionData *selection_data, guint info,
870                                  guint time, gpointer data)
871 {
872         LayoutWindow *lw = data;
873         const gchar *path;
874
875         path = layout_image_get_path(lw);
876
877         if (path)
878                 {
879                 gchar *text = NULL;
880                 gint len;
881                 gint plain_text;
882                 GList *list;
883
884                 switch (info)
885                         {
886                         case TARGET_URI_LIST:
887                                 plain_text = FALSE;
888                                 break;
889                         case TARGET_TEXT_PLAIN:
890                         default:
891                                 plain_text = TRUE;
892                                 break;
893                         }
894                 list = g_list_append(NULL, (gchar *)path);
895                 text = uri_text_from_list(list, &len, plain_text);
896                 g_list_free(list);
897                 if (text)
898                         {
899                         gtk_selection_data_set (selection_data, selection_data->target,
900                                                 8, (guchar *)text, len);
901                         g_free(text);
902                         }
903                 }
904         else
905                 {
906                 gtk_selection_data_set (selection_data, selection_data->target,
907                                         8, NULL, 0);
908                 }
909 }
910
911 static void layout_image_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
912 {
913         LayoutWindow *lw = data;
914         if (context->action == GDK_ACTION_MOVE)
915                 {
916                 const gchar *path;
917                 gint row;
918
919                 path = layout_image_get_path(lw);
920                 row = layout_list_get_index(lw, path);
921                 if (row < 0) return;
922
923                 if (!isfile(path))
924                         {
925                         if (row < layout_list_count(lw, NULL) - 1)
926                                 {
927                                 layout_image_next(lw);
928                                 }
929                         else
930                                 {
931                                 layout_image_prev(lw);
932                                 }
933                         }
934                 layout_refresh(lw);
935                 }
936 }
937
938 static void layout_image_dnd_init(LayoutWindow *lw)
939 {
940         gtk_drag_source_set(lw->image->pr, GDK_BUTTON2_MASK,
941                             dnd_file_drag_types, dnd_file_drag_types_count,
942                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
943         g_signal_connect(G_OBJECT(lw->image->pr), "drag_data_get",
944                          G_CALLBACK(layout_image_dnd_get), lw);
945         g_signal_connect(G_OBJECT(lw->image->pr), "drag_end",
946                          G_CALLBACK(layout_image_dnd_end), lw);
947
948         gtk_drag_dest_set(lw->image->pr,
949                           GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
950                           dnd_file_drop_types, dnd_file_drop_types_count,
951                           GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
952         g_signal_connect(G_OBJECT(lw->image->pr), "drag_data_received",
953                          G_CALLBACK(layout_image_dnd_receive), lw);
954 }
955
956
957 /*
958  *----------------------------------------------------------------------------
959  * misc
960  *----------------------------------------------------------------------------
961  */
962
963 void layout_image_to_root(LayoutWindow *lw)
964 {
965         image_to_root_window(lw->image, (image_zoom_get(lw->image) == 0));
966 }
967
968 /*
969  *----------------------------------------------------------------------------
970  * manipulation + accessors
971  *----------------------------------------------------------------------------
972  */
973
974 void layout_image_scroll(LayoutWindow *lw, gint x, gint y)
975 {
976         if (!layout_valid(&lw)) return;
977
978         image_scroll(lw->image, x, y);
979 }
980
981 void layout_image_zoom_adjust(LayoutWindow *lw, gdouble increment)
982 {
983         if (!layout_valid(&lw)) return;
984
985         image_zoom_adjust(lw->image, increment);
986 }
987
988 void layout_image_zoom_set(LayoutWindow *lw, gdouble zoom)
989 {
990         if (!layout_valid(&lw)) return;
991
992         image_zoom_set(lw->image, zoom);
993 }
994
995 void layout_image_zoom_set_fill_geometry(LayoutWindow *lw, gint vertical)
996 {
997         if (!layout_valid(&lw)) return;
998
999         image_zoom_set_fill_geometry(lw->image, vertical);
1000 }
1001
1002 void layout_image_alter(LayoutWindow *lw, AlterType type)
1003 {
1004         if (!layout_valid(&lw)) return;
1005
1006         image_alter(lw->image, type);
1007 }
1008
1009 const gchar *layout_image_get_path(LayoutWindow *lw)
1010 {
1011         if (!layout_valid(&lw)) return NULL;
1012
1013         return image_get_path(lw->image);
1014 }
1015
1016 const gchar *layout_image_get_name(LayoutWindow *lw)
1017 {
1018         if (!layout_valid(&lw)) return NULL;
1019
1020         return image_get_name(lw->image);
1021 }
1022
1023 CollectionData *layout_image_get_collection(LayoutWindow *lw, CollectInfo **info)
1024 {
1025         if (!layout_valid(&lw)) return NULL;
1026
1027         return image_get_collection(lw->image, info);
1028 }
1029
1030 gint layout_image_get_index(LayoutWindow *lw)
1031 {
1032         return layout_list_get_index(lw, image_get_path(lw->image));
1033 }
1034
1035 /*
1036  *----------------------------------------------------------------------------
1037  * image changers
1038  *----------------------------------------------------------------------------
1039  */
1040
1041 void layout_image_set_path(LayoutWindow *lw, const gchar *path)
1042 {
1043         if (!layout_valid(&lw)) return;
1044
1045         image_change_path(lw->image, path, image_zoom_get_default(lw->image, zoom_mode));
1046
1047         layout_list_sync_path(lw, path);
1048         layout_image_slideshow_continue_check(lw);
1049         layout_bars_new_image(lw);
1050 }
1051
1052 void layout_image_set_with_ahead(LayoutWindow *lw, const gchar *path, const gchar *read_ahead_path)
1053 {
1054         if (!layout_valid(&lw)) return;
1055
1056         if (path)
1057                 {
1058                 const gchar *old_path;
1059
1060                 old_path = layout_image_get_path(lw);
1061                 if (old_path && strcmp(path, old_path) == 0) return;
1062                 }
1063
1064         layout_image_set_path(lw, path);
1065         if (enable_read_ahead) image_prebuffer_set(lw->image, read_ahead_path);
1066 }
1067
1068 void layout_image_set_index(LayoutWindow *lw, gint index)
1069 {
1070         const gchar *path;
1071         const gchar *read_ahead_path;
1072         gint old;
1073
1074         if (!layout_valid(&lw)) return;
1075
1076         old = layout_list_get_index(lw, layout_image_get_path(lw));
1077         path = layout_list_get_path(lw, index);
1078
1079         if (old > index)
1080                 {
1081                 read_ahead_path = layout_list_get_path(lw, index - 1);
1082                 }
1083         else
1084                 {
1085                 read_ahead_path = layout_list_get_path(lw, index + 1);
1086                 }
1087
1088         layout_image_set_with_ahead(lw, path, read_ahead_path);
1089 }
1090
1091 static void layout_image_set_collection_real(LayoutWindow *lw, CollectionData *cd, CollectInfo *info, gint forward)
1092 {
1093         if (!layout_valid(&lw)) return;
1094
1095         image_change_from_collection(lw->image, cd, info, image_zoom_get_default(lw->image, zoom_mode));
1096         if (enable_read_ahead)
1097                 {
1098                 CollectInfo *r_info;
1099                 if (forward)
1100                         {
1101                         r_info = collection_next_by_info(cd, info);
1102                         if (!r_info) r_info = collection_prev_by_info(cd, info);
1103                         }
1104                 else
1105                         {
1106                         r_info = collection_prev_by_info(cd, info);
1107                         if (!r_info) r_info = collection_next_by_info(cd, info);
1108                         }
1109                 if (r_info) image_prebuffer_set(lw->image, r_info->path);
1110                 }
1111
1112         layout_image_slideshow_continue_check(lw);
1113         layout_bars_new_image(lw);
1114 }
1115
1116 void layout_image_set_collection(LayoutWindow *lw, CollectionData *cd, CollectInfo *info)
1117 {
1118         layout_image_set_collection_real(lw, cd, info, TRUE);
1119         layout_list_sync_path(lw, layout_image_get_path(lw));
1120 }
1121
1122 void layout_image_refresh(LayoutWindow *lw)
1123 {
1124         if (!layout_valid(&lw)) return;
1125
1126         image_reload(lw->image);
1127 }
1128
1129 void layout_image_color_profile_set(LayoutWindow *lw,
1130                                     gint input_type, gint screen_type,
1131                                     gint use_image)
1132 {
1133         if (!layout_valid(&lw)) return;
1134
1135         image_color_profile_set(lw->image, input_type, screen_type, use_image);
1136 }
1137
1138 gint layout_image_color_profile_get(LayoutWindow *lw,
1139                                     gint *input_type, gint *screen_type,
1140                                     gint *use_image)
1141 {
1142         if (!layout_valid(&lw)) return FALSE;
1143
1144         return image_color_profile_get(lw->image, input_type, screen_type, use_image);
1145 }
1146
1147 void layout_image_color_profile_set_use(LayoutWindow *lw, gint enable)
1148 {
1149         if (!layout_valid(&lw)) return;
1150
1151         image_color_profile_set_use(lw->image, enable);
1152
1153         if (lw->info_color)
1154                 {
1155 #ifndef HAVE_LCMS
1156                 enable = FALSE;
1157 #endif
1158                 gtk_widget_set_sensitive(GTK_BIN(lw->info_color)->child, enable);
1159                 }
1160 }
1161
1162 gint layout_image_color_profile_get_use(LayoutWindow *lw)
1163 {
1164         if (!layout_valid(&lw)) return FALSE;
1165
1166         return image_color_profile_get_use(lw->image);
1167 }
1168
1169 /*
1170  *----------------------------------------------------------------------------
1171  * list walkers
1172  *----------------------------------------------------------------------------
1173  */
1174
1175 void layout_image_next(LayoutWindow *lw)
1176 {
1177         gint current;
1178         CollectionData *cd;
1179         CollectInfo *info;
1180
1181         if (!layout_valid(&lw)) return;
1182
1183         if (layout_image_slideshow_active(lw))
1184                 {
1185                 layout_image_slideshow_next(lw);
1186                 return;
1187                 }
1188
1189         cd = image_get_collection(lw->image, &info);
1190
1191         if (cd && info)
1192                 {
1193                 info = collection_next_by_info(cd, info);
1194                 if (info) layout_image_set_collection_real(lw, cd, info, TRUE);
1195                 return;
1196                 }
1197
1198         current = layout_image_get_index(lw);
1199
1200         if (current >= 0)
1201                 {
1202                 if (current < layout_list_count(lw, NULL) - 1)
1203                         {
1204                         layout_image_set_index(lw, current + 1);
1205                         }
1206                 }
1207         else
1208                 {
1209                 layout_image_set_index(lw, 0);
1210                 }
1211 }
1212
1213 void layout_image_prev(LayoutWindow *lw)
1214 {
1215         gint current;
1216         CollectionData *cd;
1217         CollectInfo *info;
1218
1219         if (!layout_valid(&lw)) return;
1220
1221         if (layout_image_slideshow_active(lw))
1222                 {
1223                 layout_image_slideshow_prev(lw);
1224                 return;
1225                 }
1226
1227         cd = image_get_collection(lw->image, &info);
1228
1229         if (cd && info)
1230                 {
1231                 info = collection_prev_by_info(cd, info);
1232                 if (info) layout_image_set_collection_real(lw, cd, info, FALSE);
1233                 return;
1234                 }
1235
1236         current = layout_image_get_index(lw);
1237
1238         if (current >= 0)
1239                 {
1240                 if (current > 0)
1241                         {
1242                         layout_image_set_index(lw, current - 1);
1243                         }
1244                 }
1245         else
1246                 {
1247                 layout_image_set_index(lw, layout_list_count(lw, NULL) - 1);
1248                 }
1249 }
1250
1251 void layout_image_first(LayoutWindow *lw)
1252 {
1253         gint current;
1254         CollectionData *cd;
1255         CollectInfo *info;
1256
1257         if (!layout_valid(&lw)) return;
1258
1259         cd = image_get_collection(lw->image, &info);
1260
1261         if (cd && info)
1262                 {
1263                 CollectInfo *new;
1264                 new = collection_get_first(cd);
1265                 if (new != info) layout_image_set_collection_real(lw, cd, new, TRUE);
1266                 return;
1267                 }
1268
1269         current = layout_image_get_index(lw);
1270         if (current != 0 && layout_list_count(lw, NULL) > 0)
1271                 {
1272                 layout_image_set_index(lw, 0);
1273                 }
1274 }
1275
1276 void layout_image_last(LayoutWindow *lw)
1277 {
1278         gint current;
1279         gint count;
1280         CollectionData *cd;
1281         CollectInfo *info;
1282
1283         if (!layout_valid(&lw)) return;
1284
1285         cd = image_get_collection(lw->image, &info);
1286
1287         if (cd && info)
1288                 {
1289                 CollectInfo *new;
1290                 new = collection_get_last(cd);
1291                 if (new != info) layout_image_set_collection_real(lw, cd, new, FALSE);
1292                 return;
1293                 }
1294
1295         current = layout_image_get_index(lw);
1296         count = layout_list_count(lw, NULL);
1297         if (current != count - 1 && count > 0)
1298                 {
1299                 layout_image_set_index(lw, count - 1);
1300                 }
1301 }
1302
1303 /*
1304  *----------------------------------------------------------------------------
1305  * mouse callbacks
1306  *----------------------------------------------------------------------------
1307  */
1308
1309 static void layout_image_button_cb(ImageWindow *imd, gint button, guint32 time,
1310                                    gdouble x, gdouble y, guint state, gpointer data)
1311 {
1312         LayoutWindow *lw = data;
1313         GtkWidget *menu;
1314
1315         switch (button)
1316                 {
1317                 case 1:
1318                         layout_image_next(lw);
1319                         break;
1320                 case 2:
1321                         layout_image_prev(lw);
1322                         break;
1323                 case 3:
1324                         menu = layout_image_pop_menu(lw);
1325                         if (imd == lw->image)
1326                                 {
1327                                 g_object_set_data(G_OBJECT(menu), "click_parent", imd->widget);
1328                                 }
1329                         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, time);
1330                         break;
1331                 default:
1332                         break;
1333                 }
1334 }
1335
1336 static void layout_image_scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
1337                                    gdouble x, gdouble y, guint state, gpointer data)
1338 {
1339         LayoutWindow *lw = data;
1340
1341         if (state & GDK_CONTROL_MASK)
1342                 {
1343                 switch (direction)
1344                         {
1345                         case GDK_SCROLL_UP:
1346                                 image_zoom_adjust_at_point(imd, get_zoom_increment(), x, y);
1347                                 break;
1348                         case GDK_SCROLL_DOWN:
1349                                 image_zoom_adjust_at_point(imd, -get_zoom_increment(), x, y);
1350                                 break;
1351                         default:
1352                                 break;
1353                         }
1354                 }
1355         else if ( (state & GDK_SHIFT_MASK) != (mousewheel_scrolls))
1356                 {
1357                 switch (direction)
1358                         {
1359                         case GDK_SCROLL_UP:
1360                                 image_scroll(imd, 0, -MOUSEWHEEL_SCROLL_SIZE);
1361                                 break;
1362                         case GDK_SCROLL_DOWN:
1363                                 image_scroll(imd, 0, MOUSEWHEEL_SCROLL_SIZE);
1364                                 break;
1365                         case GDK_SCROLL_LEFT:
1366                                 image_scroll(imd, -MOUSEWHEEL_SCROLL_SIZE, 0);
1367                                 break;
1368                         case GDK_SCROLL_RIGHT:
1369                                 image_scroll(imd, MOUSEWHEEL_SCROLL_SIZE, 0);
1370                                 break;
1371                         default:
1372                                 break;
1373                         }
1374                 }
1375         else
1376                 {
1377                 switch (direction)
1378                         {
1379                         case GDK_SCROLL_UP:
1380                                 layout_image_prev(lw);
1381                                 break;
1382                         case GDK_SCROLL_DOWN:
1383                                 layout_image_next(lw);
1384                                 break;
1385                         default:
1386                                 break;
1387                         }
1388                 }
1389 }
1390
1391 static void layout_image_set_buttons(LayoutWindow *lw)
1392 {
1393         image_set_button_func(lw->image, layout_image_button_cb, lw);
1394         image_set_scroll_func(lw->image, layout_image_scroll_cb, lw);
1395 }
1396
1397 /*
1398  *----------------------------------------------------------------------------
1399  * setup
1400  *----------------------------------------------------------------------------
1401  */
1402
1403 static void layout_image_update_cb(ImageWindow *imd, gpointer data)
1404 {
1405         LayoutWindow *lw = data;
1406         layout_status_update_image(lw);
1407 }
1408
1409 GtkWidget *layout_image_new(LayoutWindow *lw, const gchar *path)
1410 {
1411         if (!lw->image) 
1412                 {
1413                 lw->image = image_new( (!lw->tools_float && !lw->tools_hidden) );
1414                 if (black_window_background) image_background_set_black(lw->image, TRUE);
1415                 image_set_update_func(lw->image, layout_image_update_cb, lw);
1416                 layout_image_set_buttons(lw);
1417                 layout_image_dnd_init(lw);
1418
1419                 image_attach_window(lw->image, lw->window, NULL, "GQview", FALSE);
1420
1421                 image_auto_refresh(lw->image, 0);
1422
1423                 image_color_profile_set(lw->image,
1424                                         color_profile_input_type, color_profile_screen_type,
1425                                         color_profile_use_image);
1426                 image_color_profile_set_use(lw->image, color_profile_enabled);
1427                 }
1428
1429         return lw->image->widget;
1430 }
1431
1432 /*
1433  *-----------------------------------------------------------------------------
1434  * maintenance (for rename, move, remove)
1435  *-----------------------------------------------------------------------------
1436  */
1437
1438 void layout_image_maint_renamed(LayoutWindow *lw, const gchar *source, const gchar *dest)
1439 {
1440         const gchar *img_path;
1441
1442         img_path = layout_image_get_path(lw);
1443         if (img_path && strcmp(img_path, source) == 0)
1444                 {
1445                 image_set_path(lw->image, dest);
1446                 layout_bars_maint_renamed(lw);
1447                 }
1448 }
1449
1450 void layout_image_maint_removed(LayoutWindow *lw, const gchar *path)
1451 {
1452         const gchar *img_path;
1453
1454         img_path = layout_image_get_path(lw);
1455         if (img_path && strcmp(img_path, path) == 0)
1456                 {
1457                 CollectionData *cd;
1458                 CollectInfo *info;
1459
1460                 cd = image_get_collection(lw->image, &info);
1461                 if (cd && info)
1462                         {
1463                         CollectInfo *new;
1464
1465                         new = collection_next_by_info(cd, info);
1466                         if (!new) new = collection_prev_by_info(cd, info);
1467
1468                         if (new)
1469                                 {
1470                                 layout_image_set_collection(lw, cd, new);
1471                                 return;
1472                                 }
1473                         }
1474
1475                 layout_image_set_path(lw, NULL);
1476                 }
1477 }
1478
1479 void layout_image_maint_moved(LayoutWindow *lw, const gchar *source, const gchar *dest)
1480 {
1481         layout_image_maint_renamed(lw, source, dest);
1482 }
1483