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