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