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