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