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