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