439c335ff5a1c0de72595a12b3b3ef44198e40ae
[geeqie.git] / src / pan-view / pan-view.cc
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "pan-view.h"
23
24 #include "bar-exif.h"
25 #include "dnd.h"
26 #include "editors.h"
27 #include "exif.h"
28 #include "fullscreen.h"
29 #include "image.h"
30 #include "img-view.h"
31 #include "layout-util.h"
32 #include "menu.h"
33 #include "metadata.h"
34 #include "pan-calendar.h"
35 #include "pan-folder.h"
36 #include "pan-grid.h"
37 #include "pan-timeline.h"
38 #include "pan-util.h"
39 #include "pan-view-filter.h"
40 #include "pan-view-search.h"
41 #include "pixbuf-util.h"
42 #include "thumb.h"
43 #include "ui-fileops.h"
44 #include "ui-menu.h"
45 #include "ui-misc.h"
46 #include "ui-tabcomp.h"
47 #include "uri-utils.h"
48 #include "utilops.h"
49 #include "window.h"
50
51 #include <math.h>
52
53
54 #define PAN_WINDOW_DEFAULT_WIDTH 720
55 #define PAN_WINDOW_DEFAULT_HEIGHT 500
56
57 #define PAN_TILE_SIZE 512
58
59 #define ZOOM_INCREMENT 1.0
60 #define ZOOM_LABEL_WIDTH 64
61
62
63 #define PAN_PREF_GROUP          "pan_view_options"
64 #define PAN_PREF_HIDE_WARNING   "hide_performance_warning"
65 #define PAN_PREF_EXIF_PAN_DATE  "use_exif_date"
66 #define PAN_PREF_INFO_IMAGE     "info_image_size"
67 #define PAN_PREF_INFO_EXIF      "info_includes_exif"
68
69
70 static GList *pan_window_list = NULL;
71
72
73 static void pan_layout_update_idle(PanWindow *pw);
74
75 static void pan_fullscreen_toggle(PanWindow *pw, gboolean force_off);
76
77 static void pan_window_close(PanWindow *pw);
78
79 static GtkWidget *pan_popup_menu(PanWindow *pw);
80
81 static void pan_window_dnd_init(PanWindow *pw);
82
83 /**
84  * This array must be kept in sync with the contents of:\n
85  * @link pan_window_key_press_cb @endlink \n
86  * @link pan_popup_menu @endlink
87  *
88  * See also @link hard_coded_window_keys @endlink
89  **/
90 hard_coded_window_keys pan_view_window_keys[] = {
91         {GDK_CONTROL_MASK, 'C', N_("Copy")},
92         {GDK_CONTROL_MASK, 'M', N_("Move")},
93         {GDK_CONTROL_MASK, 'R', N_("Rename")},
94         {GDK_CONTROL_MASK, 'D', N_("Move to Trash")},
95         {GDK_CONTROL_MASK, 'W', N_("Close window")},
96         {GDK_CONTROL_MASK, 'F', N_("Display Find search bar")},
97         {GDK_CONTROL_MASK, 'G', N_("Start search")},
98         {static_cast<GdkModifierType>(0), GDK_KEY_Escape, N_("Exit fullscreen")},
99         {static_cast<GdkModifierType>(0), GDK_KEY_Escape, N_("Hide Find search bar")},
100         {static_cast<GdkModifierType>(0), GDK_KEY_equal, N_("Zoom in")},
101         {static_cast<GdkModifierType>(0), GDK_KEY_plus, N_("Zoom in")},
102         {static_cast<GdkModifierType>(0), GDK_KEY_minus, N_("Zoom out")},
103         {static_cast<GdkModifierType>(0), GDK_KEY_Z, N_("Zoom 1:1")},
104         {static_cast<GdkModifierType>(0), GDK_KEY_1, N_("Zoom 1:1")},
105         {static_cast<GdkModifierType>(0), GDK_KEY_KP_Divide, N_("Zoom 1:1")},
106         {static_cast<GdkModifierType>(0), '2', N_("Zoom 2:1")},
107         {static_cast<GdkModifierType>(0), '3', N_("Zoom 3:1")},
108         {static_cast<GdkModifierType>(0), '4', N_("Zoom 4:1")},
109         {static_cast<GdkModifierType>(0), '7', N_("Zoom 1:4")},
110         {static_cast<GdkModifierType>(0), '8', N_("Zoom 1:3")},
111         {static_cast<GdkModifierType>(0), '9', N_("Zoom 1:2")},
112         {static_cast<GdkModifierType>(0), 'F', N_("Full screen")},
113         {static_cast<GdkModifierType>(0), 'V', N_("Full screen")},
114         {static_cast<GdkModifierType>(0), GDK_KEY_F11, N_("Full screen")},
115         {static_cast<GdkModifierType>(0), '/', N_("Display Find search bar")},
116         {static_cast<GdkModifierType>(0), GDK_KEY_Left, N_("Scroll left")},
117         {static_cast<GdkModifierType>(0), GDK_KEY_Right, N_("Scroll right")},
118         {static_cast<GdkModifierType>(0), GDK_KEY_Up, N_("Scroll up")},
119         {static_cast<GdkModifierType>(0), GDK_KEY_Down, N_("Scroll down")},
120         {GDK_SHIFT_MASK, GDK_KEY_Left, N_("Scroll left faster")},
121         {GDK_SHIFT_MASK, GDK_KEY_Right, N_("Scroll right faster")},
122         {GDK_SHIFT_MASK, GDK_KEY_Up, N_("Scroll up faster")},
123         {GDK_SHIFT_MASK, GDK_KEY_Down, N_("Scroll down faster")},
124         {static_cast<GdkModifierType>(0), GDK_KEY_Page_Up, N_("Scroll display half screen up")},
125         {static_cast<GdkModifierType>(0), GDK_KEY_Page_Down, N_("Scroll display half screen down")},
126         {static_cast<GdkModifierType>(0), GDK_KEY_Home, N_("Scroll display half screen left")},
127         {static_cast<GdkModifierType>(0), GDK_KEY_End, N_("Scroll display half screen right")},
128         {static_cast<GdkModifierType>(0), 0, NULL}
129 };
130
131 /*
132  *-----------------------------------------------------------------------------
133  * the image/thumb loader queue
134  *-----------------------------------------------------------------------------
135  */
136
137 static gboolean pan_queue_step(PanWindow *pw);
138
139
140 static void pan_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
141 {
142         PanWindow *pw = (PanWindow *)data;
143
144         if (pw->queue_pi)
145                 {
146                 PanItem *pi;
147                 gint rc;
148
149                 pi = pw->queue_pi;
150                 pw->queue_pi = NULL;
151
152                 pi->queued = FALSE;
153
154                 if (pi->pixbuf) g_object_unref(pi->pixbuf);
155                 pi->pixbuf = thumb_loader_get_pixbuf(tl);
156
157                 rc = pi->refcount;
158                 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
159                 pi->refcount = rc;
160                 }
161
162         thumb_loader_free(pw->tl);
163         pw->tl = NULL;
164
165         while (pan_queue_step(pw));
166 }
167
168 static void pan_queue_image_done_cb(ImageLoader *il, gpointer data)
169 {
170         PanWindow *pw = (PanWindow *)data;
171         GdkPixbuf *rotated = NULL;
172
173         if (pw->queue_pi)
174                 {
175                 PanItem *pi;
176                 gint rc;
177
178                 pi = pw->queue_pi;
179                 pw->queue_pi = NULL;
180
181                 pi->queued = FALSE;
182
183                 if (pi->pixbuf) g_object_unref(pi->pixbuf);
184                 pi->pixbuf = image_loader_get_pixbuf(pw->il);
185
186                 if (pi->pixbuf && options->image.exif_rotate_enable)
187                         {
188                         if (!il->fd->exif_orientation)
189                                 {
190                                 if (g_strcmp0(il->fd->format_name, "heif") != 0)
191                                         {
192                                         il->fd->exif_orientation = metadata_read_int(il->fd, ORIENTATION_KEY, EXIF_ORIENTATION_TOP_LEFT);
193                                         }
194                                 else
195                                         {
196                                         il->fd->exif_orientation = EXIF_ORIENTATION_TOP_LEFT;
197                                         }
198                                 }
199
200                         if (il->fd->exif_orientation != EXIF_ORIENTATION_TOP_LEFT)
201                                 {
202                                 rotated = pixbuf_apply_orientation(pi->pixbuf, il->fd->exif_orientation);
203                                 pi->pixbuf = rotated;
204                                 }
205                         }
206
207                 if (pi->pixbuf) g_object_ref(pi->pixbuf);
208
209                 if (pi->pixbuf && pw->size != PAN_IMAGE_SIZE_100 &&
210                     (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
211                      gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
212                         {
213                         GdkPixbuf *tmp;
214
215                         tmp = pi->pixbuf;
216                         pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
217                                                              (GdkInterpType)options->image.zoom_quality);
218                         g_object_unref(tmp);
219                         }
220
221                 rc = pi->refcount;
222                 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
223                 pi->refcount = rc;
224                 }
225
226         image_loader_free(pw->il);
227         pw->il = NULL;
228
229         while (pan_queue_step(pw));
230 }
231
232 static gboolean pan_queue_step(PanWindow *pw)
233 {
234         PanItem *pi;
235
236         if (!pw->queue) return FALSE;
237
238         pi = static_cast<PanItem *>(pw->queue->data);
239         pw->queue = g_list_remove(pw->queue, pi);
240         pw->queue_pi = pi;
241
242         if (!pw->queue_pi->fd)
243                 {
244                 pw->queue_pi->queued = FALSE;
245                 pw->queue_pi = NULL;
246                 return TRUE;
247                 }
248
249         image_loader_free(pw->il);
250         pw->il = NULL;
251         thumb_loader_free(pw->tl);
252         pw->tl = NULL;
253
254         if (pi->type == PAN_ITEM_IMAGE)
255                 {
256                 pw->il = image_loader_new(pi->fd);
257
258                 if (pw->size != PAN_IMAGE_SIZE_100)
259                         {
260                         image_loader_set_requested_size(pw->il, pi->width, pi->height);
261                         }
262
263                 g_signal_connect(G_OBJECT(pw->il), "error", (GCallback)pan_queue_image_done_cb, pw);
264                 g_signal_connect(G_OBJECT(pw->il), "done", (GCallback)pan_queue_image_done_cb, pw);
265
266                 if (image_loader_start(pw->il)) return FALSE;
267
268                 image_loader_free(pw->il);
269                 pw->il = NULL;
270                 }
271         else if (pi->type == PAN_ITEM_THUMB)
272                 {
273                 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
274
275                 if (!pw->tl->standard_loader)
276                         {
277                         /* The classic loader will recreate a thumbnail any time we
278                          * request a different size than what exists. This view will
279                          * almost never use the user configured sizes so disable cache.
280                          */
281                         thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
282                         }
283
284                 thumb_loader_set_callbacks(pw->tl,
285                                            pan_queue_thumb_done_cb,
286                                            pan_queue_thumb_done_cb,
287                                            NULL, pw);
288
289                 if (thumb_loader_start(pw->tl, pi->fd)) return FALSE;
290
291                 thumb_loader_free(pw->tl);
292                 pw->tl = NULL;
293                 }
294
295         pw->queue_pi->queued = FALSE;
296         pw->queue_pi = NULL;
297         return TRUE;
298 }
299
300 static void pan_queue_add(PanWindow *pw, PanItem *pi)
301 {
302         if (!pi || pi->queued || pi->pixbuf) return;
303         if (pw->size <= PAN_IMAGE_SIZE_THUMB_NONE &&
304             (!pi->key || strcmp(pi->key, "info") != 0) )
305                 {
306                 return;
307                 }
308
309         pi->queued = TRUE;
310         pw->queue = g_list_prepend(pw->queue, pi);
311
312         if (!pw->tl && !pw->il) while (pan_queue_step(pw));
313 }
314
315
316 /*
317  *-----------------------------------------------------------------------------
318  * tile request/dispose handlers
319  *-----------------------------------------------------------------------------
320  */
321
322 static gboolean pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
323                                            gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
324 {
325         PanWindow *pw = (PanWindow *)data;
326         GList *list;
327         GList *work;
328         gint i;
329
330         pixbuf_set_rect_fill(pixbuf,
331                              0, 0, width, height,
332                              PAN_BACKGROUND_COLOR, 255);
333
334         for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
335                 {
336                 gint rx, ry, rw, rh;
337
338                 if (util_clip_region(x, y, width, height,
339                                      i, y, 1, height,
340                                      &rx, &ry, &rw, &rh))
341                         {
342                         pixbuf_draw_rect_fill(pixbuf,
343                                               rx - x, ry - y, rw, rh,
344                                               PAN_GRID_COLOR, PAN_GRID_ALPHA);
345                         }
346                 }
347         for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
348                 {
349                 gint rx, ry, rw, rh;
350
351                 if (util_clip_region(x, y, width, height,
352                                      x, i, width, 1,
353                                      &rx, &ry, &rw, &rh))
354                         {
355                         pixbuf_draw_rect_fill(pixbuf,
356                                               rx - x, ry - y, rw, rh,
357                                               PAN_GRID_COLOR, PAN_GRID_ALPHA);
358                         }
359                 }
360
361         list = pan_layout_intersect(pw, x, y, width, height);
362         work = list;
363         while (work)
364                 {
365                 PanItem *pi;
366                 gboolean queue = FALSE;
367
368                 pi = static_cast<PanItem *>(work->data);
369                 work = work->next;
370
371                 pi->refcount++;
372
373                 switch (pi->type)
374                         {
375                         case PAN_ITEM_BOX:
376                                 queue = pan_item_box_draw(pw, pi, pixbuf, pr, x, y, width, height);
377                                 break;
378                         case PAN_ITEM_TRIANGLE:
379                                 queue = pan_item_tri_draw(pw, pi, pixbuf, pr, x, y, width, height);
380                                 break;
381                         case PAN_ITEM_TEXT:
382                                 queue = pan_item_text_draw(pw, pi, pixbuf, pr, x, y, width, height);
383                                 break;
384                         case PAN_ITEM_THUMB:
385                                 queue = pan_item_thumb_draw(pw, pi, pixbuf, pr, x, y, width, height);
386                                 break;
387                         case PAN_ITEM_IMAGE:
388                                 queue = pan_item_image_draw(pw, pi, pixbuf, pr, x, y, width, height);
389                                 break;
390                         case PAN_ITEM_NONE:
391                         default:
392                                 break;
393                         }
394
395                 if (queue) pan_queue_add(pw, pi);
396                 }
397
398         g_list_free(list);
399
400         return TRUE;
401 }
402
403 static void pan_window_dispose_tile_cb(PixbufRenderer *UNUSED(pr), gint x, gint y,
404                                        gint width, gint height, GdkPixbuf *UNUSED(pixbuf), gpointer data)
405 {
406         PanWindow *pw = (PanWindow *)data;
407         GList *list;
408         GList *work;
409
410         list = pan_layout_intersect(pw, x, y, width, height);
411         work = list;
412         while (work)
413                 {
414                 PanItem *pi;
415
416                 pi = static_cast<PanItem *>(work->data);
417                 work = work->next;
418
419                 if (pi->refcount > 0)
420                         {
421                         pi->refcount--;
422
423                         if (pi->refcount == 0)
424                                 {
425                                 if (pi->queued)
426                                         {
427                                         pw->queue = g_list_remove(pw->queue, pi);
428                                         pi->queued = FALSE;
429                                         }
430                                 if (pw->queue_pi == pi) pw->queue_pi = NULL;
431                                 if (pi->pixbuf)
432                                         {
433                                         g_object_unref(pi->pixbuf);
434                                         pi->pixbuf = NULL;
435                                         }
436                                 }
437                         }
438                 }
439
440         g_list_free(list);
441 }
442
443
444 /*
445  *-----------------------------------------------------------------------------
446  * misc
447  *-----------------------------------------------------------------------------
448  */
449
450 static void pan_window_message(PanWindow *pw, const gchar *text)
451 {
452         GList *work;
453         gint count = 0;
454         gint64 size = 0;
455         gchar *ss;
456         gchar *buf;
457
458         if (text)
459                 {
460                 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
461                 return;
462                 }
463
464         work = pw->list_static;
465         if (pw->layout == PAN_LAYOUT_CALENDAR)
466                 {
467                 while (work)
468                         {
469                         PanItem *pi;
470
471                         pi = static_cast<PanItem *>(work->data);
472                         work = work->next;
473
474                         if (pi->fd &&
475                             pi->type == PAN_ITEM_BOX &&
476                             pi->key && strcmp(pi->key, "dot") == 0)
477                                 {
478                                 size += pi->fd->size;
479                                 count++;
480                                 }
481                         }
482                 }
483         else
484                 {
485                 while (work)
486                         {
487                         PanItem *pi;
488
489                         pi = static_cast<PanItem *>(work->data);
490                         work = work->next;
491
492                         if (pi->fd &&
493                             (pi->type == PAN_ITEM_THUMB || pi->type == PAN_ITEM_IMAGE))
494                                 {
495                                 size += pi->fd->size;
496                                 count++;
497                                 }
498                         }
499                 }
500
501         ss = text_from_size_abrev(size);
502         buf = g_strdup_printf(_("%d images, %s"), count, ss);
503         g_free(ss);
504         gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
505         g_free(buf);
506 }
507
508 static void pan_warning_folder(const gchar *path, GtkWidget *parent)
509 {
510         gchar *message;
511
512         message = g_strdup_printf(_("The pan view does not support the folder \"%s\"."), path);
513         warning_dialog(_("Folder not supported"), message,
514                       GTK_STOCK_DIALOG_INFO, parent);
515         g_free(message);
516 }
517
518 static void pan_window_zoom_limit(PanWindow *pw)
519 {
520         gdouble min;
521
522         switch (pw->size)
523                 {
524                 case PAN_IMAGE_SIZE_THUMB_DOTS:
525                 case PAN_IMAGE_SIZE_THUMB_NONE:
526                 case PAN_IMAGE_SIZE_THUMB_SMALL:
527                 case PAN_IMAGE_SIZE_THUMB_NORMAL:
528 #if 0
529                         /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
530                         min = -16.0;
531                         break;
532 #endif
533                 case PAN_IMAGE_SIZE_THUMB_LARGE:
534                         min = -6.0;
535                         break;
536                 case PAN_IMAGE_SIZE_10:
537                 case PAN_IMAGE_SIZE_25:
538                         min = -4.0;
539                         break;
540                 case PAN_IMAGE_SIZE_33:
541                 case PAN_IMAGE_SIZE_50:
542                 case PAN_IMAGE_SIZE_100:
543                 default:
544                         min = -2.0;
545                         break;
546                 }
547
548         image_zoom_set_limits(pw->imd, min, 32.0);
549 }
550
551
552 /*
553  *-----------------------------------------------------------------------------
554  * cache
555  *-----------------------------------------------------------------------------
556  */
557
558 static gint pan_cache_sort_file_cb(gpointer a, gpointer b)
559 {
560         PanCacheData *pca = static_cast<PanCacheData *>(a);
561         PanCacheData *pcb = static_cast<PanCacheData *>(b);
562         return filelist_sort_compare_filedata(pca->fd, pcb->fd);
563 }
564 GList *pan_cache_sort(GList *list, SortType method, gboolean ascend)
565 {
566         return filelist_sort_full(list, method, ascend, (GCompareFunc) pan_cache_sort_file_cb);
567 }
568
569
570 static void pan_cache_free(PanWindow *pw)
571 {
572         GList *work;
573
574         work = pw->cache_list;
575         while (work)
576                 {
577                 PanCacheData *pc;
578
579                 pc = static_cast<PanCacheData *>(work->data);
580                 work = work->next;
581
582                 cache_sim_data_free(pc->cd);
583                 file_data_unref(pc->fd);
584                 g_free(pc);
585                 }
586
587         g_list_free(pw->cache_list);
588         pw->cache_list = NULL;
589
590         filelist_free(pw->cache_todo);
591         pw->cache_todo = NULL;
592
593         pw->cache_count = 0;
594         pw->cache_total = 0;
595         pw->cache_tick = 0;
596
597         cache_loader_free(pw->cache_cl);
598         pw->cache_cl = NULL;
599 }
600
601 static void pan_cache_fill(PanWindow *pw, FileData *dir_fd)
602 {
603         GList *list;
604
605         pan_cache_free(pw);
606
607         list = pan_list_tree(dir_fd, SORT_NAME, TRUE, pw->ignore_symlinks);
608         pw->cache_todo = g_list_reverse(list);
609
610         pw->cache_total = g_list_length(pw->cache_todo);
611 }
612
613 static void pan_cache_step_done_cb(CacheLoader *cl, gint UNUSED(error), gpointer data)
614 {
615         PanWindow *pw = (PanWindow *)data;
616
617         if (pw->cache_list)
618                 {
619                 PanCacheData *pc;
620                 pc = static_cast<PanCacheData *>(pw->cache_list->data);
621
622                 if (!pc->cd)
623                         {
624                         pc->cd = cl->cd;
625                         cl->cd = NULL;
626                         }
627                 }
628
629         cache_loader_free(cl);
630         pw->cache_cl = NULL;
631
632         pan_layout_update_idle(pw);
633 }
634
635 static gboolean pan_cache_step(PanWindow *pw)
636 {
637         FileData *fd;
638         PanCacheData *pc;
639         CacheDataType load_mask;
640
641         if (!pw->cache_todo) return TRUE;
642
643         fd = static_cast<FileData *>(pw->cache_todo->data);
644         pw->cache_todo = g_list_remove(pw->cache_todo, fd);
645
646         pc = g_new0(PanCacheData, 1);
647         pc->fd = file_data_ref(fd);
648
649         pc->cd = NULL;
650
651         pw->cache_list = g_list_prepend(pw->cache_list, pc);
652
653         cache_loader_free(pw->cache_cl);
654
655         load_mask = CACHE_LOADER_NONE;
656         if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) load_mask = static_cast<CacheDataType>(load_mask | CACHE_LOADER_DIMENSIONS);
657         if (pw->exif_date_enable) load_mask = static_cast<CacheDataType>(load_mask | CACHE_LOADER_DATE);
658         pw->cache_cl = cache_loader_new(pc->fd, load_mask,
659                                         pan_cache_step_done_cb, pw);
660         return (pw->cache_cl == NULL);
661 }
662
663 /* This sync date function is optimized for lists with a common sort */
664 void pan_cache_sync_date(PanWindow *pw, GList *list)
665 {
666         GList *haystack;
667         GList *work;
668
669         haystack = g_list_copy(pw->cache_list);
670
671         work = list;
672         while (work)
673                 {
674                 FileData *fd;
675                 GList *needle;
676
677                 fd = static_cast<FileData *>(work->data);
678                 work = work->next;
679
680                 needle = haystack;
681                 while (needle)
682                         {
683                         PanCacheData *pc;
684
685                         pc = static_cast<PanCacheData *>(needle->data);
686                         if (pc->fd == fd)
687                                 {
688                                 if (pc->cd && pc->cd->have_date && pc->cd->date >= 0)
689                                         {
690                                         fd->date = pc->cd->date;
691                                         }
692
693                                 haystack = g_list_delete_link(haystack, needle);
694                                 needle = NULL;
695                                 }
696                         else
697                                 {
698                                 needle = needle->next;
699                                 }
700                         }
701                 }
702
703         g_list_free(haystack);
704 }
705
706 /*
707  *-----------------------------------------------------------------------------
708  * item grid
709  *-----------------------------------------------------------------------------
710  */
711
712 static void pan_grid_clear(PanWindow *pw)
713 {
714         GList *work;
715
716         work = pw->list_grid;
717         while (work)
718                 {
719                 PanGrid *pg;
720
721                 pg = static_cast<PanGrid *>(work->data);
722                 work = work->next;
723
724                 g_list_free(pg->list);
725                 g_free(pg);
726                 }
727
728         g_list_free(pw->list_grid);
729         pw->list_grid = NULL;
730
731         pw->list = g_list_concat(pw->list, pw->list_static);
732         pw->list_static = NULL;
733 }
734
735 static void pan_grid_build(PanWindow *pw, gint width, gint height, gint grid_size)
736 {
737         GList *work;
738         gint col, row;
739         gint cw, ch;
740         gint l;
741         gint i, j;
742
743         pan_grid_clear(pw);
744
745         l = g_list_length(pw->list);
746
747         if (l < 1) return;
748
749         col = (gint)(sqrt((gdouble)l / grid_size) * width / height + 0.999);
750         col = CLAMP(col, 1, l / grid_size + 1);
751         row = (gint)((gdouble)l / grid_size / col);
752         if (row < 1) row = 1;
753
754         /* limit minimum size of grid so that a tile will always fit regardless of position */
755         cw = MAX((gint)ceil((gdouble)width / col), PAN_TILE_SIZE * 2);
756         ch = MAX((gint)ceil((gdouble)height / row), PAN_TILE_SIZE * 2);
757
758         row = row * 2 - 1;
759         col = col * 2 - 1;
760
761         DEBUG_1("intersect speedup grid is %dx%d, based on %d average per grid", col, row, grid_size);
762
763         for (j = 0; j < row; j++)
764             for (i = 0; i < col; i++)
765                 {
766                 if ((i + 1) * cw / 2 < width && (j + 1) * ch / 2 < height)
767                         {
768                         PanGrid *pg;
769
770                         pg = g_new0(PanGrid, 1);
771                         pg->x = i * cw / 2;
772                         pg->y = j * ch / 2;
773                         pg->w = cw;
774                         pg->h = ch;
775
776                         pw->list_grid = g_list_prepend(pw->list_grid, pg);
777
778                         DEBUG_1("grid section: %d,%d (%dx%d)", pg->x, pg->y, pg->w, pg->h);
779                         }
780                 }
781
782         work = pw->list;
783         while (work)
784                 {
785                 PanItem *pi;
786                 GList *grid;
787
788                 pi = static_cast<PanItem *>(work->data);
789                 work = work->next;
790
791                 grid = pw->list_grid;
792                 while (grid)
793                         {
794                         PanGrid *pg;
795                         gint rx, ry, rw, rh;
796
797                         pg = static_cast<PanGrid *>(grid->data);
798                         grid = grid->next;
799
800                         if (util_clip_region(pi->x, pi->y, pi->width, pi->height,
801                                              pg->x, pg->y, pg->w, pg->h,
802                                              &rx, &ry, &rw, &rh))
803                                 {
804                                 pg->list = g_list_prepend(pg->list, pi);
805                                 }
806                         }
807                 }
808
809         work = pw->list_grid;
810         while (work)
811                 {
812                 PanGrid *pg;
813
814                 pg = static_cast<PanGrid *>(work->data);
815                 work = work->next;
816
817                 pg->list = g_list_reverse(pg->list);
818                 }
819
820         pw->list_static = pw->list;
821         pw->list = NULL;
822 }
823
824
825 /*
826  *-----------------------------------------------------------------------------
827  * layout state reset
828  *-----------------------------------------------------------------------------
829  */
830
831 static void pan_window_items_free(PanWindow *pw)
832 {
833         GList *work;
834
835         pan_grid_clear(pw);
836
837         work = pw->list;
838         while (work)
839                 {
840                 PanItem *pi = (PanItem *)work->data;
841                 work = work->next;
842
843                 pan_item_free(pi);
844                 }
845
846         g_list_free(pw->list);
847         pw->list = NULL;
848
849         g_list_free(pw->queue);
850         pw->queue = NULL;
851         pw->queue_pi = NULL;
852
853         image_loader_free(pw->il);
854         pw->il = NULL;
855
856         thumb_loader_free(pw->tl);
857         pw->tl = NULL;
858
859         pw->click_pi = NULL;
860         pw->search_pi = NULL;
861 }
862
863
864 /*
865  *-----------------------------------------------------------------------------
866  * layout generation, queries, sizing
867  *-----------------------------------------------------------------------------
868  */
869
870 static void pan_layout_compute(PanWindow *pw, FileData *dir_fd,
871                                gint *width, gint *height,
872                                gint *scroll_x, gint *scroll_y)
873 {
874         pan_window_items_free(pw);
875
876         switch (pw->size)
877                 {
878                 case PAN_IMAGE_SIZE_THUMB_DOTS:
879                         pw->thumb_size = PAN_THUMB_SIZE_DOTS;
880                         pw->thumb_gap = PAN_THUMB_GAP_DOTS;
881                         break;
882                 case PAN_IMAGE_SIZE_THUMB_NONE:
883                         pw->thumb_size = PAN_THUMB_SIZE_NONE;
884                         pw->thumb_gap = PAN_THUMB_GAP_SMALL;
885                         break;
886                 case PAN_IMAGE_SIZE_THUMB_SMALL:
887                         pw->thumb_size = PAN_THUMB_SIZE_SMALL;
888                         pw->thumb_gap = PAN_THUMB_GAP_SMALL;
889                         break;
890                 case PAN_IMAGE_SIZE_THUMB_NORMAL:
891                 default:
892                         pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
893                         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
894                         break;
895                 case PAN_IMAGE_SIZE_THUMB_LARGE:
896                         pw->thumb_size = PAN_THUMB_SIZE_LARGE;
897                         pw->thumb_gap = PAN_THUMB_GAP_LARGE;
898                         break;
899                 case PAN_IMAGE_SIZE_10:
900                         pw->image_size = 10;
901                         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
902                         break;
903                 case PAN_IMAGE_SIZE_25:
904                         pw->image_size = 25;
905                         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
906                         break;
907                 case PAN_IMAGE_SIZE_33:
908                         pw->image_size = 33;
909                         pw->thumb_gap = PAN_THUMB_GAP_LARGE;
910                         break;
911                 case PAN_IMAGE_SIZE_50:
912                         pw->image_size = 50;
913                         pw->thumb_gap = PAN_THUMB_GAP_HUGE;
914                         break;
915                 case PAN_IMAGE_SIZE_100:
916                         pw->image_size = 100;
917                         pw->thumb_gap = PAN_THUMB_GAP_HUGE;
918                         break;
919                 }
920
921         *width = 0;
922         *height = 0;
923         *scroll_x = 0;
924         *scroll_y = 0;
925
926         switch (pw->layout)
927                 {
928                 case PAN_LAYOUT_GRID:
929                 default:
930                         pan_grid_compute(pw, dir_fd, width, height);
931                         break;
932                 case PAN_LAYOUT_FOLDERS_LINEAR:
933                         pan_folder_tree_compute(pw, dir_fd, width, height);
934                         break;
935                 case PAN_LAYOUT_FOLDERS_FLOWER:
936                         pan_flower_compute(pw, dir_fd, width, height, scroll_x, scroll_y);
937                         break;
938                 case PAN_LAYOUT_CALENDAR:
939                         pan_calendar_compute(pw, dir_fd, width, height);
940                         break;
941                 case PAN_LAYOUT_TIMELINE:
942                         pan_timeline_compute(pw, dir_fd, width, height);
943                         break;
944                 }
945
946         pan_cache_free(pw);
947
948         DEBUG_1("computed %d objects", g_list_length(pw->list));
949 }
950
951 static GList *pan_layout_intersect_l(GList *list, GList *item_list,
952                                      gint x, gint y, gint width, gint height)
953 {
954         GList *work;
955
956         work = item_list;
957         while (work)
958                 {
959                 PanItem *pi;
960                 gint rx, ry, rw, rh;
961
962                 pi = static_cast<PanItem *>(work->data);
963                 work = work->next;
964
965                 if (util_clip_region(x, y, width, height,
966                                      pi->x, pi->y, pi->width, pi->height,
967                                      &rx, &ry, &rw, &rh))
968                         {
969                         list = g_list_prepend(list, pi);
970                         }
971                 }
972
973         return list;
974 }
975
976 GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
977 {
978         GList *list = NULL;
979         GList *grid;
980         PanGrid *pg = NULL;
981
982         grid = pw->list_grid;
983         while (grid && !pg)
984                 {
985                 pg = static_cast<PanGrid *>(grid->data);
986                 grid = grid->next;
987
988                 if (x < pg->x || x + width > pg->x + pg->w ||
989                     y < pg->y || y + height > pg->y + pg->h)
990                         {
991                         pg = NULL;
992                         }
993                 }
994
995         list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
996
997         if (pg)
998                 {
999                 list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
1000                 }
1001         else
1002                 {
1003                 list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
1004                 }
1005
1006         return list;
1007 }
1008
1009 void pan_layout_resize(PanWindow *pw)
1010 {
1011         gint width = 0;
1012         gint height = 0;
1013         GList *work;
1014         PixbufRenderer *pr;
1015
1016         work = pw->list;
1017         while (work)
1018                 {
1019                 PanItem *pi;
1020
1021                 pi = static_cast<PanItem *>(work->data);
1022                 work = work->next;
1023
1024                 if (width < pi->x + pi->width) width = pi->x + pi->width;
1025                 if (height < pi->y + pi->height) height = pi->y + pi->height;
1026                 }
1027         work = pw->list_static;
1028         while (work)
1029                 {
1030                 PanItem *pi;
1031
1032                 pi = static_cast<PanItem *>(work->data);
1033                 work = work->next;
1034
1035                 if (width < pi->x + pi->width) width = pi->x + pi->width;
1036                 if (height < pi->y + pi->height) height = pi->y + pi->height;
1037                 }
1038
1039         width += PAN_BOX_BORDER * 2;
1040         height += PAN_BOX_BORDER * 2;
1041
1042         pr = PIXBUF_RENDERER(pw->imd->pr);
1043         if (width < pr->window_width) width = pr->window_width;
1044         if (height < pr->window_width) height = pr->window_height;
1045
1046         pixbuf_renderer_set_tiles_size(PIXBUF_RENDERER(pw->imd->pr), width, height);
1047 }
1048
1049 static gint pan_layout_update_idle_cb(gpointer data)
1050 {
1051         PanWindow *pw = (PanWindow *)data;
1052         gint width;
1053         gint height;
1054         gint scroll_x;
1055         gint scroll_y;
1056
1057         if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE ||
1058             (pw->exif_date_enable && (pw->layout == PAN_LAYOUT_TIMELINE || pw->layout == PAN_LAYOUT_CALENDAR)))
1059                 {
1060                 if (!pw->cache_list && !pw->cache_todo)
1061                         {
1062                         pan_cache_fill(pw, pw->dir_fd);
1063                         if (pw->cache_todo)
1064                                 {
1065                                 pan_window_message(pw, _("Reading image data..."));
1066                                 return TRUE;
1067                                 }
1068                         }
1069                 if (pw->cache_todo)
1070                         {
1071                         pw->cache_count++;
1072                         pw->cache_tick++;
1073                         if (pw->cache_count == pw->cache_total)
1074                                 {
1075                                 pan_window_message(pw, _("Sorting..."));
1076                                 }
1077                         else if (pw->cache_tick > 9)
1078                                 {
1079                                 gchar *buf;
1080
1081                                 buf = g_strdup_printf("%s %d / %d", _("Reading image data..."),
1082                                                       pw->cache_count, pw->cache_total);
1083                                 pan_window_message(pw, buf);
1084                                 g_free(buf);
1085
1086                                 pw->cache_tick = 0;
1087                                 }
1088
1089                         if (pan_cache_step(pw)) return TRUE;
1090
1091                         pw->idle_id = 0;
1092                         return FALSE;
1093                         }
1094                 }
1095
1096         pan_layout_compute(pw, pw->dir_fd, &width, &height, &scroll_x, &scroll_y);
1097
1098         pan_window_zoom_limit(pw);
1099
1100         if (width > 0 && height > 0)
1101                 {
1102                 gdouble align;
1103
1104                 DEBUG_1("Canvas size is %d x %d", width, height);
1105
1106                 pan_grid_build(pw, width, height, 1000);
1107
1108                 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
1109                                           PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
1110                                           pan_window_request_tile_cb,
1111                                           pan_window_dispose_tile_cb, pw, 1.0);
1112
1113                 if (scroll_x == 0 && scroll_y == 0)
1114                         {
1115                         align = 0.0;
1116                         }
1117                 else
1118                         {
1119                         align = 0.5;
1120                         }
1121                 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
1122                 }
1123
1124         pan_window_message(pw, NULL);
1125
1126         pw->idle_id = 0;
1127         return FALSE;
1128 }
1129
1130 static void pan_layout_update_idle(PanWindow *pw)
1131 {
1132         if (!pw->idle_id)
1133                 {
1134                 pw->idle_id = g_idle_add(pan_layout_update_idle_cb, pw);
1135                 }
1136 }
1137
1138 void pan_layout_update(PanWindow *pw)
1139 {
1140         pan_window_message(pw, _("Sorting images..."));
1141         pan_layout_update_idle(pw);
1142 }
1143
1144 static void pan_layout_set_fd(PanWindow *pw, FileData *dir_fd)
1145 {
1146         if (!dir_fd) return;
1147
1148         if (strcmp(dir_fd->path, G_DIR_SEPARATOR_S) == 0)
1149                 {
1150                 pan_warning_folder(dir_fd->path, pw->window);
1151                 return;
1152                 }
1153
1154         file_data_unref(pw->dir_fd);
1155         pw->dir_fd = file_data_ref(dir_fd);
1156
1157         pan_layout_update(pw);
1158 }
1159
1160
1161 /*
1162  *-----------------------------------------------------------------------------
1163  * keyboard handlers
1164  *-----------------------------------------------------------------------------
1165  */
1166
1167 FileData *pan_menu_click_fd(PanWindow *pw)
1168 {
1169         if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd;
1170         return NULL;
1171 }
1172
1173 static gboolean pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1174 {
1175         PanWindow *pw = (PanWindow *)data;
1176         PixbufRenderer *pr;
1177         FileData *fd;
1178         gboolean stop_signal = FALSE;
1179         GtkWidget *menu;
1180         GtkWidget *imd_widget;
1181         gint x = 0;
1182         gint y = 0;
1183         gint focused;
1184         gint on_entry;
1185
1186         pr = PIXBUF_RENDERER(pw->imd->pr);
1187         fd = pan_menu_click_fd(pw);
1188
1189         imd_widget = gtk_container_get_focus_child(GTK_CONTAINER(pw->imd->widget));
1190         focused = (pw->fs || (imd_widget && gtk_widget_has_focus(imd_widget)));
1191         on_entry = (gtk_widget_has_focus(pw->path_entry) ||
1192                     gtk_widget_has_focus(pw->search_ui->search_entry) ||
1193                     gtk_widget_has_focus(pw->filter_ui->filter_entry));
1194
1195         if (focused)
1196                 {
1197                 stop_signal = TRUE;
1198                 switch (event->keyval)
1199                         {
1200                         case GDK_KEY_Left: case GDK_KEY_KP_Left:
1201                                 x -= 1;
1202                                 break;
1203                         case GDK_KEY_Right: case GDK_KEY_KP_Right:
1204                                 x += 1;
1205                                 break;
1206                         case GDK_KEY_Up: case GDK_KEY_KP_Up:
1207                                 y -= 1;
1208                                 break;
1209                         case GDK_KEY_Down: case GDK_KEY_KP_Down:
1210                                 y += 1;
1211                                 break;
1212                         case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
1213                                 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
1214                                 break;
1215                         case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
1216                                 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
1217                                 break;
1218                         case GDK_KEY_Home: case GDK_KEY_KP_Home:
1219                                 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
1220                                 break;
1221                         case GDK_KEY_End: case GDK_KEY_KP_End:
1222                                 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
1223                                 break;
1224                         default:
1225                                 stop_signal = FALSE;
1226                                 break;
1227                         }
1228
1229                 if (x != 0 || y!= 0)
1230                         {
1231                         if (event->state & GDK_SHIFT_MASK)
1232                                 {
1233                                 x *= 3;
1234                                 y *= 3;
1235                                 }
1236                         keyboard_scroll_calc(&x, &y, event);
1237                         pixbuf_renderer_scroll(pr, x, y);
1238                         }
1239                 }
1240
1241         if (stop_signal) return stop_signal;
1242
1243         if (event->state & GDK_CONTROL_MASK)
1244                 {
1245                 stop_signal = TRUE;
1246                 switch (event->keyval)
1247                         {
1248                         case '1':
1249                         case '2':
1250                         case '3':
1251                         case '4':
1252                         case '5':
1253                         case '6':
1254                         case '7':
1255                         case '8':
1256                         case '9':
1257                         case '0':
1258                                 break;
1259                         case 'C': case 'c':
1260                                 if (fd) file_util_copy(fd, NULL, NULL, GTK_WIDGET(pr));
1261                                 break;
1262                         case 'M': case 'm':
1263                                 if (fd) file_util_move(fd, NULL, NULL, GTK_WIDGET(pr));
1264                                 break;
1265                         case 'R': case 'r':
1266                                 if (fd) file_util_rename(fd, NULL, GTK_WIDGET(pr));
1267                                 break;
1268                         case 'D': case 'd':
1269                                 if (fd)
1270                                         {
1271                                         options->file_ops.safe_delete_enable = TRUE;
1272                                         file_util_delete(fd, NULL, GTK_WIDGET(pr));
1273                                         }
1274                                 break;
1275                         case 'F': case 'f':
1276                                 pan_search_toggle_visible(pw, TRUE);
1277                                 break;
1278                         case 'G': case 'g':
1279                                 pan_search_activate(pw);
1280                                 break;
1281                         case 'W': case 'w':
1282                                 pan_window_close(pw);
1283                                 break;
1284                         default:
1285                                 stop_signal = FALSE;
1286                                 break;
1287                         }
1288                 }
1289         else
1290                 {
1291                 stop_signal = TRUE;
1292                 switch (event->keyval)
1293                         {
1294                         case GDK_KEY_Escape:
1295                                 if (pw->fs)
1296                                         {
1297                                         pan_fullscreen_toggle(pw, TRUE);
1298                                         }
1299                                 else
1300                                         {
1301                                         pan_search_toggle_visible(pw, FALSE);
1302                                         }
1303                                 break;
1304                         default:
1305                                 stop_signal = FALSE;
1306                                 break;
1307                         }
1308
1309                 if (stop_signal) return stop_signal;
1310
1311                 // Don't steal characters from entry boxes.
1312                 if (!on_entry)
1313                         {
1314                         stop_signal = TRUE;
1315                         switch (event->keyval)
1316                                 {
1317                                 case '+': case '=': case GDK_KEY_KP_Add:
1318                                         pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
1319                                         break;
1320                                 case '-': case GDK_KEY_KP_Subtract:
1321                                         pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
1322                                         break;
1323                                 case 'Z': case 'z': case GDK_KEY_KP_Divide: case '1':
1324                                         pixbuf_renderer_zoom_set(pr, 1.0);
1325                                         break;
1326                                 case '2':
1327                                         pixbuf_renderer_zoom_set(pr, 2.0);
1328                                         break;
1329                                 case '3':
1330                                         pixbuf_renderer_zoom_set(pr, 3.0);
1331                                         break;
1332                                 case '4':
1333                                         pixbuf_renderer_zoom_set(pr, 4.0);
1334                                         break;
1335                                 case '7':
1336                                         pixbuf_renderer_zoom_set(pr, -4.0);
1337                                         break;
1338                                 case '8':
1339                                         pixbuf_renderer_zoom_set(pr, -3.0);
1340                                         break;
1341                                 case '9':
1342                                         pixbuf_renderer_zoom_set(pr, -2.0);
1343                                         break;
1344                                 case 'F': case 'f':
1345                                 case 'V': case 'v':
1346                                 case GDK_KEY_F11:
1347                                         pan_fullscreen_toggle(pw, FALSE);
1348                                         break;
1349                                 case 'I': case 'i':
1350                                         break;
1351                                 case GDK_KEY_Delete: case GDK_KEY_KP_Delete:
1352                                         break;
1353                                 case GDK_KEY_Menu:
1354                                 case GDK_KEY_F10:
1355                                         menu = pan_popup_menu(pw);
1356                                         gtk_menu_popup_at_widget(GTK_MENU(menu), widget, GDK_GRAVITY_SOUTH, GDK_GRAVITY_CENTER, NULL);
1357
1358                                         break;
1359                                 case '/':
1360                                         pan_search_toggle_visible(pw, TRUE);
1361                                         break;
1362                                         stop_signal = FALSE;
1363                                         break;
1364                                 }
1365                         }
1366                 }
1367         if (!stop_signal && is_help_key(event))
1368                 {
1369                 help_window_show("GuideOtherWindowsPanView.html");
1370                 stop_signal = TRUE;
1371                 }
1372
1373         return stop_signal;
1374 }
1375
1376 /*
1377  *-----------------------------------------------------------------------------
1378  * info popup
1379  *-----------------------------------------------------------------------------
1380  */
1381
1382 static void pan_info_add_exif(PanTextAlignment *ta, FileData *fd)
1383 {
1384         GList *exif_list;
1385         gchar *text;
1386         gchar *title;
1387         gchar *key;
1388
1389         if (!fd) return;
1390
1391         exif_list = bar_pane_exif_list();
1392         while (exif_list)
1393                 {
1394                 title = static_cast<gchar *>(exif_list->data);
1395                 exif_list = exif_list->next;
1396                 key = static_cast<gchar *>(exif_list->data);
1397                 exif_list = exif_list->next;
1398
1399                 text = metadata_read_string(fd, key, METADATA_FORMATTED);
1400                 if (text && text[0] != '\0')
1401                         {
1402                         pan_text_alignment_add(ta, title, text);
1403                         }
1404
1405                 g_free(text);
1406                 }
1407
1408         string_list_free(exif_list);
1409 }
1410
1411
1412 void pan_info_update(PanWindow *pw, PanItem *pi)
1413 {
1414         PanTextAlignment *ta;
1415         PanItem *pbox;
1416         PanItem *p;
1417         gchar *buf;
1418         gint x1, y1, x2, y2, x3, y3;
1419         gint x, y, w, h;
1420
1421         if (pw->click_pi == pi) return;
1422         if (pi && !pi->fd) pi = NULL;
1423
1424         while ((p = pan_item_find_by_key(pw, PAN_ITEM_NONE, "info"))) pan_item_remove(pw, p);
1425         pw->click_pi = pi;
1426
1427         if (!pi) return;
1428
1429         DEBUG_1("info set to %s", pi->fd->path);
1430
1431         pbox = pan_item_box_new(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
1432                                 PAN_POPUP_BORDER,
1433                                 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
1434                                 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1435         pan_item_set_key(pbox, "info");
1436
1437         if (pi->type == PAN_ITEM_THUMB && pi->pixbuf)
1438                 {
1439                 w = gdk_pixbuf_get_width(pi->pixbuf);
1440                 h = gdk_pixbuf_get_height(pi->pixbuf);
1441
1442                 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
1443                 y1 = pi->y + (pi->height - h) / 2 + 8;
1444                 }
1445         else
1446                 {
1447                 x1 = pi->x + pi->width - 8;
1448                 y1 = pi->y + 8;
1449                 }
1450
1451         x2 = pbox->x + 1;
1452         y2 = pbox->y + 36;
1453         x3 = pbox->x + 1;
1454         y3 = pbox->y + 12;
1455         util_clip_triangle(x1, y1, x2, y2, x3, y3,
1456                            &x, &y, &w, &h);
1457
1458         p = pan_item_tri_new(pw, NULL, x, y, w, h,
1459                              x1, y1, x2, y2, x3, y3,
1460                              PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
1461         pan_item_tri_border(p, PAN_BORDER_1 | PAN_BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1462         pan_item_set_key(p, "info");
1463         pan_item_added(pw, p);
1464
1465         ta = pan_text_alignment_new(pw, pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, "info");
1466
1467         pan_text_alignment_add(ta, _("Filename:"), pi->fd->name);
1468         buf = remove_level_from_path(pi->fd->path);
1469         pan_text_alignment_add(ta, _("Location:"), buf);
1470         g_free(buf);
1471         pan_text_alignment_add(ta, _("Date:"), text_from_time(pi->fd->date));
1472         buf = text_from_size(pi->fd->size);
1473         pan_text_alignment_add(ta, _("Size:"), buf);
1474         g_free(buf);
1475
1476         if (pw->info_includes_exif)
1477                 {
1478                 pan_info_add_exif(ta, pi->fd);
1479                 }
1480
1481         pan_text_alignment_calc(ta, pbox);
1482         pan_text_alignment_free(ta);
1483
1484         pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
1485         pan_item_added(pw, pbox);
1486
1487         if (pw->info_image_size > PAN_IMAGE_SIZE_THUMB_NONE)
1488                 {
1489                 gint iw, ih;
1490                 if (image_load_dimensions(pi->fd, &iw, &ih))
1491                         {
1492                         gint scale = 25;
1493
1494                         switch (pw->info_image_size)
1495                                 {
1496                                 case PAN_IMAGE_SIZE_10:
1497                                         scale = 10;
1498                                         break;
1499                                 case PAN_IMAGE_SIZE_25:
1500                                         scale = 25;
1501                                         break;
1502                                 case PAN_IMAGE_SIZE_33:
1503                                         scale = 33;
1504                                         break;
1505                                 case PAN_IMAGE_SIZE_50:
1506                                         scale = 50;
1507                                         break;
1508                                 case PAN_IMAGE_SIZE_100:
1509                                         scale = 100;
1510                                         break;
1511                                 }
1512
1513                         iw = MAX(1, iw * scale / 100);
1514                         ih = MAX(1, ih * scale / 100);
1515
1516                         pbox = pan_item_box_new(pw, NULL, pbox->x, pbox->y + pbox->height + 8, 10, 10,
1517                                                 PAN_POPUP_BORDER,
1518                                                 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
1519                                                 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1520                         pan_item_set_key(pbox, "info");
1521
1522                         p = pan_item_image_new(pw, file_data_new_group(pi->fd->path),
1523                                                pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, iw, ih);
1524                         pan_item_set_key(p, "info");
1525                         pan_item_size_by_item(pbox, p, PREF_PAD_BORDER);
1526
1527                         pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
1528                         pan_item_added(pw, pbox);
1529                         }
1530                 }
1531
1532         pan_layout_resize(pw);
1533 }
1534
1535
1536 /*
1537  *-----------------------------------------------------------------------------
1538  * main window
1539  *-----------------------------------------------------------------------------
1540  */
1541
1542 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
1543 {
1544         PanWindow *pw = (PanWindow *)data;
1545         PanItem *pi = NULL;
1546         GtkWidget *menu;
1547         gint rx, ry;
1548
1549         rx = ry = 0;
1550         if (pr->scale)
1551                 {
1552                 rx = (gdouble)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
1553                 ry = (gdouble)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
1554                 }
1555
1556         pi = pan_item_find_by_coord(pw, PAN_ITEM_BOX, rx, ry, "info");
1557         if (pi && event->button == MOUSE_BUTTON_LEFT)
1558                 {
1559                 pan_info_update(pw, NULL);
1560                 return;
1561                 }
1562
1563         pi = pan_item_find_by_coord(pw, (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB,
1564                                     rx, ry, NULL);
1565
1566         switch (event->button)
1567                 {
1568                 case MOUSE_BUTTON_LEFT:
1569                         pan_info_update(pw, pi);
1570
1571                         if (!pi && pw->layout == PAN_LAYOUT_CALENDAR)
1572                                 {
1573                                 pi = pan_item_find_by_coord(pw, PAN_ITEM_BOX, rx, ry, "day");
1574                                 pan_calendar_update(pw, pi);
1575                                 }
1576                         break;
1577                 case MOUSE_BUTTON_MIDDLE:
1578                         break;
1579                 case MOUSE_BUTTON_RIGHT:
1580                         pan_info_update(pw, pi);
1581                         menu = pan_popup_menu(pw);
1582                         gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
1583                         break;
1584                 default:
1585                         break;
1586                 }
1587 }
1588
1589 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer UNUSED(data))
1590 {
1591         gint w, h;
1592
1593         w = pr->vis_width;
1594         h = pr->vis_height;
1595
1596         if (!(event->state & GDK_SHIFT_MASK))
1597                 {
1598                 w /= 3;
1599                 h /= 3;
1600                 }
1601
1602         if (event->state & GDK_CONTROL_MASK)
1603                 {
1604                 switch (event->direction)
1605                         {
1606                         case GDK_SCROLL_UP:
1607                                 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
1608                                                                      (gint)event->x, (gint)event->y);
1609                                 break;
1610                         case GDK_SCROLL_DOWN:
1611                                 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
1612                                                                      (gint)event->x, (gint)event->y);
1613                                 break;
1614                         default:
1615                                 break;
1616                         }
1617                 }
1618         else
1619                 {
1620                 switch (event->direction)
1621                         {
1622                         case GDK_SCROLL_UP:
1623                                 pixbuf_renderer_scroll(pr, 0, -h);
1624                                 break;
1625                         case GDK_SCROLL_DOWN:
1626                                 pixbuf_renderer_scroll(pr, 0, h);
1627                                 break;
1628                         case GDK_SCROLL_LEFT:
1629                                 pixbuf_renderer_scroll(pr, -w, 0);
1630                                 break;
1631                         case GDK_SCROLL_RIGHT:
1632                                 pixbuf_renderer_scroll(pr, w, 0);
1633                                 break;
1634                         default:
1635                                 break;
1636                         }
1637                 }
1638 }
1639
1640 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
1641 {
1642         g_signal_connect(G_OBJECT(imd->pr), "clicked",
1643                          G_CALLBACK(button_cb), pw);
1644         g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
1645                          G_CALLBACK(scroll_cb), pw);
1646 }
1647
1648 static void pan_fullscreen_stop_func(FullScreenData *UNUSED(fs), gpointer data)
1649 {
1650         PanWindow *pw = (PanWindow *)data;
1651
1652         pw->fs = NULL;
1653         pw->imd = pw->imd_normal;
1654 }
1655
1656 static void pan_fullscreen_toggle(PanWindow *pw, gboolean force_off)
1657 {
1658         if (force_off && !pw->fs) return;
1659
1660         if (pw->fs)
1661                 {
1662                 fullscreen_stop(pw->fs);
1663                 }
1664         else
1665                 {
1666                 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
1667                 pan_image_set_buttons(pw, pw->fs->imd);
1668                 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
1669                                  G_CALLBACK(pan_window_key_press_cb), pw);
1670
1671                 pw->imd = pw->fs->imd;
1672                 }
1673 }
1674
1675 static void pan_window_image_zoom_cb(PixbufRenderer *UNUSED(pr), gdouble UNUSED(zoom), gpointer data)
1676 {
1677         PanWindow *pw = (PanWindow *)data;
1678         gchar *text;
1679
1680         text = image_zoom_get_as_text(pw->imd);
1681         gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
1682         g_free(text);
1683 }
1684
1685 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
1686 {
1687         PanWindow *pw = (PanWindow *)data;
1688         GtkAdjustment *adj;
1689         GdkRectangle rect;
1690         gint width, height;
1691
1692         if (pr->scale == 0.0) return;
1693
1694         pixbuf_renderer_get_visible_rect(pr, &rect);
1695         pixbuf_renderer_get_image_size(pr, &width, &height);
1696
1697         adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
1698         gtk_adjustment_set_page_size(adj, rect.width);
1699         gtk_adjustment_set_page_increment(adj, gtk_adjustment_get_page_size(adj) / 2.0);
1700         gtk_adjustment_set_step_increment(adj, 48.0 / pr->scale);
1701         gtk_adjustment_set_lower(adj, 0.0);
1702         gtk_adjustment_set_upper(adj, MAX((gdouble)width, 1.0));
1703         gtk_adjustment_set_value(adj, (gdouble)rect.x);
1704
1705         pref_signal_block_data(pw->scrollbar_h, pw);
1706         gtk_adjustment_changed(adj);
1707         gtk_adjustment_value_changed(adj);
1708         pref_signal_unblock_data(pw->scrollbar_h, pw);
1709
1710         adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
1711         gtk_adjustment_set_page_size(adj, rect.height);
1712         gtk_adjustment_set_page_increment(adj, gtk_adjustment_get_page_size(adj) / 2.0);
1713         gtk_adjustment_set_step_increment(adj, 48.0 / pr->scale);
1714         gtk_adjustment_set_lower(adj, 0.0);
1715         gtk_adjustment_set_upper(adj, MAX((gdouble)height, 1.0));
1716         gtk_adjustment_set_value(adj, (gdouble)rect.y);
1717
1718         pref_signal_block_data(pw->scrollbar_v, pw);
1719         gtk_adjustment_changed(adj);
1720         gtk_adjustment_value_changed(adj);
1721         pref_signal_unblock_data(pw->scrollbar_v, pw);
1722 }
1723
1724 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
1725 {
1726         PanWindow *pw = (PanWindow *)data;
1727         PixbufRenderer *pr;
1728         gint x;
1729
1730         pr = PIXBUF_RENDERER(pw->imd_normal->pr);
1731
1732         if (!pr->scale) return;
1733
1734         x = (gint)gtk_range_get_value(range);
1735
1736         pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
1737 }
1738
1739 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
1740 {
1741         PanWindow *pw = (PanWindow *)data;
1742         PixbufRenderer *pr;
1743         gint y;
1744
1745         pr = PIXBUF_RENDERER(pw->imd_normal->pr);
1746
1747         if (!pr->scale) return;
1748
1749         y = (gint)gtk_range_get_value(range);
1750
1751         pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
1752 }
1753
1754 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
1755 {
1756         PanWindow *pw = (PanWindow *)data;
1757
1758         pw->layout = static_cast<PanLayoutType>(gtk_combo_box_get_active(GTK_COMBO_BOX(combo)));
1759         pan_layout_update(pw);
1760 }
1761
1762 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
1763 {
1764         PanWindow *pw = (PanWindow *)data;
1765
1766         pw->size = static_cast<PanImageSize>(gtk_combo_box_get_active(GTK_COMBO_BOX(combo)));
1767         pan_layout_update(pw);
1768 }
1769
1770 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
1771 {
1772         PanWindow *pw = (PanWindow *)data;
1773         gchar *path;
1774
1775         path = remove_trailing_slash(new_text);
1776         parse_out_relatives(path);
1777
1778         if (!isdir(path))
1779                 {
1780                 warning_dialog(_("Folder not found"),
1781                                _("The entered path is not a folder"),
1782                                GTK_STOCK_DIALOG_WARNING, pw->path_entry);
1783                 }
1784         else
1785                 {
1786                 FileData *dir_fd = file_data_new_dir(path);
1787                 tab_completion_append_to_history(pw->path_entry, path);
1788
1789                 pan_layout_set_fd(pw, dir_fd);
1790                 file_data_unref(dir_fd);
1791                 }
1792
1793         g_free(path);
1794 }
1795
1796 static void pan_window_close(PanWindow *pw)
1797 {
1798         pan_window_list = g_list_remove(pan_window_list, pw);
1799
1800         pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_EXIF_PAN_DATE, pw->exif_date_enable);
1801         pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_INFO_IMAGE, pw->info_image_size);
1802         pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_INFO_EXIF, pw->info_includes_exif);
1803
1804         if (pw->idle_id)
1805                 {
1806                 g_source_remove(pw->idle_id);
1807                 }
1808
1809         pan_fullscreen_toggle(pw, TRUE);
1810         pan_search_ui_destroy(&pw->search_ui);
1811         pan_filter_ui_destroy(&pw->filter_ui);
1812         gtk_widget_destroy(pw->window);
1813
1814         pan_window_items_free(pw);
1815         pan_cache_free(pw);
1816
1817         file_data_unref(pw->dir_fd);
1818
1819         g_free(pw);
1820 }
1821
1822 static gboolean pan_window_delete_cb(GtkWidget *UNUSED(w), GdkEventAny *UNUSED(event), gpointer data)
1823 {
1824         PanWindow *pw = (PanWindow *)data;
1825
1826         pan_window_close(pw);
1827         return TRUE;
1828 }
1829
1830 static void pan_window_new_real(FileData *dir_fd)
1831 {
1832         PanWindow *pw;
1833         GtkWidget *vbox;
1834         GtkWidget *box;
1835         GtkWidget *combo;
1836         GtkWidget *hbox;
1837         GtkWidget *frame;
1838         GtkWidget *table;
1839         GdkGeometry geometry;
1840
1841         pw = g_new0(PanWindow, 1);
1842
1843         pw->dir_fd = file_data_ref(dir_fd);
1844         pw->layout = PAN_LAYOUT_TIMELINE;
1845         pw->size = PAN_IMAGE_SIZE_THUMB_NORMAL;
1846         pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
1847         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
1848
1849         if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_EXIF_PAN_DATE, &pw->exif_date_enable))
1850                 {
1851                 pw->exif_date_enable = FALSE;
1852                 }
1853         if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_INFO_IMAGE, &pw->info_image_size))
1854                 {
1855                 pw->info_image_size = PAN_IMAGE_SIZE_THUMB_NONE;
1856                 }
1857         if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_INFO_EXIF, &pw->info_includes_exif))
1858                 {
1859                 pw->info_includes_exif = TRUE;
1860                 }
1861
1862         pw->ignore_symlinks = TRUE;
1863
1864         pw->idle_id = 0;
1865
1866         pw->window = window_new(GTK_WINDOW_TOPLEVEL, "panview", NULL, NULL, _("Pan View"));
1867         DEBUG_NAME(pw->window);
1868
1869         geometry.min_width = DEFAULT_MINIMAL_WINDOW_SIZE;
1870         geometry.min_height = DEFAULT_MINIMAL_WINDOW_SIZE;
1871         gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
1872
1873         gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
1874         gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
1875
1876         vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1877         DEBUG_NAME(vbox);
1878         gtk_container_add(GTK_CONTAINER(pw->window), vbox);
1879         gtk_widget_show(vbox);
1880
1881         box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1882
1883         pref_spacer(box, 0);
1884         pref_label_new(box, _("Location:"));
1885         combo = tab_completion_new_with_history(&pw->path_entry, dir_fd->path, "pan_view_path", -1,
1886                                                 pan_window_entry_activate_cb, pw);
1887         gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
1888         gtk_widget_show(combo);
1889
1890         combo = gtk_combo_box_text_new();
1891         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Timeline"));
1892         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Calendar"));
1893         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Folders"));
1894         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Folders (flower)"));
1895         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Grid"));
1896
1897         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
1898         g_signal_connect(G_OBJECT(combo), "changed",
1899                          G_CALLBACK(pan_window_layout_change_cb), pw);
1900         gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
1901         gtk_widget_show(combo);
1902
1903         combo = gtk_combo_box_text_new();
1904         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Dots"));
1905         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("No Images"));
1906         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Small Thumbnails"));
1907         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Normal Thumbnails"));
1908         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Large Thumbnails"));
1909         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:10 (10%)"));
1910         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:4 (25%)"));
1911         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:3 (33%)"));
1912         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:2 (50%)"));
1913         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:1 (100%)"));
1914
1915         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
1916         g_signal_connect(G_OBJECT(combo), "changed",
1917                          G_CALLBACK(pan_window_layout_size_cb), pw);
1918         gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
1919         gtk_widget_show(combo);
1920
1921         table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
1922         gtk_table_set_row_spacings(GTK_TABLE(table), 2);
1923         gtk_table_set_col_spacings(GTK_TABLE(table), 2);
1924
1925         /** @FIXME pan view does not work correctly when renderer-clutter()
1926          * is used.
1927          */
1928         gint temp = options->image.use_clutter_renderer;
1929         options->image.use_clutter_renderer = FALSE;
1930         pw->imd = image_new(TRUE);
1931         options->image.use_clutter_renderer = temp;
1932
1933         pw->imd_normal = pw->imd;
1934
1935         g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
1936                          G_CALLBACK(pan_window_image_zoom_cb), pw);
1937         g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
1938                          G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
1939
1940         gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
1941                          static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), 0, 0);
1942         gtk_widget_show(GTK_WIDGET(pw->imd->widget));
1943
1944         pan_window_dnd_init(pw);
1945
1946         pan_image_set_buttons(pw, pw->imd);
1947
1948         pw->scrollbar_h = gtk_hscrollbar_new(NULL);
1949         g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
1950                          G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
1951         gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
1952                          static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
1953         gtk_widget_show(pw->scrollbar_h);
1954
1955         pw->scrollbar_v = gtk_vscrollbar_new(NULL);
1956         g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
1957                          G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
1958         gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
1959                          static_cast<GtkAttachOptions>(0), static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), 0, 0);
1960         gtk_widget_show(pw->scrollbar_v);
1961
1962         /* find bar */
1963
1964         pw->search_ui = pan_search_ui_new(pw);
1965         gtk_box_pack_start(GTK_BOX(vbox), pw->search_ui->search_box, FALSE, FALSE, 2);
1966
1967     /* filter bar */
1968     pw->filter_ui = pan_filter_ui_new(pw);
1969     gtk_box_pack_start(GTK_BOX(vbox), pw->filter_ui->filter_box, FALSE, FALSE, 2);
1970
1971         /* status bar */
1972
1973         box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
1974
1975         frame = gtk_frame_new(NULL);
1976         DEBUG_NAME(frame);
1977         gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
1978         gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
1979         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
1980         gtk_widget_show(frame);
1981
1982         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1983         gtk_container_add(GTK_CONTAINER(frame), hbox);
1984         gtk_widget_show(hbox);
1985
1986         pref_spacer(hbox, 0);
1987         pw->label_message = pref_label_new(hbox, "");
1988
1989         frame = gtk_frame_new(NULL);
1990         DEBUG_NAME(frame);
1991         gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
1992         gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
1993         gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
1994         gtk_widget_show(frame);
1995
1996         pw->label_zoom = gtk_label_new("");
1997         gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
1998         gtk_widget_show(pw->label_zoom);
1999
2000         // Add the "Find" button to the status bar area.
2001         gtk_box_pack_end(GTK_BOX(box), pw->search_ui->search_button, FALSE, FALSE, 0);
2002         gtk_widget_show(pw->search_ui->search_button);
2003
2004         // Add the "Filter" button to the status bar area.
2005         gtk_box_pack_end(GTK_BOX(box), pw->filter_ui->filter_button, FALSE, FALSE, 0);
2006         gtk_widget_show(pw->filter_ui->filter_button);
2007
2008         g_signal_connect(G_OBJECT(pw->window), "delete_event",
2009                          G_CALLBACK(pan_window_delete_cb), pw);
2010         g_signal_connect(G_OBJECT(pw->window), "key_press_event",
2011                          G_CALLBACK(pan_window_key_press_cb), pw);
2012
2013         gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
2014
2015         pan_layout_update(pw);
2016
2017         gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
2018         gtk_widget_show(pw->window);
2019
2020         pan_window_list = g_list_append(pan_window_list, pw);
2021 }
2022
2023 /*
2024  *-----------------------------------------------------------------------------
2025  * performance warnings
2026  *-----------------------------------------------------------------------------
2027  */
2028
2029 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
2030 {
2031         FileData *dir_fd = (FileData *)data;
2032
2033         generic_dialog_close(gd);
2034
2035         pan_window_new_real(dir_fd);
2036         file_data_unref(dir_fd);
2037 }
2038
2039 static void pan_warning_hide_cb(GtkWidget *button, gpointer UNUSED(data))
2040 {
2041         gboolean hide_dlg;
2042
2043         hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
2044         pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
2045 }
2046
2047 static gboolean pan_warning(FileData *dir_fd)
2048 {
2049         GenericDialog *gd;
2050         GtkWidget *box;
2051         GtkWidget *group;
2052         GtkWidget *button;
2053         GtkWidget *ct_button;
2054         gboolean hide_dlg;
2055
2056         if (dir_fd && strcmp(dir_fd->path, G_DIR_SEPARATOR_S) == 0)
2057                 {
2058                 pan_warning_folder(dir_fd->path, NULL);
2059                 return TRUE;
2060                 }
2061
2062         if (options->thumbnails.enable_caching &&
2063             options->thumbnails.spec_standard) return FALSE;
2064
2065         if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
2066         if (hide_dlg) return FALSE;
2067
2068         gd = generic_dialog_new(_("Pan View Performance"), "pan_view_warning", NULL, FALSE,
2069                                 NULL, NULL);
2070         gd->data = file_data_ref(dir_fd);
2071         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
2072                                   pan_warning_ok_cb, TRUE);
2073
2074         box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
2075                                          _("Pan view performance may be poor."),
2076                                          _("To improve the performance of thumbnails in\npan view the following options can be enabled.\n\nNote that both options must be enabled to\nnotice a change in performance."), TRUE);
2077
2078         group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
2079         pref_spacer(group, PREF_PAD_INDENT);
2080         group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2081
2082         ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
2083                                           options->thumbnails.enable_caching, &options->thumbnails.enable_caching);
2084         button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
2085                                        options->thumbnails.spec_standard, &options->thumbnails.spec_standard);
2086         pref_checkbox_link_sensitivity(ct_button, button);
2087
2088         pref_line(box, 0);
2089
2090         pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
2091                           G_CALLBACK(pan_warning_hide_cb), NULL);
2092
2093         gtk_widget_show(gd->dialog);
2094
2095         return TRUE;
2096 }
2097
2098
2099 /*
2100  *-----------------------------------------------------------------------------
2101  * entry point
2102  *-----------------------------------------------------------------------------
2103  */
2104
2105 void pan_window_new(FileData *dir_fd)
2106 {
2107         if (pan_warning(dir_fd)) return;
2108
2109         pan_window_new_real(dir_fd);
2110 }
2111
2112
2113 /*
2114  *-----------------------------------------------------------------------------
2115  * menus
2116  *-----------------------------------------------------------------------------
2117  */
2118
2119 #define INFO_IMAGE_SIZE_KEY "image_size_data"
2120
2121
2122 static void pan_new_window_cb(GtkWidget *UNUSED(widget), gpointer data)
2123 {
2124         PanWindow *pw = (PanWindow *)data;
2125         FileData *fd;
2126
2127         fd = pan_menu_click_fd(pw);
2128         if (fd)
2129                 {
2130                 pan_fullscreen_toggle(pw, TRUE);
2131                 view_window_new(fd);
2132                 }
2133 }
2134
2135 static void pan_go_to_original_cb(GtkWidget *UNUSED(widget), gpointer data)
2136 {
2137         LayoutWindow *lw = NULL;
2138         PanWindow *pw = (PanWindow *)data;
2139         FileData *fd;
2140
2141         if (!layout_valid(&lw)) return;
2142
2143         fd = pan_menu_click_fd(pw);
2144         if (fd)
2145                 {
2146                 layout_set_fd(lw, fd);
2147                 }
2148 }
2149
2150 static void pan_edit_cb(GtkWidget *widget, gpointer data)
2151 {
2152         PanWindow *pw;
2153         FileData *fd;
2154         const gchar *key = static_cast<const gchar *>(data);
2155
2156         pw = static_cast<PanWindow *>(submenu_item_get_data(widget));
2157         if (!pw) return;
2158
2159         fd = pan_menu_click_fd(pw);
2160         if (fd)
2161                 {
2162                 if (!editor_window_flag_set(key))
2163                         {
2164                         pan_fullscreen_toggle(pw, TRUE);
2165                         }
2166                 file_util_start_editor_from_file(key, fd, pw->imd->widget);
2167                 }
2168 }
2169
2170 static void pan_zoom_in_cb(GtkWidget *UNUSED(widget), gpointer data)
2171 {
2172         PanWindow *pw = (PanWindow *)data;
2173
2174         image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
2175 }
2176
2177 static void pan_zoom_out_cb(GtkWidget *UNUSED(widget), gpointer data)
2178 {
2179         PanWindow *pw = (PanWindow *)data;
2180
2181         image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
2182 }
2183
2184 static void pan_zoom_1_1_cb(GtkWidget *UNUSED(widget), gpointer data)
2185 {
2186         PanWindow *pw = (PanWindow *)data;
2187
2188         image_zoom_set(pw->imd, 1.0);
2189 }
2190
2191 static void pan_copy_cb(GtkWidget *UNUSED(widget), gpointer data)
2192 {
2193         PanWindow *pw = (PanWindow *)data;
2194         FileData *fd;
2195
2196         fd = pan_menu_click_fd(pw);
2197         if (fd) file_util_copy(fd, NULL, NULL, pw->imd->widget);
2198 }
2199
2200 static void pan_move_cb(GtkWidget *UNUSED(widget), gpointer data)
2201 {
2202         PanWindow *pw = (PanWindow *)data;
2203         FileData *fd;
2204
2205         fd = pan_menu_click_fd(pw);
2206         if (fd) file_util_move(fd, NULL, NULL, pw->imd->widget);
2207 }
2208
2209 static void pan_rename_cb(GtkWidget *UNUSED(widget), gpointer data)
2210 {
2211         PanWindow *pw = (PanWindow *)data;
2212         FileData *fd;
2213
2214         fd = pan_menu_click_fd(pw);
2215         if (fd) file_util_rename(fd, NULL, pw->imd->widget);
2216 }
2217
2218 static void pan_delete_cb(GtkWidget *UNUSED(widget), gpointer data)
2219 {
2220         PanWindow *pw = (PanWindow *)data;
2221         FileData *fd;
2222
2223         fd = pan_menu_click_fd(pw);
2224         if (fd)
2225                 {
2226                 options->file_ops.safe_delete_enable = FALSE;
2227                 file_util_delete(fd, NULL, pw->imd->widget);
2228                 }
2229 }
2230
2231 static void pan_move_to_trash_cb(GtkWidget *UNUSED(widget), gpointer data)
2232 {
2233         PanWindow *pw = (PanWindow *)data;
2234         FileData *fd;
2235
2236         fd = pan_menu_click_fd(pw);
2237         if (fd)
2238                 {
2239                 options->file_ops.safe_delete_enable = TRUE;
2240                 file_util_delete(fd, NULL, pw->imd->widget);
2241                 }
2242 }
2243
2244 static void pan_copy_path_cb(GtkWidget *UNUSED(widget), gpointer data)
2245 {
2246         PanWindow *pw = (PanWindow *)data;
2247         FileData *fd;
2248
2249         fd = pan_menu_click_fd(pw);
2250         if (fd) file_util_copy_path_to_clipboard(fd, TRUE);
2251 }
2252
2253 static void pan_copy_path_unquoted_cb(GtkWidget *UNUSED(widget), gpointer data)
2254 {
2255         PanWindow *pw = (PanWindow *)data;
2256         FileData *fd;
2257
2258         fd = pan_menu_click_fd(pw);
2259         if (fd) file_util_copy_path_to_clipboard(fd, FALSE);
2260 }
2261
2262 static void pan_exif_date_toggle_cb(GtkWidget *widget, gpointer data)
2263 {
2264         PanWindow *pw = (PanWindow *)data;
2265
2266         pw->exif_date_enable = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
2267         pan_layout_update(pw);
2268 }
2269
2270 static void pan_info_toggle_exif_cb(GtkWidget *widget, gpointer data)
2271 {
2272         PanWindow *pw = (PanWindow *)data;
2273
2274         pw->info_includes_exif = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
2275         /** @FIXME sync info now */
2276 }
2277
2278 static void pan_info_toggle_image_cb(GtkWidget *widget, gpointer data)
2279 {
2280         PanWindow *pw = (PanWindow *)data;
2281
2282         pw->info_image_size = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), INFO_IMAGE_SIZE_KEY));
2283         /** @FIXME sync info now */
2284 }
2285
2286 static void pan_fullscreen_cb(GtkWidget *UNUSED(widget), gpointer data)
2287 {
2288         PanWindow *pw = (PanWindow *)data;
2289
2290         pan_fullscreen_toggle(pw, FALSE);
2291 }
2292
2293 static void pan_close_cb(GtkWidget *UNUSED(widget), gpointer data)
2294 {
2295         PanWindow *pw = (PanWindow *)data;
2296
2297         pan_window_close(pw);
2298 }
2299
2300 static void pan_popup_menu_destroy_cb(GtkWidget *UNUSED(widget), gpointer data)
2301 {
2302         GList *editmenu_fd_list = (GList *)data;
2303
2304         filelist_free(editmenu_fd_list);
2305 }
2306
2307 static void pan_play_cb(GtkWidget *UNUSED(widget), gpointer data)
2308 {
2309         PanWindow *pw = (PanWindow *)data;
2310
2311         start_editor_from_file(options->image_l_click_video_editor, pw->click_pi->fd);
2312 }
2313
2314 static GList *pan_view_get_fd_list(PanWindow *pw)
2315 {
2316         GList *list = NULL;
2317         FileData *fd = pan_menu_click_fd(pw);
2318
2319         if (fd) list = g_list_prepend(filelist_copy(fd->sidecar_files), file_data_ref(fd));
2320
2321         return list;
2322 }
2323
2324 /**
2325  * @brief Add file selection list to a collection
2326  * @param[in] widget 
2327  * @param[in] data Index to the collection list menu item selected, or -1 for new collection
2328  * 
2329  * 
2330  */
2331 static void pan_pop_menu_collections_cb(GtkWidget *widget, gpointer data)
2332 {
2333         PanWindow *pw;
2334         GList *selection_list = NULL;
2335
2336         pw = static_cast<PanWindow *>(submenu_item_get_data(widget));
2337         selection_list = g_list_append(selection_list, pan_menu_click_fd(pw));
2338         pop_menu_collections(selection_list, data);
2339
2340         filelist_free(selection_list);
2341 }
2342
2343 static GtkWidget *pan_popup_menu(PanWindow *pw)
2344 {
2345         GtkWidget *menu;
2346         GtkWidget *submenu;
2347         GtkWidget *item;
2348         gboolean active, video;
2349         GList *editmenu_fd_list;
2350         GtkAccelGroup *accel_group;
2351
2352         active = (pw->click_pi != NULL);
2353         video = (active && pw->click_pi->fd && pw->click_pi->fd->format_class == FORMAT_CLASS_VIDEO);
2354
2355         menu = popup_menu_short_lived();
2356         accel_group = gtk_accel_group_new();
2357         gtk_menu_set_accel_group(GTK_MENU(menu), accel_group);
2358
2359         g_object_set_data(G_OBJECT(menu), "window_keys", pan_view_window_keys);
2360         g_object_set_data(G_OBJECT(menu), "accel_group", accel_group);
2361
2362         menu_item_add_stock_sensitive(menu, _("_Play"), GTK_STOCK_MEDIA_PLAY, video,
2363                             G_CALLBACK(pan_play_cb), pw);
2364         menu_item_add_divider(menu);
2365
2366         menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
2367                             G_CALLBACK(pan_zoom_in_cb), pw);
2368         menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
2369                             G_CALLBACK(pan_zoom_out_cb), pw);
2370         menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
2371                             G_CALLBACK(pan_zoom_1_1_cb), pw);
2372         menu_item_add_divider(menu);
2373
2374         editmenu_fd_list = pan_view_get_fd_list(pw);
2375         g_signal_connect(G_OBJECT(menu), "destroy",
2376                          G_CALLBACK(pan_popup_menu_destroy_cb), editmenu_fd_list);
2377
2378         submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw, editmenu_fd_list);
2379         gtk_widget_set_sensitive(item, active);
2380
2381         menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
2382                                       G_CALLBACK(pan_new_window_cb), pw);
2383         menu_item_add_stock(menu, _("Go to original"), GTK_STOCK_FIND,
2384                         G_CALLBACK(pan_go_to_original_cb), pw);
2385
2386         menu_item_add_divider(menu);
2387         menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
2388                                       G_CALLBACK(pan_copy_cb), pw);
2389         menu_item_add_sensitive(menu, _("_Move..."), active,
2390                                 G_CALLBACK(pan_move_cb), pw);
2391         menu_item_add_sensitive(menu, _("_Rename..."), active,
2392                                 G_CALLBACK(pan_rename_cb), pw);
2393         menu_item_add_sensitive(menu, _("_Copy path to clipboard"), active,
2394                                 G_CALLBACK(pan_copy_path_cb), pw);
2395         menu_item_add_sensitive(menu, _("_Copy path unquoted to clipboard"), active,
2396                                 G_CALLBACK(pan_copy_path_unquoted_cb), pw);
2397
2398         menu_item_add_divider(menu);
2399         menu_item_add_stock_sensitive(menu,
2400                                 options->file_ops.confirm_move_to_trash ? _("Move to Trash...") :
2401                                         _("Move to Trash"), PIXBUF_INLINE_ICON_TRASH, active,
2402                                                 G_CALLBACK(pan_move_to_trash_cb), pw);
2403         menu_item_add_stock_sensitive(menu,
2404                                 options->file_ops.confirm_delete ? _("_Delete...") :
2405                                         _("_Delete"), GTK_STOCK_DELETE, active,
2406                                                 G_CALLBACK(pan_delete_cb), pw);
2407
2408         menu_item_add_divider(menu);
2409
2410         submenu = submenu_add_collections(menu, &item,
2411                                 G_CALLBACK(pan_pop_menu_collections_cb), pw);
2412         gtk_widget_set_sensitive(item, TRUE);
2413         menu_item_add_divider(menu);
2414
2415
2416         item = menu_item_add_check(menu, _("Sort by E_xif date"), pw->exif_date_enable,
2417                                    G_CALLBACK(pan_exif_date_toggle_cb), pw);
2418         gtk_widget_set_sensitive(item, (pw->layout == PAN_LAYOUT_TIMELINE || pw->layout == PAN_LAYOUT_CALENDAR));
2419
2420         menu_item_add_divider(menu);
2421
2422         menu_item_add_check(menu, _("_Show Exif information"), pw->info_includes_exif,
2423                             G_CALLBACK(pan_info_toggle_exif_cb), pw);
2424         item = menu_item_add(menu, _("Show im_age"), NULL, NULL);
2425         submenu = gtk_menu_new();
2426         gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
2427
2428         item = menu_item_add_check(submenu, _("_None"), (pw->info_image_size == PAN_IMAGE_SIZE_THUMB_NONE),
2429                                    G_CALLBACK(pan_info_toggle_image_cb), pw);
2430         g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_THUMB_NONE));
2431
2432         item = menu_item_add_check(submenu, _("_Full size"), (pw->info_image_size == PAN_IMAGE_SIZE_100),
2433                                    G_CALLBACK(pan_info_toggle_image_cb), pw);
2434         g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_100));
2435
2436         item = menu_item_add_check(submenu, _("1:2 (50%)"), (pw->info_image_size == PAN_IMAGE_SIZE_50),
2437                                    G_CALLBACK(pan_info_toggle_image_cb), pw);
2438         g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_50));
2439
2440         item = menu_item_add_check(submenu, _("1:3 (33%)"), (pw->info_image_size == PAN_IMAGE_SIZE_33),
2441                                    G_CALLBACK(pan_info_toggle_image_cb), pw);
2442         g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_33));
2443
2444         item = menu_item_add_check(submenu, _("1:4 (25%)"), (pw->info_image_size == PAN_IMAGE_SIZE_25),
2445                                    G_CALLBACK(pan_info_toggle_image_cb), pw);
2446         g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_25));
2447
2448         item = menu_item_add_check(submenu, _("1:10 (10%)"), (pw->info_image_size == PAN_IMAGE_SIZE_10),
2449                                    G_CALLBACK(pan_info_toggle_image_cb), pw);
2450         g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_10));
2451
2452
2453
2454         menu_item_add_divider(menu);
2455
2456         if (pw->fs)
2457                 {
2458                 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
2459                 }
2460         else
2461                 {
2462                 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
2463                 }
2464
2465         menu_item_add_divider(menu);
2466         menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
2467
2468         return menu;
2469 }
2470
2471
2472 /*
2473  *-----------------------------------------------------------------------------
2474  * drag and drop
2475  *-----------------------------------------------------------------------------
2476  */
2477
2478 static void pan_window_get_dnd_data(GtkWidget *UNUSED(widget), GdkDragContext *context,
2479                                     gint UNUSED(x), gint UNUSED(y),
2480                                     GtkSelectionData *selection_data, guint info,
2481                                     guint UNUSED(time), gpointer data)
2482 {
2483         PanWindow *pw = (PanWindow *)data;
2484
2485         if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
2486
2487         if (info == TARGET_URI_LIST)
2488                 {
2489                 GList *list;
2490
2491                 list = uri_filelist_from_gtk_selection_data(selection_data);
2492                 if (list && isdir(((FileData *)list->data)->path))
2493                         {
2494                         FileData *fd = static_cast<FileData *>(list->data);
2495
2496                         pan_layout_set_fd(pw, fd);
2497                         }
2498
2499                 filelist_free(list);
2500                 }
2501 }
2502
2503 static void pan_window_set_dnd_data(GtkWidget *UNUSED(widget), GdkDragContext *UNUSED(context),
2504                                     GtkSelectionData *selection_data, guint UNUSED(info),
2505                                     guint UNUSED(time), gpointer data)
2506 {
2507         PanWindow *pw = (PanWindow *)data;
2508         FileData *fd;
2509
2510         fd = pan_menu_click_fd(pw);
2511         if (fd)
2512                 {
2513                 GList *list;
2514
2515                 list = g_list_append(NULL, fd);
2516                 uri_selection_data_set_uris_from_filelist(selection_data, list);
2517                 g_list_free(list);
2518                 }
2519         else
2520                 {
2521                 gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data),
2522                                        8, NULL, 0);
2523                 }
2524 }
2525
2526 static void pan_window_dnd_init(PanWindow *pw)
2527 {
2528         GtkWidget *widget;
2529
2530         widget = pw->imd->pr;
2531
2532         gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
2533                             dnd_file_drag_types, dnd_file_drag_types_count,
2534                             static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK));
2535         g_signal_connect(G_OBJECT(widget), "drag_data_get",
2536                          G_CALLBACK(pan_window_set_dnd_data), pw);
2537
2538         gtk_drag_dest_set(widget,
2539                           static_cast<GtkDestDefaults>(GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP),
2540                           dnd_file_drop_types, dnd_file_drop_types_count,
2541                           static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK));
2542         g_signal_connect(G_OBJECT(widget), "drag_data_received",
2543                          G_CALLBACK(pan_window_get_dnd_data), pw);
2544 }
2545
2546 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */