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