6ef063bc9c78f160732062e67a0421c310c82d4d
[geeqie.git] / src / img-view.c
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "img-view.h"
24
25 #include "collect.h"
26 #include "collect-io.h"
27 #include "dnd.h"
28 #include "editors.h"
29 #include "filedata.h"
30 #include "fullscreen.h"
31 #include "image.h"
32 #include "image-load.h"
33 #include "image-overlay.h"
34 #include "layout.h"
35 #include "layout_image.h"
36 #include "layout_util.h"
37 #include "menu.h"
38 #include "misc.h"
39 #include "pixbuf_util.h"
40 #include "pixbuf-renderer.h"
41 #include "print.h"
42 #include "slideshow.h"
43 #include "ui_fileops.h"
44 #include "ui_menu.h"
45 #include "uri_utils.h"
46 #include "utilops.h"
47 #include "window.h"
48
49 #include <gdk/gdkkeysyms.h> /* for keyboard values */
50
51
52 typedef struct _ViewWindow ViewWindow;
53 struct _ViewWindow
54 {
55         GtkWidget *window;
56         ImageWindow *imd;
57         FullScreenData *fs;
58         SlideShowData *ss;
59
60         GList *list;
61         GList *list_pointer;
62 };
63
64
65 static GList *view_window_list = NULL;
66
67
68 static GtkWidget *view_popup_menu(ViewWindow *vw);
69 static void view_fullscreen_toggle(ViewWindow *vw, gboolean force_off);
70 static void view_overlay_toggle(ViewWindow *vw);
71
72 static void view_slideshow_next(ViewWindow *vw);
73 static void view_slideshow_prev(ViewWindow *vw);
74 static void view_slideshow_start(ViewWindow *vw);
75 static void view_slideshow_stop(ViewWindow *vw);
76
77 static void view_window_close(ViewWindow *vw);
78
79 static void view_window_dnd_init(ViewWindow *vw);
80
81 static void view_window_notify_cb(FileData *fd, NotifyType type, gpointer data);
82
83
84 /**
85  * This array must be kept in sync with the contents of:\n
86  *  @link view_popup_menu() @endlink \n
87  *  @link view_window_key_press_cb() @endlink
88  * 
89  * See also @link hard_coded_window_keys @endlink
90  **/
91 hard_coded_window_keys image_window_keys[] = {
92         {GDK_CONTROL_MASK, 'C', N_("Copy")},
93         {GDK_CONTROL_MASK, 'M', N_("Move")},
94         {GDK_CONTROL_MASK, 'R', N_("Rename")},
95         {GDK_CONTROL_MASK, 'D', N_("Move to Trash")},
96         {0, GDK_KEY_Delete, N_("Move to Trash")},
97         {GDK_SHIFT_MASK, GDK_KEY_Delete, N_("Delete")},
98         {GDK_CONTROL_MASK, 'W', N_("Close window")},
99         {GDK_SHIFT_MASK, 'R', N_("Rotate 180°")},
100         {GDK_SHIFT_MASK, 'M', N_("Rotate mirror")},
101         {GDK_SHIFT_MASK, 'F', N_("Rotate flip")},
102         {0, ']', N_(" Rotate counterclockwise 90°")},
103         {0, '[', N_(" Rotate clockwise 90°")},
104         {0, GDK_KEY_Page_Up, N_("Previous")},
105         {0, GDK_KEY_KP_Page_Up, N_("Previous")},
106         {0, GDK_KEY_BackSpace, N_("Previous")},
107         {0, 'B', N_("Previous")},
108         {0, GDK_KEY_Page_Down, N_("Next")},
109         {0, GDK_KEY_KP_Page_Down, N_("Next")},
110         {0, GDK_KEY_space, N_("Next")},
111         {0, 'N', N_("Next")},
112         {0, GDK_KEY_equal, N_("Zoom in")},
113         {0, GDK_KEY_plus, N_("Zoom in")},
114         {0, GDK_KEY_minus, N_("Zoom out")},
115         {0, 'X', N_("Zoom to fit")},
116         {0, GDK_KEY_KP_Multiply, N_("Zoom to fit")},
117         {0, 'Z', N_("Zoom 1:1")},
118         {0, GDK_KEY_KP_Divide, N_("Zoom 1:1")},
119         {0, GDK_KEY_1, N_("Zoom 1:1")},
120         {0, '2', N_("Zoom 2:1")},
121         {0, '3', N_("Zoom 3:1")},
122         {0, '4', N_("Zoom 4:1")},
123         {0, '7', N_("Zoom 1:4")},
124         {0, '8', N_("Zoom 1:3")},
125         {0, '9', N_("Zoom 1:2")},
126         {0, 'W', N_("Zoom fit window width")},
127         {0, 'H', N_("Zoom fit window height")},
128         {0, 'S', N_("Toggle slideshow")},
129         {0, 'P', N_("Pause slideshow")},
130         {0, 'R', N_("Reload image")},
131         {0, 'F', N_("Full screen")},
132         {0, 'V', N_("Fullscreen")},
133         {0, GDK_KEY_F11, N_("Fullscreen")},
134         {0, 'I', N_("Image overlay")},
135         {0, GDK_KEY_Escape, N_("Exit fullscreen")},
136         {0, GDK_KEY_Escape, N_("Close window")},
137         {GDK_SHIFT_MASK, 'G', N_("Desaturate")},
138         {GDK_SHIFT_MASK, 'P', N_("Print")},
139         {0, 0, NULL}
140 };
141
142
143 /*
144  *-----------------------------------------------------------------------------
145  * misc
146  *-----------------------------------------------------------------------------
147  */
148
149 static ImageWindow *view_window_active_image(ViewWindow *vw)
150 {
151         if (vw->fs) return vw->fs->imd;
152
153         return vw->imd;
154 }
155
156 static void view_window_set_list(ViewWindow *vw, GList *list)
157 {
158
159         filelist_free(vw->list);
160         vw->list = NULL;
161         vw->list_pointer = NULL;
162
163         vw->list = filelist_copy(list);
164 }
165
166 static gboolean view_window_contains_collection(ViewWindow *vw)
167 {
168         CollectionData *cd;
169         CollectInfo *info;
170
171         cd = image_get_collection(view_window_active_image(vw), &info);
172
173         return (cd && info);
174 }
175
176 static void view_collection_step(ViewWindow *vw, gboolean next)
177 {
178         ImageWindow *imd = view_window_active_image(vw);
179         CollectionData *cd;
180         CollectInfo *info;
181         CollectInfo *read_ahead_info = NULL;
182
183         cd = image_get_collection(imd, &info);
184
185         if (!cd || !info) return;
186
187         if (next)
188                 {
189                 info = collection_next_by_info(cd, info);
190                 if (options->image.enable_read_ahead)
191                         {
192                         read_ahead_info = collection_next_by_info(cd, info);
193                         if (!read_ahead_info) read_ahead_info = collection_prev_by_info(cd, info);
194                         }
195                 }
196         else
197                 {
198                 info = collection_prev_by_info(cd, info);
199                 if (options->image.enable_read_ahead)
200                         {
201                         read_ahead_info = collection_prev_by_info(cd, info);
202                         if (!read_ahead_info) read_ahead_info = collection_next_by_info(cd, info);
203                         }
204                 }
205
206         if (info)
207                 {
208                 image_change_from_collection(imd, cd, info, image_zoom_get_default(imd));
209
210                 if (read_ahead_info) image_prebuffer_set(imd, read_ahead_info->fd);
211                 }
212
213 }
214
215 static void view_collection_step_to_end(ViewWindow *vw, gboolean last)
216 {
217         ImageWindow *imd = view_window_active_image(vw);
218         CollectionData *cd;
219         CollectInfo *info;
220         CollectInfo *read_ahead_info = NULL;
221
222         cd = image_get_collection(imd, &info);
223
224         if (!cd || !info) return;
225
226         if (last)
227                 {
228                 info = collection_get_last(cd);
229                 if (options->image.enable_read_ahead) read_ahead_info = collection_prev_by_info(cd, info);
230                 }
231         else
232                 {
233                 info = collection_get_first(cd);
234                 if (options->image.enable_read_ahead) read_ahead_info = collection_next_by_info(cd, info);
235                 }
236
237         if (info)
238                 {
239                 image_change_from_collection(imd, cd, info, image_zoom_get_default(imd));
240                 if (read_ahead_info) image_prebuffer_set(imd, read_ahead_info->fd);
241                 }
242 }
243
244 static void view_list_step(ViewWindow *vw, gboolean next)
245 {
246         ImageWindow *imd = view_window_active_image(vw);
247         FileData *fd;
248         GList *work;
249         GList *work_ahead;
250
251         if (!vw->list) return;
252
253         fd = image_get_fd(imd);
254         if (!fd) return;
255
256         if (g_list_position(vw->list, vw->list_pointer) >= 0)
257                 {
258                 work = vw->list_pointer;
259                 }
260         else
261                 {
262                 gboolean found = FALSE;
263
264                 work = vw->list;
265                 while (work && !found)
266                         {
267                         FileData *temp;
268
269                         temp = work->data;
270
271                         if (fd == temp)
272                                 {
273                                 found = TRUE;
274                                 }
275                         else
276                                 {
277                                 work = work->next;
278                                 }
279                         }
280                 }
281         if (!work) return;
282
283         work_ahead = NULL;
284         if (next)
285                 {
286                 work = work->next;
287                 if (work) work_ahead = work->next;
288                 }
289         else
290                 {
291                 work = work->prev;
292                 if (work) work_ahead = work->prev;
293                 }
294
295         if (!work) return;
296
297         vw->list_pointer = work;
298         fd = work->data;
299         image_change_fd(imd, fd, image_zoom_get_default(imd));
300
301         if (options->image.enable_read_ahead && work_ahead)
302                 {
303                 FileData *next_fd = work_ahead->data;
304                 image_prebuffer_set(imd, next_fd);
305                 }
306 }
307
308 static void view_list_step_to_end(ViewWindow *vw, gboolean last)
309 {
310         ImageWindow *imd = view_window_active_image(vw);
311         FileData *fd;
312         GList *work;
313         GList *work_ahead;
314
315         if (!vw->list) return;
316
317         if (last)
318                 {
319                 work = g_list_last(vw->list);
320                 work_ahead = work->prev;
321                 }
322         else
323                 {
324                 work = vw->list;
325                 work_ahead = work->next;
326                 }
327
328         vw->list_pointer = work;
329         fd = work->data;
330         image_change_fd(imd, fd, image_zoom_get_default(imd));
331
332         if (options->image.enable_read_ahead && work_ahead)
333                 {
334                 FileData *next_fd = work_ahead->data;
335                 image_prebuffer_set(imd, next_fd);
336                 }
337 }
338
339 static void view_step_next(ViewWindow *vw)
340 {
341         if (vw->ss)
342                 {
343                 view_slideshow_next(vw);
344                 }
345         else if (vw->list)
346                 {
347                 view_list_step(vw, TRUE);
348                 }
349         else
350                 {
351                 view_collection_step(vw, TRUE);
352                 }
353 }
354
355 static void view_step_prev(ViewWindow *vw)
356 {
357         if (vw->ss)
358                 {
359                 view_slideshow_prev(vw);
360                 }
361         else if (vw->list)
362                 {
363                 view_list_step(vw, FALSE);
364                 }
365         else
366                 {
367                 view_collection_step(vw, FALSE);
368                 }
369 }
370
371 static void view_step_to_end(ViewWindow *vw, gboolean last)
372 {
373         if (vw->list)
374                 {
375                 view_list_step_to_end(vw, last);
376                 }
377         else
378                 {
379                 view_collection_step_to_end(vw, last);
380                 }
381 }
382
383 /*
384  *-----------------------------------------------------------------------------
385  * view window keyboard
386  *-----------------------------------------------------------------------------
387  */
388
389 static gboolean view_window_key_press_cb(GtkWidget * (widget), GdkEventKey *event, gpointer data)
390 {
391         ViewWindow *vw = data;
392         ImageWindow *imd;
393         gint stop_signal;
394         GtkWidget *menu;
395         gint x = 0;
396         gint y = 0;
397
398         imd = view_window_active_image(vw);
399
400         stop_signal = TRUE;
401         switch (event->keyval)
402                 {
403                 case GDK_KEY_Left: case GDK_KEY_KP_Left:
404                         x -= 1;
405                         break;
406                 case GDK_KEY_Right: case GDK_KEY_KP_Right:
407                         x += 1;
408                         break;
409                 case GDK_KEY_Up: case GDK_KEY_KP_Up:
410                         y -= 1;
411                         break;
412                 case GDK_KEY_Down: case GDK_KEY_KP_Down:
413                         y += 1;
414                         break;
415                 default:
416                         stop_signal = FALSE;
417                         break;
418                 }
419
420         if (x != 0 || y!= 0)
421                 {
422                 if (event->state & GDK_SHIFT_MASK)
423                         {
424                         x *= 3;
425                         y *= 3;
426                         }
427
428                 keyboard_scroll_calc(&x, &y, event);
429                 image_scroll(imd, x, y);
430                 }
431
432         if (stop_signal) return stop_signal;
433
434         if (event->state & GDK_CONTROL_MASK)
435                 {
436                 stop_signal = TRUE;
437                 switch (event->keyval)
438                         {
439                         case '1':
440                         case '2':
441                         case '3':
442                         case '4':
443                         case '5':
444                         case '6':
445                         case '7':
446                         case '8':
447                         case '9':
448                         case '0':
449                                 break;
450                         case 'C': case 'c':
451                                 file_util_copy(image_get_fd(imd), NULL, NULL, imd->widget);
452                                 break;
453                         case 'M': case 'm':
454                                 file_util_move(image_get_fd(imd), NULL, NULL, imd->widget);
455                                 break;
456                         case 'R': case 'r':
457                                 file_util_rename(image_get_fd(imd), NULL, imd->widget);
458                                 break;
459                         case 'D': case 'd':
460                                 options->file_ops.safe_delete_enable = TRUE;
461                                 file_util_delete(image_get_fd(imd), NULL, imd->widget);
462                                 break;
463                         case 'W': case 'w':
464                                 view_window_close(vw);
465                                 break;
466                         default:
467                                 stop_signal = FALSE;
468                                 break;
469                         }
470                 }
471         else if (event->state & GDK_SHIFT_MASK)
472                 {
473                 stop_signal = TRUE;
474                 switch (event->keyval)
475                         {
476                         case 'R': case 'r':
477                                 image_alter_orientation(imd, imd->image_fd, ALTER_ROTATE_180);
478                                 break;
479                         case 'M': case 'm':
480                                 image_alter_orientation(imd, imd->image_fd, ALTER_MIRROR);
481                                 break;
482                         case 'F': case 'f':
483                                 image_alter_orientation(imd, imd->image_fd, ALTER_FLIP);
484                                 break;
485                         case 'G': case 'g':
486                                 image_set_desaturate(imd, !image_get_desaturate(imd));
487                                 break;
488                         case 'P': case 'p':
489                                 {
490                                 FileData *fd;
491
492                                 view_fullscreen_toggle(vw, TRUE);
493                                 imd = view_window_active_image(vw);
494                                 fd = image_get_fd(imd);
495                                 print_window_new(fd,
496                                                  fd ? g_list_append(NULL, file_data_ref(fd)) : NULL,
497                                                  filelist_copy(vw->list), vw->window);
498                                 }
499                                 break;
500                         case GDK_KEY_Delete: case GDK_KEY_KP_Delete:
501                                 if (options->file_ops.enable_delete_key)
502                                         {
503                                         options->file_ops.safe_delete_enable = FALSE;
504                                         file_util_delete(image_get_fd(imd), NULL, imd->widget);
505                                         }
506                                 break;
507                         default:
508                                 stop_signal = FALSE;
509                                 break;
510                         }
511                 }
512         else
513                 {
514                 stop_signal = TRUE;
515                 switch (event->keyval)
516                         {
517                         case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
518                         case GDK_KEY_BackSpace:
519                         case 'B': case 'b':
520                                 view_step_prev(vw);
521                                 break;
522                         case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
523                         case GDK_KEY_space:
524                         case 'N': case 'n':
525                                 view_step_next(vw);
526                                 break;
527                         case GDK_KEY_Home: case GDK_KEY_KP_Home:
528                                 view_step_to_end(vw, FALSE);
529                                 break;
530                         case GDK_KEY_End: case GDK_KEY_KP_End:
531                                 view_step_to_end(vw, TRUE);
532                                 break;
533                         case '+': case '=': case GDK_KEY_KP_Add:
534                                 image_zoom_adjust(imd, get_zoom_increment());
535                                 break;
536                         case '-': case GDK_KEY_KP_Subtract:
537                                 image_zoom_adjust(imd, -get_zoom_increment());
538                                 break;
539                         case 'X': case 'x': case GDK_KEY_KP_Multiply:
540                                 image_zoom_set(imd, 0.0);
541                                 break;
542                         case 'Z': case 'z': case GDK_KEY_KP_Divide: case '1':
543                                 image_zoom_set(imd, 1.0);
544                                 break;
545                         case '2':
546                                 image_zoom_set(imd, 2.0);
547                                 break;
548                         case '3':
549                                 image_zoom_set(imd, 3.0);
550                                 break;
551                         case '4':
552                                 image_zoom_set(imd, 4.0);
553                                 break;
554                         case '7':
555                                 image_zoom_set(imd, -4.0);
556                                 break;
557                         case '8':
558                                 image_zoom_set(imd, -3.0);
559                                 break;
560                         case '9':
561                                 image_zoom_set(imd, -2.0);
562                                 break;
563                         case 'W': case 'w':
564                                 image_zoom_set_fill_geometry(imd, FALSE);
565                                 break;
566                         case 'H': case 'h':
567                                 image_zoom_set_fill_geometry(imd, TRUE);
568                                 break;
569                         case 'R': case 'r':
570                                 image_reload(imd);
571                                 break;
572                         case 'S': case 's':
573                                 if (vw->ss)
574                                         {
575                                         view_slideshow_stop(vw);
576                                         }
577                                 else
578                                         {
579                                         view_slideshow_start(vw);
580                                         }
581                                 break;
582                         case 'P': case 'p':
583                                 slideshow_pause_toggle(vw->ss);
584                                 break;
585                         case 'F': case 'f':
586                         case 'V': case 'v':
587                         case GDK_KEY_F11:
588                                 view_fullscreen_toggle(vw, FALSE);
589                                 break;
590                         case 'I': case 'i':
591                                 view_overlay_toggle(vw);
592                                 break;
593                         case ']':
594                                 image_alter_orientation(imd, imd->image_fd, ALTER_ROTATE_90);
595                                 break;
596                         case '[':
597                                 image_alter_orientation(imd, imd->image_fd, ALTER_ROTATE_90_CC);
598                                 break;
599                         case GDK_KEY_Delete: case GDK_KEY_KP_Delete:
600                                 if (options->file_ops.enable_delete_key)
601                                         {
602                                         options->file_ops.safe_delete_enable = TRUE;
603                                         file_util_delete(image_get_fd(imd), NULL, imd->widget);
604                                         }
605                                 break;
606                         case GDK_KEY_Escape:
607                                 if (vw->fs)
608                                         {
609                                         view_fullscreen_toggle(vw, TRUE);
610                                         }
611                                 else
612                                         {
613                                         view_window_close(vw);
614                                         }
615                                 break;
616                         case GDK_KEY_Menu:
617                         case GDK_KEY_F10:
618                                 menu = view_popup_menu(vw);
619                                 gtk_menu_popup_at_widget(GTK_MENU(menu), widget, GDK_GRAVITY_CENTER, GDK_GRAVITY_CENTER, NULL);
620                                 break;
621                         default:
622                                 stop_signal = FALSE;
623                                 break;
624                         }
625                 }
626         if (!stop_signal && is_help_key(event))
627                 {
628                 help_window_show("GuideOtherWindowsImageWindow.html");
629                 stop_signal = TRUE;
630                 }
631
632         return stop_signal;
633 }
634
635 /*
636  *-----------------------------------------------------------------------------
637  * view window main routines
638  *-----------------------------------------------------------------------------
639  */
640 static void button_cb(ImageWindow *imd, GdkEventButton *event, gpointer data)
641 {
642         ViewWindow *vw = data;
643         GtkWidget *menu;
644         gchar *dest_dir;
645         LayoutWindow *lw_new;
646
647         switch (event->button)
648                 {
649                 case MOUSE_BUTTON_LEFT:
650                         if (options->image_l_click_archive && imd->image_fd->format_class == FORMAT_CLASS_ARCHIVE)
651                                 {
652                                 dest_dir = open_archive(imd->image_fd);
653                                 if (dest_dir)
654                                         {
655                                         lw_new = layout_new_from_default();
656                                         layout_set_path(lw_new, dest_dir);
657                                         g_free(dest_dir);
658                                         }
659                                 else
660                                         {
661                                         warning_dialog(_("Cannot open archive file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, NULL);
662                                         }
663                                 }
664                         else if (options->image_l_click_video && options->image_l_click_video_editor && imd->image_fd->format_class == FORMAT_CLASS_VIDEO)
665                                 {
666                                 start_editor_from_file(options->image_l_click_video_editor, imd->image_fd);
667                                 }
668                         else if (options->image_lm_click_nav)
669                                 view_step_next(vw);
670                         break;
671                 case MOUSE_BUTTON_MIDDLE:
672                         if (options->image_lm_click_nav)
673                                 view_step_prev(vw);
674                         break;
675                 case MOUSE_BUTTON_RIGHT:
676                         menu = view_popup_menu(vw);
677                         gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
678                         break;
679                 default:
680                         break;
681                 }
682 }
683
684 static void scroll_cb(ImageWindow *imd, GdkEventScroll *event, gpointer data)
685 {
686         ViewWindow *vw = data;
687
688         if ((event->state & GDK_CONTROL_MASK) ||
689                                 (imd->mouse_wheel_mode && !options->image_lm_click_nav))
690                 {
691                 switch (event->direction)
692                         {
693                         case GDK_SCROLL_UP:
694                                 image_zoom_adjust_at_point(imd, get_zoom_increment(), event->x, event->y);
695                                 break;
696                         case GDK_SCROLL_DOWN:
697                                 image_zoom_adjust_at_point(imd, -get_zoom_increment(), event->x, event->y);
698                                 break;
699                         default:
700                                 break;
701                         }
702                 }
703         else if ( (event->state & GDK_SHIFT_MASK) != (guint) (options->mousewheel_scrolls))
704                 {
705                 switch (event->direction)
706                         {
707                         case GDK_SCROLL_UP:
708                                 image_scroll(imd, 0, -MOUSEWHEEL_SCROLL_SIZE);
709                                 break;
710                         case GDK_SCROLL_DOWN:
711                                 image_scroll(imd, 0, MOUSEWHEEL_SCROLL_SIZE);
712                                 break;
713                         case GDK_SCROLL_LEFT:
714                                 image_scroll(imd, -MOUSEWHEEL_SCROLL_SIZE, 0);
715                                 break;
716                         case GDK_SCROLL_RIGHT:
717                                 image_scroll(imd, MOUSEWHEEL_SCROLL_SIZE, 0);
718                                 break;
719                         default:
720                                 break;
721                         }
722                 }
723         else
724                 {
725                 switch (event->direction)
726                         {
727                         case GDK_SCROLL_UP:
728                                 view_step_prev(vw);
729                                 break;
730                         case GDK_SCROLL_DOWN:
731                                 view_step_next(vw);
732                                 break;
733                         default:
734                                 break;
735                         }
736                 }
737 }
738
739 static void view_image_set_buttons(ViewWindow *vw, ImageWindow *imd)
740 {
741         image_set_button_func(imd, button_cb, vw);
742         image_set_scroll_func(imd, scroll_cb, vw);
743 }
744
745 static void view_fullscreen_stop_func(FullScreenData *UNUSED(fs), gpointer data)
746 {
747         ViewWindow *vw = data;
748
749         vw->fs = NULL;
750
751         if (vw->ss) vw->ss->imd = vw->imd;
752 }
753
754 static void view_fullscreen_toggle(ViewWindow *vw, gboolean force_off)
755 {
756         if (force_off && !vw->fs) return;
757
758         if (vw->fs)
759                 {
760                 if (image_osd_get(vw->imd) & OSD_SHOW_INFO)
761                         image_osd_set(vw->imd, image_osd_get(vw->fs->imd));
762
763                 fullscreen_stop(vw->fs);
764                 }
765         else
766                 {
767                 vw->fs = fullscreen_start(vw->window, vw->imd, view_fullscreen_stop_func, vw);
768
769                 view_image_set_buttons(vw, vw->fs->imd);
770                 g_signal_connect(G_OBJECT(vw->fs->window), "key_press_event",
771                                  G_CALLBACK(view_window_key_press_cb), vw);
772
773                 if (vw->ss) vw->ss->imd = vw->fs->imd;
774
775                 if (image_osd_get(vw->imd) & OSD_SHOW_INFO)
776                         {
777                         image_osd_set(vw->fs->imd, image_osd_get(vw->imd));
778                         image_osd_set(vw->imd, OSD_SHOW_NOTHING);
779                         }
780                 }
781 }
782
783 static void view_overlay_toggle(ViewWindow *vw)
784 {
785         ImageWindow *imd;
786
787         imd = view_window_active_image(vw);
788
789         image_osd_toggle(imd);
790 }
791
792 static void view_slideshow_next(ViewWindow *vw)
793 {
794         if (vw->ss) slideshow_next(vw->ss);
795 }
796
797 static void view_slideshow_prev(ViewWindow *vw)
798 {
799         if (vw->ss) slideshow_prev(vw->ss);
800 }
801
802 static void view_slideshow_stop_func(SlideShowData *UNUSED(fs), gpointer data)
803 {
804         ViewWindow *vw = data;
805         GList *work;
806         FileData *fd;
807
808         vw->ss = NULL;
809
810         work = vw->list;
811         fd = image_get_fd(view_window_active_image(vw));
812         while (work)
813                 {
814                 FileData *temp;
815
816                 temp = work->data;
817                 if (fd == temp)
818                         {
819                         vw->list_pointer = work;
820                         work = NULL;
821                         }
822                 else
823                         {
824                         work = work->next;
825                         }
826                 }
827 }
828
829 static void view_slideshow_start(ViewWindow *vw)
830 {
831         if (!vw->ss)
832                 {
833                 CollectionData *cd;
834                 CollectInfo *info;
835
836                 if (vw->list)
837                         {
838                         vw->ss = slideshow_start_from_filelist(NULL, view_window_active_image(vw),
839                                                                 filelist_copy(vw->list),
840                                                                 view_slideshow_stop_func, vw);
841                         vw->list_pointer = NULL;
842                         return;
843                         }
844
845                 cd = image_get_collection(view_window_active_image(vw), &info);
846                 if (cd && info)
847                         {
848                         vw->ss = slideshow_start_from_collection(NULL, view_window_active_image(vw), cd,
849                                                                  view_slideshow_stop_func, vw, info);
850                         }
851                 }
852 }
853
854 static void view_slideshow_stop(ViewWindow *vw)
855 {
856         if (vw->ss) slideshow_free(vw->ss);
857 }
858
859 static void view_window_destroy_cb(GtkWidget *UNUSED(widget), gpointer data)
860 {
861         ViewWindow *vw = data;
862
863         view_window_list = g_list_remove(view_window_list, vw);
864
865         view_slideshow_stop(vw);
866         fullscreen_stop(vw->fs);
867
868         filelist_free(vw->list);
869
870         file_data_unregister_notify_func(view_window_notify_cb, vw);
871
872         g_free(vw);
873 }
874
875 static void view_window_close(ViewWindow *vw)
876 {
877         view_slideshow_stop(vw);
878         view_fullscreen_toggle(vw, TRUE);
879         gtk_widget_destroy(vw->window);
880 }
881
882 static gboolean view_window_delete_cb(GtkWidget *UNUSED(w), GdkEventAny *UNUSED(event), gpointer data)
883 {
884         ViewWindow *vw = data;
885
886         view_window_close(vw);
887         return TRUE;
888 }
889
890 static ViewWindow *real_view_window_new(FileData *fd, GList *list, CollectionData *cd, CollectInfo *info)
891 {
892         ViewWindow *vw;
893         GtkAllocation req_size;
894         GdkGeometry geometry;
895         gint w, h;
896
897         if (!fd && !list && (!cd || !info)) return NULL;
898
899         vw = g_new0(ViewWindow, 1);
900
901         vw->window = window_new(GTK_WINDOW_TOPLEVEL, "view", PIXBUF_INLINE_ICON_VIEW, NULL, NULL);
902         DEBUG_NAME(vw->window);
903
904         geometry.min_width = DEFAULT_MINIMAL_WINDOW_SIZE;
905         geometry.min_height = DEFAULT_MINIMAL_WINDOW_SIZE;
906         gtk_window_set_geometry_hints(GTK_WINDOW(vw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
907
908         gtk_window_set_resizable(GTK_WINDOW(vw->window), TRUE);
909         gtk_container_set_border_width(GTK_CONTAINER(vw->window), 0);
910
911         vw->imd = image_new(FALSE);
912         image_color_profile_set(vw->imd,
913                                 options->color_profile.input_type,
914                                 options->color_profile.use_image);
915         image_color_profile_set_use(vw->imd, options->color_profile.enabled);
916
917         image_background_set_color_from_options(vw->imd, FALSE);
918
919         image_attach_window(vw->imd, vw->window, NULL, GQ_APPNAME, TRUE);
920
921         image_auto_refresh_enable(vw->imd, TRUE);
922         image_top_window_set_sync(vw->imd, TRUE);
923
924         gtk_container_add(GTK_CONTAINER(vw->window), vw->imd->widget);
925         gtk_widget_show(vw->imd->widget);
926
927         view_window_dnd_init(vw);
928
929         view_image_set_buttons(vw, vw->imd);
930
931         g_signal_connect(G_OBJECT(vw->window), "destroy",
932                          G_CALLBACK(view_window_destroy_cb), vw);
933         g_signal_connect(G_OBJECT(vw->window), "delete_event",
934                          G_CALLBACK(view_window_delete_cb), vw);
935         g_signal_connect(G_OBJECT(vw->window), "key_press_event",
936                          G_CALLBACK(view_window_key_press_cb), vw);
937         if (cd && info)
938                 {
939                 image_change_from_collection(vw->imd, cd, info, image_zoom_get_default(NULL));
940                 /* Grab the fd so we can correctly size the window in
941                    the call to image_load_dimensions() below. */
942                 fd = info->fd;
943                 if (options->image.enable_read_ahead)
944                         {
945                         CollectInfo * r_info = collection_next_by_info(cd, info);
946                         if (!r_info) r_info = collection_prev_by_info(cd, info);
947                         if (r_info) image_prebuffer_set(vw->imd, r_info->fd);
948                         }
949                 }
950         else if (list)
951                 {
952                 view_window_set_list(vw, list);
953                 vw->list_pointer = vw->list;
954                 image_change_fd(vw->imd, (FileData *)vw->list->data, image_zoom_get_default(NULL));
955                 /* Set fd to first in list */
956                 fd = vw->list->data;
957
958                 if (options->image.enable_read_ahead)
959                         {
960                         GList *work = vw->list->next;
961                         if (work) image_prebuffer_set(vw->imd, (FileData *)work->data);
962                         }
963                 }
964         else
965                 {
966                 image_change_fd(vw->imd, fd, image_zoom_get_default(NULL));
967                 }
968
969         /* Wait until image is loaded otherwise size is not defined */
970         image_load_dimensions(fd, &w, &h);
971
972         if (options->image.limit_window_size)
973                 {
974                 gint mw = gdk_screen_width() * options->image.max_window_size / 100;
975                 gint mh = gdk_screen_height() * options->image.max_window_size / 100;
976
977                 if (w > mw) w = mw;
978                 if (h > mh) h = mh;
979                 }
980
981         gtk_window_set_default_size(GTK_WINDOW(vw->window), w, h);
982         req_size.x = req_size.y = 0;
983         req_size.width = w;
984         req_size.height = h;
985         gtk_widget_size_allocate(GTK_WIDGET(vw->window), &req_size);
986
987         gtk_window_set_focus_on_map(GTK_WINDOW(vw->window), FALSE);
988         gtk_widget_show(vw->window);
989
990         view_window_list = g_list_append(view_window_list, vw);
991
992         file_data_register_notify_func(view_window_notify_cb, vw, NOTIFY_PRIORITY_LOW);
993
994         /** @FIXME This is a hack to fix #965 View in new window - blank image
995          * The problem occurs when zoom is set to Original Size and Preload
996          * Next Image is set.
997          * An extra reload is required to force the image to be displayed.
998          * This is probably not the correct solution.
999          **/
1000         image_reload(vw->imd);
1001
1002         return vw;
1003 }
1004
1005 static void view_window_collection_unref_cb(GtkWidget *UNUSED(widget), gpointer data)
1006 {
1007         CollectionData *cd = data;
1008
1009         collection_unref(cd);
1010 }
1011
1012 void view_window_new(FileData *fd)
1013 {
1014         GList *list;
1015
1016         if (fd)
1017                 {
1018                 if (file_extension_match(fd->path, GQ_COLLECTION_EXT))
1019                         {
1020                         ViewWindow *vw;
1021                         CollectionData *cd;
1022                         CollectInfo *info;
1023
1024                         cd = collection_new(fd->path);
1025                         if (collection_load(cd, fd->path, COLLECTION_LOAD_NONE))
1026                                 {
1027                                 info = collection_get_first(cd);
1028                                 }
1029                         else
1030                                 {
1031                                 collection_unref(cd);
1032                                 cd = NULL;
1033                                 info = NULL;
1034                                 }
1035                         vw = real_view_window_new(NULL, NULL, cd, info);
1036                         if (vw && cd)
1037                                 {
1038                                 g_signal_connect(G_OBJECT(vw->window), "destroy",
1039                                                  G_CALLBACK(view_window_collection_unref_cb), cd);
1040                                 }
1041                         }
1042                 else if (isdir(fd->path) && filelist_read(fd, &list, NULL))
1043                         {
1044                         list = filelist_sort_path(list);
1045                         list = filelist_filter(list, FALSE);
1046                         real_view_window_new(NULL, list, NULL, NULL);
1047                         filelist_free(list);
1048                         }
1049                 else
1050                         {
1051                         real_view_window_new(fd, NULL, NULL, NULL);
1052                         }
1053                 }
1054 }
1055
1056 void view_window_new_from_list(GList *list)
1057 {
1058         real_view_window_new(NULL, list, NULL, NULL);
1059 }
1060
1061 void view_window_new_from_collection(CollectionData *cd, CollectInfo *info)
1062 {
1063         real_view_window_new(NULL, NULL, cd, info);
1064 }
1065
1066 /*
1067  *-----------------------------------------------------------------------------
1068  * public
1069  *-----------------------------------------------------------------------------
1070  */
1071
1072 void view_window_colors_update(void)
1073 {
1074         GList *work;
1075
1076         work = view_window_list;
1077         while (work)
1078                 {
1079                 ViewWindow *vw = work->data;
1080                 work = work->next;
1081
1082                 image_background_set_color_from_options(vw->imd, !!vw->fs);
1083                 }
1084 }
1085
1086 gboolean view_window_find_image(ImageWindow *imd, gint *index, gint *total)
1087 {
1088         GList *work;
1089
1090         work = view_window_list;
1091         while (work)
1092                 {
1093                 ViewWindow *vw = work->data;
1094                 work = work->next;
1095
1096                 if (vw->imd == imd ||
1097                     (vw->fs && vw->fs->imd == imd))
1098                         {
1099                         if (vw->ss)
1100                                 {
1101                                 gint n;
1102                                 gint t;
1103
1104                                 n = g_list_length(vw->ss->list_done);
1105                                 t = n + g_list_length(vw->ss->list);
1106                                 if (n == 0) n = t;
1107                                 if (index) *index = n - 1;
1108                                 if (total) *total = t;
1109                                 }
1110                         else
1111                                 {
1112                                 if (index) *index = g_list_position(vw->list, vw->list_pointer);
1113                                 if (total) *total = g_list_length(vw->list);
1114                                 }
1115                         return TRUE;
1116                         }
1117                 }
1118
1119         return FALSE;
1120 }
1121
1122 /*
1123  *-----------------------------------------------------------------------------
1124  * view window menu routines and callbacks
1125  *-----------------------------------------------------------------------------
1126  */
1127
1128 static void view_new_window_cb(GtkWidget *UNUSED(widget), gpointer data)
1129 {
1130         ViewWindow *vw = data;
1131         CollectionData *cd;
1132         CollectInfo *info;
1133
1134         cd = image_get_collection(vw->imd, &info);
1135
1136         if (cd && info)
1137                 {
1138                 view_window_new_from_collection(cd, info);
1139                 }
1140         else
1141                 {
1142                 view_window_new(image_get_fd(vw->imd));
1143                 }
1144 }
1145
1146 static void view_edit_cb(GtkWidget *widget, gpointer data)
1147 {
1148         ViewWindow *vw;
1149         ImageWindow *imd;
1150         const gchar *key = data;
1151
1152         vw = submenu_item_get_data(widget);
1153         if (!vw) return;
1154
1155         if (!editor_window_flag_set(key))
1156                 {
1157                 view_fullscreen_toggle(vw, TRUE);
1158                 }
1159
1160         imd = view_window_active_image(vw);
1161         file_util_start_editor_from_file(key, image_get_fd(imd), imd->widget);
1162 }
1163
1164 static void view_alter_cb(GtkWidget *widget, gpointer data)
1165 {
1166         ViewWindow *vw;
1167         AlterType type;
1168
1169         vw = submenu_item_get_data(widget);
1170         type = GPOINTER_TO_INT(data);
1171
1172         if (!vw) return;
1173         image_alter_orientation(vw->imd, vw->imd->image_fd, type);
1174 }
1175
1176 static void view_zoom_in_cb(GtkWidget *UNUSED(widget), gpointer data)
1177 {
1178         ViewWindow *vw = data;
1179
1180         image_zoom_adjust(view_window_active_image(vw), get_zoom_increment());
1181 }
1182
1183 static void view_zoom_out_cb(GtkWidget *UNUSED(widget), gpointer data)
1184 {
1185         ViewWindow *vw = data;
1186
1187         image_zoom_adjust(view_window_active_image(vw), -get_zoom_increment());
1188 }
1189
1190 static void view_zoom_1_1_cb(GtkWidget *UNUSED(widget), gpointer data)
1191 {
1192         ViewWindow *vw = data;
1193
1194         image_zoom_set(view_window_active_image(vw), 1.0);
1195 }
1196
1197 static void view_zoom_fit_cb(GtkWidget *UNUSED(widget), gpointer data)
1198 {
1199         ViewWindow *vw = data;
1200
1201         image_zoom_set(view_window_active_image(vw), 0.0);
1202 }
1203
1204 static void view_copy_cb(GtkWidget *UNUSED(widget), gpointer data)
1205 {
1206         ViewWindow *vw = data;
1207         ImageWindow *imd;
1208
1209         imd = view_window_active_image(vw);
1210         file_util_copy(image_get_fd(imd), NULL, NULL, imd->widget);
1211 }
1212
1213 static void view_move_cb(GtkWidget *UNUSED(widget), gpointer data)
1214 {
1215         ViewWindow *vw = data;
1216         ImageWindow *imd;
1217
1218         imd = view_window_active_image(vw);
1219         file_util_move(image_get_fd(imd), NULL, NULL, imd->widget);
1220 }
1221
1222 static void view_rename_cb(GtkWidget *UNUSED(widget), gpointer data)
1223 {
1224         ViewWindow *vw = data;
1225         ImageWindow *imd;
1226
1227         imd = view_window_active_image(vw);
1228         file_util_rename(image_get_fd(imd), NULL, imd->widget);
1229 }
1230
1231 static void view_delete_cb(GtkWidget *UNUSED(widget), gpointer data)
1232 {
1233         ViewWindow *vw = data;
1234         ImageWindow *imd;
1235
1236         imd = view_window_active_image(vw);
1237         options->file_ops.safe_delete_enable = FALSE;
1238         file_util_delete(image_get_fd(imd), NULL, imd->widget);
1239 }
1240
1241 static void view_move_to_trash_cb(GtkWidget *UNUSED(widget), gpointer data)
1242 {
1243         ViewWindow *vw = data;
1244         ImageWindow *imd;
1245
1246         imd = view_window_active_image(vw);
1247         options->file_ops.safe_delete_enable = TRUE;
1248         file_util_delete(image_get_fd(imd), NULL, imd->widget);
1249 }
1250
1251 static void view_copy_path_cb(GtkWidget *UNUSED(widget), gpointer data)
1252 {
1253         ViewWindow *vw = data;
1254         ImageWindow *imd;
1255
1256         imd = view_window_active_image(vw);
1257         file_util_copy_path_to_clipboard(image_get_fd(imd), TRUE);
1258 }
1259
1260 static void view_copy_path_unquoted_cb(GtkWidget *UNUSED(widget), gpointer data)
1261 {
1262         ViewWindow *vw = data;
1263         ImageWindow *imd;
1264
1265         imd = view_window_active_image(vw);
1266         file_util_copy_path_to_clipboard(image_get_fd(imd), FALSE);
1267 }
1268
1269 static void view_fullscreen_cb(GtkWidget *UNUSED(widget), gpointer data)
1270 {
1271         ViewWindow *vw = data;
1272
1273         view_fullscreen_toggle(vw, FALSE);
1274 }
1275
1276 static void view_slideshow_start_cb(GtkWidget *UNUSED(widget), gpointer data)
1277 {
1278         ViewWindow *vw = data;
1279
1280         view_slideshow_start(vw);
1281 }
1282
1283 static void view_slideshow_stop_cb(GtkWidget *UNUSED(widget), gpointer data)
1284 {
1285         ViewWindow *vw = data;
1286
1287         view_slideshow_stop(vw);
1288 }
1289
1290 static void view_slideshow_pause_cb(GtkWidget *UNUSED(widget), gpointer data)
1291 {
1292         ViewWindow *vw = data;
1293
1294         slideshow_pause_toggle(vw->ss);
1295 }
1296
1297 static void view_close_cb(GtkWidget *UNUSED(widget), gpointer data)
1298 {
1299         ViewWindow *vw = data;
1300
1301         view_window_close(vw);
1302 }
1303
1304 static LayoutWindow *view_new_layout_with_fd(FileData *fd)
1305 {
1306         LayoutWindow *nw;
1307
1308         nw = layout_new(NULL, NULL);
1309         layout_sort_set(nw, options->file_sort.method, options->file_sort.ascending);
1310         layout_set_fd(nw, fd);
1311         return nw;
1312 }
1313
1314
1315 static void view_set_layout_path_cb(GtkWidget *UNUSED(widget), gpointer data)
1316 {
1317         ViewWindow *vw = data;
1318         LayoutWindow *lw;
1319         ImageWindow *imd;
1320
1321         imd = view_window_active_image(vw);
1322
1323         if (!imd || !imd->image_fd) return;
1324
1325         lw = layout_find_by_image_fd(imd);
1326         if (lw)
1327                 layout_set_fd(lw, imd->image_fd);
1328         else
1329                 view_new_layout_with_fd(imd->image_fd);
1330         view_window_close(vw);
1331 }
1332
1333 static void view_popup_menu_destroy_cb(GtkWidget *UNUSED(widget), gpointer data)
1334 {
1335         GList *editmenu_fd_list = data;
1336
1337         filelist_free(editmenu_fd_list);
1338 }
1339
1340 static GList *view_window_get_fd_list(ViewWindow *vw)
1341 {
1342         GList *list = NULL;
1343         ImageWindow *imd = view_window_active_image(vw);
1344
1345         if (imd)
1346                 {
1347                 FileData *fd = image_get_fd(imd);
1348                 if (fd) list = g_list_append(NULL, file_data_ref(fd));
1349                 }
1350
1351         return list;
1352 }
1353
1354 /**
1355  * @brief Add file selection list to a collection
1356  * @param[in] widget 
1357  * @param[in] data Index to the collection list menu item selected, or -1 for new collection
1358  * 
1359  * 
1360  */
1361 static void image_pop_menu_collections_cb(GtkWidget *widget, gpointer data)
1362 {
1363         ViewWindow *vw;
1364         ImageWindow *imd;
1365         FileData *fd;
1366         GList *selection_list = NULL;
1367
1368         vw = submenu_item_get_data(widget);
1369         imd = view_window_active_image(vw);
1370         fd = image_get_fd(imd);
1371         selection_list = g_list_append(selection_list, fd);
1372         pop_menu_collections(selection_list, data);
1373
1374         filelist_free(selection_list);
1375 }
1376
1377 static GtkWidget *view_popup_menu(ViewWindow *vw)
1378 {
1379         GtkWidget *menu;
1380         GtkWidget *item;
1381         GList *editmenu_fd_list;
1382         GtkAccelGroup *accel_group;
1383
1384         menu = popup_menu_short_lived();
1385
1386         accel_group = gtk_accel_group_new();
1387         gtk_menu_set_accel_group(GTK_MENU(menu), accel_group);
1388
1389         g_object_set_data(G_OBJECT(menu), "window_keys", image_window_keys);
1390         g_object_set_data(G_OBJECT(menu), "accel_group", accel_group);
1391
1392         menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN, G_CALLBACK(view_zoom_in_cb), vw);
1393         menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT, G_CALLBACK(view_zoom_out_cb), vw);
1394         menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100, G_CALLBACK(view_zoom_1_1_cb), vw);
1395         menu_item_add_stock(menu, _("Zoom to fit"), GTK_STOCK_ZOOM_FIT, G_CALLBACK(view_zoom_fit_cb), vw);
1396         menu_item_add_divider(menu);
1397
1398         editmenu_fd_list = view_window_get_fd_list(vw);
1399         g_signal_connect(G_OBJECT(menu), "destroy",
1400                          G_CALLBACK(view_popup_menu_destroy_cb), editmenu_fd_list);
1401         item = submenu_add_edit(menu, NULL, G_CALLBACK(view_edit_cb), vw, editmenu_fd_list);
1402         menu_item_add_divider(item);
1403
1404         submenu_add_alter(menu, G_CALLBACK(view_alter_cb), vw);
1405
1406         menu_item_add_stock(menu, _("View in _new window"), GTK_STOCK_NEW, G_CALLBACK(view_new_window_cb), vw);
1407         item = menu_item_add(menu, _("_Go to directory view"), G_CALLBACK(view_set_layout_path_cb), vw);
1408
1409         menu_item_add_divider(menu);
1410         menu_item_add_stock(menu, _("_Copy..."), GTK_STOCK_COPY, G_CALLBACK(view_copy_cb), vw);
1411         menu_item_add(menu, _("_Move..."), G_CALLBACK(view_move_cb), vw);
1412         menu_item_add(menu, _("_Rename..."), G_CALLBACK(view_rename_cb), vw);
1413         menu_item_add(menu, _("_Copy path"), G_CALLBACK(view_copy_path_cb), vw);
1414         menu_item_add(menu, _("_Copy path unquoted"), G_CALLBACK(view_copy_path_unquoted_cb), vw);
1415
1416         menu_item_add_divider(menu);
1417         menu_item_add_stock(menu,
1418                                 options->file_ops.confirm_move_to_trash ? _("Move to Trash...") :
1419                                         _("Move to Trash"), PIXBUF_INLINE_ICON_TRASH,
1420                                 G_CALLBACK(view_move_to_trash_cb), vw);
1421         menu_item_add_stock(menu,
1422                                 options->file_ops.confirm_delete ? _("_Delete...") :
1423                                         _("_Delete"), GTK_STOCK_DELETE,
1424                                 G_CALLBACK(view_delete_cb), vw);
1425
1426         menu_item_add_divider(menu);
1427
1428         submenu_add_collections(menu, &item,
1429                                 G_CALLBACK(image_pop_menu_collections_cb), vw);
1430         gtk_widget_set_sensitive(item, TRUE);
1431         menu_item_add_divider(menu);
1432
1433         if (vw->ss)
1434                 {
1435                 menu_item_add(menu, _("Toggle _slideshow"), G_CALLBACK(view_slideshow_stop_cb), vw);
1436                 if (slideshow_paused(vw->ss))
1437                         {
1438                         item = menu_item_add(menu, _("Continue slides_how"),
1439                                              G_CALLBACK(view_slideshow_pause_cb), vw);
1440                         }
1441                 else
1442                         {
1443                         item = menu_item_add(menu, _("Pause slides_how"),
1444                                              G_CALLBACK(view_slideshow_pause_cb), vw);
1445                         }
1446                 }
1447         else
1448                 {
1449                 item = menu_item_add(menu, _("Toggle _slideshow"), G_CALLBACK(view_slideshow_start_cb), vw);
1450                 gtk_widget_set_sensitive(item, (vw->list != NULL) || view_window_contains_collection(vw));
1451                 item = menu_item_add(menu, _("Pause slides_how"), G_CALLBACK(view_slideshow_pause_cb), vw);
1452                 gtk_widget_set_sensitive(item, FALSE);
1453                 }
1454
1455         if (vw->fs)
1456                 {
1457                 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(view_fullscreen_cb), vw);
1458                 }
1459         else
1460                 {
1461                 menu_item_add(menu, _("_Full screen"), G_CALLBACK(view_fullscreen_cb), vw);
1462                 }
1463
1464         menu_item_add_divider(menu);
1465         menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(view_close_cb), vw);
1466
1467         return menu;
1468 }
1469
1470 /*
1471  *-------------------------------------------------------------------
1472  * dnd confirm dir
1473  *-------------------------------------------------------------------
1474  */
1475
1476 typedef struct {
1477         ViewWindow *vw;
1478         GList *list;
1479 } CViewConfirmD;
1480
1481 static void view_dir_list_cancel(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
1482 {
1483         /* do nothing */
1484 }
1485
1486 static void view_dir_list_do(ViewWindow *vw, GList *list, gboolean skip, gboolean recurse)
1487 {
1488         GList *work;
1489
1490         view_window_set_list(vw, NULL);
1491
1492         work = list;
1493         while (work)
1494                 {
1495                 FileData *fd = work->data;
1496                 work = work->next;
1497
1498                 if (isdir(fd->path))
1499                         {
1500                         if (!skip)
1501                                 {
1502                                 GList *list = NULL;
1503
1504                                 if (recurse)
1505                                         {
1506                                         list = filelist_recursive(fd);
1507                                         }
1508                                 else
1509                                         { /** @FIXME ?? */
1510                                         filelist_read(fd, &list, NULL);
1511                                         list = filelist_sort_path(list);
1512                                         list = filelist_filter(list, FALSE);
1513                                         }
1514                                 if (list) vw->list = g_list_concat(vw->list, list);
1515                                 }
1516                         }
1517                 else
1518                         {
1519                         /** @FIXME no filtering here */
1520                         vw->list = g_list_append(vw->list, file_data_ref(fd));
1521                         }
1522                 }
1523
1524         if (vw->list)
1525                 {
1526                 FileData *fd;
1527
1528                 vw->list_pointer = vw->list;
1529                 fd = vw->list->data;
1530                 image_change_fd(vw->imd, fd, image_zoom_get_default(vw->imd));
1531
1532                 work = vw->list->next;
1533                 if (options->image.enable_read_ahead && work)
1534                         {
1535                         fd = work->data;
1536                         image_prebuffer_set(vw->imd, fd);
1537                         }
1538                 }
1539         else
1540                 {
1541                 image_change_fd(vw->imd, NULL, image_zoom_get_default(vw->imd));
1542                 }
1543 }
1544
1545 static void view_dir_list_add(GtkWidget *UNUSED(widget), gpointer data)
1546 {
1547         CViewConfirmD *d = data;
1548         view_dir_list_do(d->vw, d->list, FALSE, FALSE);
1549 }
1550
1551 static void view_dir_list_recurse(GtkWidget *UNUSED(widget), gpointer data)
1552 {
1553         CViewConfirmD *d = data;
1554         view_dir_list_do(d->vw, d->list, FALSE, TRUE);
1555 }
1556
1557 static void view_dir_list_skip(GtkWidget *UNUSED(widget), gpointer data)
1558 {
1559         CViewConfirmD *d = data;
1560         view_dir_list_do(d->vw, d->list, TRUE, FALSE);
1561 }
1562
1563 static void view_dir_list_destroy(GtkWidget *UNUSED(widget), gpointer data)
1564 {
1565         CViewConfirmD *d = data;
1566         filelist_free(d->list);
1567         g_free(d);
1568 }
1569
1570 static GtkWidget *view_confirm_dir_list(ViewWindow *vw, GList *list)
1571 {
1572         GtkWidget *menu;
1573         CViewConfirmD *d;
1574
1575         d = g_new(CViewConfirmD, 1);
1576         d->vw = vw;
1577         d->list = list;
1578
1579         menu = popup_menu_short_lived();
1580         g_signal_connect(G_OBJECT(menu), "destroy",
1581                          G_CALLBACK(view_dir_list_destroy), d);
1582
1583         menu_item_add_stock(menu, _("Dropped list includes folders."), GTK_STOCK_DND_MULTIPLE, NULL, NULL);
1584         menu_item_add_divider(menu);
1585         menu_item_add_stock(menu, _("_Add contents"), GTK_STOCK_OK, G_CALLBACK(view_dir_list_add), d);
1586         menu_item_add_stock(menu, _("Add contents _recursive"), GTK_STOCK_ADD, G_CALLBACK(view_dir_list_recurse), d);
1587         menu_item_add_stock(menu, _("_Skip folders"), GTK_STOCK_REMOVE, G_CALLBACK(view_dir_list_skip), d);
1588         menu_item_add_divider(menu);
1589         menu_item_add_stock(menu, _("Cancel"), GTK_STOCK_CANCEL, G_CALLBACK(view_dir_list_cancel), d);
1590
1591         return menu;
1592 }
1593
1594 /*
1595  *-----------------------------------------------------------------------------
1596  * image drag and drop routines
1597  *-----------------------------------------------------------------------------
1598  */
1599
1600 static void view_window_get_dnd_data(GtkWidget *UNUSED(widget), GdkDragContext *context,
1601                                      gint UNUSED(x), gint UNUSED(y),
1602                                      GtkSelectionData *selection_data, guint info,
1603                                      guint UNUSED(time), gpointer data)
1604 {
1605         ViewWindow *vw = data;
1606         ImageWindow *imd;
1607
1608         if (gtk_drag_get_source_widget(context) == vw->imd->pr) return;
1609
1610         imd = vw->imd;
1611
1612         if (info == TARGET_URI_LIST || info == TARGET_APP_COLLECTION_MEMBER)
1613                 {
1614                 CollectionData *source;
1615                 GList *list;
1616                 GList *info_list;
1617
1618                 if (info == TARGET_URI_LIST)
1619                         {
1620                         GList *work;
1621
1622                         list = uri_filelist_from_gtk_selection_data(selection_data);
1623
1624                         work = list;
1625                         while (work)
1626                                 {
1627                                 FileData *fd = work->data;
1628                                 if (isdir(fd->path))
1629                                         {
1630                                         GtkWidget *menu;
1631                                         menu = view_confirm_dir_list(vw, list);
1632                                         gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
1633                                         return;
1634                                         }
1635                                 work = work->next;
1636                                 }
1637
1638                         list = filelist_filter(list, FALSE);
1639
1640                         source = NULL;
1641                         info_list = NULL;
1642                         }
1643                 else
1644                         {
1645                         source = collection_from_dnd_data((gchar *)gtk_selection_data_get_data(selection_data), &list, &info_list);
1646                         }
1647
1648                 if (list)
1649                         {
1650                         FileData *fd;
1651
1652                         fd = list->data;
1653                         if (isfile(fd->path))
1654                                 {
1655                                 view_slideshow_stop(vw);
1656                                 view_window_set_list(vw, NULL);
1657
1658                                 if (source && info_list)
1659                                         {
1660                                         image_change_from_collection(imd, source, info_list->data, image_zoom_get_default(imd));
1661                                         }
1662                                 else
1663                                         {
1664                                         if (list->next)
1665                                                 {
1666                                                 vw->list = list;
1667                                                 list = NULL;
1668
1669                                                 vw->list_pointer = vw->list;
1670                                                 }
1671                                         image_change_fd(imd, fd, image_zoom_get_default(imd));
1672                                         }
1673                                 }
1674                         }
1675                 filelist_free(list);
1676                 g_list_free(info_list);
1677                 }
1678 }
1679
1680 static void view_window_set_dnd_data(GtkWidget *UNUSED(widget), GdkDragContext *UNUSED(context),
1681                                      GtkSelectionData *selection_data, guint UNUSED(info),
1682                                      guint UNUSED(time), gpointer data)
1683 {
1684         ViewWindow *vw = data;
1685         FileData *fd;
1686
1687         fd = image_get_fd(vw->imd);
1688
1689         if (fd)
1690                 {
1691                 GList *list;
1692
1693                 list = g_list_append(NULL, fd);
1694                 uri_selection_data_set_uris_from_filelist(selection_data, list);
1695                 g_list_free(list);
1696                 }
1697         else
1698                 {
1699                 gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data),
1700                                        8, NULL, 0);
1701                 }
1702 }
1703
1704 static void view_window_dnd_init(ViewWindow *vw)
1705 {
1706         ImageWindow *imd;
1707
1708         imd = vw->imd;
1709
1710         gtk_drag_source_set(imd->pr, GDK_BUTTON2_MASK,
1711                             dnd_file_drag_types, dnd_file_drag_types_count,
1712                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
1713         g_signal_connect(G_OBJECT(imd->pr), "drag_data_get",
1714                          G_CALLBACK(view_window_set_dnd_data), vw);
1715
1716         gtk_drag_dest_set(imd->pr,
1717                           GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
1718                           dnd_file_drop_types, dnd_file_drop_types_count,
1719                           GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
1720         g_signal_connect(G_OBJECT(imd->pr), "drag_data_received",
1721                          G_CALLBACK(view_window_get_dnd_data), vw);
1722 }
1723
1724 /*
1725  *-----------------------------------------------------------------------------
1726  * maintenance (for rename, move, remove)
1727  *-----------------------------------------------------------------------------
1728  */
1729
1730 static void view_real_removed(ViewWindow *vw, FileData *fd)
1731 {
1732         ImageWindow *imd;
1733         FileData *image_fd;
1734
1735         imd = view_window_active_image(vw);
1736         image_fd = image_get_fd(imd);
1737
1738         if (image_fd && image_fd == fd)
1739                 {
1740                 if (vw->list)
1741                         {
1742                         view_list_step(vw, TRUE);
1743                         if (image_get_fd(imd) == image_fd)
1744                                 {
1745                                 view_list_step(vw, FALSE);
1746                                 }
1747                         }
1748                 else if (view_window_contains_collection(vw))
1749                         {
1750                         view_collection_step(vw, TRUE);
1751                         if (image_get_fd(imd) == image_fd)
1752                                 {
1753                                 view_collection_step(vw, FALSE);
1754                                 }
1755                         }
1756                 if (image_get_fd(imd) == image_fd)
1757                         {
1758                         image_change_fd(imd, NULL, image_zoom_get_default(imd));
1759                         }
1760                 }
1761
1762         if (vw->list)
1763                 {
1764                 GList *work;
1765                 GList *old;
1766
1767                 old = vw->list_pointer;
1768
1769                 work = vw->list;
1770                 while (work)
1771                         {
1772                         FileData *chk_fd;
1773                         GList *chk_link;
1774
1775                         chk_fd = work->data;
1776                         chk_link = work;
1777                         work = work->next;
1778
1779                         if (chk_fd == fd)
1780                                 {
1781                                 if (vw->list_pointer == chk_link)
1782                                         {
1783                                         vw->list_pointer = (chk_link->next) ? chk_link->next : chk_link->prev;
1784                                         }
1785                                 vw->list = g_list_remove(vw->list, chk_fd);
1786                                 file_data_unref(chk_fd);
1787                                 }
1788                         }
1789
1790                 /* handles stepping correctly when same image is in the list more than once */
1791                 if (old && old != vw->list_pointer)
1792                         {
1793                         FileData *fd;
1794
1795                         if (vw->list_pointer)
1796                                 {
1797                                 fd = vw->list_pointer->data;
1798                                 }
1799                         else
1800                                 {
1801                                 fd = NULL;
1802                                 }
1803
1804                         image_change_fd(imd, fd, image_zoom_get_default(imd));
1805                         }
1806                 }
1807
1808         image_osd_update(imd);
1809 }
1810
1811 static void view_window_notify_cb(FileData *fd, NotifyType type, gpointer data)
1812 {
1813         ViewWindow *vw = data;
1814
1815         if (!(type & NOTIFY_CHANGE) || !fd->change) return;
1816
1817         DEBUG_1("Notify view_window: %s %04x", fd->path, type);
1818
1819         switch (fd->change->type)
1820                 {
1821                 case FILEDATA_CHANGE_MOVE:
1822                 case FILEDATA_CHANGE_RENAME:
1823                         break;
1824                 case FILEDATA_CHANGE_COPY:
1825                         break;
1826                 case FILEDATA_CHANGE_DELETE:
1827                         view_real_removed(vw, fd);
1828                         break;
1829                 case FILEDATA_CHANGE_UNSPECIFIED:
1830                 case FILEDATA_CHANGE_WRITE_METADATA:
1831                         break;
1832                 }
1833 }
1834 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */