1 /*
2  * Copyright © 2009-2018 Siyan Panayotov <contact@siyanpanayotov.com>
3  *
4  * This file is part of Viewnior.
5  *
6  * Viewnior is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Viewnior is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Viewnior.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <stdlib.h>
21 #include <libintl.h>
22 #include <glib/gi18n.h>
23 #define _(String) gettext (String)
24 
25 #include "config.h"
26 #include <glib/gstdio.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <errno.h>
30 #include <sys/wait.h>
31 #include "vnr-window.h"
32 #include "uni-scroll-win.h"
33 #include "uni-anim-view.h"
34 #include "vnr-tools.h"
35 #include "vnr-file.h"
36 #include "vnr-message-area.h"
37 #include "vnr-properties-dialog.h"
38 #include "vnr-crop.h"
39 #include "uni-exiv2.hpp"
40 #include "uni-utils.h"
41 
42 /* Timeout to hide the toolbar in fullscreen mode */
43 #define FULLSCREEN_TIMEOUT 1000
44 
45 G_DEFINE_TYPE (VnrWindow, vnr_window, GTK_TYPE_WINDOW);
46 
47 static void vnr_window_unfullscreen (VnrWindow *window);
48 static void stop_slideshow(VnrWindow *window);
49 static void start_slideshow(VnrWindow *window);
50 static void restart_slideshow(VnrWindow *window);
51 static void allow_slideshow(VnrWindow *window);
52 
53 static void leave_fs_cb (GtkButton *button, VnrWindow *window);
54 static void toggle_show_next_cb (GtkToggleButton *togglebutton, VnrWindow *window);
55 static void spin_value_change_cb (GtkSpinButton *spinbutton, VnrWindow *window);
56 static void save_image_cb (GtkWidget *widget, VnrWindow *window);
57 static void zoom_changed_cb (UniImageView *view, VnrWindow *window);
58 static gboolean fullscreen_timeout_cb (VnrWindow *window);
59 static gboolean leave_image_area_cb(GtkWidget * widget, GdkEventCrossing * ev, VnrWindow *window);
60 static gboolean fullscreen_motion_cb(GtkWidget * widget, GdkEventMotion * ev, VnrWindow *window);
61 static void open_with_launch_application_cb (GtkAction *action, VnrWindow *window);
62 
63 const gchar *ui_definition = "<ui>"
64   "<menubar name=\"MainMenu\">"
65     "<menu action=\"File\">"
66       "<menuitem action=\"FileOpen\"/>"
67       "<menuitem action=\"FileOpenDir\"/>"
68       "<menu action=\"FileOpenWith\">"
69          "<placeholder name=\"AppEntries\"/>"
70       "</menu>"
71       "<separator/>"
72       "<menuitem action=\"FileSave\"/>"
73       "<menuitem action=\"FileReload\"/>"
74       "<separator/>"
75       "<menuitem action=\"FileProperties\"/>"
76       "<separator/>"
77       "<menuitem action=\"FileClose\"/>"
78     "</menu>"
79     "<menu action=\"Edit\">"
80       "<menuitem action=\"FileDelete\"/>"
81       "<separator/>"
82       "<menuitem action=\"EditPreferences\"/>"
83     "</menu>"
84     "<menu action=\"View\">"
85       "<menuitem action=\"ViewMenuBar\"/>"
86       "<menuitem action=\"ViewToolbar\"/>"
87       "<menuitem action=\"ViewScrollbar\"/>"
88       "<separator/>"
89       "<menuitem action=\"ViewZoomIn\"/>"
90       "<menuitem action=\"ViewZoomOut\"/>"
91       "<menuitem action=\"ViewZoomNormal\"/>"
92       "<menuitem action=\"ViewZoomFit\"/>"
93       "<separator/>"
94       "<menuitem name=\"Fullscreen\" action=\"ViewFullscreen\"/>"
95       "<menuitem name=\"Slideshow\" action=\"ViewSlideshow\"/>"
96       "<separator/>"
97       "<menuitem name=\"ResizeWindow\" action=\"ViewResizeWindow\"/>"
98     "</menu>"
99     "<menu action=\"Image\">"
100       "<menuitem action=\"ImageFlipVertical\"/>"
101       "<menuitem action=\"ImageFlipHorizontal\"/>"
102       "<separator/>"
103       "<menuitem action=\"ImageRotateCW\"/>"
104       "<menuitem action=\"ImageRotateCCW\"/>"
105       "<separator/>"
106       "<menuitem action=\"ImageCrop\"/>"
107       "<placeholder name=\"WallpaperEntry\"/>"
108     "</menu>"
109     "<menu action=\"Go\">"
110       "<menuitem name=\"GoPrevious\" action=\"GoPrevious\"/>"
111       "<menuitem name=\"GoNext\" action=\"GoNext\"/>"
112       "<separator/>"
113       "<menuitem name=\"GoFirst\" action=\"GoFirst\"/>"
114       "<menuitem name=\"GoLast\" action=\"GoLast\"/>"
115     "</menu>"
116     "<menu action=\"Help\">"
117       "<menuitem action=\"HelpAbout\"/>"
118     "</menu>"
119   "</menubar>"
120   "<popup name=\"ButtonMenu\">"
121     "<menuitem action=\"FileOpen\"/>"
122     "<menuitem action=\"FileOpenDir\"/>"
123     "<menu action=\"FileOpenWith\">"
124        "<placeholder name=\"AppEntries\"/>"
125     "</menu>"
126     "<separator/>"
127     "<menuitem action=\"FileSave\"/>"
128     "<menuitem action=\"FileReload\"/>"
129     "<menuitem action=\"FileProperties\"/>"
130     "<separator/>"
131     "<menu action=\"Edit\">"
132       "<menuitem action=\"FileDelete\"/>"
133       "<separator/>"
134     "</menu>"
135     "<menu action=\"View\">"
136       "<menuitem action=\"ViewZoomIn\"/>"
137       "<menuitem action=\"ViewZoomOut\"/>"
138       "<menuitem action=\"ViewZoomNormal\"/>"
139       "<menuitem action=\"ViewZoomFit\"/>"
140       "<separator/>"
141       "<menuitem action=\"ViewMenuBar\"/>"
142       "<menuitem action=\"ViewToolbar\"/>"
143       "<menuitem action=\"ViewScrollbar\"/>"
144       "<menuitem name=\"Fullscreen\" action=\"ViewFullscreen\"/>"
145       "<menuitem name=\"Slideshow\" action=\"ViewSlideshow\"/>"
146       "<separator/>"
147       "<menuitem name=\"ResizeWindow\" action=\"ViewResizeWindow\"/>"
148     "</menu>"
149     "<menu action=\"Image\">"
150       "<menuitem action=\"ImageFlipVertical\"/>"
151       "<menuitem action=\"ImageFlipHorizontal\"/>"
152       "<separator/>"
153       "<menuitem action=\"ImageRotateCW\"/>"
154       "<menuitem action=\"ImageRotateCCW\"/>"
155       "<separator/>"
156       "<menuitem action=\"ImageCrop\"/>"
157       "<placeholder name=\"WallpaperEntry\"/>"
158     "</menu>"
159     "<separator/>"
160     "<menuitem action=\"EditPreferences\"/>"
161     "<separator/>"
162     "<menuitem action=\"HelpAbout\"/>"
163     "<menuitem action=\"FileClose\"/>"
164   "</popup>"
165   "<toolbar name=\"Toolbar\">"
166     "<toolitem action=\"GoPrevious\"/>"
167     "<toolitem action=\"GoNext\"/>"
168     "<separator/>"
169     "<toolitem action=\"ViewZoomIn\"/>"
170     "<toolitem action=\"ViewZoomOut\"/>"
171     "<toolitem action=\"ViewZoomNormal\"/>"
172     "<toolitem action=\"ViewZoomFit\"/>"
173     "<separator/>"
174     "<toolitem action=\"ImageRotateCCW\"/>"
175     "<toolitem action=\"ImageRotateCW\"/>"
176     "<separator expand=\"true\"/>"
177     "<toolitem action=\"Properties\"/>"
178   "</toolbar>"
179   "<popup name=\"PopupMenu\">"
180     "<menuitem name=\"GoPrevious\" action=\"GoPrevious\"/>"
181     "<menuitem name=\"GoNext\" action=\"GoNext\"/>"
182     "<separator/>"
183     "<menu action=\"FileOpenWith\">"
184        "<placeholder name=\"AppEntries\"/>"
185     "</menu>"
186     "<separator/>"
187     "<menuitem action=\"ViewZoomIn\"/>"
188     "<menuitem action=\"ViewZoomOut\"/>"
189     "<menuitem action=\"ViewZoomNormal\"/>"
190     "<placeholder name=\"WallpaperEntry\"/>"
191     "<separator/>"
192     "<menuitem name=\"MenuBar\" action=\"ViewMenuBar\"/>"
193     "<menuitem name=\"Toolbar\" action=\"ViewToolbar\"/>"
194     "<menuitem name=\"Scrollbar\" action=\"ViewScrollbar\"/>"
195     "<menuitem name=\"Fullscreen\" action=\"ViewFullscreen\"/>"
196     "<separator/>"
197     "<menuitem action=\"FileProperties\"/>"
198   "</popup>"
199   "<accelerator name=\"ControlEqualAccel\" action=\"ControlEqual\"/>"
200   "<accelerator name=\"ControlKPAddAccel\" action=\"ControlKpAdd\"/>"
201   "<accelerator name=\"ControlKPSubAccel\" action=\"ControlKpSub\"/>"
202   "<accelerator name=\"DeleteAccel\" action=\"Delete\"/>"
203 "</ui>";
204 
205 
206 const gchar *ui_definition_wallpaper = "<ui>"
207   "<menubar name=\"MainMenu\">"
208     "<menu action=\"Image\">"
209       "<placeholder name=\"WallpaperEntry\">"
210         "<separator/>"
211         "<menuitem name=\"Wallpaper\" action=\"SetAsWallpaper\"/>"
212       "</placeholder>"
213     "</menu>"
214   "</menubar>"
215   "<popup name=\"ButtonMenu\">"
216     "<menu action=\"Image\">"
217       "<placeholder name=\"WallpaperEntry\">"
218         "<separator/>"
219         "<menuitem name=\"Wallpaper\" action=\"SetAsWallpaper\"/>"
220       "</placeholder>"
221     "</menu>"
222   "</popup>"
223   "<popup name=\"PopupMenu\">"
224     "<placeholder name=\"WallpaperEntry\">"
225       "<separator/>"
226       "<menuitem action=\"SetAsWallpaper\"/>"
227     "</placeholder>"
228   "</popup>"
229 "</ui>";
230 
231 /*************************************************************/
232 /***** Private actions ***************************************/
233 /*************************************************************/
234 /* Modified version of eog's eog_window_update_openwith_menu */
235 static void
vnr_window_update_openwith_menu(VnrWindow * window)236 vnr_window_update_openwith_menu (VnrWindow *window)
237 {
238     GFile *file;
239     GFileInfo *file_info;
240     GList *iter;
241     gchar *label, *tip;
242     const gchar *mime_type;
243     GtkAction *action;
244     GList *apps;
245     guint action_id = 0;
246 
247     file = g_file_new_for_path ((gchar*)VNR_FILE(window->file_list->data)->path);
248     file_info = g_file_query_info (file,
249                        G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
250                        0, NULL, NULL);
251 
252     if (file_info == NULL)
253         return;
254     else
255         mime_type = g_file_info_get_content_type (file_info);
256 
257     if (window->open_with_menu_id != 0)
258     {
259            gtk_ui_manager_remove_ui (window->ui_mngr, window->open_with_menu_id);
260            window->open_with_menu_id = 0;
261     }
262 
263     if (window->actions_open_with != NULL)
264     {
265           gtk_ui_manager_remove_action_group (window->ui_mngr, window->actions_open_with);
266           window->actions_open_with = NULL;
267     }
268 
269     if (mime_type == NULL)
270     {
271             g_object_unref (file_info);
272             return;
273     }
274 
275     apps = g_app_info_get_all_for_type (mime_type);
276 
277     g_object_unref (file_info);
278 
279     if (!apps)
280             return;
281 
282     window->actions_open_with = gtk_action_group_new ("OpenWithActions");
283     gtk_ui_manager_insert_action_group (window->ui_mngr, window->actions_open_with, -1);
284 
285     window->open_with_menu_id = gtk_ui_manager_new_merge_id (window->ui_mngr);
286 
287     for (iter = apps; iter; iter = iter->next) {
288         GAppInfo *app = iter->data;
289         gchar name[64];
290 
291         /* Do not include viewnior itself */
292         if (g_ascii_strcasecmp (g_app_info_get_executable (app),
293                                 g_get_prgname ()) == 0)
294         {
295                 g_object_unref (app);
296                 continue;
297         }
298 
299         g_snprintf (name, sizeof (name), "OpenWith%u", action_id++);
300 
301         label = g_strdup (g_app_info_get_name (app));
302         tip = g_strdup_printf (_("Use \"%s\" to open the selected image"), g_app_info_get_name (app));
303         action = gtk_action_new (name, label, tip, NULL);
304 
305         g_free (label);
306         g_free (tip);
307 
308         g_object_set_data_full (G_OBJECT (action), "app", app,
309                                 (GDestroyNotify) g_object_unref);
310 
311         g_signal_connect (action,
312                           "activate",
313                           G_CALLBACK (open_with_launch_application_cb),
314                           window);
315 
316         gtk_action_group_add_action (window->actions_open_with, action);
317         g_object_unref (action);
318 
319         gtk_ui_manager_add_ui (window->ui_mngr,
320                         window->open_with_menu_id,
321                         "/MainMenu/File/FileOpenWith/AppEntries",
322                         name,
323                         name,
324                         GTK_UI_MANAGER_MENUITEM,
325                         FALSE);
326         gtk_ui_manager_add_ui (window->ui_mngr,
327                         window->open_with_menu_id,
328                         "/MainMenu/FileOpenWith/AppEntries",
329                         name,
330                         name,
331                         GTK_UI_MANAGER_MENUITEM,
332                         FALSE);
333         gtk_ui_manager_add_ui (window->ui_mngr,
334                         window->open_with_menu_id,
335                         "/PopupMenu/FileOpenWith/AppEntries",
336                         name,
337                         name,
338                         GTK_UI_MANAGER_MENUITEM,
339                         FALSE);
340     }
341 
342     g_list_free (apps);
343 }
344 
345 static void
vnr_window_save_accel_map()346 vnr_window_save_accel_map()
347 {
348     gchar *accelfile = g_build_filename (g_get_user_config_dir(), PACKAGE,
349                                          "accel_map", NULL);
350 
351     gtk_accel_map_save (accelfile);
352     g_free (accelfile);
353 }
354 
355 static void
vnr_window_load_accel_map()356 vnr_window_load_accel_map()
357 {
358     gchar *accelfile = g_build_filename (g_get_user_config_dir(), PACKAGE,
359                                          "accel_map", NULL);
360 
361     gtk_accel_map_load (accelfile);
362     g_free (accelfile);
363 }
364 
365 static void
vnr_window_hide_cursor(VnrWindow * window)366 vnr_window_hide_cursor(VnrWindow *window)
367 {
368     gdk_window_set_cursor (gtk_widget_get_window(GTK_WIDGET(window)), gdk_cursor_new(GDK_BLANK_CURSOR));
369     window->cursor_is_hidden = TRUE;
370     gdk_flush();
371 }
372 
373 static void
vnr_window_show_cursor(VnrWindow * window)374 vnr_window_show_cursor(VnrWindow *window)
375 {
376     gdk_window_set_cursor (gtk_widget_get_window(GTK_WIDGET(window)), gdk_cursor_new(GDK_LEFT_PTR));
377     window->cursor_is_hidden = FALSE;
378     gdk_flush();
379 }
380 
381 static void
update_fs_filename_label(VnrWindow * window)382 update_fs_filename_label(VnrWindow *window)
383 {
384     if(window->mode == VNR_WINDOW_MODE_NORMAL)
385         return;
386 
387     gint position, total;
388     char *buf = NULL;
389 
390     get_position_of_element_in_list(window->file_list, &position, &total);
391     buf = g_strdup_printf ("%s - %i/%i",
392                            VNR_FILE(window->file_list->data)->display_name,
393                            position, total);
394 
395     gtk_label_set_text(GTK_LABEL(window->fs_filename_label), buf);
396 
397     g_free(buf);
398 }
399 
400 static gboolean
next_image_src(VnrWindow * window)401 next_image_src(VnrWindow *window)
402 {
403     if(g_list_length(g_list_first(window->file_list)) <= 1)
404         return FALSE;
405     else
406         vnr_window_next(window, FALSE);
407 
408     window->ss_source_tag = g_timeout_add_seconds (window->ss_timeout,
409                                                    (GSourceFunc)next_image_src,
410                                                    window);
411 
412     return FALSE;
413 }
414 
415 static void
fullscreen_unset_timeout(VnrWindow * window)416 fullscreen_unset_timeout(VnrWindow *window)
417 {
418     if(window->fs_source != NULL)
419     {
420         g_source_unref (window->fs_source);
421         g_source_destroy (window->fs_source);
422         window->fs_source = NULL;
423     }
424 }
425 
426 static void
fullscreen_set_timeout(VnrWindow * window)427 fullscreen_set_timeout(VnrWindow *window)
428 {
429     fullscreen_unset_timeout(window);
430 
431     window->fs_source = g_timeout_source_new (FULLSCREEN_TIMEOUT);
432     g_source_set_callback (window->fs_source,
433                            (GSourceFunc)fullscreen_timeout_cb,
434                            window, NULL);
435 
436     g_source_attach (window->fs_source, NULL);
437 }
438 
439 static GtkWidget *
get_fs_controls(VnrWindow * window)440 get_fs_controls(VnrWindow *window)
441 {
442     if(window->fs_controls != NULL)
443         return window->fs_controls;
444 
445     GtkWidget *box;
446     GtkToolItem *item;
447     GtkWidget *widget;
448     GtkAdjustment *spinner_adj;
449 
450     /* Tool item, that contains the hbox */
451     item = gtk_tool_item_new();
452     gtk_tool_item_set_expand(item, TRUE);
453 
454     box = gtk_hbox_new(FALSE, 0);
455     gtk_container_add (GTK_CONTAINER (item), box);
456 
457     widget = gtk_button_new_from_stock(GTK_STOCK_LEAVE_FULLSCREEN);
458     g_signal_connect(widget, "clicked", G_CALLBACK(leave_fs_cb), window);
459     gtk_box_pack_end (GTK_BOX(box), widget, FALSE, FALSE, 0);
460 
461     /* Create label for the current image's filename */
462     widget = gtk_label_new(NULL);
463     gtk_label_set_ellipsize (GTK_LABEL(widget), PANGO_ELLIPSIZE_END);
464     gtk_label_set_selectable (GTK_LABEL(widget), TRUE);
465     window->fs_filename_label = widget;
466     gtk_box_pack_end (GTK_BOX(box), widget, TRUE, TRUE, 10);
467 
468 
469     widget = gtk_vseparator_new();
470     gtk_box_pack_start (GTK_BOX(box), widget, FALSE, FALSE, 0);
471 
472     widget = gtk_check_button_new_with_label(_("Show next image after: "));
473     g_signal_connect (widget, "toggled", G_CALLBACK(toggle_show_next_cb),
474                       window);
475     gtk_box_pack_start (GTK_BOX(box), widget, FALSE, FALSE, 0);
476     window->toggle_btn = widget;
477 
478     /* Create spin button to adjust slideshow's timeout */
479     //spinner_adj = (GtkAdjustment *) gtk_adjustment_new (5, 1.0, 30.0, 1.0, 1.0, 0);
480     spinner_adj = (GtkAdjustment *) gtk_adjustment_new (window->prefs->slideshow_timeout, 1.0, 30.0, 1.0, 1.0, 0);
481     widget = gtk_spin_button_new (spinner_adj, 1.0, 0);
482     gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON(widget), TRUE);
483     gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON(widget),
484                                        GTK_UPDATE_ALWAYS);
485     g_signal_connect (widget, "value-changed",
486                       G_CALLBACK(spin_value_change_cb), window);
487     gtk_box_pack_start (GTK_BOX(box), widget, FALSE, FALSE, 0);
488     window->ss_timeout_widget = widget;
489 
490     window->fs_seconds_label = gtk_label_new(ngettext(" second", " seconds", 5));
491     gtk_box_pack_start (GTK_BOX(box), window->fs_seconds_label, FALSE, FALSE, 0);
492 
493     window->fs_controls = GTK_WIDGET(item);
494 
495     gtk_widget_show_all (window->fs_controls);
496     return window->fs_controls;
497 }
498 
499 static void
vnr_window_set_drag(VnrWindow * window)500 vnr_window_set_drag(VnrWindow *window)
501 {
502     gtk_drag_dest_set (GTK_WIDGET (window),
503                        GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
504                        NULL, 0,
505                        GDK_ACTION_COPY | GDK_ACTION_ASK);
506     gtk_drag_dest_add_uri_targets (GTK_WIDGET (window));
507 }
508 
509 static void
vnr_window_fullscreen(VnrWindow * window)510 vnr_window_fullscreen(VnrWindow *window)
511 {
512     GdkColor color;
513     GtkAction *action;
514 
515     gdk_color_parse ("black", &color);
516 
517     gtk_widget_hide(window->menu_bar);
518     gtk_window_fullscreen(GTK_WINDOW(window));
519 
520     window->mode = VNR_WINDOW_MODE_FULLSCREEN;
521     action = gtk_action_group_get_action (window->actions_image,
522                                           "ViewFullscreen");
523 
524     gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
525     gtk_widget_modify_bg(window->view, GTK_STATE_NORMAL, &color);
526 
527     if (window->prefs->fit_on_fullscreen)
528         uni_image_view_set_zoom_mode (UNI_IMAGE_VIEW(window->view),
529                                       VNR_PREFS_ZOOM_FIT);
530 
531     update_fs_filename_label(window);
532     gtk_widget_hide (window->toolbar);
533 
534     if (window->prefs->show_menu_bar)
535         gtk_widget_show (window->properties_button);
536 
537     gtk_widget_show (window->fs_controls);
538 
539     stop_slideshow(window);
540 
541     /* Reset timeouts for the toolbar autohide when the mouse
542      * moves over the UniImageviewer.
543      * "after" because it must be called after the uniImageView's
544      * callback (when the image is dragged).*/
545     g_signal_connect_after (window->view,
546                             "motion-notify-event",
547                             G_CALLBACK (fullscreen_motion_cb),
548                             window);
549 
550     /* Never hide the toolbar, while the mouse is over it */
551     g_signal_connect (window->toolbar,
552                       "enter-notify-event",
553                       G_CALLBACK (leave_image_area_cb),
554                       window);
555 
556     g_signal_connect (window->msg_area,
557                       "enter-notify-event",
558                       G_CALLBACK (leave_image_area_cb),
559                       window);
560 
561     fullscreen_set_timeout(window);
562 }
563 
564 static void
vnr_window_unfullscreen(VnrWindow * window)565 vnr_window_unfullscreen(VnrWindow *window)
566 {
567     if(window->mode == VNR_WINDOW_MODE_NORMAL)
568         return;
569 
570     GtkAction *action;
571 
572     stop_slideshow(window);
573     window->mode = VNR_WINDOW_MODE_NORMAL;
574 
575     gtk_widget_show(window->menu_bar);
576     gtk_window_unfullscreen(GTK_WINDOW(window));
577     action = gtk_action_group_get_action (window->actions_image,
578                                           "ViewFullscreen");
579 
580     gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
581     gtk_widget_modify_bg(window->view, GTK_STATE_NORMAL, NULL);
582 
583     if (window->prefs->fit_on_fullscreen)
584         uni_image_view_set_zoom_mode (UNI_IMAGE_VIEW(window->view),
585                                       window->prefs->zoom);
586 
587     if(window->prefs->show_menu_bar)
588         gtk_widget_hide (window->properties_button);
589     else
590         gtk_widget_hide (window->menu_bar);
591 
592     gtk_widget_hide (window->fs_controls);
593 
594     if(!window->prefs->show_toolbar)
595         gtk_widget_hide (window->toolbar);
596     else
597         gtk_widget_show (window->toolbar);
598 
599     g_signal_handlers_disconnect_by_func(window->view,
600                                          G_CALLBACK(fullscreen_motion_cb),
601                                          window);
602 
603     g_signal_handlers_disconnect_by_func(window->toolbar,
604                                          G_CALLBACK(leave_image_area_cb),
605                                          window);
606 
607     g_signal_handlers_disconnect_by_func(window->msg_area,
608                                          G_CALLBACK(leave_image_area_cb),
609                                          window);
610 
611     fullscreen_unset_timeout(window);
612     vnr_window_show_cursor(window);
613 }
614 
615 static void
stop_slideshow(VnrWindow * window)616 stop_slideshow(VnrWindow *window)
617 {
618     if(!window->slideshow)
619         return;
620 
621     if(window->mode != VNR_WINDOW_MODE_SLIDESHOW)
622         return;
623 
624     GtkAction *action;
625 
626     action = gtk_action_group_get_action (window->actions_collection,
627                                           "ViewSlideshow");
628 
629     window->slideshow = FALSE;
630     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(window->toggle_btn), FALSE);
631     gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
632     window->slideshow = TRUE;
633 
634     window->mode = VNR_WINDOW_MODE_FULLSCREEN;
635 
636     g_source_remove (window->ss_source_tag);
637 }
638 
639 static void
start_slideshow(VnrWindow * window)640 start_slideshow(VnrWindow *window)
641 {
642     if(!window->slideshow)
643         return;
644 
645     if(window->mode == VNR_WINDOW_MODE_SLIDESHOW)
646         return;
647 
648     window->mode = VNR_WINDOW_MODE_SLIDESHOW;
649 
650     window->ss_source_tag = g_timeout_add_seconds (window->ss_timeout,
651                                                    (GSourceFunc)next_image_src,
652                                                    window);
653 
654     GtkAction *action;
655 
656     action = gtk_action_group_get_action (window->actions_collection,
657                                           "ViewSlideshow");
658 
659     window->slideshow = FALSE;
660     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(window->toggle_btn), TRUE);
661     gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
662     window->slideshow = TRUE;
663 }
664 
665 static void
restart_slideshow(VnrWindow * window)666 restart_slideshow(VnrWindow *window)
667 {
668     if(!window->slideshow)
669         return;
670 
671     if(window->mode != VNR_WINDOW_MODE_SLIDESHOW)
672         return;
673 
674     g_source_remove (window->ss_source_tag);
675     window->ss_source_tag = g_timeout_add_seconds (window->ss_timeout,
676                                                    (GSourceFunc)next_image_src,
677                                                    window);
678 }
679 
680 static void
allow_slideshow(VnrWindow * window)681 allow_slideshow(VnrWindow *window)
682 {
683     if(window->slideshow)
684         return;
685 
686     window->slideshow = TRUE;
687 
688     gtk_widget_set_sensitive(window->toggle_btn, TRUE);
689 }
690 
691 void
deny_slideshow(VnrWindow * window)692 deny_slideshow(VnrWindow *window)
693 {
694     if(!window->slideshow)
695         return;
696 
697     window->slideshow = FALSE;
698 
699     gtk_widget_set_sensitive(window->toggle_btn, FALSE);
700 }
701 
702 static void
rotate_pixbuf(VnrWindow * window,GdkPixbufRotation angle)703 rotate_pixbuf(VnrWindow *window, GdkPixbufRotation angle)
704 {
705     GdkPixbuf *result;
706 
707     if(!window->cursor_is_hidden)
708         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)),
709                               gdk_cursor_new(GDK_WATCH));
710     /* This makes the cursor show NOW */
711     gdk_flush();
712 
713     /* Stop slideshow while editing the image */
714     stop_slideshow(window);
715 
716     result = gdk_pixbuf_rotate_simple(UNI_IMAGE_VIEW(window->view)->pixbuf,
717                                       angle);
718 
719     if(result == NULL)
720     {
721         vnr_message_area_show(VNR_MESSAGE_AREA(window->msg_area),
722                               TRUE, _("Not enough virtual memory."),
723                               FALSE);
724         return;
725     }
726 
727     uni_anim_view_set_static(UNI_ANIM_VIEW(window->view), result);
728 
729     if(!window->cursor_is_hidden)
730         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)),
731                               gdk_cursor_new(GDK_LEFT_PTR));
732     g_object_unref(result);
733 
734     window->current_image_width = gdk_pixbuf_get_width (result);
735     window->current_image_height = gdk_pixbuf_get_height (result);
736 
737     if(gtk_widget_get_visible(window->props_dlg))
738         vnr_properties_dialog_update_image(VNR_PROPERTIES_DIALOG(window->props_dlg));
739 
740     /* Extra conditions. Rotating 180 degrees is also flipping horizontal and vertical */
741     if((window->modifications & (4))^((angle==GDK_PIXBUF_ROTATE_CLOCKWISE)<<2))
742         window->modifications ^= 3;
743 
744     window->modifications ^= 4;
745     gtk_action_group_set_sensitive(window->action_save, window->modifications);
746 
747     if(window->modifications == 0 && window->prefs->behavior_modify != VNR_PREFS_MODIFY_IGNORE)
748     {
749         vnr_message_area_hide(VNR_MESSAGE_AREA(window->msg_area));
750         return;
751     }
752 
753     if(window->writable_format_name == NULL)
754         vnr_message_area_show(VNR_MESSAGE_AREA(window->msg_area),
755                               TRUE,
756                               _("Image modifications cannot be saved.\nWriting in this format is not supported."),
757                               FALSE);
758     else if(window->prefs->behavior_modify == VNR_PREFS_MODIFY_SAVE)
759         save_image_cb(NULL, window);
760     else if(window->prefs->behavior_modify == VNR_PREFS_MODIFY_ASK)
761         vnr_message_area_show_with_button(VNR_MESSAGE_AREA(window->msg_area),
762                                           FALSE,
763                                           _("Save modifications?\nThis will overwrite the image and may reduce its quality!"),
764                                           FALSE, GTK_STOCK_SAVE,
765                                           G_CALLBACK(save_image_cb));
766 }
767 
768 static void
flip_pixbuf(VnrWindow * window,gboolean horizontal)769 flip_pixbuf(VnrWindow *window, gboolean horizontal)
770 {
771     GdkPixbuf *result;
772 
773     if(!window->cursor_is_hidden)
774         gdk_window_set_cursor (gtk_widget_get_window(GTK_WIDGET(window)),
775                                gdk_cursor_new(GDK_WATCH));
776     /* This makes the cursor show NOW */
777     gdk_flush();
778 
779     result = gdk_pixbuf_flip(UNI_IMAGE_VIEW(window->view)->pixbuf,
780                              horizontal);
781 
782     if(result == NULL)
783     {
784         vnr_message_area_show(VNR_MESSAGE_AREA(window->msg_area),
785                               TRUE, _("Not enough virtual memory."),
786                               FALSE);
787         return;
788     }
789 
790     uni_anim_view_set_static(UNI_ANIM_VIEW(window->view), result);
791 
792     if(gtk_widget_get_visible(window->props_dlg))
793         vnr_properties_dialog_update_image(VNR_PROPERTIES_DIALOG(window->props_dlg));
794 
795     if(!window->cursor_is_hidden)
796         gdk_window_set_cursor (gtk_widget_get_window(GTK_WIDGET(window)),
797                                gdk_cursor_new(GDK_LEFT_PTR));
798     g_object_unref(result);
799 
800     /* Extra conditions. Rotating 180 degrees is also flipping horizontal and vertical */
801     window->modifications ^= (window->modifications&4)?1+horizontal:2-horizontal;
802 
803     gtk_action_group_set_sensitive(window->action_save, window->modifications);
804 
805     if(window->modifications == 0)
806     {
807         vnr_message_area_hide(VNR_MESSAGE_AREA(window->msg_area));
808         return;
809     }
810 
811     if(window->writable_format_name == NULL)
812         vnr_message_area_show(VNR_MESSAGE_AREA(window->msg_area),
813                               TRUE,
814                               _("Image modifications cannot be saved.\nWriting in this format is not supported."),
815                               FALSE);
816     else if(window->prefs->behavior_modify == VNR_PREFS_MODIFY_SAVE)
817         save_image_cb(NULL, window);
818     else if(window->prefs->behavior_modify == VNR_PREFS_MODIFY_ASK)
819         vnr_message_area_show_with_button(VNR_MESSAGE_AREA(window->msg_area),
820                                           FALSE,
821                                           _("Save modifications?\nThis will overwrite the image and may reduce its quality!"),
822                                           FALSE, GTK_STOCK_SAVE,
823                                           G_CALLBACK(save_image_cb));
824 }
825 
826 /*************************************************************/
827 /***** Private signal handlers *******************************/
828 /*************************************************************/
829 /* Modified version of eog's open_with_launch_application_cb */
830 static void
open_with_launch_application_cb(GtkAction * action,VnrWindow * window)831 open_with_launch_application_cb (GtkAction *action, VnrWindow *window)
832 {
833     GAppInfo *app;
834     GFile *file;
835     GList *files = NULL;
836 
837     file = g_file_new_for_path ((gchar*)VNR_FILE(window->file_list->data)->path);
838 
839     app = g_object_get_data (G_OBJECT (action), "app");
840     files = g_list_append (files, file);
841     g_app_info_launch (app,
842                files,
843                NULL, NULL);
844 
845     g_object_unref (file);
846     g_list_free (files);
847 }
848 
849 static gboolean
leave_image_area_cb(GtkWidget * widget,GdkEventCrossing * ev,VnrWindow * window)850 leave_image_area_cb(GtkWidget * widget, GdkEventCrossing * ev, VnrWindow *window)
851 {
852     fullscreen_unset_timeout (window);
853     return FALSE;
854 }
855 
856 static gboolean
fullscreen_motion_cb(GtkWidget * widget,GdkEventMotion * ev,VnrWindow * window)857 fullscreen_motion_cb(GtkWidget * widget, GdkEventMotion * ev, VnrWindow *window)
858 {
859     if(window->disable_autohide)
860         return FALSE;
861 
862     /* Show the toolbar only when the moves moves to the top
863      * of the UniImageView */
864     if (ev->y < 20 && !gtk_widget_get_visible (window->toolbar))
865         gtk_widget_show (GTK_WIDGET (window->toolbar));
866 
867     if(window->cursor_is_hidden)
868         vnr_window_show_cursor(window);
869 
870     fullscreen_set_timeout(window);
871     return FALSE;
872 }
873 
874 /* Hides the toolbar */
875 static gboolean
fullscreen_timeout_cb(VnrWindow * window)876 fullscreen_timeout_cb (VnrWindow *window)
877 {
878     fullscreen_unset_timeout (window);
879 
880     if(window->disable_autohide)
881         return FALSE;
882 
883     gtk_widget_hide (window->toolbar);
884     vnr_window_hide_cursor(window);
885     return FALSE;
886 }
887 
888 static void
spin_value_change_cb(GtkSpinButton * spinbutton,VnrWindow * window)889 spin_value_change_cb (GtkSpinButton *spinbutton, VnrWindow *window)
890 {
891     int new_value = gtk_spin_button_get_value_as_int (spinbutton);
892 
893     if(new_value != window->prefs->slideshow_timeout)
894         vnr_prefs_set_slideshow_timeout(window->prefs, new_value);
895 
896     gtk_label_set_text (GTK_LABEL(window->fs_seconds_label),
897                         ngettext(" second", " seconds", new_value));
898     window->ss_timeout = new_value;
899     restart_slideshow(window);
900 }
901 
902 static void
toggle_show_next_cb(GtkToggleButton * togglebutton,VnrWindow * window)903 toggle_show_next_cb (GtkToggleButton *togglebutton, VnrWindow *window)
904 {
905     if(!window->slideshow)
906         return;
907 
908     if(window->mode == VNR_WINDOW_MODE_FULLSCREEN)
909         start_slideshow(window);
910     else if(window->mode == VNR_WINDOW_MODE_SLIDESHOW)
911         stop_slideshow(window);
912 }
913 
914 static void
save_image_cb(GtkWidget * widget,VnrWindow * window)915 save_image_cb (GtkWidget *widget, VnrWindow *window)
916 {
917     GError *error = NULL;
918     if(!window->cursor_is_hidden)
919         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)), gdk_cursor_new(GDK_WATCH));
920     /* This makes the cursor show NOW */
921     gdk_flush();
922 
923     if(window->prefs->behavior_modify == VNR_PREFS_MODIFY_ASK)
924         vnr_message_area_hide(VNR_MESSAGE_AREA(window->msg_area));
925 
926     /* Store exiv2 metadata to cache, so we can restore it afterwards */
927     uni_read_exiv2_to_cache(VNR_FILE(window->file_list->data)->path);
928 
929     if(g_strcmp0(window->writable_format_name, "jpeg" ) == 0)
930     {
931         gchar *quality;
932         quality = g_strdup_printf ("%i", window->prefs->jpeg_quality);
933 
934         gdk_pixbuf_save (uni_image_view_get_pixbuf(UNI_IMAGE_VIEW(window->view)),
935                          VNR_FILE(window->file_list->data)->path, "jpeg",
936                          &error, "quality", quality, NULL);
937         g_free(quality);
938     }
939     else if(g_strcmp0(window->writable_format_name, "png" ) == 0)
940     {
941         gchar *compression;
942         compression = g_strdup_printf ("%i", window->prefs->png_compression);
943 
944         gdk_pixbuf_save (uni_image_view_get_pixbuf(UNI_IMAGE_VIEW(window->view)),
945                          VNR_FILE(window->file_list->data)->path, "png",
946                          &error, "compression", compression, NULL);
947         g_free(compression);
948     }
949     else
950     {
951         gdk_pixbuf_save (uni_image_view_get_pixbuf(UNI_IMAGE_VIEW(window->view)),
952                          VNR_FILE(window->file_list->data)->path,
953                          window->writable_format_name, &error, NULL);
954     }
955     uni_write_exiv2_from_cache(VNR_FILE(window->file_list->data)->path);
956 
957     if(!window->cursor_is_hidden)
958         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)), gdk_cursor_new(GDK_LEFT_PTR));
959 
960     if(error != NULL)
961     {
962         vnr_message_area_show(VNR_MESSAGE_AREA(window->msg_area), TRUE,
963                                                error->message, FALSE);
964         return;
965     }
966 
967     if(window->prefs->reload_on_save)
968     {
969         vnr_window_open(window, FALSE);
970         return;
971     }
972 
973     window->modifications = 0;
974 
975     gtk_action_group_set_sensitive(window->action_save, FALSE);
976 
977     if(window->prefs->behavior_modify != VNR_PREFS_MODIFY_ASK)
978         zoom_changed_cb(UNI_IMAGE_VIEW(window->view), window);
979 
980     if(gtk_widget_get_visible(window->props_dlg))
981         vnr_properties_dialog_update(VNR_PROPERTIES_DIALOG(window->props_dlg));
982 }
983 
984 static void
vnr_window_main_menu_position(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer user_data)985 vnr_window_main_menu_position (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data)
986 {
987     VnrWindow *window = VNR_WINDOW(user_data);
988   GtkWidget *button = window->properties_button;
989     GdkWindow *gdk_window = gtk_widget_get_window(button);
990   GtkRequisition req = {0, 0};
991   GtkAllocation toolbar_allocation;
992   GtkAllocation button_allocation;
993 
994     gdk_window_get_position(gdk_window, x, y);
995 
996     // in maximuzed and fullscreen states gdk_window_get_position returns 0
997     if ( *x == 0 && gtk_widget_get_visible ( get_fs_controls(window)) ) {
998     GtkAllocation allocation;
999 
1000     gtk_widget_get_allocation(get_fs_controls(window), &allocation);
1001         *x -= allocation.width;
1002     }
1003 
1004   gtk_widget_size_request(GTK_WIDGET(menu), &req);
1005   gtk_widget_get_allocation(window->toolbar, &toolbar_allocation);
1006   gtk_widget_get_allocation(button, &button_allocation);
1007 
1008     *x += toolbar_allocation.width - req.width;
1009     *y += button_allocation.height;
1010 }
1011 
1012 static void
vnr_window_cmd_open_menu(GtkToggleAction * action,VnrWindow * window)1013 vnr_window_cmd_open_menu (GtkToggleAction *action, VnrWindow *window)
1014 {
1015     if( !gtk_toggle_action_get_active (action)) {
1016         return;
1017     }
1018     gtk_menu_popup(GTK_MENU(window->button_menu), NULL, NULL, vnr_window_main_menu_position, window, 0, gtk_get_current_event_time());
1019     return;
1020 }
1021 
1022 static void
vnr_window_cmd_main_menu_hidden(GtkWidget * widget,gpointer user_data)1023 vnr_window_cmd_main_menu_hidden (GtkWidget *widget, gpointer user_data)
1024 {
1025     gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(VNR_WINDOW(user_data)->properties_button), FALSE);
1026 }
1027 
1028 static void
leave_fs_cb(GtkButton * button,VnrWindow * window)1029 leave_fs_cb (GtkButton *button, VnrWindow *window)
1030 {
1031     vnr_window_unfullscreen (window);
1032 }
1033 
1034 static void
window_realize_cb(GtkWidget * widget,gpointer user_data)1035 window_realize_cb(GtkWidget *widget, gpointer user_data)
1036 {
1037     g_signal_handlers_disconnect_by_func(widget, window_realize_cb, user_data);
1038 
1039     if(!vnr_message_area_is_critical(VNR_MESSAGE_AREA(VNR_WINDOW(widget)->msg_area)))
1040     {
1041         if ( VNR_WINDOW(widget)->prefs->start_maximized ) {
1042             vnr_window_open(VNR_WINDOW(widget), FALSE);
1043         }
1044         else
1045         {
1046             GdkScreen *screen;
1047             GdkRectangle monitor;
1048             screen = gtk_window_get_screen (GTK_WINDOW (widget));
1049             gdk_screen_get_monitor_geometry (screen,
1050                                              gdk_screen_get_monitor_at_window (screen,
1051                                              gtk_widget_get_window (widget)),
1052                                              &monitor);
1053 
1054             VNR_WINDOW(widget)->max_width = monitor.width * 0.9 - 100;
1055             VNR_WINDOW(widget)->max_height = monitor.height * 0.9 - 100;
1056 
1057             vnr_window_open(VNR_WINDOW(widget), TRUE);
1058         }
1059         if ( VNR_WINDOW(widget)->prefs->start_slideshow && VNR_WINDOW(widget)->file_list != NULL ) {
1060             vnr_window_fullscreen(VNR_WINDOW(widget));
1061             VNR_WINDOW(widget)->mode = VNR_WINDOW_MODE_NORMAL;
1062             allow_slideshow(VNR_WINDOW(widget));
1063             start_slideshow(VNR_WINDOW(widget));
1064         } else if ( VNR_WINDOW(widget)->prefs->start_fullscreen && VNR_WINDOW(widget)->file_list != NULL ) {
1065             vnr_window_fullscreen(VNR_WINDOW(widget));
1066         }
1067     }
1068 }
1069 
1070 static gboolean
window_change_state_cb(GtkWidget * widget,GdkEventWindowState * event,gpointer user_data)1071 window_change_state_cb (GtkWidget * widget, GdkEventWindowState * event, gpointer user_data)
1072 {
1073     if ( event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED ) {
1074         /* Detect maximized state only */
1075         if ( event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED  ) {
1076             VNR_WINDOW(widget)->prefs->start_maximized = TRUE;
1077         } else {
1078             VNR_WINDOW(widget)->prefs->start_maximized = FALSE;
1079         }
1080         vnr_prefs_save(VNR_WINDOW(widget)->prefs);
1081     }
1082     return TRUE;
1083 }
1084 
1085 
1086 static void
window_destroy_cb(GtkObject * object,gpointer user_data)1087 window_destroy_cb (GtkObject *object, gpointer user_data)
1088 {
1089     vnr_window_save_accel_map();
1090     vnr_prefs_save(VNR_WINDOW(object)->prefs);
1091     gtk_main_quit();
1092 }
1093 
1094 static void
zoom_changed_cb(UniImageView * view,VnrWindow * window)1095 zoom_changed_cb (UniImageView *view, VnrWindow *window)
1096 {
1097     gint position, total;
1098     char *buf = NULL;
1099 
1100     /* Change the info, only if there is an image
1101      * (vnr_window_close isn't called on the current image) */
1102     if(gtk_action_group_get_sensitive (window->actions_image))
1103     {
1104         get_position_of_element_in_list(window->file_list, &position, &total);
1105         buf = g_strdup_printf ("%s%s - %i/%i - %ix%i - %i%%", (window->modifications)?"*":"",
1106                                VNR_FILE(window->file_list->data)->display_name,
1107                                position, total,
1108                                window->current_image_width, window->current_image_height,
1109                                (int)(view->zoom*100.));
1110 
1111         gtk_window_set_title (GTK_WINDOW(window), buf);
1112         g_free(buf);
1113     }
1114 }
1115 
1116 
1117 static void
window_drag_begin_cb(GtkWidget * widget,GdkDragContext * drag_context,GtkSelectionData * data,guint info,guint time,gpointer user_data)1118 window_drag_begin_cb (GtkWidget *widget,
1119               GdkDragContext *drag_context,
1120               GtkSelectionData *data,
1121               guint info,
1122               guint time,
1123               gpointer user_data)
1124 {
1125     gchar *uris[2];
1126 
1127     uris[0] = g_filename_to_uri((gchar*)VNR_FILE(VNR_WINDOW(user_data)->file_list->data)->path, NULL, NULL);
1128     uris[1] = NULL;
1129 
1130     gtk_selection_data_set_uris (data, uris);
1131 
1132     g_free(uris[0]);
1133 }
1134 
1135 static void
file_open_dialog_response_cb(GtkWidget * dialog,gint response_id,VnrWindow * window)1136 file_open_dialog_response_cb (GtkWidget *dialog,
1137                               gint response_id,
1138                               VnrWindow *window)
1139 {
1140     if (response_id == GTK_RESPONSE_ACCEPT)
1141     {
1142         GSList *uri_list = NULL;
1143         uri_list = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (dialog));
1144         g_return_if_fail(uri_list != NULL);
1145         vnr_window_open_from_list(window, uri_list);
1146     }
1147 
1148     gtk_widget_destroy (dialog);
1149 }
1150 
1151 static void
vnr_window_cmd_preferences(GtkAction * action,gpointer user_data)1152 vnr_window_cmd_preferences(GtkAction *action, gpointer user_data)
1153 {
1154     vnr_prefs_show_dialog(VNR_WINDOW(user_data)->prefs);
1155 }
1156 
1157 static void
vnr_window_cmd_flip_horizontal(GtkAction * action,VnrWindow * window)1158 vnr_window_cmd_flip_horizontal(GtkAction *action, VnrWindow *window)
1159 {
1160     if ( !gtk_action_group_get_sensitive(window->actions_static_image) )
1161         return;
1162 
1163     flip_pixbuf(window, TRUE);
1164 }
1165 
1166 static void
vnr_window_cmd_flip_vertical(GtkAction * action,VnrWindow * window)1167 vnr_window_cmd_flip_vertical(GtkAction *action, VnrWindow *window)
1168 {
1169     if ( !gtk_action_group_get_sensitive(window->actions_static_image) )
1170         return;
1171 
1172     flip_pixbuf(window, FALSE);
1173 }
1174 
1175 static void
vnr_window_cmd_rotate_cw(GtkAction * action,gpointer user_data)1176 vnr_window_cmd_rotate_cw(GtkAction *action, gpointer user_data)
1177 {
1178     rotate_pixbuf(VNR_WINDOW(user_data), GDK_PIXBUF_ROTATE_CLOCKWISE);
1179 }
1180 
1181 static void
vnr_window_cmd_rotate_ccw(GtkAction * action,gpointer user_data)1182 vnr_window_cmd_rotate_ccw(GtkAction *action, gpointer user_data)
1183 {
1184     rotate_pixbuf(VNR_WINDOW(user_data), GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
1185 }
1186 
1187 static void
vnr_window_cmd_zoom_in(GtkAction * action,gpointer user_data)1188 vnr_window_cmd_zoom_in (GtkAction *action, gpointer user_data)
1189 {
1190     uni_image_view_zoom_in(UNI_IMAGE_VIEW(VNR_WINDOW(user_data)->view));
1191 }
1192 
1193 static void
vnr_window_cmd_zoom_out(GtkAction * action,gpointer user_data)1194 vnr_window_cmd_zoom_out (GtkAction *action, gpointer user_data)
1195 {
1196     uni_image_view_zoom_out(UNI_IMAGE_VIEW(VNR_WINDOW(user_data)->view));
1197 }
1198 
1199 static void
vnr_window_cmd_normal_size(GtkAction * action,gpointer user_data)1200 vnr_window_cmd_normal_size (GtkAction *action, gpointer user_data)
1201 {
1202     uni_image_view_set_zoom(UNI_IMAGE_VIEW(VNR_WINDOW(user_data)->view), 1);
1203     uni_image_view_set_fitting(UNI_IMAGE_VIEW(VNR_WINDOW(user_data)->view), UNI_FITTING_NONE);
1204 }
1205 
1206 static void
vnr_window_cmd_fit(GtkAction * action,gpointer user_data)1207 vnr_window_cmd_fit (GtkAction *action, gpointer user_data)
1208 {
1209     uni_image_view_set_fitting(UNI_IMAGE_VIEW(VNR_WINDOW(user_data)->view), UNI_FITTING_FULL);
1210 }
1211 
1212 static void
vnr_window_cmd_next(GtkAction * action,gpointer user_data)1213 vnr_window_cmd_next (GtkAction *action, gpointer user_data)
1214 {
1215     vnr_window_next(VNR_WINDOW(user_data), TRUE);
1216 }
1217 
1218 static void
vnr_window_cmd_first(GtkAction * action,gpointer user_data)1219 vnr_window_cmd_first (GtkAction *action, gpointer user_data)
1220 {
1221     vnr_window_first(VNR_WINDOW(user_data));
1222 }
1223 
1224 static void
vnr_window_cmd_last(GtkAction * action,gpointer user_data)1225 vnr_window_cmd_last (GtkAction *action, gpointer user_data)
1226 {
1227     vnr_window_last(VNR_WINDOW(user_data));
1228 }
1229 
1230 static void
vnr_window_cmd_prev(GtkAction * action,gpointer user_data)1231 vnr_window_cmd_prev (GtkAction *action, gpointer user_data)
1232 {
1233     vnr_window_prev(VNR_WINDOW(user_data));
1234 }
1235 
1236 static void
vnr_window_cmd_resize(GtkToggleAction * action,VnrWindow * window)1237 vnr_window_cmd_resize (GtkToggleAction *action, VnrWindow *window)
1238 {
1239     GtkAllocation allocation;
1240 
1241     if ( action != NULL && !gtk_toggle_action_get_active(action) ) {
1242         window->prefs->auto_resize = FALSE;
1243         return;
1244     }
1245 
1246     gint img_h, img_w;          /* Width and Height of the pixbuf */
1247 
1248     img_w = window->current_image_width;
1249     img_h = window->current_image_height;
1250 
1251     if ( img_w == 0 || img_h == 0 )
1252         return;
1253 
1254     window->prefs->auto_resize = TRUE;
1255 
1256     vnr_tools_fit_to_size (&img_w, &img_h, window->max_width, window->max_height);
1257     gtk_widget_get_allocation (window->menus, &allocation);
1258     gtk_window_resize (GTK_WINDOW (window), img_w, img_h + allocation.height);
1259 }
1260 
1261 static void
vnr_window_cmd_properties(GtkAction * action,VnrWindow * window)1262 vnr_window_cmd_properties (GtkAction *action, VnrWindow *window)
1263 {
1264     vnr_properties_dialog_show(VNR_PROPERTIES_DIALOG (window->props_dlg));
1265 }
1266 
1267 static void
vnr_window_cmd_reload(GtkAction * action,VnrWindow * window)1268 vnr_window_cmd_reload (GtkAction *action, VnrWindow *window)
1269 {
1270     vnr_window_open(window, FALSE);
1271 }
1272 
1273 static void
vnr_window_cmd_open(GtkAction * action,VnrWindow * window)1274 vnr_window_cmd_open(GtkAction *action, VnrWindow *window)
1275 {
1276     GtkWidget *dialog;
1277     GtkFileFilter *img_filter = NULL;
1278     GtkFileFilter *all_filter = NULL;
1279 
1280     dialog = gtk_file_chooser_dialog_new (_("Open Image"),
1281                           GTK_WINDOW(window),
1282                           GTK_FILE_CHOOSER_ACTION_OPEN,
1283                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1284                           GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1285                           NULL);
1286 
1287     img_filter = gtk_file_filter_new ();
1288     g_assert (img_filter != NULL);
1289     gtk_file_filter_add_pixbuf_formats (img_filter);
1290     gtk_file_filter_add_mime_type (img_filter, "image/vnd.microsoft.icon");
1291     gtk_file_filter_set_name (img_filter, _("All Images"));
1292     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), img_filter);
1293 
1294     all_filter = gtk_file_filter_new ();
1295     g_assert(all_filter != NULL);
1296     gtk_file_filter_add_pattern (all_filter, "*");
1297     gtk_file_filter_set_name (all_filter, _("All Files"));
1298     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), all_filter);
1299 
1300     gtk_window_set_modal (GTK_WINDOW(dialog), FALSE);
1301     gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1302 
1303     gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), img_filter);
1304 
1305     gchar *dirname;
1306     if(window->file_list != NULL)
1307     {
1308         dirname = g_path_get_dirname (VNR_FILE(window->file_list->data)->path);
1309         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(dialog), dirname);
1310         g_free(dirname);
1311     }
1312 
1313     g_signal_connect (dialog, "response",
1314                       G_CALLBACK (file_open_dialog_response_cb),
1315                       window);
1316 
1317     gtk_widget_show_all (GTK_WIDGET(dialog));
1318 
1319     /* This only works when here. */
1320     gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER(dialog), window->prefs->show_hidden);
1321 }
1322 
1323 static void
vnr_window_cmd_open_dir(GtkAction * action,VnrWindow * window)1324 vnr_window_cmd_open_dir(GtkAction *action, VnrWindow *window)
1325 {
1326     GtkWidget *dialog;
1327     dialog = gtk_file_chooser_dialog_new (_("Open Folder"),
1328                           GTK_WINDOW(window),
1329                           GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1330                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1331                           GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1332                           NULL);
1333 
1334     gtk_window_set_modal (GTK_WINDOW(dialog), FALSE);
1335     gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1336 
1337     gchar *dirname;
1338     if(window->file_list != NULL)
1339     {
1340         dirname = g_path_get_dirname (VNR_FILE(window->file_list->data)->path);
1341         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(dialog), dirname);
1342         g_free(dirname);
1343     }
1344 
1345     g_signal_connect (dialog, "response",
1346                       G_CALLBACK (file_open_dialog_response_cb),
1347                       window);
1348 
1349     gtk_widget_show_all (GTK_WIDGET(dialog));
1350 
1351     /* This only works when here. */
1352     gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER(dialog), window->prefs->show_hidden);
1353 }
1354 
1355 static void
vnr_window_cmd_about(GtkAction * action,VnrWindow * window)1356 vnr_window_cmd_about (GtkAction *action, VnrWindow *window)
1357 {
1358     static const char *authors[] = {
1359         "Programming & icon design",
1360         "\tSiyan Panayotov <contact@siyanpanayotov.com>",
1361         "\nRefer to source code from GtkImageView",
1362         NULL
1363     };
1364 
1365     char *license =
1366           ("Viewnior is free software: you can redistribute it and/or modify "
1367            "it under the terms of the GNU General Public License as published by "
1368            "the Free Software Foundation, either version 3 of the License, or "
1369            "(at your option) any later version.\n\n"
1370            "Viewnior is distributed in the hope that it will be useful, "
1371            "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1372            "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
1373            "GNU General Public License for more details.\n\n"
1374            "You should have received a copy of the GNU General Public License "
1375            "along with Viewnior.  If not, see <http://www.gnu.org/licenses/>.\n");
1376 
1377     gtk_show_about_dialog (GTK_WINDOW (window),
1378                    "program-name", "Viewnior",
1379                    "version", VERSION,
1380                    "copyright", "Copyright \xc2\xa9 2009-2018 Siyan Panayotov <contact@siyanpanayotov.com>",
1381                    "comments",_("Elegant Image Viewer"),
1382                    "authors", authors,
1383                    "logo-icon-name", "viewnior",
1384                    "wrap-license", TRUE,
1385                    "license", license,
1386                    "website", "http://siyanpanayotov.com/project/viewnior/",
1387                    "translator-credits", _( "translator-credits" ),
1388                    NULL);
1389 }
1390 
1391 static void
vnr_set_wallpaper(GtkAction * action,VnrWindow * win)1392 vnr_set_wallpaper(GtkAction *action, VnrWindow *win)
1393 {
1394     pid_t pid;
1395 
1396     pid = fork();
1397 
1398     if ( pid == 0 ) {
1399         gchar * tmp;
1400 
1401         VnrPrefsDesktop desktop_environment = win->prefs->desktop;
1402 
1403         if (desktop_environment == VNR_PREFS_DESKTOP_AUTO)
1404         {
1405             desktop_environment = uni_detect_desktop_environment();
1406         }
1407 
1408         switch(desktop_environment) {
1409             case VNR_PREFS_DESKTOP_GNOME2:
1410                 execlp("gconftool-2", "gconftool-2",
1411                         "--set", "/desktop/gnome/background/picture_filename",
1412                         "--type", "string",
1413                         VNR_FILE(win->file_list->data)->path,
1414                         NULL);
1415                 break;
1416             case VNR_PREFS_DESKTOP_MATE:
1417                 execlp("gsettings", "gsettings",
1418                         "set", "org.mate.background",
1419                         "picture-filename", VNR_FILE(win->file_list->data)->path,
1420                         NULL);
1421                 break;
1422             case VNR_PREFS_DESKTOP_GNOME3:
1423                 tmp = g_strdup_printf("file://%s", VNR_FILE(win->file_list->data)->path);
1424                 execlp("gsettings", "gsettings",
1425                         "set", "org.gnome.desktop.background",
1426                         "picture-uri", tmp,
1427                         NULL);
1428                 break;
1429             case VNR_PREFS_DESKTOP_XFCE:
1430                 tmp = g_strdup_printf("/backdrop/screen%d/monitor0/workspace0/last-image",
1431                                         gdk_screen_get_number(gtk_widget_get_screen(GTK_WIDGET(win))));
1432                 execlp("xfconf-query", "xfconf-query",
1433                         "-c", "xfce4-desktop",
1434                         "-p", tmp,
1435                         "--type", "string",
1436                         "--set",
1437                         VNR_FILE(win->file_list->data)->path,
1438                         NULL);
1439                 break;
1440             case VNR_PREFS_DESKTOP_LXDE:
1441                 execlp("pcmanfm", "pcmanfm",
1442                         "--set-wallpaper",
1443                         VNR_FILE(win->file_list->data)->path,
1444                         NULL);
1445                 break;
1446             case VNR_PREFS_DESKTOP_PUPPY:
1447                 execlp("set_bg", "set_bg",
1448                         VNR_FILE(win->file_list->data)->path,
1449                         NULL);
1450                 break;
1451             case VNR_PREFS_DESKTOP_FLUXBOX:
1452                 execlp("fbsetbg", "fbsetbg",
1453                         "-f", VNR_FILE(win->file_list->data)->path,
1454                         NULL);
1455                 break;
1456             case VNR_PREFS_DESKTOP_NITROGEN:
1457                 execlp("nitrogen", "nitrogen",
1458                         "--set-zoom-fill", "--save",
1459                         VNR_FILE(win->file_list->data)->path,
1460                         NULL);
1461                 break;
1462             case VNR_PREFS_DESKTOP_CINNAMON:
1463                 tmp = g_strdup_printf("file://%s", VNR_FILE(win->file_list->data)->path);
1464                 execlp("gsettings", "gsettings",
1465                         "set", "org.cinnamon.desktop.background",
1466                         "picture-uri", tmp,
1467                         NULL);
1468                 break;
1469             default:
1470                 _exit(0);
1471         }
1472     } else {
1473         wait(NULL);
1474     }
1475 }
1476 
1477 static void
vnr_window_cmd_fullscreen(GtkAction * action,VnrWindow * window)1478 vnr_window_cmd_fullscreen (GtkAction *action, VnrWindow *window)
1479 {
1480     gboolean fullscreen;
1481 
1482     fullscreen = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
1483 
1484     if (fullscreen)
1485         vnr_window_fullscreen (window);
1486     else
1487         vnr_window_unfullscreen (window);
1488 }
1489 
1490 static void
vnr_window_cmd_menu_bar(GtkAction * action,VnrWindow * window)1491 vnr_window_cmd_menu_bar (GtkAction *action, VnrWindow *window)
1492 {
1493     gboolean show;
1494 
1495     show = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
1496     vnr_prefs_set_show_menu_bar(window->prefs, show);
1497 
1498     if(window->mode != VNR_WINDOW_MODE_NORMAL)
1499        return;
1500 
1501 
1502     if (show)
1503     {
1504         gtk_widget_show (window->menu_bar);
1505         gtk_widget_hide (window->properties_button);
1506     }
1507     else
1508     {
1509         gtk_widget_hide (window->menu_bar);
1510         gtk_widget_show (window->properties_button);
1511     }
1512 }
1513 
1514 static void
vnr_window_cmd_toolbar(GtkAction * action,VnrWindow * window)1515 vnr_window_cmd_toolbar (GtkAction *action, VnrWindow *window)
1516 {
1517     gboolean show;
1518 
1519     show = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
1520     vnr_prefs_set_show_toolbar(window->prefs, show);
1521 
1522     if (show)
1523         gtk_widget_show (window->toolbar);
1524     else
1525         gtk_widget_hide (window->toolbar);
1526 }
1527 
1528 static void
vnr_window_cmd_scrollbar(GtkAction * action,VnrWindow * window)1529 vnr_window_cmd_scrollbar (GtkAction *action, VnrWindow *window)
1530 {
1531     gboolean show;
1532 
1533     show = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
1534     vnr_prefs_set_show_scrollbar (window->prefs, show);
1535     uni_scroll_win_set_show_scrollbar (UNI_SCROLL_WIN (window->scroll_view), show);
1536 }
1537 
1538 static void
vnr_window_cmd_slideshow(GtkAction * action,VnrWindow * window)1539 vnr_window_cmd_slideshow (GtkAction *action, VnrWindow *window)
1540 {
1541     if(!window->slideshow)
1542         return;
1543 
1544     g_assert(window != NULL && VNR_IS_WINDOW(window));
1545 
1546     gboolean slideshow;
1547 
1548     slideshow = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
1549 
1550     if (slideshow && window->mode != VNR_WINDOW_MODE_SLIDESHOW)
1551     {
1552         /* ! Uncomment to force Fullscreen along with Slideshow */
1553         if(window->mode == VNR_WINDOW_MODE_NORMAL)
1554         {
1555             vnr_window_fullscreen (window);
1556         }
1557         start_slideshow(window);
1558     }
1559     else if(window->mode == VNR_WINDOW_MODE_SLIDESHOW)
1560     {
1561         /* ! Uncomment to force Fullscreen along with Slideshow */
1562         vnr_window_unfullscreen (window);
1563         stop_slideshow(window);
1564     }
1565 }
1566 
1567 static void
vnr_window_cmd_delete(GtkAction * action,VnrWindow * window)1568 vnr_window_cmd_delete(GtkAction *action, VnrWindow *window)
1569 {
1570     GtkWidget *dlg = NULL;
1571     const gchar *file_path;
1572     gchar *markup, *prompt, *warning;
1573     gboolean restart_slideshow = FALSE;
1574     gboolean restart_autohide_timeout = FALSE;
1575     gboolean cursor_was_hidden = FALSE;
1576 
1577     /* Used to get rid of the "may be used uninitialised" warning */
1578     markup = prompt = warning = NULL;
1579 
1580     if(window->mode == VNR_WINDOW_MODE_SLIDESHOW)
1581     {
1582        stop_slideshow(window);
1583        restart_slideshow = TRUE;
1584     }
1585 
1586     if(window->cursor_is_hidden)
1587     {
1588         cursor_was_hidden = TRUE;
1589         vnr_window_show_cursor(window);
1590     }
1591     window->disable_autohide = TRUE;
1592 
1593     if(window->fs_source != NULL)
1594         restart_autohide_timeout = TRUE;
1595 
1596     g_return_if_fail (window->file_list != NULL);
1597 
1598     file_path = VNR_FILE(window->file_list->data)->path;
1599 
1600     if(window->prefs->confirm_delete)
1601     {
1602         warning = _("If you delete an item, it will be permanently lost.");
1603 
1604         /* I18N: The '%s' is replaced with the name of the file to be deleted. */
1605         prompt = g_strdup_printf (_("Are you sure you want to\n"
1606                                     "permanently delete \"%s\"?"),
1607                                   VNR_FILE(window->file_list->data)->display_name);
1608         markup = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
1609                                   prompt, warning);
1610 
1611 
1612         dlg = gtk_message_dialog_new(GTK_WINDOW(window),
1613                                      GTK_DIALOG_MODAL,
1614                                      GTK_MESSAGE_WARNING,
1615                                      GTK_BUTTONS_NONE,
1616                                      NULL);
1617 
1618         gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dlg),
1619                                        markup);
1620 
1621         gtk_dialog_add_buttons (GTK_DIALOG (dlg),
1622                                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1623                                 GTK_STOCK_DELETE, GTK_RESPONSE_YES,
1624                                 NULL);
1625     }
1626 
1627     if(!window->prefs->confirm_delete || gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_YES )
1628     {
1629         GFile *file;
1630         GError *error = NULL;
1631 
1632         file = g_file_new_for_path(file_path);
1633         g_file_delete(file, NULL, &error);
1634 
1635         if( error != NULL )
1636         {
1637             vnr_message_area_show(VNR_MESSAGE_AREA (window->msg_area), TRUE,
1638                                    error->message, FALSE);
1639             restart_slideshow = FALSE;
1640         }
1641         else
1642         {
1643             GList *next;
1644 
1645             next = g_list_next(window->file_list);
1646             if(next == NULL)
1647                 next = g_list_first(window->file_list);
1648 
1649             if(g_list_length(g_list_first(window->file_list)) != 1)
1650                 window->file_list = g_list_delete_link (window->file_list, window->file_list);
1651             else
1652             {
1653                 g_list_free(window->file_list);
1654                 next = NULL;
1655             }
1656 
1657             if(next == NULL)
1658             {
1659                 vnr_window_close(window);
1660                 gtk_action_group_set_sensitive(window->actions_collection, FALSE);
1661                 deny_slideshow(window);
1662                 vnr_window_set_list(window, NULL, FALSE);
1663                 vnr_message_area_show(VNR_MESSAGE_AREA (window->msg_area), TRUE,
1664                                       _("The given locations contain no images."),
1665                                       TRUE);
1666                 restart_slideshow = FALSE;
1667 
1668 
1669                 if(gtk_widget_get_visible(window->props_dlg))
1670                     vnr_properties_dialog_clear(VNR_PROPERTIES_DIALOG(window->props_dlg));
1671             }
1672             else
1673             {
1674                 vnr_window_set_list(window, next, FALSE);
1675                 if(window->prefs->confirm_delete && !window->cursor_is_hidden)
1676                     gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(dlg)),
1677                                           gdk_cursor_new(GDK_WATCH));
1678 
1679                 gdk_flush();
1680 
1681                 vnr_window_close(window);
1682                 vnr_window_open(window, FALSE);
1683                 if(window->prefs->confirm_delete && !window->cursor_is_hidden)
1684                     gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(dlg)),
1685                                           gdk_cursor_new(GDK_LEFT_PTR));
1686             }
1687         }
1688     }
1689 
1690     window->disable_autohide = FALSE;
1691 
1692     if(restart_slideshow)
1693        start_slideshow(window);
1694     if(cursor_was_hidden)
1695         vnr_window_hide_cursor(window);
1696     if(restart_autohide_timeout)
1697         fullscreen_set_timeout(window);
1698 
1699     if(window->prefs->confirm_delete)
1700     {
1701         g_free(prompt);
1702         g_free(markup);
1703         gtk_widget_destroy( dlg );
1704     }
1705 }
1706 
1707 static void
vnr_window_cmd_crop(GtkAction * action,VnrWindow * window)1708 vnr_window_cmd_crop(GtkAction *action, VnrWindow *window)
1709 {
1710     VnrCrop *crop;
1711 
1712     if ( !gtk_action_group_get_sensitive(window->actions_static_image) )
1713         return;
1714 
1715     crop = (VnrCrop*) vnr_crop_new (window);
1716 
1717     if(! vnr_crop_run(crop))
1718     {
1719         g_object_unref(crop);
1720         return;
1721     }
1722 
1723     GdkPixbuf *cropped;
1724     GdkPixbuf *original;
1725 
1726     original = uni_image_view_get_pixbuf(UNI_IMAGE_VIEW(window->view));
1727 
1728     cropped = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (original),
1729                                gdk_pixbuf_get_has_alpha (original),
1730                                gdk_pixbuf_get_bits_per_sample (original),
1731                                crop->area.width, crop->area.height);
1732 
1733     gdk_pixbuf_copy_area((const GdkPixbuf*)original, crop->area.x, crop->area.y,
1734                          crop->area.width, crop->area.height, cropped, 0, 0);
1735 
1736     uni_anim_view_set_static(UNI_ANIM_VIEW(window->view), cropped);
1737 
1738     g_object_unref(cropped);
1739 
1740     window->modifications |= 8;
1741 
1742     window->current_image_width = crop->area.width;
1743     window->current_image_height = crop->area.height;
1744 
1745     gtk_action_group_set_sensitive(window->action_save, TRUE);
1746 
1747     if(window->writable_format_name == NULL)
1748         vnr_message_area_show(VNR_MESSAGE_AREA(window->msg_area),
1749                               TRUE,
1750                               _("Image modifications cannot be saved.\nWriting in this format is not supported."),
1751                               FALSE);
1752     else if(window->prefs->behavior_modify == VNR_PREFS_MODIFY_SAVE)
1753         save_image_cb(NULL, window);
1754     else if(window->prefs->behavior_modify == VNR_PREFS_MODIFY_ASK)
1755         vnr_message_area_show_with_button(VNR_MESSAGE_AREA(window->msg_area),
1756                                           FALSE,
1757                                           _("Save modifications?\nThis will overwrite the image and may reduce its quality!"),
1758                                           FALSE, GTK_STOCK_SAVE,
1759                                           G_CALLBACK(save_image_cb));
1760 
1761     g_object_unref(crop);
1762 }
1763 
1764 static const GtkActionEntry action_entries_window[] = {
1765     { "File",  NULL, N_("_File") },
1766     { "Edit",  NULL, N_("_Edit") },
1767     { "View",  NULL, N_("_View") },
1768     { "Image",  NULL, N_("_Image") },
1769     { "Go",    NULL, N_("_Go") },
1770     { "Help",  NULL, N_("_Help") },
1771 
1772     { "FileOpen", GTK_STOCK_FILE, N_("Open _Image..."), "<control>O",
1773       N_("Open an Image"),
1774       G_CALLBACK (vnr_window_cmd_open) },
1775     { "FileOpenDir", GTK_STOCK_DIRECTORY, N_("Open _Folder..."), "<control>F",
1776       N_("Open a Folder"),
1777       G_CALLBACK (vnr_window_cmd_open_dir) },
1778     { "FileClose", GTK_STOCK_CLOSE, N_("_Close"), "<control>W",
1779       N_("Close window"),
1780       G_CALLBACK (gtk_main_quit) },
1781     { "HelpAbout", GTK_STOCK_ABOUT, N_("_About"), NULL,
1782       N_("About this application"),
1783       G_CALLBACK (vnr_window_cmd_about) },
1784     { "EditPreferences", GTK_STOCK_PREFERENCES, N_("_Preferences..."), NULL,
1785       N_("User preferences for Viewnior"),
1786       G_CALLBACK (vnr_window_cmd_preferences) }
1787 };
1788 
1789 static const GtkActionEntry action_entry_save[] = {
1790     { "FileSave", GTK_STOCK_SAVE, N_("_Save"), "<control>S",
1791       N_("Save changes"),
1792       G_CALLBACK (save_image_cb) },
1793 };
1794 
1795 static const GtkToggleActionEntry toggle_entry_properties[] = {
1796     { "Properties", GTK_STOCK_PROPERTIES, N_("_Properties"), NULL,
1797       N_("Properties"),
1798       G_CALLBACK (vnr_window_cmd_open_menu) },
1799 };
1800 
1801 static const GtkActionEntry action_entry_wallpaper[] = {
1802     { "SetAsWallpaper", NULL, N_("Set as _Wallpaper"), "<control>F8",
1803       N_("Set the selected image as the desktop background"),
1804       G_CALLBACK (vnr_set_wallpaper) },
1805 };
1806 
1807 static const GtkActionEntry action_entries_image[] = {
1808     { "FileOpenWith", NULL, N_("Open _With"), NULL,
1809       N_("Open the selected image with a different application"),
1810       NULL},
1811     { "FileDelete", GTK_STOCK_DELETE, N_("_Delete"), NULL,
1812       N_("Delete the current file"),
1813       G_CALLBACK (vnr_window_cmd_delete) },
1814     { "FileProperties", GTK_STOCK_PROPERTIES, N_("_Properties..."), "<Alt>Return",
1815       N_("Show information about the current file"),
1816       G_CALLBACK (vnr_window_cmd_properties) },
1817     { "FileReload", GTK_STOCK_REFRESH, N_("_Reload"), NULL,
1818       N_("Reload the current file"),
1819       G_CALLBACK (vnr_window_cmd_reload) },
1820     { "Delete", NULL, N_("_Delete"), "Delete",
1821       N_("Delete the current file"),
1822       G_CALLBACK (vnr_window_cmd_delete) },
1823     { "ViewZoomIn", GTK_STOCK_ZOOM_IN, N_("_Zoom In"), "<control>plus",
1824       N_("Enlarge the image"),
1825       G_CALLBACK (vnr_window_cmd_zoom_in) },
1826     { "ViewZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus",
1827       N_("Shrink the image"),
1828       G_CALLBACK (vnr_window_cmd_zoom_out) },
1829     { "ViewZoomNormal", GTK_STOCK_ZOOM_100, N_("_Normal Size"), "<control>0",
1830       N_("Show the image at its normal size"),
1831       G_CALLBACK (vnr_window_cmd_normal_size) },
1832     { "ViewZoomFit", GTK_STOCK_ZOOM_FIT, N_("Best _Fit"), NULL,
1833       N_("Fit the image to the window"),
1834       G_CALLBACK (vnr_window_cmd_fit) },
1835     { "ControlEqual", GTK_STOCK_ZOOM_IN, N_("_Zoom In"), "<control>equal",
1836       N_("Shrink the image"),
1837       G_CALLBACK (vnr_window_cmd_zoom_in) },
1838     { "ControlKpAdd", GTK_STOCK_ZOOM_IN, N_("_Zoom In"), "<control>KP_Add",
1839       N_("Shrink the image"),
1840       G_CALLBACK (vnr_window_cmd_zoom_in) },
1841     { "ControlKpSub", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>KP_Subtract",
1842       N_("Shrink the image"),
1843       G_CALLBACK (vnr_window_cmd_zoom_out) },
1844 };
1845 
1846 static const GtkActionEntry action_entries_static_image[] = {
1847     { "ImageRotateCW", "object-rotate-right", N_("Rotate _Clockwise"), "<control>R",
1848       N_("Rotate image clockwise"),
1849       G_CALLBACK (vnr_window_cmd_rotate_cw) },
1850     { "ImageRotateCCW", "object-rotate-left", N_("Rotate _Anti-clockwise"), "<control><shift>R",
1851       N_("Rotate image anti-clockwise"),
1852       G_CALLBACK (vnr_window_cmd_rotate_ccw) },
1853     { "ImageFlipVertical", "object-flip-vertical", N_("Flip _Vertical"), NULL,
1854       N_("Flip image vertically"),
1855       G_CALLBACK (vnr_window_cmd_flip_vertical) },
1856     { "ImageFlipHorizontal", "object-flip-horizontal", N_("Flip _Horizontal"), NULL,
1857       N_("Flip image horizontally"),
1858       G_CALLBACK (vnr_window_cmd_flip_horizontal) },
1859     { "ImageCrop", NULL, N_("Crop..."), NULL,
1860       N_("Crop"),
1861       G_CALLBACK (vnr_window_cmd_crop) },
1862 };
1863 
1864 static const GtkToggleActionEntry toggle_entries_image[] = {
1865     { "ViewFullscreen", GTK_STOCK_FULLSCREEN, N_("Full _Screen"), "F11",
1866       N_("Show in fullscreen mode"),
1867       G_CALLBACK (vnr_window_cmd_fullscreen) },
1868     { "ViewResizeWindow", NULL, N_("_Adjust window size"), NULL,
1869       N_("Adjust window size to fit the image"),
1870       G_CALLBACK (vnr_window_cmd_resize) },
1871 };
1872 
1873 static const GtkToggleActionEntry toggle_entries_window[] = {
1874     { "ViewMenuBar", NULL, N_("Menu Bar"), NULL,
1875       N_("Show Menu Bar"),
1876       G_CALLBACK (vnr_window_cmd_menu_bar) },
1877     { "ViewToolbar", NULL, N_("Toolbar"), NULL,
1878       N_("Show Toolbar"),
1879       G_CALLBACK (vnr_window_cmd_toolbar) },
1880     { "ViewScrollbar", NULL, N_("Scrollbar"), NULL,
1881       N_("Show Scrollbar"),
1882       G_CALLBACK (vnr_window_cmd_scrollbar) },
1883 };
1884 
1885 static const GtkToggleActionEntry toggle_entries_collection[] = {
1886     { "ViewSlideshow", GTK_STOCK_NETWORK, N_("Sli_deshow"), "F5",
1887       N_("Show in slideshow mode"),
1888       G_CALLBACK (vnr_window_cmd_slideshow) },
1889 };
1890 
1891 static const GtkActionEntry action_entries_collection[] = {
1892     { "GoPrevious", GTK_STOCK_GO_BACK, N_("_Previous Image"), "<Alt>Left",
1893       N_("Go to the previous image of the collection"),
1894       G_CALLBACK (vnr_window_cmd_prev) },
1895     { "GoNext", GTK_STOCK_GO_FORWARD, N_("_Next Image"), "<Alt>Right",
1896       N_("Go to the next image of the collection"),
1897       G_CALLBACK (vnr_window_cmd_next) },
1898     { "GoFirst", GTK_STOCK_GOTO_FIRST, N_("_First Image"), "<Alt>Home",
1899       N_("Go to the first image of the collection"),
1900       G_CALLBACK (vnr_window_cmd_first) },
1901     { "GoLast", GTK_STOCK_GOTO_LAST, N_("_Last Image"), "<Alt>End",
1902       N_("Go to the last image of the collection"),
1903       G_CALLBACK (vnr_window_cmd_last) },
1904 };
1905 
1906 /*************************************************************/
1907 /***** Stuff that deals with the type ************************/
1908 /*************************************************************/
1909 /* Modified version of eog's eog_window_key_press */
1910 static gint
vnr_window_key_press(GtkWidget * widget,GdkEventKey * event)1911 vnr_window_key_press (GtkWidget *widget, GdkEventKey *event)
1912 {
1913     gint result = FALSE;
1914     VnrWindow *window = VNR_WINDOW(widget);
1915     GtkWidget *toolbar_focus_child;
1916     GtkWidget *msg_area_focus_child;
1917 
1918     toolbar_focus_child = gtk_container_get_focus_child(GTK_CONTAINER(window->toolbar));
1919     msg_area_focus_child = gtk_container_get_focus_child(GTK_CONTAINER(window->msg_area));
1920 
1921     switch(event->keyval){
1922         case GDK_KEY_Left:
1923             if (event->state & GDK_MOD1_MASK)
1924             {
1925                 vnr_window_cmd_prev (NULL, window);
1926                 result = TRUE;
1927                 break;
1928             } /* else fall-trough is intended */
1929         case GDK_KEY_Up:
1930             if (!uni_scroll_win_image_fits (UNI_SCROLL_WIN (window->scroll_view)))
1931             {
1932                 /* break to let scrollview handle the key */
1933                 break;
1934             }
1935             if (toolbar_focus_child != NULL || msg_area_focus_child != NULL)
1936                 break;
1937 
1938             vnr_window_cmd_prev (NULL, window);
1939             result = TRUE;
1940             break;
1941         case GDK_KEY_Right:
1942             if (event->state & GDK_MOD1_MASK)
1943             {
1944                 vnr_window_cmd_next (NULL, window);
1945                 result = TRUE;
1946                 break;
1947             } /* else fall-trough is intended */
1948         case GDK_KEY_Down:
1949             if (!uni_scroll_win_image_fits (UNI_SCROLL_WIN (window->scroll_view)))
1950             {
1951                 /* break to let scrollview handle the key */
1952                 break;
1953             }
1954             if (toolbar_focus_child != NULL || msg_area_focus_child != NULL)
1955                 break;
1956 
1957             vnr_window_cmd_next (NULL, window);
1958             result = TRUE;
1959             break;
1960         case GDK_KEY_Page_Up:
1961             vnr_window_cmd_prev (NULL, window);
1962             result = TRUE;
1963             break;
1964         case GDK_KEY_Page_Down:
1965             vnr_window_cmd_next (NULL, window);
1966             result = TRUE;
1967             break;
1968         case GDK_KEY_Escape:
1969         case 'q':
1970             if(window->mode != VNR_WINDOW_MODE_NORMAL)
1971                 vnr_window_unfullscreen(window);
1972             else
1973                 gtk_main_quit();
1974             break;
1975         case GDK_KEY_space:
1976             if (toolbar_focus_child != NULL || msg_area_focus_child != NULL)
1977                 break;
1978             vnr_window_next(window, TRUE);
1979             result = TRUE;
1980             break;
1981         case GDK_KEY_BackSpace:
1982             vnr_window_prev(window);
1983             result = TRUE;
1984             break;
1985         case GDK_KEY_Home:
1986             vnr_window_first(window);
1987             result = TRUE;
1988             break;
1989         case GDK_KEY_End:
1990             vnr_window_last(window);
1991             result = TRUE;
1992             break;
1993         case 'h':
1994             vnr_window_cmd_flip_horizontal(NULL, window);
1995             break;
1996         case 'v':
1997             vnr_window_cmd_flip_vertical(NULL, window);
1998             break;
1999         case 'c':
2000             vnr_window_cmd_crop(NULL, window);
2001             break;
2002     }
2003 
2004     if (result == FALSE && GTK_WIDGET_CLASS (vnr_window_parent_class)->key_press_event)
2005         result = (* GTK_WIDGET_CLASS (vnr_window_parent_class)->key_press_event) (widget, event);
2006 
2007     return result;
2008 }
2009 
2010 static void
vnr_window_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)2011 vnr_window_drag_data_received (GtkWidget *widget,
2012                                GdkDragContext *context,
2013                                gint x, gint y,
2014                                GtkSelectionData *selection_data,
2015                                guint info, guint time)
2016 {
2017     GSList *uri_list = NULL;
2018     GdkAtom target = gtk_selection_data_get_target (selection_data);
2019     GdkDragAction suggested_action;
2020 
2021     if (!gtk_targets_include_uri (&target, 1))
2022         return;
2023 
2024     suggested_action = gdk_drag_context_get_suggested_action (context);
2025     if (suggested_action == GDK_ACTION_COPY || suggested_action == GDK_ACTION_ASK)
2026     {
2027         const guchar *data = gtk_selection_data_get_data (selection_data);
2028 
2029         uri_list = vnr_tools_parse_uri_string_list_to_file_list ((gchar *) data);
2030         if(uri_list == NULL)
2031         {
2032             vnr_window_close(VNR_WINDOW (widget));
2033             gtk_action_group_set_sensitive(VNR_WINDOW (widget)->actions_collection, FALSE);
2034             deny_slideshow(VNR_WINDOW (widget));
2035             vnr_message_area_show(VNR_MESSAGE_AREA (VNR_WINDOW (widget)->msg_area), TRUE,
2036                                   _("The given locations contain no images."),
2037                                   TRUE);
2038             return;
2039         }
2040 
2041         vnr_window_open_from_list(VNR_WINDOW (widget), uri_list);
2042     }
2043 }
2044 
2045 static void
vnr_window_class_init(VnrWindowClass * klass)2046 vnr_window_class_init (VnrWindowClass * klass)
2047 {
2048     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
2049 
2050     widget_class->key_press_event = vnr_window_key_press;
2051     widget_class->drag_data_received = vnr_window_drag_data_received;
2052 }
2053 
2054 GtkWindow *
vnr_window_new()2055 vnr_window_new ()
2056 {
2057     return (GtkWindow *) g_object_new (VNR_TYPE_WINDOW, NULL);
2058 }
2059 
2060 static void
vnr_window_init(VnrWindow * window)2061 vnr_window_init (VnrWindow * window)
2062 {
2063     GError *error = NULL;
2064     GtkAction *action;
2065 
2066     window->writable_format_name = NULL;
2067     window->file_list = NULL;
2068     window->fs_controls = NULL;
2069     window->fs_source = NULL;
2070     window->ss_timeout = 5;
2071     window->slideshow = TRUE;
2072     window->cursor_is_hidden = FALSE;
2073     window->disable_autohide = FALSE;
2074     window->actions_open_with = NULL;
2075     window->open_with_menu_id = 0;
2076 
2077     window->prefs = (VnrPrefs*)vnr_prefs_new (GTK_WIDGET(window));
2078 
2079     window->mode = VNR_WINDOW_MODE_NORMAL;
2080 
2081     gtk_window_set_title ((GtkWindow *) window, "Viewnior");
2082     gtk_window_set_default_icon_name ("viewnior");
2083 
2084     /* Build MENUBAR and TOOLBAR */
2085     window->ui_mngr = gtk_ui_manager_new();
2086 
2087     window->actions_window = gtk_action_group_new("MenuActionsWindow");
2088 
2089 
2090     gtk_action_group_set_translation_domain (window->actions_window,
2091                                              GETTEXT_PACKAGE);
2092 
2093     gtk_action_group_add_actions (window->actions_window,
2094                                   action_entries_window,
2095                                   G_N_ELEMENTS (action_entries_window),
2096                                   window);
2097 
2098     gtk_ui_manager_insert_action_group (window->ui_mngr,
2099                                         window->actions_window, 0);
2100 
2101     window->action_save = gtk_action_group_new("MenuActionSave");
2102 
2103 
2104     gtk_action_group_set_translation_domain (window->action_save,
2105                                              GETTEXT_PACKAGE);
2106 
2107     gtk_action_group_add_actions (window->action_save,
2108                                   action_entry_save,
2109                                   G_N_ELEMENTS (action_entry_save),
2110                                   window);
2111 
2112     gtk_ui_manager_insert_action_group (window->ui_mngr,
2113                                         window->action_save, 0);
2114 
2115     window->action_properties = gtk_action_group_new("MenuActionProperties");
2116 
2117 
2118     gtk_action_group_set_translation_domain (window->action_properties,
2119                                              GETTEXT_PACKAGE);
2120 
2121     gtk_action_group_add_toggle_actions (window->action_properties,
2122                                   toggle_entry_properties,
2123                                   G_N_ELEMENTS (toggle_entry_properties),
2124                                   window);
2125 
2126     gtk_ui_manager_insert_action_group (window->ui_mngr,
2127                                         window->action_properties, 0);
2128 
2129     window->actions_static_image = gtk_action_group_new("MenuActionsStaticImage");
2130 
2131 
2132     gtk_action_group_set_translation_domain (window->actions_static_image,
2133                                              GETTEXT_PACKAGE);
2134 
2135     gtk_action_group_add_actions (window->actions_static_image,
2136                                   action_entries_static_image,
2137                                   G_N_ELEMENTS (action_entries_static_image),
2138                                   window);
2139 
2140     gtk_ui_manager_insert_action_group (window->ui_mngr,
2141                                         window->actions_static_image, 0);
2142 
2143 
2144     window->actions_image = gtk_action_group_new("MenuActionsImage");
2145 
2146 
2147     gtk_action_group_set_translation_domain (window->actions_image,
2148                                              GETTEXT_PACKAGE);
2149 
2150     gtk_action_group_add_actions (window->actions_image,
2151                                   action_entries_image,
2152                                   G_N_ELEMENTS (action_entries_image),
2153                                   window);
2154     gtk_action_group_add_toggle_actions (window->actions_image,
2155                                          toggle_entries_image,
2156                                          G_N_ELEMENTS (toggle_entries_image),
2157                                          window);
2158 
2159     gtk_ui_manager_insert_action_group (window->ui_mngr,
2160                                         window->actions_image, 0);
2161     /**********/
2162     window->actions_bars = gtk_action_group_new("MenuActionsBars");
2163 
2164 
2165     gtk_action_group_set_translation_domain (window->actions_bars,
2166                                               GETTEXT_PACKAGE);
2167 
2168     gtk_action_group_add_toggle_actions (window->actions_bars,
2169                                          toggle_entries_window,
2170                                          G_N_ELEMENTS (toggle_entries_window),
2171                                          window);
2172 
2173     gtk_ui_manager_insert_action_group (window->ui_mngr,
2174                                         window->actions_bars, 0);
2175 
2176     /*****************/
2177     window->actions_collection = gtk_action_group_new("MenuActionsCollection");
2178 
2179 
2180     gtk_action_group_set_translation_domain (window->actions_collection,
2181                                              GETTEXT_PACKAGE);
2182 
2183     gtk_action_group_add_actions (window->actions_collection,
2184                                   action_entries_collection,
2185                                   G_N_ELEMENTS (action_entries_collection),
2186                                   window);
2187     gtk_action_group_add_toggle_actions (window->actions_collection,
2188                                          toggle_entries_collection,
2189                                          G_N_ELEMENTS (toggle_entries_collection),
2190                                          window);
2191 
2192     gtk_ui_manager_insert_action_group (window->ui_mngr,
2193                                         window->actions_collection, 0);
2194 
2195     if (!gtk_ui_manager_add_ui_from_string (window->ui_mngr,
2196                                             ui_definition, -1,
2197                                             &error)) {
2198             g_error ("building menus failed: %s\n", error->message);
2199             g_error_free (error);
2200     }
2201 
2202     window->action_wallpaper = gtk_action_group_new("ActionWallpaper");
2203 
2204     gtk_action_group_set_translation_domain (window->action_wallpaper,
2205                                              GETTEXT_PACKAGE);
2206 
2207     gtk_action_group_add_actions (window->action_wallpaper,
2208                                   action_entry_wallpaper,
2209                                   G_N_ELEMENTS (action_entry_wallpaper),
2210                                   window);
2211 
2212     gtk_ui_manager_insert_action_group (window->ui_mngr,
2213                                         window->action_wallpaper, 0);
2214 
2215     if (!gtk_ui_manager_add_ui_from_string (window->ui_mngr,
2216                                             ui_definition_wallpaper, -1,
2217                                             &error)) {
2218             g_error ("building menus failed: %s\n", error->message);
2219             g_error_free (error);
2220     }
2221     gtk_action_group_set_sensitive(window->action_wallpaper, FALSE);
2222 
2223     gtk_action_group_set_sensitive(window->actions_collection, FALSE);
2224     gtk_action_group_set_sensitive(window->actions_image, FALSE);
2225     gtk_action_group_set_sensitive(window->actions_static_image, FALSE);
2226     gtk_action_group_set_sensitive(window->action_save, FALSE);
2227     gtk_action_group_set_sensitive(window->actions_bars, TRUE);
2228 
2229     /* Continue with layout */
2230 
2231     window->layout = gtk_vbox_new(FALSE,0);
2232     gtk_container_add (GTK_CONTAINER (window), window->layout);
2233     gtk_widget_show(window->layout);
2234 
2235     window->menus = gtk_vbox_new(FALSE,0);
2236     gtk_box_pack_start (GTK_BOX (window->layout), window->menus, FALSE,FALSE,0);
2237 
2238     window->menu_bar = gtk_ui_manager_get_widget (window->ui_mngr, "/MainMenu");
2239     g_assert(GTK_IS_WIDGET(window->menu_bar));
2240     gtk_box_pack_start (GTK_BOX (window->menus), window->menu_bar, FALSE,FALSE,0);
2241 
2242     window->properties_button = gtk_ui_manager_get_widget (window->ui_mngr, "/Toolbar/Properties");
2243     g_assert(GTK_IS_WIDGET(window->properties_button));
2244 
2245     window->button_menu = gtk_ui_manager_get_widget (window->ui_mngr, "/ButtonMenu");
2246     g_assert(GTK_IS_WIDGET(window->button_menu));
2247     gtk_menu_attach_to_widget (GTK_MENU(window->button_menu), GTK_WIDGET(window->properties_button), NULL);
2248 
2249     window->toolbar = gtk_ui_manager_get_widget (window->ui_mngr, "/Toolbar");
2250     g_assert(GTK_IS_WIDGET(window->toolbar));
2251     gtk_toolbar_set_style(GTK_TOOLBAR(window->toolbar), GTK_TOOLBAR_ICONS);
2252     g_object_set(G_OBJECT(window->toolbar), "show-arrow", FALSE, NULL);
2253     gtk_toolbar_insert (GTK_TOOLBAR (window->toolbar),
2254                         GTK_TOOL_ITEM(get_fs_controls(window)), -1);
2255     gtk_box_pack_start (GTK_BOX (window->menus), window->toolbar, FALSE,FALSE,0);
2256 
2257     window->popup_menu = gtk_ui_manager_get_widget (window->ui_mngr, "/PopupMenu");
2258     g_assert(GTK_IS_WIDGET(window->popup_menu));
2259 
2260     gtk_ui_manager_ensure_update (window->ui_mngr);
2261     gtk_widget_show_all(window->menus);
2262 
2263     gtk_widget_hide(get_fs_controls(window));
2264 
2265     // Apply menu bar preference
2266     action = gtk_action_group_get_action (window->actions_bars,
2267                                           "ViewMenuBar");
2268     if(!window->prefs->show_menu_bar)
2269         gtk_widget_hide (window->menu_bar);
2270     else
2271         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
2272 
2273 
2274     // Apply toolbar preference
2275     action = gtk_action_group_get_action (window->actions_bars,
2276                                           "ViewToolbar");
2277     if(!window->prefs->show_toolbar)
2278         gtk_widget_hide (window->toolbar);
2279     else
2280         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
2281 
2282     // Apply auto-resize preference
2283     action = gtk_action_group_get_action (window->actions_image,
2284                                           "ViewResizeWindow");
2285 
2286     if(window->prefs->auto_resize)
2287         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
2288 
2289     window->msg_area = vnr_message_area_new();
2290     VNR_MESSAGE_AREA(window->msg_area)->vnr_win = window;
2291     gtk_box_pack_start (GTK_BOX (window->layout), window->msg_area, FALSE,FALSE,0);
2292     gtk_widget_show(GTK_WIDGET (window->msg_area));
2293 
2294     window->view = uni_anim_view_new ();
2295     gtk_widget_set_can_focus(window->view, TRUE);
2296     window->scroll_view = uni_scroll_win_new (UNI_IMAGE_VIEW (window->view));
2297 
2298     // Apply scrollbar preference
2299     action = gtk_action_group_get_action (window->actions_bars,
2300                                           "ViewScrollbar");
2301     uni_scroll_win_set_show_scrollbar (UNI_SCROLL_WIN (window->scroll_view), window->prefs->show_scrollbar);
2302     gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), window->prefs->show_scrollbar);
2303 
2304     gtk_box_pack_end (GTK_BOX (window->layout), window->scroll_view, TRUE,TRUE,0);
2305     gtk_widget_show_all(GTK_WIDGET (window->scroll_view));
2306 
2307     gtk_widget_grab_focus(window->view);
2308 
2309     /* Care for Properties dialog */
2310     window->props_dlg = vnr_properties_dialog_new(window,
2311                              gtk_action_group_get_action (window->actions_collection,
2312                                                           "GoNext"),
2313                              gtk_action_group_get_action (window->actions_collection,
2314                                                           "GoPrevious"));
2315 
2316     vnr_window_apply_preferences(window);
2317 
2318     vnr_window_set_drag(window);
2319 
2320     g_signal_connect (G_OBJECT (window), "destroy",
2321                       G_CALLBACK (window_destroy_cb), NULL);
2322 
2323     g_signal_connect (G_OBJECT (window), "realize",
2324                       G_CALLBACK (window_realize_cb), NULL);
2325 
2326     g_signal_connect (G_OBJECT (window), "window-state-event",
2327                       G_CALLBACK (window_change_state_cb), NULL);
2328 
2329     g_signal_connect (G_OBJECT (window->view), "zoom_changed",
2330                       G_CALLBACK (zoom_changed_cb), window);
2331 
2332     g_signal_connect (G_OBJECT (window->view), "drag-data-get",
2333                       G_CALLBACK (window_drag_begin_cb), window);
2334 
2335     g_signal_connect (G_OBJECT (window->button_menu), "hide",
2336                       G_CALLBACK(vnr_window_cmd_main_menu_hidden), window);
2337 
2338     gtk_window_add_accel_group (GTK_WINDOW (window),
2339                 gtk_ui_manager_get_accel_group (window->ui_mngr));
2340 
2341      vnr_window_load_accel_map();
2342 }
2343 
2344 /*************************************************************/
2345 /***** Actions ***********************************************/
2346 /*************************************************************/
2347 gboolean
vnr_window_open(VnrWindow * window,gboolean fit_to_screen)2348 vnr_window_open (VnrWindow * window, gboolean fit_to_screen)
2349 {
2350     VnrFile *file;
2351     GdkPixbufAnimation *pixbuf;
2352     GdkPixbufFormat *format;
2353     UniFittingMode last_fit_mode;
2354     GError *error = NULL;
2355 
2356     if(window->file_list == NULL)
2357         return FALSE;
2358 
2359     file = VNR_FILE(window->file_list->data);
2360 
2361     update_fs_filename_label(window);
2362 
2363     pixbuf = gdk_pixbuf_animation_new_from_file (file->path, &error);
2364 
2365     if (error != NULL)
2366     {
2367         vnr_message_area_show(VNR_MESSAGE_AREA (window->msg_area),
2368                               TRUE, error->message, TRUE);
2369 
2370         if(gtk_widget_get_visible(window->props_dlg))
2371             vnr_properties_dialog_clear(VNR_PROPERTIES_DIALOG(window->props_dlg));
2372         return FALSE;
2373     }
2374 
2375     if(vnr_message_area_is_visible(VNR_MESSAGE_AREA(window->msg_area)))
2376     {
2377         vnr_message_area_hide(VNR_MESSAGE_AREA(window->msg_area));
2378     }
2379 
2380     gtk_action_group_set_sensitive(window->actions_image, TRUE);
2381     gtk_action_group_set_sensitive(window->action_wallpaper, TRUE);
2382 
2383     format = gdk_pixbuf_get_file_info (file->path, NULL, NULL);
2384 
2385     g_free(window->writable_format_name);
2386     if(gdk_pixbuf_format_is_writable (format))
2387         window->writable_format_name = gdk_pixbuf_format_get_name (format);
2388     else
2389         window->writable_format_name = NULL;
2390 
2391     vnr_tools_apply_embedded_orientation (&pixbuf);
2392     window->current_image_width = gdk_pixbuf_animation_get_width (pixbuf);
2393     window->current_image_height = gdk_pixbuf_animation_get_height (pixbuf);
2394     window->modifications = 0;
2395 
2396     if(fit_to_screen)
2397     {
2398         GtkAllocation allocation;
2399         gint img_h, img_w;          /* Width and Height of the pixbuf */
2400 
2401         img_w = window->current_image_width;
2402         img_h = window->current_image_height;
2403 
2404         vnr_tools_fit_to_size (&img_w, &img_h, window->max_width, window->max_height);
2405 
2406         gtk_widget_get_allocation (window->menus, &allocation);
2407         gtk_window_resize (GTK_WINDOW (window), img_w, img_h + allocation.height);
2408     }
2409 
2410     last_fit_mode = UNI_IMAGE_VIEW(window->view)->fitting;
2411 
2412     /* Return TRUE if the image is static */
2413     if ( uni_anim_view_set_anim (UNI_ANIM_VIEW (window->view), pixbuf) )
2414         gtk_action_group_set_sensitive(window->actions_static_image, TRUE);
2415     else
2416         gtk_action_group_set_sensitive(window->actions_static_image, FALSE);
2417 
2418     if(window->mode != VNR_WINDOW_MODE_NORMAL && window->prefs->fit_on_fullscreen)
2419     {
2420         uni_image_view_set_zoom_mode (UNI_IMAGE_VIEW(window->view), VNR_PREFS_ZOOM_FIT);
2421     }
2422     else if(window->prefs->zoom == VNR_PREFS_ZOOM_LAST_USED )
2423     {
2424         uni_image_view_set_fitting (UNI_IMAGE_VIEW(window->view), last_fit_mode);
2425         zoom_changed_cb(UNI_IMAGE_VIEW(window->view), window);
2426     }
2427     else
2428     {
2429         uni_image_view_set_zoom_mode (UNI_IMAGE_VIEW(window->view), window->prefs->zoom);
2430     }
2431 
2432     if ( window->prefs->auto_resize ) {
2433         vnr_window_cmd_resize(NULL, window);
2434     }
2435 
2436     if(gtk_widget_get_visible(window->props_dlg))
2437         vnr_properties_dialog_update(VNR_PROPERTIES_DIALOG(window->props_dlg));
2438 
2439     vnr_window_update_openwith_menu (window);
2440 
2441     g_object_unref(pixbuf);
2442     return TRUE;
2443 }
2444 
2445 void
vnr_window_open_from_list(VnrWindow * window,GSList * uri_list)2446 vnr_window_open_from_list(VnrWindow *window, GSList *uri_list)
2447 {
2448     GList *file_list = NULL;
2449     GError *error = NULL;
2450 
2451     if (g_slist_length(uri_list) == 1)
2452     {
2453         vnr_file_load_single_uri (uri_list->data, &file_list, window->prefs->show_hidden, &error);
2454     }
2455     else
2456     {
2457         vnr_file_load_uri_list (uri_list, &file_list, window->prefs->show_hidden, &error);
2458     }
2459 
2460     if(error != NULL && file_list != NULL)
2461     {
2462         vnr_window_close(window);
2463         gtk_action_group_set_sensitive(window->actions_collection, FALSE);
2464         deny_slideshow(window);
2465         vnr_message_area_show(VNR_MESSAGE_AREA (window->msg_area),
2466                               TRUE, error->message, TRUE);
2467 
2468         vnr_window_set_list(window, file_list, TRUE);
2469     }
2470     else if(error != NULL)
2471     {
2472         vnr_window_close(window);
2473         deny_slideshow(window);
2474         vnr_message_area_show(VNR_MESSAGE_AREA (window->msg_area),
2475                               TRUE, error->message, TRUE);
2476     }
2477     else if(file_list == NULL)
2478     {
2479         vnr_window_close(window);
2480         gtk_action_group_set_sensitive(window->actions_collection, FALSE);
2481         deny_slideshow(window);
2482         vnr_message_area_show(VNR_MESSAGE_AREA (window->msg_area), TRUE,
2483                               _("The given locations contain no images."),
2484                               TRUE);
2485     }
2486     else
2487     {
2488         vnr_window_set_list(window, file_list, TRUE);
2489         if(!window->cursor_is_hidden)
2490             gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)),
2491                                   gdk_cursor_new(GDK_WATCH));
2492         /* This makes the cursor show NOW */
2493         gdk_flush();
2494 
2495         vnr_window_close(window);
2496         vnr_window_open(window, FALSE);
2497         if(!window->cursor_is_hidden)
2498             gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)),
2499                                   gdk_cursor_new(GDK_LEFT_PTR));
2500     }
2501 }
2502 
2503 void
vnr_window_close(VnrWindow * window)2504 vnr_window_close(VnrWindow *window)
2505 {
2506     gtk_window_set_title (GTK_WINDOW (window), "Viewnior");
2507     uni_anim_view_set_anim (UNI_ANIM_VIEW (window->view), NULL);
2508     gtk_action_group_set_sensitive(window->actions_image, FALSE);
2509     gtk_action_group_set_sensitive(window->action_wallpaper, FALSE);
2510     gtk_action_group_set_sensitive(window->actions_static_image, FALSE);
2511 }
2512 
2513 void
vnr_window_set_list(VnrWindow * window,GList * list,gboolean free_current)2514 vnr_window_set_list (VnrWindow *window, GList *list, gboolean free_current)
2515 {
2516     if (free_current == TRUE && window->file_list != NULL)
2517         g_list_free (window->file_list);
2518     if (g_list_length(g_list_first(list)) > 1)
2519     {
2520         gtk_action_group_set_sensitive(window->actions_collection, TRUE);
2521         allow_slideshow(window);
2522     }
2523     else
2524     {
2525         gtk_action_group_set_sensitive(window->actions_collection, FALSE);
2526         deny_slideshow(window);
2527     }
2528     window->file_list = list;
2529 }
2530 
2531 gboolean
vnr_window_next(VnrWindow * window,gboolean rem_timeout)2532 vnr_window_next (VnrWindow *window, gboolean rem_timeout){
2533     GList *next;
2534 
2535     /* Don't reload current image
2536      * if the list contains only one (or no) image */
2537     if (g_list_length(g_list_first(window->file_list)) <2)
2538         return FALSE;
2539 
2540     if(window->mode == VNR_WINDOW_MODE_SLIDESHOW && rem_timeout)
2541         g_source_remove (window->ss_source_tag);
2542 
2543     next = g_list_next(window->file_list);
2544     if(next == NULL)
2545     {
2546         next = g_list_first(window->file_list);
2547     }
2548 
2549     window->file_list = next;
2550 
2551     if(!window->cursor_is_hidden)
2552         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)),
2553                               gdk_cursor_new(GDK_WATCH));
2554     /* This makes the cursor show NOW */
2555     gdk_flush();
2556 
2557     vnr_window_open(window, FALSE);
2558     if(!window->cursor_is_hidden)
2559         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)),
2560                               gdk_cursor_new(GDK_LEFT_PTR));
2561 
2562     if(window->mode == VNR_WINDOW_MODE_SLIDESHOW && rem_timeout)
2563         window->ss_source_tag = g_timeout_add_seconds (window->ss_timeout,
2564                                                        (GSourceFunc)next_image_src,
2565                                                        window);
2566 
2567     return TRUE;
2568 }
2569 
2570 gboolean
vnr_window_prev(VnrWindow * window)2571 vnr_window_prev (VnrWindow *window){
2572     GList *prev;
2573 
2574     /* Don't reload current image
2575      * if the list contains only one (or no) image */
2576     if (g_list_length(g_list_first(window->file_list)) <2)
2577         return FALSE;
2578 
2579     if(window->mode == VNR_WINDOW_MODE_SLIDESHOW)
2580         g_source_remove (window->ss_source_tag);
2581 
2582     prev = g_list_previous(window->file_list);
2583     if(prev == NULL)
2584     {
2585         prev = g_list_last(window->file_list);
2586     }
2587 
2588     window->file_list = prev;
2589 
2590     if(!window->cursor_is_hidden)
2591         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)),
2592                               gdk_cursor_new(GDK_WATCH));
2593     /* This makes the cursor show NOW */
2594     gdk_flush();
2595 
2596     vnr_window_open(window, FALSE);
2597     if(!window->cursor_is_hidden)
2598         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)),
2599                               gdk_cursor_new(GDK_LEFT_PTR));
2600 
2601     if(window->mode == VNR_WINDOW_MODE_SLIDESHOW)
2602         window->ss_source_tag = g_timeout_add_seconds (window->ss_timeout,
2603                                                        (GSourceFunc)next_image_src,
2604                                                        window);
2605 
2606     return TRUE;
2607 }
2608 
2609 gboolean
vnr_window_first(VnrWindow * window)2610 vnr_window_first (VnrWindow *window){
2611     GList *prev;
2612 
2613     prev = g_list_first(window->file_list);
2614 
2615     if(vnr_message_area_is_critical(VNR_MESSAGE_AREA(window->msg_area)))
2616     {
2617         vnr_message_area_hide(VNR_MESSAGE_AREA(window->msg_area));
2618     }
2619 
2620     window->file_list = prev;
2621 
2622     if(!window->cursor_is_hidden)
2623         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)),
2624                               gdk_cursor_new(GDK_WATCH));
2625     /* This makes the cursor show NOW */
2626     gdk_flush();
2627 
2628     vnr_window_open(window, FALSE);
2629     if(!window->cursor_is_hidden)
2630         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)),
2631                               gdk_cursor_new(GDK_LEFT_PTR));
2632     return TRUE;
2633 }
2634 
2635 gboolean
vnr_window_last(VnrWindow * window)2636 vnr_window_last (VnrWindow *window){
2637     GList *prev;
2638 
2639     prev = g_list_last(window->file_list);
2640 
2641     if(vnr_message_area_is_critical(VNR_MESSAGE_AREA(window->msg_area)))
2642     {
2643         vnr_message_area_hide(VNR_MESSAGE_AREA(window->msg_area));
2644     }
2645 
2646     window->file_list = prev;
2647 
2648     if(!window->cursor_is_hidden)
2649         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)),
2650                               gdk_cursor_new(GDK_WATCH));
2651     /* This makes the cursor show NOW */
2652     gdk_flush();
2653 
2654     vnr_window_open(window, FALSE);
2655     if(!window->cursor_is_hidden)
2656         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)),
2657                               gdk_cursor_new(GDK_LEFT_PTR));
2658     return TRUE;
2659 }
2660 
2661 void
vnr_window_apply_preferences(VnrWindow * window)2662 vnr_window_apply_preferences (VnrWindow *window)
2663 {
2664     if(window->prefs->smooth_images && UNI_IMAGE_VIEW(window->view)->interp != GDK_INTERP_BILINEAR)
2665     {
2666         UNI_IMAGE_VIEW(window->view)->interp = GDK_INTERP_BILINEAR;
2667         gtk_widget_queue_draw(window->view);
2668     }
2669     else if(!window->prefs->smooth_images && UNI_IMAGE_VIEW(window->view)->interp != GDK_INTERP_NEAREST)
2670     {
2671         UNI_IMAGE_VIEW(window->view)->interp = GDK_INTERP_NEAREST;
2672         gtk_widget_queue_draw(window->view);
2673     }
2674 
2675 
2676     if(gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(window->ss_timeout_widget)) != window->prefs->slideshow_timeout)
2677     {
2678         gtk_spin_button_set_value(GTK_SPIN_BUTTON(window->ss_timeout_widget), (gdouble) window->prefs->slideshow_timeout);
2679     }
2680 }
2681 
2682 void
vnr_window_toggle_fullscreen(VnrWindow * window)2683 vnr_window_toggle_fullscreen (VnrWindow *window)
2684 {
2685     gboolean fullscreen;
2686 
2687     fullscreen = (window->mode == VNR_WINDOW_MODE_NORMAL)?TRUE:FALSE;
2688 
2689     if (fullscreen)
2690         vnr_window_fullscreen (window);
2691     else
2692         vnr_window_unfullscreen (window);
2693 }
2694