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