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