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