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