Deduplicate cr3 image loader
[geeqie.git] / src / layout-image.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 "layout-image.h"
23
24 #include <cstring>
25
26 #include <gdk-pixbuf/gdk-pixbuf.h>
27 #include <gdk/gdk.h>
28 #include <gio/gio.h>
29 #include <glib-object.h>
30 #include <pango/pango.h>
31
32 #include <config.h>
33
34 #include "collect.h"
35 #include "debug.h"
36 #include "dnd.h"
37 #include "editors.h"
38 #include "exif.h"
39 #include "filedata.h"
40 #include "fullscreen.h"
41 #include "history-list.h"
42 #include "image-overlay.h"
43 #include "image.h"
44 #include "img-view.h"
45 #include "intl.h"
46 #include "layout-util.h"
47 #include "layout.h"
48 #include "main-defines.h"
49 #include "menu.h"
50 #include "metadata.h"
51 #include "misc.h"
52 #include "options.h"
53 #include "pixbuf-renderer.h"
54 #include "slideshow.h"
55 #include "ui-fileops.h"
56 #include "ui-menu.h"
57 #include "ui-utildlg.h"
58 #include "uri-utils.h"
59 #include "utilops.h"
60 #include "view-file.h"
61
62 enum {
63         FILE_COLUMN_POINTER = 0
64 };
65
66 static GtkWidget *layout_image_pop_menu(LayoutWindow *lw);
67 static void layout_image_set_buttons(LayoutWindow *lw);
68 static gboolean layout_image_animate_new_file(LayoutWindow *lw);
69 static void layout_image_animate_update_image(LayoutWindow *lw);
70
71 /*
72  *----------------------------------------------------------------------------
73  * full screen overlay
74  *----------------------------------------------------------------------------
75  */
76 #pragma GCC diagnostic push
77 #pragma GCC diagnostic ignored "-Wunused-function"
78 static void layout_image_overlay_toggle_unused(LayoutWindow *lw)
79 {
80         if (!lw) return;
81         image_osd_toggle(lw->image);
82 }
83 #pragma GCC diagnostic pop
84
85 /*
86  *----------------------------------------------------------------------------
87  * full screen
88  *----------------------------------------------------------------------------
89  */
90
91 static void layout_image_full_screen_stop_func(FullScreenData *fs, gpointer data)
92 {
93         auto lw = static_cast<LayoutWindow *>(data);
94
95         /* restore image window */
96         if (lw->image == fs->imd)
97                 lw->image = fs->normal_imd;
98
99         lw->full_screen = nullptr;
100 }
101
102 void layout_image_full_screen_start(LayoutWindow *lw)
103 {
104         if (!layout_valid(&lw)) return;
105
106         if (lw->full_screen) return;
107
108         lw->full_screen = fullscreen_start(lw->window, lw->image,
109                                            layout_image_full_screen_stop_func, lw);
110
111         /* set to new image window */
112         if (lw->full_screen->same_region)
113                 lw->image = lw->full_screen->imd;
114
115         layout_image_set_buttons(lw);
116
117         g_signal_connect(G_OBJECT(lw->full_screen->window), "key_press_event",
118                          G_CALLBACK(layout_key_press_cb), lw);
119
120         layout_actions_add_window(lw, lw->full_screen->window);
121
122         image_osd_copy_status(lw->full_screen->normal_imd, lw->image);
123         layout_image_animate_update_image(lw);
124
125         /** @FIXME This is a hack to fix #1037 Fullscreen loads black
126          * The problem occurs when zoom is set to Original Size.
127          * An extra reload is required to force the image to be displayed.
128          * See also image-view.cc real_view_window_new()
129          * This is probably not the correct solution.
130          **/
131         image_reload(lw->image);
132 }
133
134 void layout_image_full_screen_stop(LayoutWindow *lw)
135 {
136         if (!layout_valid(&lw)) return;
137         if (!lw->full_screen) return;
138
139         if (lw->image == lw->full_screen->imd)
140                 image_osd_copy_status(lw->image, lw->full_screen->normal_imd);
141
142         fullscreen_stop(lw->full_screen);
143
144         layout_image_animate_update_image(lw);
145 }
146
147 void layout_image_full_screen_toggle(LayoutWindow *lw)
148 {
149         if (!layout_valid(&lw)) return;
150         if (lw->full_screen)
151                 {
152                 layout_image_full_screen_stop(lw);
153                 }
154         else
155                 {
156                 layout_image_full_screen_start(lw);
157                 }
158 }
159
160 gboolean layout_image_full_screen_active(LayoutWindow *lw)
161 {
162         if (!layout_valid(&lw)) return FALSE;
163
164         return (lw->full_screen != nullptr);
165 }
166
167 /*
168  *----------------------------------------------------------------------------
169  * slideshow
170  *----------------------------------------------------------------------------
171  */
172
173 static void layout_image_slideshow_next(LayoutWindow *lw)
174 {
175         if (lw->slideshow) slideshow_next(lw->slideshow);
176 }
177
178 static void layout_image_slideshow_prev(LayoutWindow *lw)
179 {
180         if (lw->slideshow) slideshow_prev(lw->slideshow);
181 }
182
183 static void layout_image_slideshow_stop_func(SlideShowData *, gpointer data)
184 {
185         auto lw = static_cast<LayoutWindow *>(data);
186
187         lw->slideshow = nullptr;
188         layout_status_update_info(lw, nullptr);
189 }
190
191 void layout_image_slideshow_start(LayoutWindow *lw)
192 {
193         CollectionData *cd;
194         CollectInfo *info;
195
196         if (!layout_valid(&lw)) return;
197         if (lw->slideshow) return;
198
199         cd = image_get_collection(lw->image, &info);
200
201         if (cd && info)
202                 {
203                 lw->slideshow = slideshow_start_from_collection(lw, nullptr, cd,
204                                 layout_image_slideshow_stop_func, lw, info);
205                 }
206         else
207                 {
208                 lw->slideshow = slideshow_start(lw,
209                                 layout_list_get_index(lw, layout_image_get_fd(lw)),
210                                 layout_image_slideshow_stop_func, lw);
211                 }
212
213         layout_status_update_info(lw, nullptr);
214 }
215
216 /* note that slideshow will take ownership of the list, do not free it */
217 void layout_image_slideshow_start_from_list(LayoutWindow *lw, GList *list)
218 {
219         if (!layout_valid(&lw)) return;
220
221         if (lw->slideshow || !list)
222                 {
223                 filelist_free(list);
224                 return;
225                 }
226
227         lw->slideshow = slideshow_start_from_filelist(lw, nullptr, list,
228                                                        layout_image_slideshow_stop_func, lw);
229
230         layout_status_update_info(lw, nullptr);
231 }
232
233 void layout_image_slideshow_stop(LayoutWindow *lw)
234 {
235         if (!layout_valid(&lw)) return;
236
237         if (!lw->slideshow) return;
238
239         slideshow_free(lw->slideshow);
240         /* the stop_func sets lw->slideshow to NULL for us */
241 }
242
243 void layout_image_slideshow_toggle(LayoutWindow *lw)
244 {
245         if (!layout_valid(&lw)) return;
246
247         if (lw->slideshow)
248                 {
249                 layout_image_slideshow_stop(lw);
250                 }
251         else
252                 {
253                 layout_image_slideshow_start(lw);
254                 }
255 }
256
257 gboolean layout_image_slideshow_active(LayoutWindow *lw)
258 {
259         if (!layout_valid(&lw)) return FALSE;
260
261         return (lw->slideshow != nullptr);
262 }
263
264 gboolean layout_image_slideshow_pause_toggle(LayoutWindow *lw)
265 {
266         gboolean ret;
267
268         if (!layout_valid(&lw)) return FALSE;
269
270         ret = slideshow_pause_toggle(lw->slideshow);
271
272         layout_status_update_info(lw, nullptr);
273
274         return ret;
275 }
276
277 gboolean layout_image_slideshow_paused(LayoutWindow *lw)
278 {
279         if (!layout_valid(&lw)) return FALSE;
280
281         return (slideshow_paused(lw->slideshow));
282 }
283
284 static gboolean layout_image_slideshow_continue_check(LayoutWindow *lw)
285 {
286         if (!lw->slideshow) return FALSE;
287
288         if (!slideshow_should_continue(lw->slideshow))
289                 {
290                 layout_image_slideshow_stop(lw);
291                 return FALSE;
292                 }
293
294         return TRUE;
295 }
296
297 /*
298  *----------------------------------------------------------------------------
299  * Animation
300  *----------------------------------------------------------------------------
301  */
302
303 struct AnimationData
304 {
305         ImageWindow *iw;
306         LayoutWindow *lw;
307         GdkPixbufAnimation *gpa;
308         GdkPixbufAnimationIter *iter;
309         GdkPixbuf *gpb;
310         FileData *data_adr;
311         gint delay;
312         gboolean valid;
313         GCancellable *cancellable;
314         GFile *in_file;
315         GFileInputStream *gfstream;
316 };
317
318 static void image_animation_data_free(AnimationData *fd)
319 {
320         if(!fd) return;
321         if(fd->iter) g_object_unref(fd->iter);
322         if(fd->gpa) g_object_unref(fd->gpa);
323         if(fd->cancellable) g_object_unref(fd->cancellable);
324         g_free(fd);
325 }
326
327 static gboolean animation_should_continue(AnimationData *fd)
328 {
329         if (!fd->valid)
330                 return FALSE;
331
332         return TRUE;
333 }
334
335 static gboolean show_next_frame(gpointer data)
336 {
337         auto fd = static_cast<AnimationData*>(data);
338         int delay;
339         PixbufRenderer *pr;
340
341         if(animation_should_continue(fd)==FALSE)
342                 {
343                 image_animation_data_free(fd);
344                 return FALSE;
345                 }
346
347         pr = reinterpret_cast<PixbufRenderer*>(fd->iw->pr);
348
349         if (gdk_pixbuf_animation_iter_advance(fd->iter,nullptr)==FALSE)
350                 {
351                 /* This indicates the animation is complete.
352                    Return FALSE here to disable looping. */
353                 }
354
355         fd->gpb = gdk_pixbuf_animation_iter_get_pixbuf(fd->iter);
356         image_change_pixbuf(fd->iw,fd->gpb,pr->zoom,FALSE);
357
358         if (fd->iw->func_update)
359                 fd->iw->func_update(fd->iw, fd->iw->data_update);
360
361         delay = gdk_pixbuf_animation_iter_get_delay_time(fd->iter);
362         if (delay!=fd->delay)
363                 {
364                 if (delay>0) /* Current frame not static. */
365                         {
366                         fd->delay=delay;
367                         g_timeout_add(delay,show_next_frame,fd);
368                         }
369                 else
370                         {
371                         image_animation_data_free(fd);
372                         }
373                 return FALSE;
374                 }
375
376         return TRUE;
377 }
378
379 static gboolean layout_image_animate_check(LayoutWindow *lw)
380 {
381         if (!layout_valid(&lw)) return FALSE;
382
383         if(!lw->options.animate || lw->image->image_fd == nullptr || lw->image->image_fd->extension == nullptr || (g_ascii_strcasecmp(lw->image->image_fd->extension,".GIF")!=0 && g_ascii_strcasecmp(lw->image->image_fd->extension,".WEBP")!=0))
384                 {
385                 if(lw->animation)
386                         {
387                         lw->animation->valid = FALSE;
388                         if (lw->animation->cancellable)
389                                 {
390                                 g_cancellable_cancel(lw->animation->cancellable);
391                                 }
392                         lw->animation = nullptr;
393                         }
394                 return FALSE;
395                 }
396
397         return TRUE;
398 }
399
400 static void layout_image_animate_update_image(LayoutWindow *lw)
401 {
402         if (!layout_valid(&lw)) return;
403
404         if(lw->options.animate && lw->animation)
405                 {
406                 if (lw->full_screen && lw->image != lw->full_screen->imd)
407                         lw->animation->iw = lw->full_screen->imd;
408                 else
409                         lw->animation->iw = lw->image;
410                 }
411 }
412
413
414 static void animation_async_ready_cb(GObject *, GAsyncResult *res, gpointer data)
415 {
416         GError *error = nullptr;
417         auto animation = static_cast<AnimationData *>(data);
418
419         if (animation)
420                 {
421                 if (g_cancellable_is_cancelled(animation->cancellable))
422                         {
423                         gdk_pixbuf_animation_new_from_stream_finish(res, nullptr);
424                         g_object_unref(animation->in_file);
425                         g_object_unref(animation->gfstream);
426                         image_animation_data_free(animation);
427                         return;
428                         }
429
430                 animation->gpa = gdk_pixbuf_animation_new_from_stream_finish(res, &error);
431                 if (animation->gpa)
432                         {
433                         if (!gdk_pixbuf_animation_is_static_image(animation->gpa))
434                                 {
435                                 animation->iter = gdk_pixbuf_animation_get_iter(animation->gpa, nullptr);
436                                 if (animation->iter)
437                                         {
438                                         animation->data_adr = animation->lw->image->image_fd;
439                                         animation->delay = gdk_pixbuf_animation_iter_get_delay_time(animation->iter);
440                                         animation->valid = TRUE;
441
442                                         layout_image_animate_update_image(animation->lw);
443
444                                         g_timeout_add(animation->delay, show_next_frame, animation);
445                                         }
446                                 }
447                         }
448                 else
449                         {
450                         log_printf("Error reading GIF file: %s\n", error->message);
451                         }
452
453                 g_object_unref(animation->in_file);
454                 g_object_unref(animation->gfstream);
455                 }
456 }
457
458 static gboolean layout_image_animate_new_file(LayoutWindow *lw)
459 {
460         GFileInputStream *gfstream;
461         GError *error = nullptr;
462         AnimationData *animation;
463         GFile *in_file;
464
465         if(!layout_image_animate_check(lw)) return FALSE;
466
467         if(lw->animation) lw->animation->valid = FALSE;
468
469         if (lw->animation)
470                 {
471                 g_cancellable_cancel(lw->animation->cancellable);
472                 }
473
474         animation = g_new0(AnimationData, 1);
475         lw->animation = animation;
476         animation->lw = lw;
477         animation->cancellable = g_cancellable_new();
478
479         in_file = g_file_new_for_path(lw->image->image_fd->path);
480         animation->in_file = in_file;
481         gfstream = g_file_read(in_file, nullptr, &error);
482         if (gfstream)
483                 {
484                 animation->gfstream = gfstream;
485                 gdk_pixbuf_animation_new_from_stream_async(reinterpret_cast<GInputStream*>(gfstream), animation->cancellable, animation_async_ready_cb, animation);
486                 }
487         else
488                 {
489                 log_printf("Error reading animation file: %s\nError: %s\n", lw->image->image_fd->path, error->message);
490                 }
491
492         return TRUE;
493 }
494
495 void layout_image_animate_toggle(LayoutWindow *lw)
496 {
497         GtkAction *action;
498
499         if (!lw) return;
500
501         lw->options.animate = !lw->options.animate;
502
503         action = gtk_action_group_get_action(lw->action_group, "Animate");
504         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), lw->options.animate);
505
506         layout_image_animate_new_file(lw);
507 }
508
509 /*
510  *----------------------------------------------------------------------------
511  * pop-up menus
512  *----------------------------------------------------------------------------
513  */
514
515 static void li_pop_menu_zoom_in_cb(GtkWidget *, gpointer data)
516 {
517         auto lw = static_cast<LayoutWindow *>(data);
518
519         layout_image_zoom_adjust(lw, get_zoom_increment(), FALSE);
520 }
521
522 static void li_pop_menu_zoom_out_cb(GtkWidget *, gpointer data)
523 {
524         auto lw = static_cast<LayoutWindow *>(data);
525         layout_image_zoom_adjust(lw, -get_zoom_increment(), FALSE);
526 }
527
528 static void li_pop_menu_zoom_1_1_cb(GtkWidget *, gpointer data)
529 {
530         auto lw = static_cast<LayoutWindow *>(data);
531
532         layout_image_zoom_set(lw, 1.0, FALSE);
533 }
534
535 static void li_pop_menu_zoom_fit_cb(GtkWidget *, gpointer data)
536 {
537         auto lw = static_cast<LayoutWindow *>(data);
538
539         layout_image_zoom_set(lw, 0.0, FALSE);
540 }
541
542 static void li_pop_menu_edit_cb(GtkWidget *widget, gpointer data)
543 {
544         LayoutWindow *lw;
545         auto key = static_cast<const gchar *>(data);
546
547         lw = static_cast<LayoutWindow *>(submenu_item_get_data(widget));
548
549         if (!editor_window_flag_set(key))
550                 {
551                 layout_image_full_screen_stop(lw);
552                 }
553         file_util_start_editor_from_file(key, layout_image_get_fd(lw), lw->window);
554 }
555
556 static void li_pop_menu_alter_cb(GtkWidget *widget, gpointer data)
557 {
558         auto lw = static_cast<LayoutWindow *>(data);
559         AlterType type;
560
561         lw = static_cast<LayoutWindow *>(submenu_item_get_data(widget));
562         type = static_cast<AlterType>GPOINTER_TO_INT(data);
563
564         image_alter_orientation(lw->image, lw->image->image_fd, type);
565 }
566
567 static void li_pop_menu_new_cb(GtkWidget *, gpointer data)
568 {
569         auto lw = static_cast<LayoutWindow *>(data);
570
571         view_window_new(layout_image_get_fd(lw));
572 }
573
574 static GtkWidget *li_pop_menu_click_parent(GtkWidget *widget, LayoutWindow *lw)
575 {
576         GtkWidget *menu;
577         GtkWidget *parent;
578
579         menu = gtk_widget_get_toplevel(widget);
580         if (!menu) return nullptr;
581
582         parent = static_cast<GtkWidget *>(g_object_get_data(G_OBJECT(menu), "click_parent"));
583
584         if (!parent && lw->full_screen)
585                 {
586                 parent = lw->full_screen->imd->widget;
587                 }
588
589         return parent;
590 }
591
592 static void li_pop_menu_copy_cb(GtkWidget *widget, gpointer data)
593 {
594         auto lw = static_cast<LayoutWindow *>(data);
595
596         file_util_copy(layout_image_get_fd(lw), nullptr, nullptr,
597                        li_pop_menu_click_parent(widget, lw));
598 }
599
600 static void li_pop_menu_copy_path_cb(GtkWidget *, gpointer data)
601 {
602         auto lw = static_cast<LayoutWindow *>(data);
603
604         file_util_copy_path_to_clipboard(layout_image_get_fd(lw), TRUE);
605 }
606
607 static void li_pop_menu_copy_path_unquoted_cb(GtkWidget *, gpointer data)
608 {
609         auto lw = static_cast<LayoutWindow *>(data);
610
611         file_util_copy_path_to_clipboard(layout_image_get_fd(lw), FALSE);
612 }
613
614 #if HAVE_GTK4
615 static void li_pop_menu_copy_image_cb(GtkWidget *, gpointer data)
616 {
617 /* @FIXME GTK4 stub */
618 }
619 #else
620 static void li_pop_menu_copy_image_cb(GtkWidget *, gpointer data)
621 {
622         auto lw = static_cast<LayoutWindow *>(data);
623         ImageWindow *imd = lw->image;
624
625         GdkPixbuf *pixbuf;
626         pixbuf = image_get_pixbuf(imd);
627         if (!pixbuf) return;
628         gtk_clipboard_set_image(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), pixbuf);
629 }
630 #endif
631
632 static void li_pop_menu_move_cb(GtkWidget *widget, gpointer data)
633 {
634         auto lw = static_cast<LayoutWindow *>(data);
635
636         file_util_move(layout_image_get_fd(lw), nullptr, nullptr,
637                        li_pop_menu_click_parent(widget, lw));
638 }
639
640 static void li_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
641 {
642         auto lw = static_cast<LayoutWindow *>(data);
643
644         file_util_rename(layout_image_get_fd(lw), nullptr,
645                          li_pop_menu_click_parent(widget, lw));
646 }
647
648 static void li_pop_menu_delete_cb(GtkWidget *widget, gpointer data)
649 {
650         auto lw = static_cast<LayoutWindow *>(data);
651
652         options->file_ops.safe_delete_enable = FALSE;
653         file_util_delete(layout_image_get_fd(lw), nullptr,
654                          li_pop_menu_click_parent(widget, lw));
655 }
656
657 static void li_pop_menu_move_to_trash_cb(GtkWidget *widget, gpointer data)
658 {
659         auto lw = static_cast<LayoutWindow *>(data);
660
661         options->file_ops.safe_delete_enable = TRUE;
662         file_util_delete(layout_image_get_fd(lw), nullptr,
663                          li_pop_menu_click_parent(widget, lw));
664 }
665
666 static void li_pop_menu_slide_start_cb(GtkWidget *, gpointer data)
667 {
668         auto lw = static_cast<LayoutWindow *>(data);
669
670         layout_image_slideshow_start(lw);
671 }
672
673 static void li_pop_menu_slide_stop_cb(GtkWidget *, gpointer data)
674 {
675         auto lw = static_cast<LayoutWindow *>(data);
676
677         layout_image_slideshow_stop(lw);
678 }
679
680 static void li_pop_menu_slide_pause_cb(GtkWidget *, gpointer data)
681 {
682         auto lw = static_cast<LayoutWindow *>(data);
683
684         layout_image_slideshow_pause_toggle(lw);
685 }
686
687 static void li_pop_menu_full_screen_cb(GtkWidget *, gpointer data)
688 {
689         auto lw = static_cast<LayoutWindow *>(data);
690
691         layout_image_full_screen_toggle(lw);
692 }
693
694 static void li_pop_menu_animate_cb(GtkWidget *, gpointer data)
695 {
696         auto lw = static_cast<LayoutWindow *>(data);
697
698         layout_image_animate_toggle(lw);
699 }
700
701 static void li_pop_menu_hide_cb(GtkWidget *, gpointer data)
702 {
703         auto lw = static_cast<LayoutWindow *>(data);
704
705         layout_tools_hide_toggle(lw);
706 }
707
708 static void li_set_layout_path_cb(GtkWidget *, gpointer data)
709 {
710         auto lw = static_cast<LayoutWindow *>(data);
711         FileData *fd;
712
713         if (!layout_valid(&lw)) return;
714
715         fd = layout_image_get_fd(lw);
716         if (fd) layout_set_fd(lw, fd);
717 }
718
719 static void li_open_archive_cb(GtkWidget *, gpointer data)
720 {
721         auto lw = static_cast<LayoutWindow *>(data);
722         LayoutWindow *lw_new;
723         gchar *dest_dir;
724
725         if (!layout_valid(&lw)) return;
726
727         dest_dir = open_archive(layout_image_get_fd(lw));
728         if (dest_dir)
729                 {
730                 lw_new = layout_new_from_default();
731                 layout_set_path(lw_new, dest_dir);
732                 g_free(dest_dir);
733                 }
734         else
735                 {
736                 warning_dialog(_("Cannot open archive file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
737                 }
738 }
739
740 static gboolean li_check_if_current_path(LayoutWindow *lw, const gchar *path)
741 {
742         gchar *dirname;
743         gboolean ret;
744
745         if (!path || !layout_valid(&lw) || !lw->dir_fd) return FALSE;
746
747         dirname = g_path_get_dirname(path);
748         ret = (strcmp(lw->dir_fd->path, dirname) == 0);
749         g_free(dirname);
750         return ret;
751 }
752
753 static void layout_image_popup_menu_destroy_cb(GtkWidget *, gpointer data)
754 {
755         auto editmenu_fd_list = static_cast<GList *>(data);
756
757         filelist_free(editmenu_fd_list);
758 }
759
760 static GList *layout_image_get_fd_list(LayoutWindow *lw)
761 {
762         GList *list = nullptr;
763         FileData *fd = layout_image_get_fd(lw);
764
765         if (fd)
766                 {
767                 if (lw->vf)
768                         /* optionally include sidecars if the filelist entry is not expanded */
769                         list = vf_selection_get_one(lw->vf, fd);
770                 else
771                         list = g_list_append(nullptr, file_data_ref(fd));
772                 }
773
774         return list;
775 }
776
777 /**
778  * @brief Add file selection list to a collection
779  * @param[in] widget
780  * @param[in] data Index to the collection list menu item selected, or -1 for new collection
781  *
782  *
783  */
784 static void layout_pop_menu_collections_cb(GtkWidget *widget, gpointer data)
785 {
786         LayoutWindow *lw;
787         GList *selection_list = nullptr;
788
789         lw = static_cast<LayoutWindow *>(submenu_item_get_data(widget));
790         selection_list = g_list_append(selection_list, layout_image_get_fd(lw));
791         pop_menu_collections(selection_list, data);
792
793         filelist_free(selection_list);
794 }
795
796 static GtkWidget *layout_image_pop_menu(LayoutWindow *lw)
797 {
798         GtkWidget *menu;
799         GtkWidget *item;
800         GtkWidget *submenu;
801         const gchar *path;
802         gboolean fullscreen;
803         GList *editmenu_fd_list;
804         GtkAccelGroup *accel_group;
805
806         path = layout_image_get_path(lw);
807         fullscreen = layout_image_full_screen_active(lw);
808
809         menu = popup_menu_short_lived();
810
811         accel_group = gtk_accel_group_new();
812         gtk_menu_set_accel_group(GTK_MENU(menu), accel_group);
813
814         g_object_set_data(G_OBJECT(menu), "window_keys", nullptr);
815         g_object_set_data(G_OBJECT(menu), "accel_group", accel_group);
816
817         menu_item_add_icon(menu, _("Zoom _in"), GQ_ICON_ZOOM_IN, G_CALLBACK(li_pop_menu_zoom_in_cb), lw);
818         menu_item_add_icon(menu, _("Zoom _out"), GQ_ICON_ZOOM_OUT, G_CALLBACK(li_pop_menu_zoom_out_cb), lw);
819         menu_item_add_icon(menu, _("Zoom _1:1"), GQ_ICON_ZOOM_100, G_CALLBACK(li_pop_menu_zoom_1_1_cb), lw);
820         menu_item_add_icon(menu, _("Zoom to fit"), GQ_ICON_ZOOM_FIT, G_CALLBACK(li_pop_menu_zoom_fit_cb), lw);
821         menu_item_add_divider(menu);
822
823         editmenu_fd_list = layout_image_get_fd_list(lw);
824         g_signal_connect(G_OBJECT(menu), "destroy",
825                          G_CALLBACK(layout_image_popup_menu_destroy_cb), editmenu_fd_list);
826         submenu = submenu_add_edit(menu, &item, G_CALLBACK(li_pop_menu_edit_cb), lw, editmenu_fd_list);
827         if (!path) gtk_widget_set_sensitive(item, FALSE);
828         menu_item_add_divider(submenu);
829         item = submenu_add_alter(menu, G_CALLBACK(li_pop_menu_alter_cb), lw);
830
831         item = menu_item_add_icon(menu, _("View in _new window"), GQ_ICON_NEW, G_CALLBACK(li_pop_menu_new_cb), lw);
832         if (!path || fullscreen) gtk_widget_set_sensitive(item, FALSE);
833
834         item = menu_item_add(menu, _("_Go to directory view"), G_CALLBACK(li_set_layout_path_cb), lw);
835         if (!path || li_check_if_current_path(lw, path)) gtk_widget_set_sensitive(item, FALSE);
836
837         item = menu_item_add_icon(menu, _("Open archive"), GQ_ICON_OPEN, G_CALLBACK(li_open_archive_cb), lw);
838         if (!path || lw->image->image_fd->format_class != FORMAT_CLASS_ARCHIVE)
839                 {
840                 gtk_widget_set_sensitive(item, FALSE);
841                 }
842
843         menu_item_add_divider(menu);
844
845         item = menu_item_add_icon(menu, _("_Copy..."), GQ_ICON_COPY, G_CALLBACK(li_pop_menu_copy_cb), lw);
846         if (!path) gtk_widget_set_sensitive(item, FALSE);
847         item = menu_item_add(menu, _("_Move..."), G_CALLBACK(li_pop_menu_move_cb), lw);
848         if (!path) gtk_widget_set_sensitive(item, FALSE);
849         item = menu_item_add(menu, _("_Rename..."), G_CALLBACK(li_pop_menu_rename_cb), lw);
850         if (!path) gtk_widget_set_sensitive(item, FALSE);
851         item = menu_item_add(menu, _("_Copy path to clipboard"), G_CALLBACK(li_pop_menu_copy_path_cb), lw);
852         item = menu_item_add(menu, _("_Copy path unquoted to clipboard"), G_CALLBACK(li_pop_menu_copy_path_unquoted_cb), lw);
853         item = menu_item_add(menu, _("Copy _image to clipboard"), G_CALLBACK(li_pop_menu_copy_image_cb), lw);
854         if (!path) gtk_widget_set_sensitive(item, FALSE);
855         menu_item_add_divider(menu);
856
857         item = menu_item_add_icon(menu,
858                                 options->file_ops.confirm_move_to_trash ? _("Move to Trash...") :
859                                         _("Move to Trash"), GQ_ICON_DELETE,
860                                                                 G_CALLBACK(li_pop_menu_move_to_trash_cb), lw);
861         if (!path) gtk_widget_set_sensitive(item, FALSE);
862         item = menu_item_add_icon(menu,
863                                 options->file_ops.confirm_delete ? _("_Delete...") :
864                                         _("_Delete"), GQ_ICON_DELETE_SHRED,
865                                                                 G_CALLBACK(li_pop_menu_delete_cb), lw);
866         if (!path) gtk_widget_set_sensitive(item, FALSE);
867         menu_item_add_divider(menu);
868
869         submenu = submenu_add_collections(menu, &item,
870                                 G_CALLBACK(layout_pop_menu_collections_cb), lw);
871         gtk_widget_set_sensitive(item, TRUE);
872         menu_item_add_divider(menu);
873
874         if (layout_image_slideshow_active(lw))
875                 {
876                 menu_item_add(menu, _("Toggle _slideshow"), G_CALLBACK(li_pop_menu_slide_stop_cb), lw);
877                 if (layout_image_slideshow_paused(lw))
878                         {
879                         item = menu_item_add(menu, _("Continue slides_how"),
880                                              G_CALLBACK(li_pop_menu_slide_pause_cb), lw);
881                         }
882                 else
883                         {
884                         item = menu_item_add(menu, _("Pause slides_how"),
885                                              G_CALLBACK(li_pop_menu_slide_pause_cb), lw);
886                         }
887                 }
888         else
889                 {
890                 menu_item_add(menu, _("Toggle _slideshow"), G_CALLBACK(li_pop_menu_slide_start_cb), lw);
891                 item = menu_item_add(menu, _("Pause slides_how"), G_CALLBACK(li_pop_menu_slide_pause_cb), lw);
892                 gtk_widget_set_sensitive(item, FALSE);
893                 }
894
895         if (!fullscreen)
896                 {
897                 menu_item_add_icon(menu, _("_Full screen"), GQ_ICON_FULLSCREEN, G_CALLBACK(li_pop_menu_full_screen_cb), lw);
898                 }
899         else
900                 {
901                 menu_item_add_icon(menu, _("Exit _full screen"), GQ_ICON_LEAVE_FULLSCREEN, G_CALLBACK(li_pop_menu_full_screen_cb), lw);
902                 }
903
904         menu_item_add_check(menu, _("GIF _animation"), lw->options.animate, G_CALLBACK(li_pop_menu_animate_cb), lw);
905
906         menu_item_add_divider(menu);
907
908         item = menu_item_add_check(menu, _("Hide file _list"), lw->options.tools_hidden,
909                                    G_CALLBACK(li_pop_menu_hide_cb), lw);
910
911         item = menu_item_add_check(menu, _("Hide Selectable Bars"), lw->options.selectable_toolbars_hidden, G_CALLBACK(layout_selectable_toolbars_toggle), lw);
912         if (fullscreen) gtk_widget_set_sensitive(item, FALSE);
913
914         return menu;
915 }
916
917 void layout_image_menu_popup(LayoutWindow *lw)
918 {
919         GtkWidget *menu;
920
921         menu = layout_image_pop_menu(lw);
922         gtk_menu_popup_at_widget(GTK_MENU(menu), lw->image->widget, GDK_GRAVITY_EAST, GDK_GRAVITY_CENTER, nullptr);
923 }
924
925 /*
926  *----------------------------------------------------------------------------
927  * dnd
928  *----------------------------------------------------------------------------
929  */
930
931 static void layout_image_dnd_receive(GtkWidget *widget, GdkDragContext *,
932                                      gint, gint,
933                                      GtkSelectionData *selection_data, guint info,
934                                      guint, gpointer data)
935 {
936         auto lw = static_cast<LayoutWindow *>(data);
937         gint i;
938         gchar *url;
939
940
941         for (i = 0; i < MAX_SPLIT_IMAGES; i++)
942                 {
943                 if (lw->split_images[i] && lw->split_images[i]->pr == widget)
944                         break;
945                 }
946         if (i < MAX_SPLIT_IMAGES)
947                 {
948                 DEBUG_1("dnd image activate %d", i);
949                 layout_image_activate(lw, i, FALSE);
950                 }
951
952         if (info == TARGET_TEXT_PLAIN)
953                 {
954                 url = g_strdup(reinterpret_cast<const gchar *>(gtk_selection_data_get_data(selection_data)));
955                 download_web_file(url, FALSE, lw);
956                 g_free(url);
957                 }
958         else if (info == TARGET_URI_LIST || info == TARGET_APP_COLLECTION_MEMBER)
959                 {
960                 CollectionData *source;
961                 GList *list;
962                 GList *info_list;
963
964                 if (info == TARGET_URI_LIST)
965                         {
966                         list = uri_filelist_from_gtk_selection_data(selection_data);
967                         source = nullptr;
968                         info_list = nullptr;
969                         }
970                 else
971                         {
972                         source = collection_from_dnd_data(reinterpret_cast<const gchar *>(gtk_selection_data_get_data(selection_data)), &list, &info_list);
973                         }
974
975                 if (list)
976                         {
977                         auto fd = static_cast<FileData *>(list->data);
978
979                         if (isfile(fd->path))
980                                 {
981                                 gchar *base;
982                                 gint row;
983                                 FileData *dir_fd;
984
985                                 base = remove_level_from_path(fd->path);
986                                 dir_fd = file_data_new_dir(base);
987                                 if (dir_fd != lw->dir_fd)
988                                         {
989                                         layout_set_fd(lw, dir_fd);
990                                         }
991                                 file_data_unref(dir_fd);
992                                 g_free(base);
993
994                                 row = layout_list_get_index(lw, fd);
995                                 if (source && info_list)
996                                         {
997                                         layout_image_set_collection(lw, source, static_cast<CollectInfo *>(info_list->data));
998                                         }
999                                 else if (row == -1)
1000                                         {
1001                                         layout_image_set_fd(lw, fd);
1002                                         }
1003                                 else
1004                                         {
1005                                         layout_image_set_index(lw, row);
1006                                         }
1007                                 }
1008                         else if (isdir(fd->path))
1009                                 {
1010                                 layout_set_fd(lw, fd);
1011                                 layout_image_set_fd(lw, nullptr);
1012                                 }
1013                         }
1014
1015                 filelist_free(list);
1016                 g_list_free(info_list);
1017                 }
1018 }
1019
1020 static void layout_image_dnd_get(GtkWidget *widget, GdkDragContext *,
1021                                  GtkSelectionData *selection_data, guint,
1022                                  guint, gpointer data)
1023 {
1024         auto lw = static_cast<LayoutWindow *>(data);
1025         FileData *fd;
1026         gint i;
1027
1028
1029         for (i = 0; i < MAX_SPLIT_IMAGES; i++)
1030                 {
1031                 if (lw->split_images[i] && lw->split_images[i]->pr == widget)
1032                         break;
1033                 }
1034         if (i < MAX_SPLIT_IMAGES)
1035                 {
1036                 DEBUG_1("dnd get from %d", i);
1037                 fd = image_get_fd(lw->split_images[i]);
1038                 }
1039         else
1040                 fd = layout_image_get_fd(lw);
1041
1042         if (fd)
1043                 {
1044                 GList *list;
1045
1046                 list = g_list_append(nullptr, fd);
1047                 uri_selection_data_set_uris_from_filelist(selection_data, list);
1048                 g_list_free(list);
1049                 }
1050         else
1051                 {
1052                 gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data),
1053                                        8, nullptr, 0);
1054                 }
1055 }
1056
1057 static void layout_image_dnd_end(GtkWidget *, GdkDragContext *context, gpointer data)
1058 {
1059         auto lw = static_cast<LayoutWindow *>(data);
1060         if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
1061                 {
1062                 FileData *fd;
1063                 gint row;
1064
1065                 fd = layout_image_get_fd(lw);
1066                 row = layout_list_get_index(lw, fd);
1067                 if (row < 0) return;
1068
1069                 if (!isfile(fd->path))
1070                         {
1071                         if (static_cast<guint>(row) < layout_list_count(lw, nullptr) - 1)
1072                                 {
1073                                 layout_image_next(lw);
1074                                 }
1075                         else
1076                                 {
1077                                 layout_image_prev(lw);
1078                                 }
1079                         }
1080                 layout_refresh(lw);
1081                 }
1082 }
1083
1084 static void layout_image_dnd_init(LayoutWindow *lw, gint i)
1085 {
1086         ImageWindow *imd = lw->split_images[i];
1087
1088         gtk_drag_source_set(imd->pr, GDK_BUTTON2_MASK,
1089                             dnd_file_drag_types, dnd_file_drag_types_count,
1090                             static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK));
1091         g_signal_connect(G_OBJECT(imd->pr), "drag_data_get",
1092                          G_CALLBACK(layout_image_dnd_get), lw);
1093         g_signal_connect(G_OBJECT(imd->pr), "drag_end",
1094                          G_CALLBACK(layout_image_dnd_end), lw);
1095
1096         gtk_drag_dest_set(imd->pr,
1097                           static_cast<GtkDestDefaults>(GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP),
1098                           dnd_file_drop_types, dnd_file_drop_types_count,
1099                           static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK));
1100         g_signal_connect(G_OBJECT(imd->pr), "drag_data_received",
1101                          G_CALLBACK(layout_image_dnd_receive), lw);
1102 }
1103
1104
1105 /*
1106  *----------------------------------------------------------------------------
1107  * misc
1108  *----------------------------------------------------------------------------
1109  */
1110
1111 void layout_image_to_root(LayoutWindow *lw)
1112 {
1113         image_to_root_window(lw->image, (image_zoom_get(lw->image) == 0));
1114 }
1115
1116 /*
1117  *----------------------------------------------------------------------------
1118  * manipulation + accessors
1119  *----------------------------------------------------------------------------
1120  */
1121
1122 void layout_image_scroll(LayoutWindow *lw, gint x, gint y, gboolean connect_scroll)
1123 {
1124         gint i;
1125         if (!layout_valid(&lw)) return;
1126
1127         image_scroll(lw->image, x, y);
1128
1129         if (lw->full_screen && lw->image != lw->full_screen->imd)
1130                 {
1131                 image_scroll(lw->full_screen->imd, x, y);
1132                 }
1133
1134         if (!connect_scroll) return;
1135
1136         for (i = 0; i < MAX_SPLIT_IMAGES; i++)
1137                 {
1138                 if (lw->split_images[i] && lw->split_images[i] != lw->image)
1139                         {
1140                         image_scroll(lw->split_images[i], x, y);
1141                         }
1142                 }
1143
1144 }
1145
1146 void layout_image_zoom_adjust(LayoutWindow *lw, gdouble increment, gboolean connect_zoom)
1147 {
1148         gint i;
1149         if (!layout_valid(&lw)) return;
1150
1151         image_zoom_adjust(lw->image, increment);
1152
1153         if (lw->full_screen && lw->image != lw->full_screen->imd)
1154                 {
1155                 image_zoom_adjust(lw->full_screen->imd, increment);
1156                 }
1157
1158         if (!connect_zoom) return;
1159
1160         for (i = 0; i < MAX_SPLIT_IMAGES; i++)
1161                 {
1162                 if (lw->split_images[i] && lw->split_images[i] != lw->image)
1163                         image_zoom_adjust(lw->split_images[i], increment); ;
1164                 }
1165 }
1166
1167 void layout_image_zoom_adjust_at_point(LayoutWindow *lw, gdouble increment, gint x, gint y, gboolean connect_zoom)
1168 {
1169         gint i;
1170         if (!layout_valid(&lw)) return;
1171
1172         image_zoom_adjust_at_point(lw->image, increment, x, y);
1173
1174         if (lw->full_screen && lw->image != lw->full_screen->imd)
1175                 {
1176                 image_zoom_adjust_at_point(lw->full_screen->imd, increment, x, y);
1177                 }
1178         if (!connect_zoom && !lw->split_mode) return;
1179
1180         for (i = 0; i < MAX_SPLIT_IMAGES; i++)
1181                 {
1182                 if (lw->split_images[i] && lw->split_images[i] != lw->image &&
1183                                                 lw->split_images[i]->mouse_wheel_mode)
1184                         image_zoom_adjust_at_point(lw->split_images[i], increment, x, y);
1185                 }
1186 }
1187
1188 void layout_image_zoom_set(LayoutWindow *lw, gdouble zoom, gboolean connect_zoom)
1189 {
1190         gint i;
1191         if (!layout_valid(&lw)) return;
1192
1193         image_zoom_set(lw->image, zoom);
1194
1195         if (lw->full_screen && lw->image != lw->full_screen->imd)
1196                 {
1197                 image_zoom_set(lw->full_screen->imd, zoom);
1198                 }
1199
1200         if (!connect_zoom) return;
1201
1202         for (i = 0; i < MAX_SPLIT_IMAGES; i++)
1203                 {
1204                 if (lw->split_images[i] && lw->split_images[i] != lw->image)
1205                         image_zoom_set(lw->split_images[i], zoom);
1206                 }
1207 }
1208
1209 void layout_image_zoom_set_fill_geometry(LayoutWindow *lw, gboolean vertical, gboolean connect_zoom)
1210 {
1211         gint i;
1212         if (!layout_valid(&lw)) return;
1213
1214         image_zoom_set_fill_geometry(lw->image, vertical);
1215
1216         if (lw->full_screen && lw->image != lw->full_screen->imd)
1217                 {
1218                 image_zoom_set_fill_geometry(lw->full_screen->imd, vertical);
1219                 }
1220
1221         if (!connect_zoom) return;
1222
1223         for (i = 0; i < MAX_SPLIT_IMAGES; i++)
1224                 {
1225                 if (lw->split_images[i] && lw->split_images[i] != lw->image)
1226                         image_zoom_set_fill_geometry(lw->split_images[i], vertical);
1227                 }
1228 }
1229
1230 void layout_image_alter_orientation(LayoutWindow *lw, AlterType type)
1231 {
1232         if (!layout_valid(&lw)) return;
1233
1234         GtkTreeModel *store;
1235         GList *work;
1236         GtkTreeSelection *selection;
1237         GtkTreePath *tpath;
1238         FileData *fd_n;
1239         GtkTreeIter iter;
1240
1241         if (!lw || !lw->vf) return;
1242
1243         if (lw->vf->type == FILEVIEW_ICON)
1244                 {
1245                 if (!VFICON(lw->vf)->selection) return;
1246                 work = VFICON(lw->vf)->selection;
1247                 }
1248         else
1249                 {
1250                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(lw->vf->listview));
1251                 work = gtk_tree_selection_get_selected_rows(selection, &store);
1252                 }
1253
1254         while (work)
1255                 {
1256                 if (lw->vf->type == FILEVIEW_ICON)
1257                         {
1258                         fd_n = static_cast<FileData *>(work->data);
1259                         work = work->next;
1260                         }
1261                 else
1262                         {
1263                         tpath = static_cast<GtkTreePath *>(work->data);
1264                         gtk_tree_model_get_iter(store, &iter, tpath);
1265                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1266                         work = work->next;
1267                         }
1268
1269                 image_alter_orientation(lw->image, fd_n, type);
1270                 }
1271 }
1272
1273 static void image_alter_rating(FileData *fd_n, const gchar *rating)
1274 {
1275         metadata_write_string(fd_n, RATING_KEY, rating);
1276         read_rating_data(fd_n);
1277 }
1278
1279 void layout_image_rating(LayoutWindow *lw, const gchar *rating)
1280 {
1281         if (!layout_valid(&lw)) return;
1282
1283         GtkTreeModel *store;
1284         GList *work;
1285         GtkTreeSelection *selection;
1286         GtkTreePath *tpath;
1287         FileData *fd_n;
1288         GtkTreeIter iter;
1289
1290         if (!lw || !lw->vf) return;
1291
1292         if (lw->vf->type == FILEVIEW_ICON)
1293                 {
1294                 if (!VFICON(lw->vf)->selection) return;
1295                 work = VFICON(lw->vf)->selection;
1296                 }
1297         else
1298                 {
1299                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(lw->vf->listview));
1300                 work = gtk_tree_selection_get_selected_rows(selection, &store);
1301                 }
1302
1303         while (work)
1304                 {
1305                 if (lw->vf->type == FILEVIEW_ICON)
1306                         {
1307                         fd_n = static_cast<FileData *>(work->data);
1308                         work = work->next;
1309                         }
1310                 else
1311                         {
1312                         tpath = static_cast<GtkTreePath *>(work->data);
1313                         gtk_tree_model_get_iter(store, &iter, tpath);
1314                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1315                         work = work->next;
1316                         }
1317
1318                 image_alter_rating(fd_n, rating);
1319                 }
1320 }
1321
1322 void layout_image_reset_orientation(LayoutWindow *lw)
1323 {
1324         ImageWindow *imd= lw->image;
1325
1326         if (!layout_valid(&lw)) return;
1327         if (!imd || !imd->pr || !imd->image_fd) return;
1328
1329         if (imd->orientation < 1 || imd->orientation > 8) imd->orientation = 1;
1330
1331         if (options->image.exif_rotate_enable)
1332                 {
1333                 if (g_strcmp0(imd->image_fd->format_name, "heif") != 0)
1334                         {
1335                         imd->orientation = metadata_read_int(imd->image_fd, ORIENTATION_KEY, EXIF_ORIENTATION_TOP_LEFT);
1336                         }
1337                 else
1338                         {
1339                         imd->orientation = EXIF_ORIENTATION_TOP_LEFT;
1340                         }
1341                 }
1342         else
1343                 {
1344                 imd->orientation = 1;
1345                 }
1346
1347         if (imd->image_fd->user_orientation != 0)
1348                 {
1349                  imd->orientation = imd->image_fd->user_orientation;
1350                 }
1351
1352         pixbuf_renderer_set_orientation(reinterpret_cast<PixbufRenderer *>(imd->pr), imd->orientation);
1353 }
1354
1355 void layout_image_set_desaturate(LayoutWindow *lw, gboolean desaturate)
1356 {
1357         if (!layout_valid(&lw)) return;
1358
1359         image_set_desaturate(lw->image, desaturate);
1360 }
1361
1362 gboolean layout_image_get_desaturate(LayoutWindow *lw)
1363 {
1364         if (!layout_valid(&lw)) return FALSE;
1365
1366         return image_get_desaturate(lw->image);
1367 }
1368
1369 void layout_image_set_overunderexposed(LayoutWindow *lw, gboolean overunderexposed)
1370 {
1371         if (!layout_valid(&lw)) return;
1372
1373         image_set_overunderexposed(lw->image, overunderexposed);
1374 }
1375
1376 #pragma GCC diagnostic push
1377 #pragma GCC diagnostic ignored "-Wunused-function"
1378 gboolean layout_image_get_overunderexposed_unused(LayoutWindow *lw)
1379 {
1380         if (!layout_valid(&lw)) return FALSE;
1381
1382 //      return image_get_overunderexposed(lw->image);
1383         return FALSE;
1384 }
1385 #pragma GCC diagnostic pop
1386
1387 void layout_image_set_ignore_alpha(LayoutWindow *lw, gboolean ignore_alpha)
1388 {
1389    if (!layout_valid(&lw)) return;
1390
1391    lw->options.ignore_alpha = ignore_alpha;
1392    image_set_ignore_alpha(lw->image, ignore_alpha);
1393 }
1394
1395 /* stereo */
1396 #pragma GCC diagnostic push
1397 #pragma GCC diagnostic ignored "-Wunused-function"
1398 gint layout_image_stereo_get_unused(LayoutWindow *lw)
1399 {
1400         if (!layout_valid(&lw)) return 0;
1401
1402 //      return image_stereo_get(lw->image);
1403         return 0;
1404 }
1405
1406 void layout_image_stereo_set_unused(LayoutWindow *lw, gint stereo_mode)
1407 {
1408         if (!layout_valid(&lw)) return;
1409
1410         image_stereo_set(lw->image, stereo_mode);
1411 }
1412 void layout_image_stereo_swap_unused(LayoutWindow *lw)
1413 {
1414         if (!layout_valid(&lw)) return;
1415
1416 //      image_stereo_swap(lw->image);
1417 }
1418 #pragma GCC diagnostic pop
1419
1420 gint layout_image_stereo_pixbuf_get(LayoutWindow *lw)
1421 {
1422         if (!layout_valid(&lw)) return 0;
1423
1424         return image_stereo_pixbuf_get(lw->image);
1425 }
1426
1427 void layout_image_stereo_pixbuf_set(LayoutWindow *lw, gint stereo_mode)
1428 {
1429         if (!layout_valid(&lw)) return;
1430
1431         image_stereo_pixbuf_set(lw->image, static_cast<StereoPixbufData>(stereo_mode));
1432 }
1433
1434 const gchar *layout_image_get_path(LayoutWindow *lw)
1435 {
1436         if (!layout_valid(&lw)) return nullptr;
1437
1438         return image_get_path(lw->image);
1439 }
1440
1441 #pragma GCC diagnostic push
1442 #pragma GCC diagnostic ignored "-Wunused-function"
1443 const gchar *layout_image_get_name_unused(LayoutWindow *lw)
1444 {
1445         if (!layout_valid(&lw)) return nullptr;
1446
1447         return image_get_name(lw->image);
1448 }
1449 #pragma GCC diagnostic pop
1450
1451 FileData *layout_image_get_fd(LayoutWindow *lw)
1452 {
1453         if (!layout_valid(&lw)) return nullptr;
1454
1455         return image_get_fd(lw->image);
1456 }
1457
1458 CollectionData *layout_image_get_collection(LayoutWindow *lw, CollectInfo **info)
1459 {
1460         if (!layout_valid(&lw)) return nullptr;
1461
1462         return image_get_collection(lw->image, info);
1463 }
1464
1465 gint layout_image_get_index(LayoutWindow *lw)
1466 {
1467         return layout_list_get_index(lw, image_get_fd(lw->image));
1468 }
1469
1470 /*
1471  *----------------------------------------------------------------------------
1472  * image changers
1473  *----------------------------------------------------------------------------
1474  */
1475
1476 void layout_image_set_fd(LayoutWindow *lw, FileData *fd)
1477 {
1478         if (!layout_valid(&lw)) return;
1479
1480         image_change_fd(lw->image, fd, image_zoom_get_default(lw->image));
1481
1482         if (lw->full_screen && lw->image != lw->full_screen->imd)
1483                 {
1484                 image_change_fd(lw->full_screen->imd, fd, image_zoom_get_default(lw->full_screen->imd));
1485                 }
1486
1487
1488         layout_list_sync_fd(lw, fd);
1489         layout_image_slideshow_continue_check(lw);
1490         layout_bars_new_image(lw);
1491         layout_image_animate_new_file(lw);
1492
1493         if (fd)
1494                 {
1495                 image_chain_append_end(fd->path);
1496                 }
1497 }
1498
1499 void layout_image_set_with_ahead(LayoutWindow *lw, FileData *fd, FileData *read_ahead_fd)
1500 {
1501         if (!layout_valid(&lw)) return;
1502
1503 /** @FIXME This should be handled at the caller: in vflist_select_image
1504         if (path)
1505                 {
1506                 const gchar *old_path;
1507
1508                 old_path = layout_image_get_path(lw);
1509                 if (old_path && strcmp(path, old_path) == 0) return;
1510                 }
1511 */
1512         layout_image_set_fd(lw, fd);
1513         if (options->image.enable_read_ahead) image_prebuffer_set(lw->image, read_ahead_fd);
1514 }
1515
1516 void layout_image_set_index(LayoutWindow *lw, gint index)
1517 {
1518         FileData *fd;
1519         FileData *read_ahead_fd;
1520         gint old;
1521
1522         if (!layout_valid(&lw)) return;
1523
1524         old = layout_list_get_index(lw, layout_image_get_fd(lw));
1525         fd = layout_list_get_fd(lw, index);
1526
1527         if (old > index)
1528                 {
1529                 read_ahead_fd = layout_list_get_fd(lw, index - 1);
1530                 }
1531         else
1532                 {
1533                 read_ahead_fd = layout_list_get_fd(lw, index + 1);
1534                 }
1535
1536         if (layout_selection_count(lw, nullptr) > 1)
1537                 {
1538                 GList *x = layout_selection_list_by_index(lw);
1539                 GList *y;
1540                 GList *last;
1541
1542                 for (last = y = x; y; y = y->next)
1543                         last = y;
1544                 for (y = x; y && (GPOINTER_TO_INT(y->data)) != index; y = y->next)
1545                         ;
1546
1547                 if (y)
1548                         {
1549                         gint newindex;
1550
1551                         if ((index > old && (index != GPOINTER_TO_INT(last->data) || old != GPOINTER_TO_INT(x->data)))
1552                             || (old == GPOINTER_TO_INT(last->data) && index == GPOINTER_TO_INT(x->data)))
1553                                 {
1554                                 if (y->next)
1555                                         newindex = GPOINTER_TO_INT(y->next->data);
1556                                 else
1557                                         newindex = GPOINTER_TO_INT(x->data);
1558                                 }
1559                         else
1560                                 {
1561                                 if (y->prev)
1562                                         newindex = GPOINTER_TO_INT(y->prev->data);
1563                                 else
1564                                         newindex = GPOINTER_TO_INT(last->data);
1565                                 }
1566
1567                         read_ahead_fd = layout_list_get_fd(lw, newindex);
1568                         }
1569
1570                 while (x)
1571                         x = g_list_remove(x, x->data);
1572                 }
1573
1574         layout_image_set_with_ahead(lw, fd, read_ahead_fd);
1575 }
1576
1577 static void layout_image_set_collection_real(LayoutWindow *lw, CollectionData *cd, CollectInfo *info, gboolean forward)
1578 {
1579         if (!layout_valid(&lw)) return;
1580
1581         image_change_from_collection(lw->image, cd, info, image_zoom_get_default(lw->image));
1582         if (options->image.enable_read_ahead)
1583                 {
1584                 CollectInfo *r_info;
1585                 if (forward)
1586                         {
1587                         r_info = collection_next_by_info(cd, info);
1588                         if (!r_info) r_info = collection_prev_by_info(cd, info);
1589                         }
1590                 else
1591                         {
1592                         r_info = collection_prev_by_info(cd, info);
1593                         if (!r_info) r_info = collection_next_by_info(cd, info);
1594                         }
1595                 if (r_info) image_prebuffer_set(lw->image, r_info->fd);
1596                 }
1597
1598         layout_image_slideshow_continue_check(lw);
1599         layout_bars_new_image(lw);
1600 }
1601
1602 void layout_image_set_collection(LayoutWindow *lw, CollectionData *cd, CollectInfo *info)
1603 {
1604         layout_image_set_collection_real(lw, cd, info, TRUE);
1605         layout_list_sync_fd(lw, layout_image_get_fd(lw));
1606 }
1607
1608 void layout_image_refresh(LayoutWindow *lw)
1609 {
1610         if (!layout_valid(&lw)) return;
1611
1612         image_reload(lw->image);
1613 }
1614
1615 void layout_image_color_profile_set(LayoutWindow *lw,
1616                                     gint input_type,
1617                                     gboolean use_image)
1618 {
1619         if (!layout_valid(&lw)) return;
1620
1621         image_color_profile_set(lw->image, input_type, use_image);
1622 }
1623
1624 gboolean layout_image_color_profile_get(LayoutWindow *lw,
1625                                         gint *input_type,
1626                                         gboolean *use_image)
1627 {
1628         if (!layout_valid(&lw)) return FALSE;
1629
1630         return image_color_profile_get(lw->image, input_type, use_image);
1631 }
1632
1633 void layout_image_color_profile_set_use(LayoutWindow *lw, gboolean enable)
1634 {
1635         if (!layout_valid(&lw)) return;
1636
1637         image_color_profile_set_use(lw->image, enable);
1638 }
1639
1640 gboolean layout_image_color_profile_get_use(LayoutWindow *lw)
1641 {
1642         if (!layout_valid(&lw)) return FALSE;
1643
1644         return image_color_profile_get_use(lw->image);
1645 }
1646
1647 gboolean layout_image_color_profile_get_status(LayoutWindow *lw, gchar **image_profile, gchar **screen_profile)
1648 {
1649         if (!layout_valid(&lw)) return FALSE;
1650
1651         return image_color_profile_get_status(lw->image, image_profile, screen_profile);
1652 }
1653
1654 /*
1655  *----------------------------------------------------------------------------
1656  * list walkers
1657  *----------------------------------------------------------------------------
1658  */
1659
1660 void layout_image_next(LayoutWindow *lw)
1661 {
1662         gint current;
1663         CollectionData *cd;
1664         CollectInfo *info;
1665
1666         if (!layout_valid(&lw)) return;
1667
1668         if (layout_image_slideshow_active(lw))
1669                 {
1670                 layout_image_slideshow_next(lw);
1671                 return;
1672                 }
1673
1674         if (layout_selection_count(lw, nullptr) > 1)
1675                 {
1676                 GList *x = layout_selection_list_by_index(lw);
1677                 gint old = layout_list_get_index(lw, layout_image_get_fd(lw));
1678                 GList *y;
1679
1680                 for (y = x; y && (GPOINTER_TO_INT(y->data)) != old; y = y->next)
1681                         ;
1682                 if (y)
1683                         {
1684                         if (y->next)
1685                                 layout_image_set_index(lw, GPOINTER_TO_INT(y->next->data));
1686                         else
1687                                 {
1688                                 if (options->circular_selection_lists)
1689                                         {
1690                                         layout_image_set_index(lw, GPOINTER_TO_INT(x->data));
1691                                         }
1692                                 }
1693                         }
1694                 while (x)
1695                         x = g_list_remove(x, x->data);
1696                 if (y) /* not dereferenced */
1697                         return;
1698                 }
1699
1700         cd = image_get_collection(lw->image, &info);
1701
1702         if (cd && info)
1703                 {
1704                 info = collection_next_by_info(cd, info);
1705                 if (info)
1706                         {
1707                         layout_image_set_collection_real(lw, cd, info, TRUE);
1708                         }
1709                 else
1710                         {
1711                         image_osd_icon(lw->image, IMAGE_OSD_LAST, -1);
1712                         }
1713                 return;
1714                 }
1715
1716         current = layout_image_get_index(lw);
1717
1718         if (current >= 0)
1719                 {
1720                 if (static_cast<guint>(current) < layout_list_count(lw, nullptr) - 1)
1721                         {
1722                         layout_image_set_index(lw, current + 1);
1723                         }
1724                 else
1725                         {
1726                         image_osd_icon(lw->image, IMAGE_OSD_LAST, -1);
1727                         }
1728                 }
1729         else
1730                 {
1731                 layout_image_set_index(lw, 0);
1732                 }
1733 }
1734
1735 void layout_image_prev(LayoutWindow *lw)
1736 {
1737         gint current;
1738         CollectionData *cd;
1739         CollectInfo *info;
1740
1741         if (!layout_valid(&lw)) return;
1742
1743         if (layout_image_slideshow_active(lw))
1744                 {
1745                 layout_image_slideshow_prev(lw);
1746                 return;
1747                 }
1748
1749         if (layout_selection_count(lw, nullptr) > 1)
1750                 {
1751                 GList *x = layout_selection_list_by_index(lw);
1752                 gint old = layout_list_get_index(lw, layout_image_get_fd(lw));
1753                 GList *y;
1754                 GList *last;
1755
1756                 for (last = y = x; y; y = y->next)
1757                         last = y;
1758                 for (y = x; y && (GPOINTER_TO_INT(y->data)) != old; y = y->next)
1759                         ;
1760                 if (y)
1761                         {
1762                         if (y->prev)
1763                                 layout_image_set_index(lw, GPOINTER_TO_INT(y->prev->data));
1764                         else
1765                                 {
1766                                 if (options->circular_selection_lists)
1767                                         {
1768                                         layout_image_set_index(lw, GPOINTER_TO_INT(last->data));
1769                                         }
1770                                 }
1771                         }
1772                 while (x)
1773                         x = g_list_remove(x, x->data);
1774                 if (y) /* not dereferenced */
1775                         return;
1776                 }
1777
1778         cd = image_get_collection(lw->image, &info);
1779
1780         if (cd && info)
1781                 {
1782                 info = collection_prev_by_info(cd, info);
1783                 if (info)
1784                         {
1785                         layout_image_set_collection_real(lw, cd, info, FALSE);
1786                         }
1787                 else
1788                         {
1789                         image_osd_icon(lw->image, IMAGE_OSD_FIRST, -1);
1790                         }
1791                 return;
1792                 }
1793
1794         current = layout_image_get_index(lw);
1795
1796         if (current >= 0)
1797                 {
1798                 if (current > 0)
1799                         {
1800                         layout_image_set_index(lw, current - 1);
1801                         }
1802                 else
1803                         {
1804                         image_osd_icon(lw->image, IMAGE_OSD_FIRST, -1);
1805                         }
1806                 }
1807         else
1808                 {
1809                 layout_image_set_index(lw, layout_list_count(lw, nullptr) - 1);
1810                 }
1811 }
1812
1813 void layout_image_first(LayoutWindow *lw)
1814 {
1815         gint current;
1816         CollectionData *cd;
1817         CollectInfo *info;
1818
1819         if (!layout_valid(&lw)) return;
1820
1821         cd = image_get_collection(lw->image, &info);
1822
1823         if (cd && info)
1824                 {
1825                 CollectInfo *first_collection;
1826                 first_collection = collection_get_first(cd);
1827                 if (first_collection != info)
1828                         {
1829                         layout_image_set_collection_real(lw, cd, first_collection, TRUE);
1830                         }
1831                 return;
1832                 }
1833
1834         current = layout_image_get_index(lw);
1835         if (current != 0 && layout_list_count(lw, nullptr) > 0)
1836                 {
1837                 layout_image_set_index(lw, 0);
1838                 }
1839 }
1840
1841 void layout_image_last(LayoutWindow *lw)
1842 {
1843         gint current;
1844         gint count;
1845         CollectionData *cd;
1846         CollectInfo *info;
1847
1848         if (!layout_valid(&lw)) return;
1849
1850         cd = image_get_collection(lw->image, &info);
1851
1852         if (cd && info)
1853                 {
1854                 CollectInfo *last_collection;
1855                 last_collection = collection_get_last(cd);
1856                 if (last_collection != info)
1857                         {
1858                         layout_image_set_collection_real(lw, cd, last_collection, FALSE);
1859                         }
1860                 return;
1861                 }
1862
1863         current = layout_image_get_index(lw);
1864         count = layout_list_count(lw, nullptr);
1865         if (current != count - 1 && count > 0)
1866                 {
1867                 layout_image_set_index(lw, count - 1);
1868                 }
1869 }
1870
1871 /*
1872  *----------------------------------------------------------------------------
1873  * mouse callbacks
1874  *----------------------------------------------------------------------------
1875  */
1876
1877 static gint image_idx(LayoutWindow *lw, ImageWindow *imd)
1878 {
1879         gint i;
1880
1881         for (i = 0; i < MAX_SPLIT_IMAGES; i++)
1882                 {
1883                 if (lw->split_images[i] == imd)
1884                         break;
1885                 }
1886         if (i < MAX_SPLIT_IMAGES)
1887                 {
1888                 return i;
1889                 }
1890         return -1;
1891 }
1892
1893 static void layout_image_focus_in_cb(ImageWindow *imd, gpointer data)
1894 {
1895         auto lw = static_cast<LayoutWindow *>(data);
1896
1897         gint i = image_idx(lw, imd);
1898
1899         if (i != -1)
1900                 {
1901                 DEBUG_1("image activate focus_in %d", i);
1902                 layout_image_activate(lw, i, FALSE);
1903                 }
1904 }
1905
1906
1907 static void layout_image_button_cb(ImageWindow *imd, GdkEventButton *event, gpointer data)
1908 {
1909         auto lw = static_cast<LayoutWindow *>(data);
1910         GtkWidget *menu;
1911         LayoutWindow *lw_new;
1912         gchar *dest_dir;
1913
1914         switch (event->button)
1915                 {
1916                 case MOUSE_BUTTON_LEFT:
1917                         if (event->button == MOUSE_BUTTON_LEFT && event->type == GDK_2BUTTON_PRESS)
1918                                 {
1919                                 layout_image_full_screen_toggle(lw);
1920                                 }
1921
1922                         else if (options->image_l_click_archive && imd-> image_fd && imd->image_fd->format_class == FORMAT_CLASS_ARCHIVE)
1923                                 {
1924                                 dest_dir = open_archive(imd->image_fd);
1925                                 if (dest_dir)
1926                                         {
1927                                         lw_new = layout_new_from_default();
1928                                         layout_set_path(lw_new, dest_dir);
1929                                         g_free(dest_dir);
1930                                         }
1931                                 else
1932                                         {
1933                                         warning_dialog(_("Cannot open archive file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
1934                                         }
1935                                 }
1936                         else if (options->image_l_click_video && options->image_l_click_video_editor && imd-> image_fd && imd->image_fd->format_class == FORMAT_CLASS_VIDEO)
1937                                 {
1938                                 start_editor_from_file(options->image_l_click_video_editor, imd->image_fd);
1939                                 }
1940                         else if (options->image_lm_click_nav && lw->split_mode == SPLIT_NONE)
1941                                 layout_image_next(lw);
1942                         break;
1943                 case MOUSE_BUTTON_MIDDLE:
1944                         if (options->image_lm_click_nav && lw->split_mode == SPLIT_NONE)
1945                                 layout_image_prev(lw);
1946                         break;
1947                 case MOUSE_BUTTON_RIGHT:
1948                         menu = layout_image_pop_menu(lw);
1949                         if (imd == lw->image)
1950                                 {
1951                                 g_object_set_data(G_OBJECT(menu), "click_parent", imd->widget);
1952                                 }
1953                         gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr);
1954                         break;
1955                 default:
1956                         break;
1957                 }
1958 }
1959
1960 static void layout_image_scroll_cb(ImageWindow *imd, GdkEventScroll *event, gpointer data)
1961 {
1962         auto lw = static_cast<LayoutWindow *>(data);
1963
1964         gint i = image_idx(lw, imd);
1965
1966         if (i != -1)
1967                 {
1968                 DEBUG_1("image activate scroll %d", i);
1969                 layout_image_activate(lw, i, FALSE);
1970                 }
1971
1972
1973         if ((event->state & GDK_CONTROL_MASK) ||
1974                                 (imd->mouse_wheel_mode && !options->image_lm_click_nav))
1975                 {
1976                 switch (event->direction)
1977                         {
1978                         case GDK_SCROLL_UP:
1979                                 layout_image_zoom_adjust_at_point(lw, get_zoom_increment(), event->x, event->y, event->state & GDK_SHIFT_MASK);
1980                                 break;
1981                         case GDK_SCROLL_DOWN:
1982                                 layout_image_zoom_adjust_at_point(lw, -get_zoom_increment(), event->x, event->y, event->state & GDK_SHIFT_MASK);
1983                                 break;
1984                         default:
1985                                 break;
1986                         }
1987                 }
1988         else if (options->mousewheel_scrolls)
1989                 {
1990                 switch (event->direction)
1991                         {
1992                         case GDK_SCROLL_UP:
1993                                 image_scroll(imd, 0, -MOUSEWHEEL_SCROLL_SIZE);
1994                                 break;
1995                         case GDK_SCROLL_DOWN:
1996                                 image_scroll(imd, 0, MOUSEWHEEL_SCROLL_SIZE);
1997                                 break;
1998                         case GDK_SCROLL_LEFT:
1999                                 image_scroll(imd, -MOUSEWHEEL_SCROLL_SIZE, 0);
2000                                 break;
2001                         case GDK_SCROLL_RIGHT:
2002                                 image_scroll(imd, MOUSEWHEEL_SCROLL_SIZE, 0);
2003                                 break;
2004                         default:
2005                                 break;
2006                         }
2007                 }
2008         else
2009                 {
2010                 switch (event->direction)
2011                         {
2012                         case GDK_SCROLL_UP:
2013                                 layout_image_prev(lw);
2014                                 break;
2015                         case GDK_SCROLL_DOWN:
2016                                 layout_image_next(lw);
2017                                 break;
2018                         default:
2019                                 break;
2020                         }
2021                 }
2022 }
2023
2024 static void layout_image_drag_cb(ImageWindow *imd, GdkEventMotion *event, gdouble dx, gdouble dy, gpointer data)
2025 {
2026         gint i;
2027         auto lw = static_cast<LayoutWindow *>(data);
2028         gdouble sx;
2029         gdouble sy;
2030
2031         if (lw->full_screen && lw->image != lw->full_screen->imd &&
2032             imd != lw->full_screen->imd)
2033                 {
2034                 if (event->state & GDK_CONTROL_MASK)
2035                         {
2036                         image_get_scroll_center(imd, &sx, &sy);
2037                         }
2038                 else
2039                         {
2040                         image_get_scroll_center(lw->full_screen->imd, &sx, &sy);
2041                         sx += dx;
2042                         sy += dy;
2043                         }
2044                 image_set_scroll_center(lw->full_screen->imd, sx, sy);
2045                 }
2046
2047         if (!(event->state & GDK_SHIFT_MASK)) return;
2048
2049         for (i = 0; i < MAX_SPLIT_IMAGES; i++)
2050                 {
2051                 if (lw->split_images[i] && lw->split_images[i] != imd)
2052                         {
2053
2054                         if (event->state & GDK_CONTROL_MASK)
2055                                 {
2056                                 image_get_scroll_center(imd, &sx, &sy);
2057                                 }
2058                         else
2059                                 {
2060                                 image_get_scroll_center(lw->split_images[i], &sx, &sy);
2061                                 sx += dx;
2062                                 sy += dy;
2063                                 }
2064                         image_set_scroll_center(lw->split_images[i], sx, sy);
2065                         }
2066                 }
2067 }
2068
2069 static void layout_image_button_inactive_cb(ImageWindow *imd, GdkEventButton *event, gpointer data)
2070 {
2071         auto lw = static_cast<LayoutWindow *>(data);
2072         GtkWidget *menu;
2073         gint i = image_idx(lw, imd);
2074
2075         if (i != -1)
2076                 {
2077                 layout_image_activate(lw, i, FALSE);
2078                 }
2079
2080         switch (event->button)
2081                 {
2082                 case MOUSE_BUTTON_RIGHT:
2083                         menu = layout_image_pop_menu(lw);
2084                         if (imd == lw->image)
2085                                 {
2086                                 g_object_set_data(G_OBJECT(menu), "click_parent", imd->widget);
2087                                 }
2088                         gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr);
2089                         break;
2090                 default:
2091                         break;
2092                 }
2093
2094 }
2095
2096 static void layout_image_drag_inactive_cb(ImageWindow *imd, GdkEventMotion *event, gdouble dx, gdouble dy, gpointer data)
2097 {
2098         auto lw = static_cast<LayoutWindow *>(data);
2099         gint i = image_idx(lw, imd);
2100
2101         if (i != -1)
2102                 {
2103                 layout_image_activate(lw, i, FALSE);
2104                 }
2105
2106         /* continue as with active image */
2107         layout_image_drag_cb(imd, event, dx, dy, data);
2108 }
2109
2110
2111 static void layout_image_set_buttons(LayoutWindow *lw)
2112 {
2113         image_set_button_func(lw->image, layout_image_button_cb, lw);
2114         image_set_scroll_func(lw->image, layout_image_scroll_cb, lw);
2115 }
2116
2117 static void layout_image_set_buttons_inactive(LayoutWindow *lw, gint i)
2118 {
2119         image_set_button_func(lw->split_images[i], layout_image_button_inactive_cb, lw);
2120         image_set_scroll_func(lw->split_images[i], layout_image_scroll_cb, lw);
2121 }
2122
2123 /* Returns the length of an integer */
2124 static gint num_length(gint num)
2125 {
2126         gint len = 0;
2127         if (num < 0) num = -num;
2128         while (num)
2129                 {
2130                 num /= 10;
2131                 len++;
2132                 }
2133         return len;
2134 }
2135
2136 void layout_status_update_pixel_cb(PixbufRenderer *pr, gpointer data)
2137 {
2138         auto lw = static_cast<LayoutWindow *>(data);
2139         gint x_pixel;
2140         gint y_pixel;
2141         gint width;
2142         gint height;
2143         gchar *text;
2144         PangoAttrList *attrs;
2145
2146         if (!data || !layout_valid(&lw) || !lw->image
2147             || !lw->options.show_info_pixel || lw->image->unknown) return;
2148
2149         pixbuf_renderer_get_image_size(pr, &width, &height);
2150         if (width < 1 || height < 1) return;
2151
2152         pixbuf_renderer_get_mouse_position(pr, &x_pixel, &y_pixel);
2153
2154         if(x_pixel >= 0 && y_pixel >= 0)
2155                 {
2156                 gint r_mouse;
2157                 gint g_mouse;
2158                 gint b_mouse;
2159
2160                 pixbuf_renderer_get_pixel_colors(pr, x_pixel, y_pixel,
2161                                                  &r_mouse, &g_mouse, &b_mouse);
2162
2163                 text = g_strdup_printf(_("[%*d,%*d]: RGB(%3d,%3d,%3d)"),
2164                                          num_length(width - 1), x_pixel,
2165                                          num_length(height - 1), y_pixel,
2166                                          r_mouse, g_mouse, b_mouse);
2167
2168                 }
2169         else
2170                 {
2171                 text = g_strdup_printf(_("[%*s,%*s]: RGB(---,---,---)"),
2172                                          num_length(width - 1), " ",
2173                                          num_length(height - 1), " ");
2174                 }
2175
2176         attrs = pango_attr_list_new();
2177         pango_attr_list_insert(attrs, pango_attr_family_new("Monospace"));
2178         gtk_label_set_text(GTK_LABEL(lw->info_pixel), text);
2179         gtk_label_set_attributes(GTK_LABEL(lw->info_pixel), attrs);
2180         pango_attr_list_unref(attrs);
2181         g_free(text);
2182 }
2183
2184
2185 /*
2186  *----------------------------------------------------------------------------
2187  * setup
2188  *----------------------------------------------------------------------------
2189  */
2190
2191 static void layout_image_update_cb(ImageWindow *, gpointer data)
2192 {
2193         auto lw = static_cast<LayoutWindow *>(data);
2194         layout_status_update_image(lw);
2195 }
2196
2197 GtkWidget *layout_image_new(LayoutWindow *lw, gint i)
2198 {
2199         if (!lw->split_image_sizegroup) lw->split_image_sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
2200
2201         if (!lw->split_images[i])
2202                 {
2203                 lw->split_images[i] = image_new(TRUE);
2204
2205                 g_object_ref(lw->split_images[i]->widget);
2206
2207                 g_signal_connect(G_OBJECT(lw->split_images[i]->pr), "update-pixel",
2208                                  G_CALLBACK(layout_status_update_pixel_cb), lw);
2209
2210                 image_background_set_color_from_options(lw->split_images[i], FALSE);
2211
2212                 image_auto_refresh_enable(lw->split_images[i], TRUE);
2213
2214                 layout_image_dnd_init(lw, i);
2215                 image_color_profile_set(lw->split_images[i],
2216                                         options->color_profile.input_type,
2217                                         options->color_profile.use_image);
2218                 image_color_profile_set_use(lw->split_images[i], options->color_profile.enabled);
2219
2220                 gtk_size_group_add_widget(lw->split_image_sizegroup, lw->split_images[i]->widget);
2221                 gtk_widget_set_size_request(lw->split_images[i]->widget, IMAGE_MIN_WIDTH, -1);
2222
2223                 image_set_focus_in_func(lw->split_images[i], layout_image_focus_in_cb, lw);
2224
2225                 }
2226
2227         return lw->split_images[i]->widget;
2228 }
2229
2230 void layout_image_deactivate(LayoutWindow *lw, gint i)
2231 {
2232         if (!lw->split_images[i]) return;
2233         image_set_update_func(lw->split_images[i], nullptr, nullptr);
2234         layout_image_set_buttons_inactive(lw, i);
2235         image_set_drag_func(lw->split_images[i], layout_image_drag_inactive_cb, lw);
2236
2237         image_attach_window(lw->split_images[i], nullptr, nullptr, nullptr, FALSE);
2238         image_select(lw->split_images[i], FALSE);
2239 }
2240
2241 /* force should be set after change of lw->split_mode */
2242 void layout_image_activate(LayoutWindow *lw, gint i, gboolean force)
2243 {
2244         FileData *fd;
2245
2246         if (!lw->split_images[i]) return;
2247         if (!force && lw->active_split_image == i) return;
2248
2249         /* deactivate currently active */
2250         if (lw->active_split_image != i)
2251                 layout_image_deactivate(lw, lw->active_split_image);
2252
2253         lw->image = lw->split_images[i];
2254         lw->active_split_image = i;
2255
2256         image_set_update_func(lw->image, layout_image_update_cb, lw);
2257         layout_image_set_buttons(lw);
2258         image_set_drag_func(lw->image, layout_image_drag_cb, lw);
2259
2260         image_attach_window(lw->image, lw->window, nullptr, GQ_APPNAME, FALSE);
2261
2262         /* do not highlight selected image in SPLIT_NONE */
2263         /* maybe the image should be selected always and highlight should be controlled by
2264            another image option */
2265         if (lw->split_mode != SPLIT_NONE)
2266                 image_select(lw->split_images[i], TRUE);
2267         else
2268                 image_select(lw->split_images[i], FALSE);
2269
2270         fd = image_get_fd(lw->image);
2271
2272         if (fd)
2273                 {
2274                 layout_set_fd(lw, fd);
2275                 }
2276         layout_status_update_image(lw);
2277 }
2278
2279
2280 static void layout_image_setup_split_common(LayoutWindow *lw, gint n)
2281 {
2282         gboolean frame = (n > 1) || (!lw->options.tools_float && !lw->options.tools_hidden);
2283         gint i;
2284
2285         for (i = 0; i < n; i++)
2286                 if (!lw->split_images[i])
2287                         {
2288                         FileData *img_fd = nullptr;
2289                         double zoom = 0.0;
2290
2291                         layout_image_new(lw, i);
2292                         image_set_frame(lw->split_images[i], frame);
2293                         image_set_selectable(lw->split_images[i], (n > 1));
2294
2295                         if (lw->image)
2296                                 {
2297                                 image_osd_copy_status(lw->image, lw->split_images[i]);
2298                                 }
2299
2300                         if (layout_selection_count(lw, nullptr) > 1)
2301                                 {
2302                                 GList *work = g_list_last(layout_selection_list(lw));
2303                                 gint j = 0;
2304
2305                                 while (work && j < i)
2306                                         {
2307                                         auto fd = static_cast<FileData *>(work->data);
2308                                         work = work->prev;
2309
2310                                         if (!fd || !*fd->path || fd->parent ||
2311                                                                                 fd == lw->split_images[0]->image_fd)
2312                                                 {
2313                                                 continue;
2314                                                 }
2315                                         img_fd = fd;
2316
2317                                         j++;
2318                                         }
2319                                 }
2320
2321                         if (!img_fd && lw->image)
2322                                 {
2323                                 img_fd = image_get_fd(lw->image);
2324                                 zoom = image_zoom_get(lw->image);
2325                                 }
2326
2327                         if (img_fd)
2328                                 {
2329                                 gdouble sx;
2330                                 gdouble sy;
2331                                 image_change_fd(lw->split_images[i], img_fd, zoom);
2332                                 image_get_scroll_center(lw->image, &sx, &sy);
2333                                 image_set_scroll_center(lw->split_images[i], sx, sy);
2334                                 }
2335                         layout_image_deactivate(lw, i);
2336                         }
2337                 else
2338                         {
2339                         image_set_frame(lw->split_images[i], frame);
2340                         image_set_selectable(lw->split_images[i], (n > 1));
2341                         }
2342
2343         for (i = n; i < MAX_SPLIT_IMAGES; i++)
2344                 {
2345                 if (lw->split_images[i])
2346                         {
2347                         g_object_unref(lw->split_images[i]->widget);
2348                         lw->split_images[i] = nullptr;
2349                         }
2350                 }
2351
2352         if (!lw->image || lw->active_split_image < 0 || lw->active_split_image >= n)
2353                 {
2354                 layout_image_activate(lw, 0, TRUE);
2355                 }
2356         else
2357                 {
2358                 /* this will draw the frame around selected image (image_select)
2359                    on switch from single to split images */
2360                 layout_image_activate(lw, lw->active_split_image, TRUE);
2361                 }
2362 }
2363
2364 GtkWidget *layout_image_setup_split_none(LayoutWindow *lw)
2365 {
2366         lw->split_mode = SPLIT_NONE;
2367
2368         layout_image_setup_split_common(lw, 1);
2369
2370         lw->split_image_widget = lw->split_images[0]->widget;
2371
2372         return lw->split_image_widget;
2373 }
2374
2375
2376 GtkWidget *layout_image_setup_split_hv(LayoutWindow *lw, gboolean horizontal)
2377 {
2378         GtkWidget *paned;
2379
2380         lw->split_mode = horizontal ? SPLIT_HOR : SPLIT_VERT;
2381
2382         layout_image_setup_split_common(lw, 2);
2383
2384         /* horizontal split means vpaned and vice versa */
2385         paned = gtk_paned_new(horizontal ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL);
2386         DEBUG_NAME(paned);
2387
2388         gtk_paned_pack1(GTK_PANED(paned), lw->split_images[0]->widget, TRUE, TRUE);
2389         gtk_paned_pack2(GTK_PANED(paned), lw->split_images[1]->widget, TRUE, TRUE);
2390
2391         gtk_widget_show(lw->split_images[0]->widget);
2392         gtk_widget_show(lw->split_images[1]->widget);
2393
2394         lw->split_image_widget = paned;
2395
2396         return lw->split_image_widget;
2397
2398 }
2399
2400 static GtkWidget *layout_image_setup_split_triple(LayoutWindow *lw)
2401 {
2402         GtkWidget *hpaned1;
2403         GtkWidget *hpaned2;
2404         GtkAllocation allocation;
2405         gint i;
2406         gint pane_pos;
2407
2408         lw->split_mode = SPLIT_TRIPLE;
2409
2410         layout_image_setup_split_common(lw, 3);
2411
2412         gtk_widget_get_allocation(lw->utility_paned, &allocation);
2413
2414         hpaned1 = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
2415         DEBUG_NAME(hpaned1);
2416         hpaned2 = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
2417         DEBUG_NAME(hpaned2);
2418
2419         if (lw->bar && gtk_widget_get_visible(lw->bar))
2420                 {
2421                 pane_pos = (gtk_paned_get_position(GTK_PANED(lw->utility_paned))) / 3;
2422                 }
2423         else
2424                 {
2425                 pane_pos = allocation.width / 3;
2426                 }
2427
2428         gtk_paned_set_position(GTK_PANED(hpaned1), pane_pos);
2429         gtk_paned_set_position(GTK_PANED(hpaned2), pane_pos);
2430
2431         gtk_paned_pack1(GTK_PANED(hpaned1), lw->split_images[0]->widget, TRUE, TRUE);
2432         gtk_paned_pack1(GTK_PANED(hpaned2), lw->split_images[1]->widget, TRUE, TRUE);
2433         gtk_paned_pack2(GTK_PANED(hpaned2), lw->split_images[2]->widget, TRUE, TRUE);
2434         gtk_paned_pack2(GTK_PANED(hpaned1), hpaned2, TRUE, TRUE);
2435
2436         for (i = 0; i < 3; i++)
2437                 {
2438                 gtk_widget_show(lw->split_images[i]->widget);
2439                 }
2440
2441         gtk_widget_show(hpaned1);
2442         gtk_widget_show(hpaned2);
2443
2444         lw->split_image_widget = hpaned1;
2445
2446         return lw->split_image_widget;
2447 }
2448
2449 GtkWidget *layout_image_setup_split_quad(LayoutWindow *lw)
2450 {
2451         GtkWidget *hpaned;
2452         GtkWidget *vpaned1;
2453         GtkWidget *vpaned2;
2454         gint i;
2455
2456         lw->split_mode = SPLIT_QUAD;
2457
2458         layout_image_setup_split_common(lw, 4);
2459
2460         hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
2461         DEBUG_NAME(hpaned);
2462         vpaned1 = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
2463         DEBUG_NAME(vpaned1);
2464         vpaned2 = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
2465         DEBUG_NAME(vpaned2);
2466
2467         gtk_paned_pack1(GTK_PANED(vpaned1), lw->split_images[0]->widget, TRUE, TRUE);
2468         gtk_paned_pack2(GTK_PANED(vpaned1), lw->split_images[2]->widget, TRUE, TRUE);
2469
2470         gtk_paned_pack1(GTK_PANED(vpaned2), lw->split_images[1]->widget, TRUE, TRUE);
2471         gtk_paned_pack2(GTK_PANED(vpaned2), lw->split_images[3]->widget, TRUE, TRUE);
2472
2473         gtk_paned_pack1(GTK_PANED(hpaned), vpaned1, TRUE, TRUE);
2474         gtk_paned_pack2(GTK_PANED(hpaned), vpaned2, TRUE, TRUE);
2475
2476         for (i = 0; i < 4; i++)
2477                 gtk_widget_show(lw->split_images[i]->widget);
2478
2479         gtk_widget_show(vpaned1);
2480         gtk_widget_show(vpaned2);
2481
2482         lw->split_image_widget = hpaned;
2483
2484         return lw->split_image_widget;
2485
2486 }
2487
2488 GtkWidget *layout_image_setup_split(LayoutWindow *lw, ImageSplitMode mode)
2489 {
2490         switch (mode)
2491                 {
2492                 case SPLIT_HOR:
2493                         return layout_image_setup_split_hv(lw, TRUE);
2494                 case SPLIT_VERT:
2495                         return layout_image_setup_split_hv(lw, FALSE);
2496                 case SPLIT_TRIPLE:
2497                         return layout_image_setup_split_triple(lw);
2498                 case SPLIT_QUAD:
2499                         return layout_image_setup_split_quad(lw);
2500                 case SPLIT_NONE:
2501                 default:
2502                         return layout_image_setup_split_none(lw);
2503                 }
2504 }
2505
2506
2507 /*
2508  *-----------------------------------------------------------------------------
2509  * maintenance (for rename, move, remove)
2510  *-----------------------------------------------------------------------------
2511  */
2512
2513 static void layout_image_maint_renamed(LayoutWindow *lw, FileData *fd)
2514 {
2515         if (fd == layout_image_get_fd(lw))
2516                 {
2517                 image_set_fd(lw->image, fd);
2518                 }
2519 }
2520
2521 static void layout_image_maint_removed(LayoutWindow *lw, FileData *fd)
2522 {
2523         if (fd == layout_image_get_fd(lw))
2524                 {
2525                 CollectionData *cd;
2526                 CollectInfo *info;
2527
2528                 cd = image_get_collection(lw->image, &info);
2529                 if (cd && info)
2530                         {
2531                         CollectInfo *next_collection;
2532
2533                         next_collection = collection_next_by_info(cd, info);
2534                         if (!next_collection) next_collection = collection_prev_by_info(cd, info);
2535
2536                         if (next_collection)
2537                                 {
2538                                 layout_image_set_collection(lw, cd, next_collection);
2539                                 return;
2540                                 }
2541                         layout_image_set_fd(lw, nullptr);
2542                         }
2543
2544                 /* the image will be set to the next image from the list soon,
2545                    setting it to NULL here is not necessary*/
2546                 }
2547 }
2548
2549
2550 void layout_image_notify_cb(FileData *fd, NotifyType type, gpointer data)
2551 {
2552         auto lw = static_cast<LayoutWindow *>(data);
2553
2554         if (!(type & NOTIFY_CHANGE) || !fd->change) return;
2555
2556         DEBUG_1("Notify layout_image: %s %04x", fd->path, type);
2557
2558         switch (fd->change->type)
2559                 {
2560                 case FILEDATA_CHANGE_MOVE:
2561                 case FILEDATA_CHANGE_RENAME:
2562                         layout_image_maint_renamed(lw, fd);
2563                         break;
2564                 case FILEDATA_CHANGE_DELETE:
2565                         layout_image_maint_removed(lw, fd);
2566                         break;
2567                 case FILEDATA_CHANGE_COPY:
2568                 case FILEDATA_CHANGE_UNSPECIFIED:
2569                 case FILEDATA_CHANGE_WRITE_METADATA:
2570                         break;
2571                 }
2572
2573 }
2574 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */