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