1 /* Eye Of Gnome - Main Window
2 *
3 * Copyright (C) 2000-2008 The Free Software Foundation
4 *
5 * Author: Lucas Rocha <lucasr@gnome.org>
6 *
7 * Based on code by:
8 * - Federico Mena-Quintero <federico@gnome.org>
9 * - Jens Finke <jens@gnome.org>
10 * Based on evince code (shell/ev-window.c) by:
11 * - Martin Kretzschmar <martink@gnome.org>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <math.h>
33
34 #include "eog-window.h"
35 #include "eog-scroll-view.h"
36 #include "eog-debug.h"
37 #include "eog-file-chooser.h"
38 #include "eog-thumb-view.h"
39 #include "eog-list-store.h"
40 #include "eog-sidebar.h"
41 #include "eog-statusbar.h"
42 #include "eog-preferences-dialog.h"
43 #include "eog-properties-dialog.h"
44 #include "eog-print.h"
45 #include "eog-error-message-area.h"
46 #include "eog-application.h"
47 #include "eog-application-internal.h"
48 #include "eog-thumb-nav.h"
49 #include "eog-config-keys.h"
50 #include "eog-job-scheduler.h"
51 #include "eog-jobs.h"
52 #include "eog-util.h"
53 #include "eog-save-as-dialog-helper.h"
54 #include "eog-plugin-engine.h"
55 #include "eog-close-confirmation-dialog.h"
56 #include "eog-clipboard-handler.h"
57 #include "eog-window-activatable.h"
58 #include "eog-metadata-sidebar.h"
59 #include "eog-zoom-entry.h"
60
61 #include "eog-enum-types.h"
62
63 #include <glib.h>
64 #include <glib-object.h>
65 #include <glib/gi18n.h>
66 #include <gio/gio.h>
67 #include <gdk/gdkkeysyms.h>
68 #include <gio/gdesktopappinfo.h>
69 #include <gtk/gtk.h>
70
71 #include <libpeas/peas-extension-set.h>
72 #include <libpeas/peas-activatable.h>
73
74 #if defined(HAVE_LCMS) && defined(GDK_WINDOWING_X11)
75 #include <X11/Xlib.h>
76 #include <X11/Xatom.h>
77 #include <gdk/gdkx.h>
78 #include <lcms2.h>
79 #endif
80
81 #define EOG_WINDOW_MIN_WIDTH 360
82 #define EOG_WINDOW_MIN_HEIGHT 350
83
84 #define EOG_WINDOW_DEFAULT_WIDTH 540
85 #define EOG_WINDOW_DEFAULT_HEIGHT 450
86
87 #define EOG_WINDOW_FULLSCREEN_TIMEOUT 2 * 1000
88 #define EOG_WINDOW_FULLSCREEN_POPUP_THRESHOLD 5
89
90 #define EOG_RECENT_FILES_GROUP "Graphics"
91 #define EOG_RECENT_FILES_APP_NAME "Image Viewer"
92
93 #define EOG_WALLPAPER_FILENAME "eog-wallpaper"
94
95 #define is_rtl (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL)
96
97 typedef enum {
98 EOG_WINDOW_STATUS_UNKNOWN,
99 EOG_WINDOW_STATUS_INIT,
100 EOG_WINDOW_STATUS_NORMAL
101 } EogWindowStatus;
102
103 enum {
104 PROP_0,
105 PROP_GALLERY_POS,
106 PROP_GALLERY_RESIZABLE,
107 PROP_STARTUP_FLAGS
108 };
109
110 enum {
111 SIGNAL_PREPARED,
112 SIGNAL_LAST
113 };
114
115 static gint signals[SIGNAL_LAST];
116
117 struct _EogWindowPrivate {
118 GSettings *fullscreen_settings;
119 GSettings *ui_settings;
120 GSettings *view_settings;
121 GSettings *lockdown_settings;
122
123 EogListStore *store;
124 EogImage *image;
125 EogWindowMode mode;
126 EogWindowStatus status;
127
128 GtkWidget *overlay;
129 GtkWidget *box;
130 GtkWidget *layout;
131 GtkWidget *cbox;
132 GtkWidget *view;
133 GtkWidget *sidebar;
134 GtkWidget *thumbview;
135 GtkWidget *statusbar;
136 GtkWidget *nav;
137 GtkWidget *message_area;
138 GtkWidget *properties_dlg;
139
140 GtkBuilder *gear_menu_builder;
141
142 GtkWidget *fullscreen_popup;
143 GSource *fullscreen_timeout_source;
144
145 gboolean slideshow_loop;
146 gint slideshow_switch_timeout;
147 GSource *slideshow_switch_source;
148
149 guint fullscreen_idle_inhibit_cookie;
150
151 EogJob *load_job;
152 EogJob *transform_job;
153 EogJob *save_job;
154 GFile *last_save_as_folder;
155 EogJob *copy_job;
156
157 guint image_info_message_cid;
158 guint tip_message_cid;
159 guint copy_file_cid;
160
161 EogStartupFlags flags;
162 GSList *file_list;
163
164 EogWindowGalleryPos gallery_position;
165 gboolean gallery_resizable;
166
167 gboolean save_disabled;
168 gboolean needs_reload_confirmation;
169
170 GtkPageSetup *page_setup;
171
172 PeasExtensionSet *extensions;
173
174 #ifdef HAVE_LCMS
175 cmsHPROFILE *display_profile;
176 #endif
177 };
178
179 G_DEFINE_TYPE_WITH_PRIVATE (EogWindow, eog_window, GTK_TYPE_APPLICATION_WINDOW);
180
181 static void eog_window_action_toggle_fullscreen (GSimpleAction *action, GVariant *state, gpointer user_data);
182 static void eog_window_run_fullscreen (EogWindow *window, gboolean slideshow);
183 static void eog_window_action_save (GSimpleAction *action, GVariant *variant, gpointer user_data);
184 static void eog_window_action_save_as (GSimpleAction *action, GVariant *variant, gpointer user_data);
185 static void eog_window_action_toggle_slideshow (GSimpleAction *action, GVariant *state, gpointer user_data);
186 static void eog_window_action_pause_slideshow (GSimpleAction *action, GVariant *variant, gpointer user_data);
187 static void eog_window_stop_fullscreen (EogWindow *window, gboolean slideshow);
188 static void eog_job_load_cb (EogJobLoad *job, gpointer data);
189 static void eog_job_save_progress_cb (EogJobSave *job, float progress, gpointer data);
190 static void eog_job_progress_cb (EogJobLoad *job, float progress, gpointer data);
191 static void eog_job_transform_cb (EogJobTransform *job, gpointer data);
192 static void fullscreen_set_timeout (EogWindow *window);
193 static void fullscreen_clear_timeout (EogWindow *window);
194 static void slideshow_set_timeout (EogWindow *window);
195 static void update_action_groups_state (EogWindow *window);
196 static void eog_window_list_store_image_added (GtkTreeModel *tree_model,
197 GtkTreePath *path,
198 GtkTreeIter *iter,
199 gpointer user_data);
200 static void eog_window_list_store_image_removed (GtkTreeModel *tree_model,
201 GtkTreePath *path,
202 gpointer user_data);
203 static void eog_window_set_wallpaper (EogWindow *window, const gchar *filename, const gchar *visible_filename);
204 static gboolean eog_window_save_images (EogWindow *window, GList *images);
205 static void eog_window_finish_saving (EogWindow *window);
206 static void eog_window_error_message_area_response (GtkInfoBar *message_area,
207 gint response_id,
208 EogWindow *window);
209
210 static GQuark
eog_window_error_quark(void)211 eog_window_error_quark (void)
212 {
213 static GQuark q = 0;
214
215 if (q == 0)
216 q = g_quark_from_static_string ("eog-window-error-quark");
217
218 return q;
219 }
220
221 static gboolean
_eog_zoom_shrink_to_boolean(GBinding * binding,const GValue * source,GValue * target,gpointer user_data)222 _eog_zoom_shrink_to_boolean (GBinding *binding, const GValue *source,
223 GValue *target, gpointer user_data)
224 {
225 EogZoomMode mode = g_value_get_enum (source);
226 gboolean is_fit;
227
228 is_fit = (mode == EOG_ZOOM_MODE_SHRINK_TO_FIT);
229 g_value_set_variant (target, g_variant_new_boolean (is_fit));
230
231 return TRUE;
232 }
233
234 static void
eog_window_set_gallery_mode(EogWindow * window,EogWindowGalleryPos position,gboolean resizable)235 eog_window_set_gallery_mode (EogWindow *window,
236 EogWindowGalleryPos position,
237 gboolean resizable)
238 {
239 EogWindowPrivate *priv;
240 GtkWidget *hpaned;
241 EogThumbNavMode mode = EOG_THUMB_NAV_MODE_ONE_ROW;
242
243 eog_debug (DEBUG_PREFERENCES);
244
245 g_return_if_fail (EOG_IS_WINDOW (window));
246
247 priv = window->priv;
248
249 /* If layout is not set, ignore similar parameters
250 * to make sure a layout is set eventually */
251 if (priv->layout
252 && priv->gallery_position == position
253 && priv->gallery_resizable == resizable)
254 return;
255
256 priv->gallery_position = position;
257 priv->gallery_resizable = resizable;
258
259 hpaned = gtk_widget_get_parent (priv->sidebar);
260
261 g_object_ref (hpaned);
262 g_object_ref (priv->nav);
263
264 if (priv->layout)
265 {
266 gtk_container_remove (GTK_CONTAINER (priv->layout), hpaned);
267 gtk_container_remove (GTK_CONTAINER (priv->layout), priv->nav);
268
269 gtk_widget_destroy (priv->layout);
270 }
271
272 switch (position) {
273 case EOG_WINDOW_GALLERY_POS_BOTTOM:
274 case EOG_WINDOW_GALLERY_POS_TOP:
275 if (resizable) {
276 mode = EOG_THUMB_NAV_MODE_MULTIPLE_ROWS;
277
278 priv->layout = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
279
280 if (position == EOG_WINDOW_GALLERY_POS_BOTTOM) {
281 gtk_paned_pack1 (GTK_PANED (priv->layout), hpaned, TRUE, FALSE);
282 gtk_paned_pack2 (GTK_PANED (priv->layout), priv->nav, FALSE, TRUE);
283 } else {
284 gtk_paned_pack1 (GTK_PANED (priv->layout), priv->nav, FALSE, TRUE);
285 gtk_paned_pack2 (GTK_PANED (priv->layout), hpaned, TRUE, FALSE);
286 }
287 } else {
288 mode = EOG_THUMB_NAV_MODE_ONE_ROW;
289
290 priv->layout = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
291
292 if (position == EOG_WINDOW_GALLERY_POS_BOTTOM) {
293 gtk_box_pack_start (GTK_BOX (priv->layout), hpaned, TRUE, TRUE, 0);
294 gtk_box_pack_start (GTK_BOX (priv->layout), priv->nav, FALSE, FALSE, 0);
295 } else {
296 gtk_box_pack_start (GTK_BOX (priv->layout), priv->nav, FALSE, FALSE, 0);
297 gtk_box_pack_start (GTK_BOX (priv->layout), hpaned, TRUE, TRUE, 0);
298 }
299 }
300 break;
301
302 case EOG_WINDOW_GALLERY_POS_LEFT:
303 case EOG_WINDOW_GALLERY_POS_RIGHT:
304 if (resizable) {
305 mode = EOG_THUMB_NAV_MODE_MULTIPLE_COLUMNS;
306
307 priv->layout = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
308
309 if (position == EOG_WINDOW_GALLERY_POS_LEFT) {
310 gtk_paned_pack1 (GTK_PANED (priv->layout), priv->nav, FALSE, TRUE);
311 gtk_paned_pack2 (GTK_PANED (priv->layout), hpaned, TRUE, FALSE);
312 } else {
313 gtk_paned_pack1 (GTK_PANED (priv->layout), hpaned, TRUE, FALSE);
314 gtk_paned_pack2 (GTK_PANED (priv->layout), priv->nav, FALSE, TRUE);
315 }
316 } else {
317 mode = EOG_THUMB_NAV_MODE_ONE_COLUMN;
318
319 priv->layout = gtk_box_new (GTK_ORIENTATION_HORIZONTAL,
320 2);
321
322 if (position == EOG_WINDOW_GALLERY_POS_LEFT) {
323 gtk_box_pack_start (GTK_BOX (priv->layout), priv->nav, FALSE, FALSE, 0);
324 gtk_box_pack_start (GTK_BOX (priv->layout), hpaned, TRUE, TRUE, 0);
325 } else {
326 gtk_box_pack_start (GTK_BOX (priv->layout), hpaned, TRUE, TRUE, 0);
327 gtk_box_pack_start (GTK_BOX (priv->layout), priv->nav, FALSE, FALSE, 0);
328 }
329 }
330
331 break;
332 }
333
334 gtk_box_pack_end (GTK_BOX (priv->cbox), priv->layout, TRUE, TRUE, 0);
335 gtk_widget_show (priv->layout);
336
337 eog_thumb_nav_set_mode (EOG_THUMB_NAV (priv->nav), mode);
338
339 if (priv->mode != EOG_WINDOW_MODE_UNKNOWN) {
340 update_action_groups_state (window);
341 }
342 }
343
344 static void
eog_window_can_save_changed_cb(GSettings * settings,const gchar * key,gpointer user_data)345 eog_window_can_save_changed_cb (GSettings *settings,
346 const gchar *key,
347 gpointer user_data)
348 {
349 EogWindowPrivate *priv;
350 EogWindow *window;
351 gboolean save_disabled = FALSE;
352 GAction *action_save, *action_save_as;
353
354 eog_debug (DEBUG_PREFERENCES);
355
356 g_return_if_fail (EOG_IS_WINDOW (user_data));
357
358 window = EOG_WINDOW (user_data);
359 priv = EOG_WINDOW (user_data)->priv;
360
361 save_disabled = g_settings_get_boolean (settings, key);
362
363 priv->save_disabled = save_disabled;
364
365 action_save =
366 g_action_map_lookup_action (G_ACTION_MAP (window),
367 "save");
368 action_save_as =
369 g_action_map_lookup_action (G_ACTION_MAP (window),
370 "save-as");
371
372 if (priv->save_disabled) {
373 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_save), FALSE);
374 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_save_as), FALSE);
375 } else {
376 EogImage *image = eog_window_get_image (window);
377
378 if (EOG_IS_IMAGE (image)) {
379 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_save),
380 eog_image_is_modified (image));
381
382 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_save_as), TRUE);
383 }
384 }
385 }
386
387 #if defined(HAVE_LCMS) && defined(GDK_WINDOWING_X11)
388 static cmsHPROFILE *
eog_window_get_display_profile(GtkWidget * window)389 eog_window_get_display_profile (GtkWidget *window)
390 {
391 GdkScreen *screen;
392 Display *dpy;
393 Atom icc_atom, type;
394 int format;
395 gulong nitems;
396 gulong bytes_after;
397 gulong length;
398 guchar *str;
399 int result;
400 cmsHPROFILE *profile = NULL;
401 char *atom_name;
402
403 screen = gtk_widget_get_screen (window);
404
405 if (GDK_IS_X11_SCREEN (screen)) {
406 dpy = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
407
408 if (gdk_x11_screen_get_screen_number (screen) > 0)
409 atom_name = g_strdup_printf ("_ICC_PROFILE_%d",
410 gdk_x11_screen_get_screen_number (screen));
411 else
412 atom_name = g_strdup ("_ICC_PROFILE");
413
414 icc_atom = gdk_x11_get_xatom_by_name_for_display (gdk_screen_get_display (screen), atom_name);
415
416 g_free (atom_name);
417
418 result = XGetWindowProperty (dpy,
419 GDK_WINDOW_XID (gdk_screen_get_root_window (screen)),
420 icc_atom,
421 0,
422 G_MAXLONG,
423 False,
424 XA_CARDINAL,
425 &type,
426 &format,
427 &nitems,
428 &bytes_after,
429 (guchar **)&str);
430
431 /* TODO: handle bytes_after != 0 */
432
433 if ((result == Success) && (type == XA_CARDINAL) && (nitems > 0)) {
434 switch (format)
435 {
436 case 8:
437 length = nitems;
438 break;
439 case 16:
440 length = sizeof(short) * nitems;
441 break;
442 case 32:
443 length = sizeof(long) * nitems;
444 break;
445 default:
446 eog_debug_message (DEBUG_LCMS,
447 "Unable to read profile, not correcting");
448
449 XFree (str);
450 return NULL;
451 }
452
453 profile = cmsOpenProfileFromMem (str, length);
454
455 if (G_UNLIKELY (profile == NULL)) {
456 eog_debug_message (DEBUG_LCMS,
457 "Invalid display profile set, "
458 "not using it");
459 }
460
461 XFree (str);
462 }
463 } else {
464 /* ICC profiles cannot be queried on Wayland yet */
465 eog_debug_message (DEBUG_LCMS,
466 "Not an X11 screen. "
467 "Cannot fetch display profile.");
468 }
469
470 if (profile == NULL) {
471 profile = cmsCreate_sRGBProfile ();
472 eog_debug_message (DEBUG_LCMS,
473 "No valid display profile set, assuming sRGB");
474 }
475
476 return profile;
477 }
478 #endif
479
480 static void
update_image_pos(EogWindow * window)481 update_image_pos (EogWindow *window)
482 {
483 EogWindowPrivate *priv;
484 GAction* action;
485 gint pos = -1, n_images = 0;
486
487 priv = window->priv;
488
489 n_images = eog_list_store_length (EOG_LIST_STORE (priv->store));
490
491 /* priv->image may be NULL even if n_images > 0 */
492 if (n_images > 0 && priv->image) {
493 pos = eog_list_store_get_pos_by_image (EOG_LIST_STORE (priv->store),
494 priv->image);
495 }
496 /* Images: (image pos) / (n_total_images) */
497 eog_statusbar_set_image_number (EOG_STATUSBAR (priv->statusbar),
498 pos + 1,
499 n_images);
500
501 action = g_action_map_lookup_action (G_ACTION_MAP (window),
502 "current-image");
503
504 g_return_if_fail (action != NULL);
505 g_simple_action_set_state (G_SIMPLE_ACTION (action),
506 g_variant_new ("(ii)", pos + 1, n_images));
507
508 }
509
510 static void
update_status_bar(EogWindow * window)511 update_status_bar (EogWindow *window)
512 {
513 EogWindowPrivate *priv;
514 char *str = NULL;
515
516 g_return_if_fail (EOG_IS_WINDOW (window));
517
518 eog_debug (DEBUG_WINDOW);
519
520 priv = window->priv;
521
522 if (priv->image != NULL) {
523 if (eog_image_has_data (priv->image, EOG_IMAGE_DATA_DIMENSION)) {
524 int zoom, width, height;
525 goffset bytes = 0;
526
527 zoom = floor (100 * eog_scroll_view_get_zoom (EOG_SCROLL_VIEW (priv->view)) + 0.5);
528
529 eog_image_get_size (priv->image, &width, &height);
530
531 bytes = eog_image_get_bytes (priv->image);
532
533 if ((width > 0) && (height > 0)) {
534 gchar *size_string;
535
536 size_string = g_format_size (bytes);
537
538 /* Translators: This is the string displayed in the statusbar
539 * The tokens are from left to right:
540 * - image width
541 * - image height
542 * - image size in bytes
543 * - zoom in percent */
544 str = g_strdup_printf (ngettext("%i × %i pixel %s %i%%",
545 "%i × %i pixels %s %i%%", height),
546 width,
547 height,
548 size_string,
549 zoom);
550
551 g_free (size_string);
552 }
553 }
554
555 update_image_pos (window);
556 }
557
558 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar),
559 priv->image_info_message_cid);
560
561 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar),
562 priv->image_info_message_cid, str ? str : "");
563
564 g_free (str);
565 }
566
567 static void
eog_window_set_message_area(EogWindow * window,GtkWidget * message_area)568 eog_window_set_message_area (EogWindow *window,
569 GtkWidget *message_area)
570 {
571 if (window->priv->message_area == message_area)
572 return;
573
574 if (window->priv->message_area != NULL)
575 gtk_widget_destroy (window->priv->message_area);
576
577 window->priv->message_area = message_area;
578
579 if (message_area == NULL) return;
580
581 gtk_box_pack_start (GTK_BOX (window->priv->cbox),
582 window->priv->message_area,
583 FALSE,
584 FALSE,
585 0);
586
587 g_object_add_weak_pointer (G_OBJECT (window->priv->message_area),
588 (void *) &window->priv->message_area);
589 }
590
591 static void
_eog_window_enable_action_group(GActionMap * map,const gchar ** group,gboolean enable)592 _eog_window_enable_action_group (GActionMap *map,
593 const gchar **group,
594 gboolean enable)
595 {
596 GAction *action;
597 const gchar **it = group;
598
599 for (it = group; *it != NULL; it++) {
600 action = g_action_map_lookup_action (map, *it);
601 if (G_LIKELY (action))
602 g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enable);
603 else
604 g_warning ("Action not found in action group: %s", *it);
605 }
606 }
607
608 static void
_eog_window_enable_window_actions(EogWindow * window,gboolean enable)609 _eog_window_enable_window_actions (EogWindow *window, gboolean enable)
610 {
611 static const gchar *window_actions[] = {
612 "open",
613 "close",
614 "close-all",
615 "preferences",
616 "manual",
617 "about",
618 "view-gallery",
619 "view-sidebar",
620 "view-statusbar",
621 "view-fullscreen",
622 NULL
623 };
624
625 _eog_window_enable_action_group (G_ACTION_MAP (window),
626 window_actions,
627 enable);
628 }
629
630 static void
_eog_window_enable_image_actions(EogWindow * window,gboolean enable)631 _eog_window_enable_image_actions (EogWindow *window, gboolean enable)
632 {
633 static const gchar *image_actions[] = {
634 "save",
635 "open-with",
636 "save-as",
637 "open-folder",
638 "print",
639 "properties",
640 "undo",
641 "flip-horizontal",
642 "flip-vertical",
643 "rotate-90",
644 "rotate-270",
645 "set-wallpaper",
646 "move-trash",
647 "delete",
648 "copy",
649 "zoom-in",
650 "zoom-out",
651 "zoom-normal",
652 NULL
653 };
654
655 _eog_window_enable_action_group (G_ACTION_MAP (window),
656 image_actions,
657 enable);
658 }
659
660 static void
_eog_window_enable_gallery_actions(EogWindow * window,gboolean enable)661 _eog_window_enable_gallery_actions (EogWindow *window, gboolean enable)
662 {
663
664 static const gchar *gallery_actions[] = {
665 "go-previous",
666 "go-next",
667 "go-first",
668 "go-last",
669 "go-random",
670 NULL
671 };
672
673 _eog_window_enable_action_group (G_ACTION_MAP (window),
674 gallery_actions,
675 enable);
676 }
677
678 static void
update_action_groups_state(EogWindow * window)679 update_action_groups_state (EogWindow *window)
680 {
681 EogWindowPrivate *priv;
682 GAction *action_gallery;
683 GAction *action_sidebar;
684 GAction *action_fscreen;
685 GAction *action_sshow;
686 GAction *action_print;
687 gboolean print_disabled = FALSE;
688 gboolean show_image_gallery = FALSE;
689 gint n_images = 0;
690
691 g_return_if_fail (EOG_IS_WINDOW (window));
692
693 eog_debug (DEBUG_WINDOW);
694
695 priv = window->priv;
696
697 action_gallery =
698 g_action_map_lookup_action (G_ACTION_MAP (window),
699 "view-gallery");
700
701 action_sidebar =
702 g_action_map_lookup_action (G_ACTION_MAP (window),
703 "view-sidebar");
704
705 action_fscreen =
706 g_action_map_lookup_action (G_ACTION_MAP (window),
707 "view-fullscreen");
708
709 action_sshow =
710 g_action_map_lookup_action (G_ACTION_MAP (window),
711 "view-slideshow");
712
713 action_print =
714 g_action_map_lookup_action (G_ACTION_MAP (window),
715 "print");
716
717 g_assert (action_gallery != NULL);
718 g_assert (action_sidebar != NULL);
719 g_assert (action_fscreen != NULL);
720 g_assert (action_sshow != NULL);
721 g_assert (action_print != NULL);
722
723 if (priv->store != NULL) {
724 n_images = eog_list_store_length (EOG_LIST_STORE (priv->store));
725 }
726
727 if (priv->flags & EOG_STARTUP_DISABLE_GALLERY) {
728 g_settings_set_boolean (priv->ui_settings,
729 EOG_CONF_UI_IMAGE_GALLERY,
730 FALSE);
731
732 show_image_gallery = FALSE;
733 } else {
734 show_image_gallery =
735 g_settings_get_boolean (priv->ui_settings,
736 EOG_CONF_UI_IMAGE_GALLERY);
737 }
738
739 show_image_gallery &= n_images > 1 &&
740 priv->mode != EOG_WINDOW_MODE_SLIDESHOW;
741
742 gtk_widget_set_visible (priv->nav, show_image_gallery);
743
744 g_simple_action_set_state (G_SIMPLE_ACTION (action_gallery),
745 g_variant_new_boolean (show_image_gallery));
746
747 if (show_image_gallery)
748 gtk_widget_grab_focus (priv->thumbview);
749 else
750 gtk_widget_grab_focus (priv->view);
751
752 if (n_images == 0) {
753 _eog_window_enable_window_actions (window, TRUE);
754 _eog_window_enable_image_actions (window, FALSE);
755 _eog_window_enable_gallery_actions (window, FALSE);
756
757 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_fscreen), FALSE);
758 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_sshow), FALSE);
759
760 /* If there are no images on model, initialization
761 stops here. */
762 if (priv->status == EOG_WINDOW_STATUS_INIT) {
763 priv->status = EOG_WINDOW_STATUS_NORMAL;
764 }
765 } else {
766 _eog_window_enable_window_actions (window, TRUE);
767 _eog_window_enable_image_actions (window, TRUE);
768
769 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_fscreen), TRUE);
770
771 if (n_images == 1) {
772 _eog_window_enable_gallery_actions (window, FALSE);
773 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_gallery), FALSE);
774 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_sshow), FALSE);
775 } else {
776 _eog_window_enable_gallery_actions (window, TRUE);
777 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_sshow), TRUE);
778 }
779 }
780
781 print_disabled = g_settings_get_boolean (priv->lockdown_settings,
782 EOG_CONF_DESKTOP_CAN_PRINT);
783
784 if (print_disabled) {
785 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_print), FALSE);
786 }
787
788 if (eog_sidebar_is_empty (EOG_SIDEBAR (priv->sidebar))) {
789 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_sidebar), FALSE);
790 gtk_widget_hide (priv->sidebar);
791 }
792 }
793
794 static void
update_selection_ui_visibility(EogWindow * window)795 update_selection_ui_visibility (EogWindow *window)
796 {
797 EogWindowPrivate *priv;
798 GAction *wallpaper_action;
799 gint n_selected;
800
801 priv = window->priv;
802
803 n_selected = eog_thumb_view_get_n_selected (EOG_THUMB_VIEW (priv->thumbview));
804
805 wallpaper_action =
806 g_action_map_lookup_action (G_ACTION_MAP (window),
807 "set-wallpaper");
808
809 if (n_selected == 1) {
810 g_simple_action_set_enabled (G_SIMPLE_ACTION (wallpaper_action), TRUE);
811 } else {
812 g_simple_action_set_enabled (G_SIMPLE_ACTION (wallpaper_action), FALSE);
813 }
814 }
815
816 static gboolean
add_file_to_recent_files(GFile * file)817 add_file_to_recent_files (GFile *file)
818 {
819 gchar *text_uri;
820 GFileInfo *file_info;
821 GtkRecentData *recent_data;
822 static gchar *groups[2] = { EOG_RECENT_FILES_GROUP , NULL };
823
824 if (file == NULL) return FALSE;
825
826 /* The password gets stripped here because ~/.recently-used.xbel is
827 * readable by everyone (chmod 644). It also makes the workaround
828 * for the bug with gtk_recent_info_get_uri_display() easier
829 * (see the comment in eog_window_update_recent_files_menu()). */
830 text_uri = g_file_get_uri (file);
831
832 if (text_uri == NULL)
833 return FALSE;
834
835 file_info = g_file_query_info (file,
836 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
837 0, NULL, NULL);
838 if (file_info == NULL)
839 return FALSE;
840
841 recent_data = g_slice_new (GtkRecentData);
842 recent_data->display_name = NULL;
843 recent_data->description = NULL;
844 recent_data->mime_type = (gchar *) g_file_info_get_content_type (file_info);
845 recent_data->app_name = EOG_RECENT_FILES_APP_NAME;
846 recent_data->app_exec = g_strjoin(" ", g_get_prgname (), "%u", NULL);
847 recent_data->groups = groups;
848 recent_data->is_private = FALSE;
849
850 gtk_recent_manager_add_full (gtk_recent_manager_get_default (),
851 text_uri,
852 recent_data);
853
854 g_free (recent_data->app_exec);
855 g_free (text_uri);
856 g_object_unref (file_info);
857
858 g_slice_free (GtkRecentData, recent_data);
859
860 return FALSE;
861 }
862
863 static void
image_thumb_changed_cb(EogImage * image,gpointer data)864 image_thumb_changed_cb (EogImage *image, gpointer data)
865 {
866 EogWindow *window;
867 EogWindowPrivate *priv;
868 GdkPixbuf *thumb;
869
870 g_return_if_fail (EOG_IS_WINDOW (data));
871
872 window = EOG_WINDOW (data);
873 priv = window->priv;
874
875 thumb = eog_image_get_thumbnail (image);
876
877 if (thumb != NULL) {
878 gtk_window_set_icon (GTK_WINDOW (window), thumb);
879
880 if (window->priv->properties_dlg != NULL) {
881 eog_properties_dialog_update (EOG_PROPERTIES_DIALOG (priv->properties_dlg),
882 image);
883 }
884
885 g_object_unref (thumb);
886 } else if (!gtk_widget_get_visible (window->priv->nav)) {
887 gint img_pos = eog_list_store_get_pos_by_image (window->priv->store, image);
888 GtkTreePath *path = gtk_tree_path_new_from_indices (img_pos,-1);
889 GtkTreeIter iter;
890
891 gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->store), &iter, path);
892 eog_list_store_thumbnail_set (window->priv->store, &iter);
893 gtk_tree_path_free (path);
894 }
895 }
896
897 static void
file_changed_info_bar_response(GtkInfoBar * info_bar,gint response,EogWindow * window)898 file_changed_info_bar_response (GtkInfoBar *info_bar,
899 gint response,
900 EogWindow *window)
901 {
902 if (response == GTK_RESPONSE_YES) {
903 eog_window_reload_image (window);
904 }
905
906 window->priv->needs_reload_confirmation = TRUE;
907
908 eog_window_set_message_area (window, NULL);
909 }
910 static void
image_file_changed_cb(EogImage * img,EogWindow * window)911 image_file_changed_cb (EogImage *img, EogWindow *window)
912 {
913 GtkWidget *info_bar;
914 gchar *text, *markup;
915 GtkWidget *image;
916 GtkWidget *label;
917 GtkWidget *hbox;
918
919 if (window->priv->needs_reload_confirmation == FALSE)
920 return;
921
922 if (!eog_image_is_modified (img)) {
923 /* Auto-reload when image is unmodified (bug #555370) */
924 eog_window_reload_image (window);
925 return;
926 }
927
928 window->priv->needs_reload_confirmation = FALSE;
929
930 info_bar = gtk_info_bar_new_with_buttons (_("_Reload"),
931 GTK_RESPONSE_YES,
932 C_("MessageArea", "Hi_de"),
933 GTK_RESPONSE_NO, NULL);
934 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar),
935 GTK_MESSAGE_QUESTION);
936 image = gtk_image_new_from_icon_name ("dialog-question",
937 GTK_ICON_SIZE_DIALOG);
938 label = gtk_label_new (NULL);
939
940 text = g_strdup_printf (_("The image “%s” has been modified by an external application."
941 " Would you like to reload it?"), eog_image_get_caption (img));
942 markup = g_markup_printf_escaped ("<b>%s</b>", text);
943 gtk_label_set_markup (GTK_LABEL (label), markup);
944 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
945 g_free (text);
946 g_free (markup);
947
948 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
949 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
950 gtk_widget_set_valign (image, GTK_ALIGN_START);
951 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
952 gtk_widget_set_halign (label, GTK_ALIGN_START);
953 gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar))), hbox, TRUE, TRUE, 0);
954 gtk_widget_show_all (hbox);
955 gtk_widget_show (info_bar);
956
957 eog_window_set_message_area (window, info_bar);
958 g_signal_connect (info_bar, "response",
959 G_CALLBACK (file_changed_info_bar_response), window);
960 }
961
962 static void
eog_window_display_image(EogWindow * window,EogImage * image)963 eog_window_display_image (EogWindow *window, EogImage *image)
964 {
965 EogWindowPrivate *priv;
966 GFile *file;
967
968 g_return_if_fail (EOG_IS_WINDOW (window));
969 g_return_if_fail (EOG_IS_IMAGE (image));
970
971 eog_debug (DEBUG_WINDOW);
972
973 g_assert (eog_image_has_data (image, EOG_IMAGE_DATA_IMAGE));
974
975 priv = window->priv;
976
977 if (image != NULL) {
978 g_signal_connect (image,
979 "thumbnail_changed",
980 G_CALLBACK (image_thumb_changed_cb),
981 window);
982 g_signal_connect (image, "file-changed",
983 G_CALLBACK (image_file_changed_cb),
984 window);
985
986 image_thumb_changed_cb (image, window);
987 }
988
989 priv->needs_reload_confirmation = TRUE;
990
991 eog_scroll_view_set_image (EOG_SCROLL_VIEW (priv->view), image);
992
993 gtk_window_set_title (GTK_WINDOW (window), eog_image_get_caption (image));
994
995 update_status_bar (window);
996
997 file = eog_image_get_file (image);
998 g_idle_add_full (G_PRIORITY_LOW,
999 (GSourceFunc) add_file_to_recent_files,
1000 file,
1001 (GDestroyNotify) g_object_unref);
1002
1003 if (eog_image_is_multipaged (image)) {
1004 GtkWidget *info_bar;
1005
1006 eog_debug_message (DEBUG_IMAGE_DATA, "Image is multipaged");
1007
1008 info_bar = eog_multipage_error_message_area_new ();
1009 g_signal_connect (info_bar,
1010 "response",
1011 G_CALLBACK (eog_window_error_message_area_response),
1012 window);
1013 gtk_widget_show (info_bar);
1014 eog_window_set_message_area (window, info_bar);
1015 }
1016
1017 if (window->priv->mode == EOG_WINDOW_MODE_SLIDESHOW) {
1018 slideshow_set_timeout (window);
1019 }
1020 }
1021
1022 static void
_eog_window_launch_appinfo_with_files(EogWindow * window,GAppInfo * appinfo,GList * files)1023 _eog_window_launch_appinfo_with_files (EogWindow *window,
1024 GAppInfo *appinfo,
1025 GList *files)
1026 {
1027 GdkAppLaunchContext *context;
1028
1029 context = gdk_display_get_app_launch_context (
1030 gtk_widget_get_display (GTK_WIDGET (window)));
1031 gdk_app_launch_context_set_screen (context,
1032 gtk_widget_get_screen (GTK_WIDGET (window)));
1033 gdk_app_launch_context_set_icon (context,
1034 g_app_info_get_icon (appinfo));
1035 gdk_app_launch_context_set_timestamp (context,
1036 gtk_get_current_event_time ());
1037
1038 g_app_info_launch (appinfo, files, G_APP_LAUNCH_CONTEXT (context), NULL);
1039
1040 g_object_unref (context);
1041 }
1042
1043 static void
app_chooser_dialog_response_cb(GtkDialog * dialog,gint response_id,gpointer data)1044 app_chooser_dialog_response_cb (GtkDialog *dialog,
1045 gint response_id,
1046 gpointer data)
1047 {
1048 EogWindow *window;
1049 GAppInfo *app;
1050 GFile *file;
1051 GList *files = NULL;
1052
1053 g_return_if_fail (EOG_IS_WINDOW (data));
1054
1055 window = EOG_WINDOW (data);
1056
1057 if (response_id != GTK_RESPONSE_OK) {
1058 goto out;
1059 }
1060
1061 app = gtk_app_chooser_get_app_info (GTK_APP_CHOOSER (dialog));
1062 file = eog_image_get_file (window->priv->image);
1063 files = g_list_append (files, file);
1064
1065 _eog_window_launch_appinfo_with_files (window, app, files);
1066
1067 g_list_free (files);
1068 g_object_unref (file);
1069 out:
1070 gtk_widget_destroy (GTK_WIDGET (dialog));
1071 }
1072
1073 static void
eog_window_open_file_chooser_dialog(EogWindow * window)1074 eog_window_open_file_chooser_dialog (EogWindow *window)
1075 {
1076 GtkWidget *dialog;
1077 GFileInfo *file_info;
1078 GFile *file;
1079 const gchar *mime_type = NULL;
1080
1081 file = eog_image_get_file (window->priv->image);
1082 file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0, NULL, NULL);
1083 mime_type = g_content_type_get_mime_type (
1084 g_file_info_get_content_type (file_info));
1085 g_object_unref (file_info);
1086
1087 dialog = gtk_app_chooser_dialog_new_for_content_type (GTK_WINDOW (window),
1088 GTK_DIALOG_MODAL |
1089 GTK_DIALOG_DESTROY_WITH_PARENT |
1090 GTK_DIALOG_USE_HEADER_BAR,
1091 mime_type);
1092 gtk_widget_show (dialog);
1093
1094 g_signal_connect_object (dialog, "response",
1095 G_CALLBACK (app_chooser_dialog_response_cb),
1096 window, 0);
1097
1098 g_object_unref (file);
1099 }
1100
1101 static void
eog_window_action_open_with(GSimpleAction * action,GVariant * parameter,gpointer user_data)1102 eog_window_action_open_with (GSimpleAction *action,
1103 GVariant *parameter,
1104 gpointer user_data)
1105 {
1106 EogWindow *window;
1107
1108 g_return_if_fail (EOG_IS_WINDOW (user_data));
1109 window = EOG_WINDOW (user_data);
1110
1111 #ifdef HAVE_LIBPORTAL
1112 if (eog_util_is_running_inside_flatpak ()) {
1113 GFile *file = eog_image_get_file (window->priv->image);
1114
1115 eog_util_open_file_with_flatpak_portal (file, GTK_WINDOW (window));
1116 g_object_unref (file);
1117
1118 return;
1119 }
1120 #endif
1121 eog_window_open_file_chooser_dialog (window);
1122 }
1123
1124 static void
eog_window_clear_load_job(EogWindow * window)1125 eog_window_clear_load_job (EogWindow *window)
1126 {
1127 EogWindowPrivate *priv = window->priv;
1128
1129 if (priv->load_job != NULL) {
1130 if (!priv->load_job->finished)
1131 eog_job_cancel (priv->load_job);
1132
1133 g_signal_handlers_disconnect_by_func (priv->load_job,
1134 eog_job_progress_cb,
1135 window);
1136
1137 g_signal_handlers_disconnect_by_func (priv->load_job,
1138 eog_job_load_cb,
1139 window);
1140
1141 eog_image_cancel_load (EOG_JOB_LOAD (priv->load_job)->image);
1142
1143 g_object_unref (priv->load_job);
1144 priv->load_job = NULL;
1145
1146 /* Hide statusbar */
1147 eog_statusbar_set_progress (EOG_STATUSBAR (priv->statusbar), 0);
1148 }
1149 }
1150
1151 static void
eog_job_progress_cb(EogJobLoad * job,float progress,gpointer user_data)1152 eog_job_progress_cb (EogJobLoad *job, float progress, gpointer user_data)
1153 {
1154 EogWindow *window;
1155
1156 g_return_if_fail (EOG_IS_WINDOW (user_data));
1157
1158 window = EOG_WINDOW (user_data);
1159
1160 eog_statusbar_set_progress (EOG_STATUSBAR (window->priv->statusbar),
1161 progress);
1162 }
1163
1164 static void
eog_job_save_progress_cb(EogJobSave * job,float progress,gpointer user_data)1165 eog_job_save_progress_cb (EogJobSave *job, float progress, gpointer user_data)
1166 {
1167 EogWindowPrivate *priv;
1168 EogWindow *window;
1169
1170 static EogImage *image = NULL;
1171
1172 g_return_if_fail (EOG_IS_WINDOW (user_data));
1173
1174 window = EOG_WINDOW (user_data);
1175 priv = window->priv;
1176
1177 eog_statusbar_set_progress (EOG_STATUSBAR (priv->statusbar),
1178 progress);
1179
1180 if (image != job->current_image) {
1181 gchar *str_image, *status_message;
1182 guint n_images;
1183
1184 image = job->current_image;
1185
1186 n_images = g_list_length (job->images);
1187
1188 str_image = eog_image_get_uri_for_display (image);
1189
1190 /* Translators: This string is displayed in the statusbar
1191 * while saving images. The tokens are from left to right:
1192 * - the original filename
1193 * - the current image's position in the queue
1194 * - the total number of images queued for saving */
1195 status_message = g_strdup_printf (_("Saving image “%s” (%u/%u)"),
1196 str_image,
1197 job->current_position + 1,
1198 n_images);
1199 g_free (str_image);
1200
1201 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar),
1202 priv->image_info_message_cid);
1203
1204 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar),
1205 priv->image_info_message_cid,
1206 status_message);
1207
1208 g_free (status_message);
1209 }
1210
1211 if (progress == 1.0)
1212 image = NULL;
1213 }
1214
1215 static void
eog_window_obtain_desired_size(EogImage * image,gint width,gint height,EogWindow * window)1216 eog_window_obtain_desired_size (EogImage *image,
1217 gint width,
1218 gint height,
1219 EogWindow *window)
1220 {
1221 GdkMonitor *monitor;
1222 GdkRectangle monitor_rect;
1223 GtkAllocation allocation;
1224 gint final_width, final_height;
1225 gint screen_width, screen_height;
1226 gint window_width, window_height;
1227 gint img_width, img_height;
1228 gint view_width, view_height;
1229 gint deco_width, deco_height;
1230
1231 update_action_groups_state (window);
1232
1233 img_width = width;
1234 img_height = height;
1235
1236 if (!gtk_widget_get_realized (window->priv->view)) {
1237 gtk_widget_realize (window->priv->view);
1238 }
1239
1240 eog_debug_message (DEBUG_WINDOW, "Initial Image Size: %d x %d", img_width, img_height);
1241
1242 gtk_widget_get_allocation (window->priv->view, &allocation);
1243 view_width = allocation.width;
1244 view_height = allocation.height;
1245
1246 eog_debug_message (DEBUG_WINDOW, "Initial View Size: %d x %d", view_width, view_height);
1247
1248
1249 if (!gtk_widget_get_realized (GTK_WIDGET (window))) {
1250 gtk_widget_realize (GTK_WIDGET (window));
1251 }
1252
1253 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
1254 window_width = allocation.width;
1255 window_height = allocation.height;
1256
1257 eog_debug_message (DEBUG_WINDOW, "Initial Window Size: %d x %d", window_width, window_height);
1258
1259
1260 monitor = gdk_display_get_monitor_at_window (
1261 gtk_widget_get_display (GTK_WIDGET (window)),
1262 gtk_widget_get_window (GTK_WIDGET (window)));
1263
1264 gdk_monitor_get_geometry (monitor, &monitor_rect);
1265
1266 screen_width = monitor_rect.width;
1267 screen_height = monitor_rect.height;
1268
1269 eog_debug_message (DEBUG_WINDOW, "Screen Size: %d x %d", screen_width, screen_height);
1270
1271 deco_width = window_width - view_width;
1272 deco_height = window_height - view_height;
1273
1274 eog_debug_message (DEBUG_WINDOW, "Decoration Size: %d x %d", deco_width, deco_height);
1275
1276 if (img_width > 0 && img_height > 0) {
1277 if ((img_width + deco_width > screen_width) ||
1278 (img_height + deco_height > screen_height))
1279 {
1280 double width_factor, height_factor, factor;
1281
1282 width_factor = (screen_width * 0.85 - deco_width) / (double) img_width;
1283 height_factor = (screen_height * 0.85 - deco_height) / (double) img_height;
1284 factor = MIN (width_factor, height_factor);
1285
1286 eog_debug_message (DEBUG_WINDOW, "Scaling Factor: %.2lf", factor);
1287
1288
1289 img_width = img_width * factor;
1290 img_height = img_height * factor;
1291 }
1292 }
1293
1294 final_width = MAX (EOG_WINDOW_MIN_WIDTH, img_width + deco_width);
1295 final_height = MAX (EOG_WINDOW_MIN_HEIGHT, img_height + deco_height);
1296
1297 eog_debug_message (DEBUG_WINDOW, "Setting window size: %d x %d", final_width, final_height);
1298
1299 gtk_window_set_default_size (GTK_WINDOW (window), final_width, final_height);
1300
1301 g_signal_emit (window, signals[SIGNAL_PREPARED], 0);
1302 }
1303
1304 static void
eog_window_error_message_area_response(GtkInfoBar * message_area,gint response_id,EogWindow * window)1305 eog_window_error_message_area_response (GtkInfoBar *message_area,
1306 gint response_id,
1307 EogWindow *window)
1308 {
1309 GAction *action_save_as;
1310
1311 g_return_if_fail (GTK_IS_INFO_BAR (message_area));
1312 g_return_if_fail (EOG_IS_WINDOW (window));
1313
1314 /* remove message area */
1315 eog_window_set_message_area (window, NULL);
1316
1317 /* evaluate message area response */
1318 switch (response_id) {
1319 case EOG_ERROR_MESSAGE_AREA_RESPONSE_NONE:
1320 case EOG_ERROR_MESSAGE_AREA_RESPONSE_CANCEL:
1321 case GTK_RESPONSE_CLOSE:
1322 /* nothing to do in this case */
1323 break;
1324 case EOG_ERROR_MESSAGE_AREA_RESPONSE_RELOAD:
1325 /* TODO: trigger loading for current image again */
1326 break;
1327 case EOG_ERROR_MESSAGE_AREA_RESPONSE_SAVEAS:
1328 /* trigger save as command for current image */
1329 action_save_as =
1330 g_action_map_lookup_action (G_ACTION_MAP (window),
1331 "save-as");
1332 eog_window_action_save_as (G_SIMPLE_ACTION (action_save_as), NULL, window);
1333 break;
1334 case EOG_ERROR_MESSAGE_AREA_RESPONSE_OPEN_WITH_EVINCE:
1335 {
1336 GDesktopAppInfo *app_info;
1337 GFile *img_file;
1338 GList *img_files = NULL;
1339
1340 app_info = g_desktop_app_info_new ("evince.desktop");
1341
1342 if (app_info) {
1343 img_file = eog_image_get_file (window->priv->image);
1344 if (img_file) {
1345 img_files = g_list_append (img_files, img_file);
1346 }
1347 _eog_window_launch_appinfo_with_files (window,
1348 G_APP_INFO (app_info),
1349 img_files);
1350 g_list_free_full (img_files, g_object_unref);
1351 }
1352 }
1353 break;
1354 }
1355 }
1356
1357 static void
eog_job_load_cb(EogJobLoad * job,gpointer data)1358 eog_job_load_cb (EogJobLoad *job, gpointer data)
1359 {
1360 EogWindow *window;
1361 EogWindowPrivate *priv;
1362 GAction *action_undo, *action_save;
1363
1364 g_return_if_fail (EOG_IS_WINDOW (data));
1365
1366 eog_debug (DEBUG_WINDOW);
1367
1368 window = EOG_WINDOW (data);
1369 priv = window->priv;
1370
1371 eog_statusbar_set_progress (EOG_STATUSBAR (priv->statusbar), 0.0);
1372
1373 gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar),
1374 priv->image_info_message_cid);
1375
1376 if (priv->image != NULL) {
1377 g_signal_handlers_disconnect_by_func (priv->image,
1378 image_thumb_changed_cb,
1379 window);
1380 g_signal_handlers_disconnect_by_func (priv->image,
1381 image_file_changed_cb,
1382 window);
1383
1384 g_object_unref (priv->image);
1385 }
1386
1387 priv->image = g_object_ref (job->image);
1388
1389 if (EOG_JOB (job)->error == NULL) {
1390 #ifdef HAVE_LCMS
1391 eog_image_apply_display_profile (job->image,
1392 priv->display_profile);
1393 #endif
1394
1395 _eog_window_enable_image_actions (window, TRUE);
1396
1397 /* Make sure the window is really realized
1398 * before displaying the image. The ScrollView needs that. */
1399 if (!gtk_widget_get_realized (GTK_WIDGET (window))) {
1400 gint width = -1, height = -1;
1401
1402 eog_image_get_size (job->image, &width, &height);
1403 eog_window_obtain_desired_size (job->image, width,
1404 height, window);
1405
1406 }
1407
1408 eog_window_display_image (window, job->image);
1409 } else {
1410 GtkWidget *message_area;
1411
1412 message_area = eog_image_load_error_message_area_new (
1413 eog_image_get_caption (job->image),
1414 EOG_JOB (job)->error);
1415
1416 g_signal_connect (message_area,
1417 "response",
1418 G_CALLBACK (eog_window_error_message_area_response),
1419 window);
1420
1421 gtk_window_set_icon (GTK_WINDOW (window), NULL);
1422 gtk_window_set_title (GTK_WINDOW (window),
1423 eog_image_get_caption (job->image));
1424
1425 eog_window_set_message_area (window, message_area);
1426
1427 gtk_info_bar_set_default_response (GTK_INFO_BAR (message_area),
1428 GTK_RESPONSE_CANCEL);
1429
1430 gtk_widget_show (message_area);
1431
1432 update_status_bar (window);
1433
1434 eog_scroll_view_set_image (EOG_SCROLL_VIEW (priv->view), NULL);
1435
1436 if (window->priv->status == EOG_WINDOW_STATUS_INIT) {
1437 update_action_groups_state (window);
1438
1439 g_signal_emit (window, signals[SIGNAL_PREPARED], 0);
1440 }
1441
1442 _eog_window_enable_image_actions (window, FALSE);
1443 }
1444
1445 eog_window_clear_load_job (window);
1446
1447 if (window->priv->status == EOG_WINDOW_STATUS_INIT) {
1448 window->priv->status = EOG_WINDOW_STATUS_NORMAL;
1449
1450 g_signal_handlers_disconnect_by_func
1451 (job->image,
1452 G_CALLBACK (eog_window_obtain_desired_size),
1453 window);
1454 }
1455
1456 action_save = g_action_map_lookup_action (G_ACTION_MAP (window),
1457 "save");
1458 action_undo = g_action_map_lookup_action (G_ACTION_MAP (window),
1459 "undo");
1460
1461 /* Set Save and Undo sensitive according to image state.
1462 * Respect lockdown in case of Save.*/
1463 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_save),
1464 (!priv->save_disabled && eog_image_is_modified (job->image)));
1465 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_undo),
1466 eog_image_is_modified (job->image));
1467
1468 g_object_unref (job->image);
1469 }
1470
1471 static void
eog_window_clear_transform_job(EogWindow * window)1472 eog_window_clear_transform_job (EogWindow *window)
1473 {
1474 EogWindowPrivate *priv = window->priv;
1475
1476 if (priv->transform_job != NULL) {
1477 if (!priv->transform_job->finished)
1478 eog_job_cancel (priv->transform_job);
1479
1480 g_signal_handlers_disconnect_by_func (priv->transform_job,
1481 eog_job_transform_cb,
1482 window);
1483 g_object_unref (priv->transform_job);
1484 priv->transform_job = NULL;
1485 }
1486 }
1487
1488 static void
eog_job_transform_cb(EogJobTransform * job,gpointer data)1489 eog_job_transform_cb (EogJobTransform *job, gpointer data)
1490 {
1491 EogWindow *window;
1492 GAction *action_undo, *action_save;
1493 EogImage *image;
1494
1495 g_return_if_fail (EOG_IS_WINDOW (data));
1496
1497 window = EOG_WINDOW (data);
1498
1499 eog_window_clear_transform_job (window);
1500
1501 action_undo =
1502 g_action_map_lookup_action (G_ACTION_MAP (window),
1503 "undo");
1504 action_save =
1505 g_action_map_lookup_action (G_ACTION_MAP (window),
1506 "save");
1507
1508 image = eog_window_get_image (window);
1509
1510 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_undo),
1511 eog_image_is_modified (image));
1512
1513 if (!window->priv->save_disabled)
1514 {
1515 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_save),
1516 eog_image_is_modified (image));
1517 }
1518 }
1519
1520 static void
apply_transformation(EogWindow * window,EogTransform * trans)1521 apply_transformation (EogWindow *window, EogTransform *trans)
1522 {
1523 EogWindowPrivate *priv;
1524 GList *images;
1525
1526 g_return_if_fail (EOG_IS_WINDOW (window));
1527
1528 priv = window->priv;
1529
1530 images = eog_thumb_view_get_selected_images (EOG_THUMB_VIEW (priv->thumbview));
1531
1532 eog_window_clear_transform_job (window);
1533
1534 priv->transform_job = eog_job_transform_new (images, trans);
1535
1536 g_signal_connect (priv->transform_job,
1537 "finished",
1538 G_CALLBACK (eog_job_transform_cb),
1539 window);
1540
1541 g_signal_connect (priv->transform_job,
1542 "progress",
1543 G_CALLBACK (eog_job_progress_cb),
1544 window);
1545
1546 eog_job_scheduler_add_job (priv->transform_job);
1547 }
1548
1549 static void
handle_image_selection_changed_cb(EogThumbView * thumbview,EogWindow * window)1550 handle_image_selection_changed_cb (EogThumbView *thumbview, EogWindow *window)
1551 {
1552 EogWindowPrivate *priv;
1553 EogImage *image;
1554 gchar *status_message;
1555 gchar *str_image;
1556
1557 priv = window->priv;
1558
1559 if (eog_list_store_length (EOG_LIST_STORE (priv->store)) == 0) {
1560 gtk_window_set_title (GTK_WINDOW (window),
1561 g_get_application_name());
1562 gtk_statusbar_remove_all (GTK_STATUSBAR (priv->statusbar),
1563 priv->image_info_message_cid);
1564 eog_scroll_view_set_image (EOG_SCROLL_VIEW (priv->view),
1565 NULL);
1566 }
1567 if (eog_thumb_view_get_n_selected (EOG_THUMB_VIEW (priv->thumbview)) == 0)
1568 return;
1569
1570 update_selection_ui_visibility (window);
1571
1572 image = eog_thumb_view_get_first_selected_image (EOG_THUMB_VIEW (priv->thumbview));
1573
1574 g_assert (EOG_IS_IMAGE (image));
1575
1576 eog_window_clear_load_job (window);
1577
1578 eog_window_set_message_area (window, NULL);
1579
1580 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar),
1581 priv->image_info_message_cid);
1582
1583 if (image == priv->image) {
1584 update_status_bar (window);
1585 return;
1586 }
1587
1588 if (eog_image_has_data (image, EOG_IMAGE_DATA_IMAGE)) {
1589 if (priv->image != NULL)
1590 g_object_unref (priv->image);
1591 priv->image = image;
1592 eog_window_display_image (window, image);
1593 return;
1594 }
1595
1596 if (priv->status == EOG_WINDOW_STATUS_INIT) {
1597 g_signal_connect (image,
1598 "size-prepared",
1599 G_CALLBACK (eog_window_obtain_desired_size),
1600 window);
1601 }
1602
1603 priv->load_job = eog_job_load_new (image, EOG_IMAGE_DATA_ALL);
1604
1605 g_signal_connect (priv->load_job,
1606 "finished",
1607 G_CALLBACK (eog_job_load_cb),
1608 window);
1609
1610 g_signal_connect (priv->load_job,
1611 "progress",
1612 G_CALLBACK (eog_job_progress_cb),
1613 window);
1614
1615 eog_job_scheduler_add_job (priv->load_job);
1616
1617 str_image = eog_image_get_uri_for_display (image);
1618
1619 status_message = g_strdup_printf (_("Opening image “%s”"),
1620 str_image);
1621
1622 g_free (str_image);
1623
1624 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar),
1625 priv->image_info_message_cid, status_message);
1626
1627 g_free (status_message);
1628 }
1629
1630 static void
view_zoom_changed_cb(GtkWidget * widget,double zoom,gpointer user_data)1631 view_zoom_changed_cb (GtkWidget *widget, double zoom, gpointer user_data)
1632 {
1633 EogWindow *window;
1634 GAction *action_zoom_in;
1635 GAction *action_zoom_out;
1636
1637 g_return_if_fail (EOG_IS_WINDOW (user_data));
1638
1639 window = EOG_WINDOW (user_data);
1640
1641 update_status_bar (window);
1642
1643 action_zoom_in =
1644 g_action_map_lookup_action (G_ACTION_MAP (window),
1645 "zoom-in");
1646
1647 action_zoom_out =
1648 g_action_map_lookup_action (G_ACTION_MAP (window),
1649 "zoom-out");
1650
1651 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_zoom_in),
1652 !eog_scroll_view_get_zoom_is_max (EOG_SCROLL_VIEW (window->priv->view)));
1653 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_zoom_out),
1654 !eog_scroll_view_get_zoom_is_min (EOG_SCROLL_VIEW (window->priv->view)));
1655 }
1656
1657 static void
file_open_dialog_response_cb(GtkWidget * chooser,gint response_id,EogWindow * ev_window)1658 file_open_dialog_response_cb (GtkWidget *chooser,
1659 gint response_id,
1660 EogWindow *ev_window)
1661 {
1662 if (response_id == GTK_RESPONSE_OK) {
1663 GSList *uris;
1664
1665 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (chooser));
1666
1667 eog_application_open_uri_list (EOG_APP,
1668 uris,
1669 GDK_CURRENT_TIME,
1670 0,
1671 NULL);
1672
1673 g_slist_foreach (uris, (GFunc) g_free, NULL);
1674 g_slist_free (uris);
1675 }
1676
1677 gtk_widget_destroy (chooser);
1678 }
1679
1680 static void
eog_window_update_fullscreen_action(EogWindow * window)1681 eog_window_update_fullscreen_action (EogWindow *window)
1682 {
1683 GAction *action;
1684
1685 action = g_action_map_lookup_action (G_ACTION_MAP (window),
1686 "view-fullscreen");
1687
1688 g_simple_action_set_state (G_SIMPLE_ACTION (action),
1689 g_variant_new_boolean (window->priv->mode == EOG_WINDOW_MODE_FULLSCREEN));
1690 }
1691
1692 static void
eog_window_update_slideshow_action(EogWindow * window)1693 eog_window_update_slideshow_action (EogWindow *window)
1694 {
1695 GAction *action;
1696
1697 action = g_action_map_lookup_action (G_ACTION_MAP (window),
1698 "view-slideshow");
1699
1700 g_simple_action_set_state (G_SIMPLE_ACTION (action),
1701 g_variant_new_boolean (window->priv->mode == EOG_WINDOW_MODE_SLIDESHOW));
1702 }
1703
1704 static void
eog_window_update_pause_slideshow_action(EogWindow * window)1705 eog_window_update_pause_slideshow_action (EogWindow *window)
1706 {
1707 GAction *action;
1708
1709 action = g_action_map_lookup_action (G_ACTION_MAP (window),
1710 "pause-slideshow");
1711
1712 g_simple_action_set_state (G_SIMPLE_ACTION (action),
1713 g_variant_new_boolean (window->priv->mode != EOG_WINDOW_MODE_SLIDESHOW));
1714 }
1715
1716 static gboolean
fullscreen_timeout_cb(gpointer data)1717 fullscreen_timeout_cb (gpointer data)
1718 {
1719 EogWindow *window = EOG_WINDOW (data);
1720
1721 eog_debug (DEBUG_WINDOW);
1722
1723 gtk_revealer_set_reveal_child (
1724 GTK_REVEALER (window->priv->fullscreen_popup), FALSE);
1725 eog_scroll_view_hide_cursor (EOG_SCROLL_VIEW (window->priv->view));
1726
1727 fullscreen_clear_timeout (window);
1728
1729 return FALSE;
1730 }
1731
1732 static gboolean
slideshow_is_loop_end(EogWindow * window)1733 slideshow_is_loop_end (EogWindow *window)
1734 {
1735 EogWindowPrivate *priv = window->priv;
1736 EogImage *image = NULL;
1737 gint pos;
1738
1739 image = eog_thumb_view_get_first_selected_image (EOG_THUMB_VIEW (priv->thumbview));
1740
1741 pos = eog_list_store_get_pos_by_image (priv->store, image);
1742
1743 return (pos == (eog_list_store_length (priv->store) - 1));
1744 }
1745
1746 static gboolean
slideshow_switch_cb(gpointer data)1747 slideshow_switch_cb (gpointer data)
1748 {
1749 EogWindow *window = EOG_WINDOW (data);
1750 EogWindowPrivate *priv = window->priv;
1751
1752 eog_debug (DEBUG_WINDOW);
1753
1754 if (!priv->slideshow_loop && slideshow_is_loop_end (window)) {
1755 eog_window_stop_fullscreen (window, TRUE);
1756 return G_SOURCE_REMOVE;
1757 }
1758
1759 eog_thumb_view_select_single (EOG_THUMB_VIEW (priv->thumbview),
1760 EOG_THUMB_VIEW_SELECT_RIGHT);
1761
1762 return G_SOURCE_REMOVE;
1763 }
1764
1765 static void
fullscreen_clear_timeout(EogWindow * window)1766 fullscreen_clear_timeout (EogWindow *window)
1767 {
1768 eog_debug (DEBUG_WINDOW);
1769
1770 if (window->priv->fullscreen_timeout_source != NULL) {
1771 g_source_destroy (window->priv->fullscreen_timeout_source);
1772 g_source_unref (window->priv->fullscreen_timeout_source);
1773 }
1774
1775 window->priv->fullscreen_timeout_source = NULL;
1776 }
1777
1778 static void
fullscreen_set_timeout(EogWindow * window)1779 fullscreen_set_timeout (EogWindow *window)
1780 {
1781 GSource *source;
1782
1783 eog_debug (DEBUG_WINDOW);
1784
1785 fullscreen_clear_timeout (window);
1786
1787 source = g_timeout_source_new (EOG_WINDOW_FULLSCREEN_TIMEOUT);
1788 g_source_set_callback (source, fullscreen_timeout_cb, window, NULL);
1789
1790 g_source_attach (source, NULL);
1791
1792 window->priv->fullscreen_timeout_source = source;
1793
1794 eog_scroll_view_show_cursor (EOG_SCROLL_VIEW (window->priv->view));
1795 }
1796
1797 static void
slideshow_clear_timeout(EogWindow * window)1798 slideshow_clear_timeout (EogWindow *window)
1799 {
1800 eog_debug (DEBUG_WINDOW);
1801
1802 if (window->priv->slideshow_switch_source != NULL) {
1803 g_source_destroy (window->priv->slideshow_switch_source);
1804 g_source_unref (window->priv->slideshow_switch_source);
1805 }
1806
1807 window->priv->slideshow_switch_source = NULL;
1808 }
1809
1810 static void
slideshow_set_timeout(EogWindow * window)1811 slideshow_set_timeout (EogWindow *window)
1812 {
1813 GSource *source;
1814
1815 eog_debug (DEBUG_WINDOW);
1816
1817 slideshow_clear_timeout (window);
1818
1819 if (window->priv->mode != EOG_WINDOW_MODE_SLIDESHOW)
1820 return;
1821
1822 if (window->priv->slideshow_switch_timeout <= 0)
1823 return;
1824
1825 source = g_timeout_source_new (window->priv->slideshow_switch_timeout * 1000);
1826 g_source_set_callback (source, slideshow_switch_cb, window, NULL);
1827
1828 g_source_attach (source, NULL);
1829
1830 window->priv->slideshow_switch_source = source;
1831 }
1832
1833 static void
show_fullscreen_popup(EogWindow * window)1834 show_fullscreen_popup (EogWindow *window)
1835 {
1836 eog_debug (DEBUG_WINDOW);
1837
1838 if (!gtk_widget_get_visible (window->priv->fullscreen_popup)) {
1839 gtk_widget_show_all (GTK_WIDGET (window->priv->fullscreen_popup));
1840 }
1841
1842 gtk_revealer_set_reveal_child (
1843 GTK_REVEALER (window->priv->fullscreen_popup), TRUE);
1844
1845 fullscreen_set_timeout (window);
1846 }
1847
1848 static gboolean
fullscreen_motion_notify_cb(GtkWidget * widget,GdkEventMotion * event,gpointer user_data)1849 fullscreen_motion_notify_cb (GtkWidget *widget,
1850 GdkEventMotion *event,
1851 gpointer user_data)
1852 {
1853 EogWindow *window = EOG_WINDOW (user_data);
1854
1855 eog_debug (DEBUG_WINDOW);
1856
1857 if (event->y < EOG_WINDOW_FULLSCREEN_POPUP_THRESHOLD) {
1858 show_fullscreen_popup (window);
1859 } else {
1860 fullscreen_set_timeout (window);
1861 }
1862
1863 return FALSE;
1864 }
1865
1866 static gboolean
fullscreen_leave_notify_cb(GtkWidget * widget,GdkEventCrossing * event,gpointer user_data)1867 fullscreen_leave_notify_cb (GtkWidget *widget,
1868 GdkEventCrossing *event,
1869 gpointer user_data)
1870 {
1871 EogWindow *window = EOG_WINDOW (user_data);
1872
1873 eog_debug (DEBUG_WINDOW);
1874
1875 fullscreen_clear_timeout (window);
1876
1877 return FALSE;
1878 }
1879
1880 static void
exit_fullscreen_button_clicked_cb(GtkWidget * button,EogWindow * window)1881 exit_fullscreen_button_clicked_cb (GtkWidget *button, EogWindow *window)
1882 {
1883 GAction *action;
1884
1885 eog_debug (DEBUG_WINDOW);
1886
1887 if (window->priv->mode == EOG_WINDOW_MODE_SLIDESHOW) {
1888 action = g_action_map_lookup_action (G_ACTION_MAP (window),
1889 "view-slideshow");
1890 } else {
1891 action = g_action_map_lookup_action (G_ACTION_MAP (window),
1892 "view-fullscreen");
1893 }
1894 g_return_if_fail (action != NULL);
1895
1896 g_action_change_state (action, g_variant_new_boolean (FALSE));
1897 }
1898
1899 static GtkWidget *
eog_window_create_fullscreen_popup(EogWindow * window)1900 eog_window_create_fullscreen_popup (EogWindow *window)
1901 {
1902 GtkWidget *revealer;
1903 GtkWidget *hbox;
1904 GtkWidget *button;
1905 GtkWidget *toolbar;
1906 GtkBuilder *builder;
1907
1908 eog_debug (DEBUG_WINDOW);
1909
1910 revealer = gtk_revealer_new();
1911 gtk_widget_add_events (revealer, GDK_ENTER_NOTIFY_MASK);
1912
1913 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1914 gtk_widget_set_valign (revealer, GTK_ALIGN_START);
1915 gtk_widget_set_halign (revealer, GTK_ALIGN_FILL);
1916 gtk_container_add (GTK_CONTAINER (revealer), hbox);
1917
1918 builder = gtk_builder_new_from_resource ("/org/gnome/eog/ui/fullscreen-toolbar.ui");
1919 toolbar = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_toolbar"));
1920 g_assert (GTK_IS_TOOLBAR (toolbar));
1921
1922 gtk_box_pack_start (GTK_BOX (hbox), toolbar, TRUE, TRUE, 0);
1923
1924 button = GTK_WIDGET (gtk_builder_get_object (builder, "exit_fullscreen_button"));
1925 g_signal_connect (button, "clicked",
1926 G_CALLBACK (exit_fullscreen_button_clicked_cb),
1927 window);
1928
1929 /* Disable timer when the pointer enters the toolbar window. */
1930 g_signal_connect (revealer,
1931 "enter-notify-event",
1932 G_CALLBACK (fullscreen_leave_notify_cb),
1933 window);
1934
1935 g_object_unref (builder);
1936 return revealer;
1937 }
1938
1939 static void
update_ui_visibility(EogWindow * window)1940 update_ui_visibility (EogWindow *window)
1941 {
1942 EogWindowPrivate *priv;
1943
1944 GAction *action;
1945
1946 gboolean fullscreen_mode, visible;
1947
1948 g_return_if_fail (EOG_IS_WINDOW (window));
1949
1950 eog_debug (DEBUG_WINDOW);
1951
1952 priv = window->priv;
1953
1954 fullscreen_mode = priv->mode == EOG_WINDOW_MODE_FULLSCREEN ||
1955 priv->mode == EOG_WINDOW_MODE_SLIDESHOW;
1956
1957 visible = g_settings_get_boolean (priv->ui_settings,
1958 EOG_CONF_UI_STATUSBAR);
1959 visible = visible && !fullscreen_mode;
1960 action = g_action_map_lookup_action (G_ACTION_MAP (window), "view-statusbar");
1961 g_assert (action != NULL);
1962 g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (visible));
1963 gtk_widget_set_visible (priv->statusbar, visible);
1964
1965 if (priv->status != EOG_WINDOW_STATUS_INIT) {
1966 visible = g_settings_get_boolean (priv->ui_settings,
1967 EOG_CONF_UI_IMAGE_GALLERY);
1968 visible &= gtk_widget_get_visible (priv->nav);
1969 visible &= (priv->mode != EOG_WINDOW_MODE_SLIDESHOW);
1970 action = g_action_map_lookup_action (G_ACTION_MAP (window), "view-gallery");
1971 g_assert (action != NULL);
1972 g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (visible));
1973 gtk_widget_set_visible (priv->nav, visible);
1974 }
1975
1976 visible = g_settings_get_boolean (priv->ui_settings,
1977 EOG_CONF_UI_SIDEBAR);
1978 visible = visible && !fullscreen_mode;
1979 action = g_action_map_lookup_action (G_ACTION_MAP (window), "view-sidebar");
1980 g_assert (action != NULL);
1981 g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (visible));
1982 gtk_widget_set_visible (priv->sidebar, visible);
1983
1984 if (priv->fullscreen_popup != NULL) {
1985 gtk_widget_hide (priv->fullscreen_popup);
1986 }
1987 }
1988
1989 static void
eog_window_inhibit_screensaver(EogWindow * window)1990 eog_window_inhibit_screensaver (EogWindow *window)
1991 {
1992 EogWindowPrivate *priv = window->priv;
1993
1994 /* Already inhibited */
1995 if (G_UNLIKELY (priv->fullscreen_idle_inhibit_cookie != 0))
1996 return;
1997
1998 eog_debug (DEBUG_WINDOW);
1999
2000 window->priv->fullscreen_idle_inhibit_cookie =
2001 gtk_application_inhibit (GTK_APPLICATION (EOG_APP),
2002 GTK_WINDOW (window),
2003 GTK_APPLICATION_INHIBIT_IDLE,
2004 /* L10N: This the reason why the screensaver is inhibited. */
2005 _("Viewing a slideshow"));
2006 }
2007
2008 static void
eog_window_uninhibit_screensaver(EogWindow * window)2009 eog_window_uninhibit_screensaver (EogWindow *window)
2010 {
2011 EogWindowPrivate *priv = window->priv;
2012
2013 if (G_UNLIKELY (priv->fullscreen_idle_inhibit_cookie == 0))
2014 return;
2015
2016 eog_debug (DEBUG_WINDOW);
2017
2018 gtk_application_uninhibit (GTK_APPLICATION (EOG_APP),
2019 priv->fullscreen_idle_inhibit_cookie);
2020 priv->fullscreen_idle_inhibit_cookie = 0;
2021 }
2022
2023 static void
eog_window_run_fullscreen(EogWindow * window,gboolean slideshow)2024 eog_window_run_fullscreen (EogWindow *window, gboolean slideshow)
2025 {
2026 static const GdkRGBA black = { 0., 0., 0., 1.};
2027 EogWindowPrivate *priv;
2028 gboolean upscale;
2029
2030 eog_debug (DEBUG_WINDOW);
2031
2032 priv = window->priv;
2033
2034 if (slideshow) {
2035 priv->mode = EOG_WINDOW_MODE_SLIDESHOW;
2036 } else {
2037 /* Stop the timer if we come from slideshowing */
2038 if (priv->mode == EOG_WINDOW_MODE_SLIDESHOW) {
2039 slideshow_clear_timeout (window);
2040 eog_window_uninhibit_screensaver (window);
2041 }
2042
2043 priv->mode = EOG_WINDOW_MODE_FULLSCREEN;
2044 }
2045
2046 if (window->priv->fullscreen_popup == NULL)
2047 {
2048 priv->fullscreen_popup
2049 = eog_window_create_fullscreen_popup (window);
2050 gtk_overlay_add_overlay (GTK_OVERLAY(priv->overlay),
2051 priv->fullscreen_popup);
2052 }
2053
2054 update_ui_visibility (window);
2055
2056 g_signal_connect (priv->view,
2057 "motion-notify-event",
2058 G_CALLBACK (fullscreen_motion_notify_cb),
2059 window);
2060
2061 g_signal_connect (priv->view,
2062 "leave-notify-event",
2063 G_CALLBACK (fullscreen_leave_notify_cb),
2064 window);
2065
2066 g_signal_connect (priv->thumbview,
2067 "motion-notify-event",
2068 G_CALLBACK (fullscreen_motion_notify_cb),
2069 window);
2070
2071 g_signal_connect (priv->thumbview,
2072 "leave-notify-event",
2073 G_CALLBACK (fullscreen_leave_notify_cb),
2074 window);
2075
2076 fullscreen_set_timeout (window);
2077
2078 if (slideshow) {
2079 priv->slideshow_loop =
2080 g_settings_get_boolean (priv->fullscreen_settings,
2081 EOG_CONF_FULLSCREEN_LOOP);
2082
2083 priv->slideshow_switch_timeout =
2084 g_settings_get_int (priv->fullscreen_settings,
2085 EOG_CONF_FULLSCREEN_SECONDS);
2086
2087 slideshow_set_timeout (window);
2088 }
2089
2090 upscale = g_settings_get_boolean (priv->fullscreen_settings,
2091 EOG_CONF_FULLSCREEN_UPSCALE);
2092
2093 eog_scroll_view_set_zoom_upscale (EOG_SCROLL_VIEW (priv->view),
2094 upscale);
2095
2096 gtk_widget_grab_focus (priv->view);
2097
2098 eog_scroll_view_override_bg_color (EOG_SCROLL_VIEW (window->priv->view),
2099 &black);
2100
2101 gtk_window_fullscreen (GTK_WINDOW (window));
2102
2103 if (slideshow)
2104 eog_window_inhibit_screensaver (window);
2105
2106 /* Update both actions as we could've already been in one those modes */
2107 eog_window_update_slideshow_action (window);
2108 eog_window_update_fullscreen_action (window);
2109 eog_window_update_pause_slideshow_action (window);
2110 }
2111
2112 static void
eog_window_stop_fullscreen(EogWindow * window,gboolean slideshow)2113 eog_window_stop_fullscreen (EogWindow *window, gboolean slideshow)
2114 {
2115 EogWindowPrivate *priv;
2116
2117 eog_debug (DEBUG_WINDOW);
2118
2119 priv = window->priv;
2120
2121 if (priv->mode != EOG_WINDOW_MODE_SLIDESHOW &&
2122 priv->mode != EOG_WINDOW_MODE_FULLSCREEN) return;
2123
2124 priv->mode = EOG_WINDOW_MODE_NORMAL;
2125
2126 fullscreen_clear_timeout (window);
2127 gtk_revealer_set_reveal_child (GTK_REVEALER(window->priv->fullscreen_popup), FALSE);
2128
2129 if (slideshow) {
2130 slideshow_clear_timeout (window);
2131 }
2132
2133 g_signal_handlers_disconnect_by_func (priv->view,
2134 (gpointer) fullscreen_motion_notify_cb,
2135 window);
2136
2137 g_signal_handlers_disconnect_by_func (priv->view,
2138 (gpointer) fullscreen_leave_notify_cb,
2139 window);
2140
2141 g_signal_handlers_disconnect_by_func (priv->thumbview,
2142 (gpointer) fullscreen_motion_notify_cb,
2143 window);
2144
2145 g_signal_handlers_disconnect_by_func (priv->thumbview,
2146 (gpointer) fullscreen_leave_notify_cb,
2147 window);
2148
2149 update_ui_visibility (window);
2150
2151 eog_scroll_view_set_zoom_upscale (EOG_SCROLL_VIEW (priv->view), FALSE);
2152
2153 eog_scroll_view_override_bg_color (EOG_SCROLL_VIEW (window->priv->view),
2154 NULL);
2155 gtk_window_unfullscreen (GTK_WINDOW (window));
2156
2157 if (slideshow) {
2158 eog_window_update_slideshow_action (window);
2159 eog_window_uninhibit_screensaver (window);
2160 } else {
2161 eog_window_update_fullscreen_action (window);
2162 }
2163
2164 eog_scroll_view_show_cursor (EOG_SCROLL_VIEW (priv->view));
2165 }
2166
2167 static void
set_basename_for_print_settings(GtkPrintSettings * print_settings,EogWindow * window)2168 set_basename_for_print_settings (GtkPrintSettings *print_settings, EogWindow *window)
2169 {
2170 const char *basename = NULL;
2171
2172 if(G_LIKELY (window->priv->image != NULL))
2173 basename = eog_image_get_caption (window->priv->image);
2174
2175 if (G_LIKELY(basename))
2176 gtk_print_settings_set (print_settings,
2177 GTK_PRINT_SETTINGS_OUTPUT_BASENAME,
2178 basename);
2179 }
2180
2181 static void
eog_window_print(EogWindow * window)2182 eog_window_print (EogWindow *window)
2183 {
2184 GtkWidget *dialog;
2185 GError *error = NULL;
2186 GtkPrintOperation *print;
2187 GtkPrintOperationResult res;
2188 GtkPageSetup *page_setup;
2189 GtkPrintSettings *print_settings;
2190 gboolean page_setup_disabled = FALSE;
2191
2192 eog_debug (DEBUG_PRINTING);
2193
2194 print_settings = eog_print_get_print_settings ();
2195 set_basename_for_print_settings (print_settings, window);
2196
2197 /* Make sure the window stays valid while printing */
2198 g_object_ref (window);
2199
2200 if (window->priv->page_setup != NULL)
2201 page_setup = g_object_ref (window->priv->page_setup);
2202 else
2203 page_setup = NULL;
2204
2205 print = eog_print_operation_new (window->priv->image,
2206 print_settings,
2207 page_setup);
2208
2209
2210 // Disable page setup options if they are locked down
2211 page_setup_disabled = g_settings_get_boolean (window->priv->lockdown_settings,
2212 EOG_CONF_DESKTOP_CAN_SETUP_PAGE);
2213 if (page_setup_disabled)
2214 gtk_print_operation_set_embed_page_setup (print, FALSE);
2215
2216
2217 res = gtk_print_operation_run (print,
2218 GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
2219 GTK_WINDOW (window), &error);
2220
2221 if (res == GTK_PRINT_OPERATION_RESULT_ERROR) {
2222 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
2223 GTK_DIALOG_DESTROY_WITH_PARENT,
2224 GTK_MESSAGE_ERROR,
2225 GTK_BUTTONS_CLOSE,
2226 _("Error printing file:\n%s"),
2227 error->message);
2228 g_signal_connect (dialog, "response",
2229 G_CALLBACK (gtk_widget_destroy), NULL);
2230 gtk_widget_show (dialog);
2231 g_error_free (error);
2232 } else if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
2233 GtkPageSetup *new_page_setup;
2234 eog_print_set_print_settings (gtk_print_operation_get_print_settings (print));
2235 new_page_setup = gtk_print_operation_get_default_page_setup (print);
2236 if (window->priv->page_setup != NULL)
2237 g_object_unref (window->priv->page_setup);
2238 window->priv->page_setup = g_object_ref (new_page_setup);
2239 }
2240
2241 if (page_setup != NULL)
2242 g_object_unref (page_setup);
2243 g_object_unref (print_settings);
2244 g_object_unref (window);
2245 }
2246
2247 static void
eog_window_action_file_open(GSimpleAction * action,GVariant * parameter,gpointer user_data)2248 eog_window_action_file_open (GSimpleAction *action,
2249 GVariant *parameter,
2250 gpointer user_data)
2251 {
2252 EogWindow *window;
2253 EogWindowPrivate *priv;
2254 EogImage *current;
2255 GtkWidget *dlg;
2256
2257 g_return_if_fail (EOG_IS_WINDOW (user_data));
2258
2259 window = EOG_WINDOW (user_data);
2260
2261 priv = window->priv;
2262
2263 dlg = eog_file_chooser_new (GTK_FILE_CHOOSER_ACTION_OPEN);
2264 gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (window));
2265
2266 current = eog_thumb_view_get_first_selected_image (EOG_THUMB_VIEW (priv->thumbview));
2267
2268 if (current != NULL) {
2269 gchar *dir_uri, *file_uri;
2270
2271 file_uri = eog_image_get_uri_for_display (current);
2272 dir_uri = g_path_get_dirname (file_uri);
2273
2274 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg),
2275 dir_uri);
2276 g_free (file_uri);
2277 g_free (dir_uri);
2278 g_object_unref (current);
2279 } else {
2280 /* If desired by the user,
2281 fallback to the XDG_PICTURES_DIR (if available) */
2282 const gchar *pics_dir;
2283 gboolean use_fallback;
2284
2285 use_fallback = g_settings_get_boolean (priv->ui_settings,
2286 EOG_CONF_UI_FILECHOOSER_XDG_FALLBACK);
2287 pics_dir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
2288 if (use_fallback && pics_dir) {
2289 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dlg),
2290 pics_dir);
2291 }
2292 }
2293
2294 g_signal_connect (dlg, "response",
2295 G_CALLBACK (file_open_dialog_response_cb),
2296 window);
2297
2298 gtk_widget_show_all (dlg);
2299 }
2300
2301 static void
eog_job_close_save_cb(EogJobSave * job,gpointer user_data)2302 eog_job_close_save_cb (EogJobSave *job, gpointer user_data)
2303 {
2304 EogWindow *window = EOG_WINDOW (user_data);
2305 GAction *action_save;
2306
2307 g_signal_handlers_disconnect_by_func (job,
2308 eog_job_close_save_cb,
2309 window);
2310
2311 /* clean the last save job */
2312 g_object_unref (window->priv->save_job);
2313 window->priv->save_job = NULL;
2314
2315 /* recover save action from actions group */
2316 action_save = g_action_map_lookup_action (G_ACTION_MAP (window),
2317 "save");
2318
2319 /* check if job contains any error */
2320 if (EOG_JOB (job)->error == NULL) {
2321 gtk_widget_destroy (GTK_WIDGET (window));
2322 } else {
2323 GtkWidget *message_area;
2324
2325 eog_thumb_view_set_current_image (EOG_THUMB_VIEW (window->priv->thumbview),
2326 job->current_image,
2327 TRUE);
2328
2329 message_area = eog_image_save_error_message_area_new (
2330 eog_image_get_caption (job->current_image),
2331 EOG_JOB (job)->error);
2332
2333 g_signal_connect (message_area,
2334 "response",
2335 G_CALLBACK (eog_window_error_message_area_response),
2336 window);
2337
2338 gtk_window_set_icon (GTK_WINDOW (window), NULL);
2339 gtk_window_set_title (GTK_WINDOW (window),
2340 eog_image_get_caption (job->current_image));
2341
2342 eog_window_set_message_area (window, message_area);
2343
2344 gtk_info_bar_set_default_response (GTK_INFO_BAR (message_area),
2345 GTK_RESPONSE_CANCEL);
2346
2347 gtk_widget_show (message_area);
2348
2349 update_status_bar (window);
2350
2351 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_save), TRUE);
2352 }
2353 }
2354
2355 static void
close_confirmation_dialog_response_handler(EogCloseConfirmationDialog * dlg,gint response_id,EogWindow * window)2356 close_confirmation_dialog_response_handler (EogCloseConfirmationDialog *dlg,
2357 gint response_id,
2358 EogWindow *window)
2359 {
2360 GList *selected_images;
2361 EogWindowPrivate *priv;
2362 GAction *action_save_as;
2363
2364 priv = window->priv;
2365
2366 switch (response_id) {
2367 case EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVE:
2368 selected_images = eog_close_confirmation_dialog_get_selected_images (dlg);
2369 gtk_widget_destroy (GTK_WIDGET (dlg));
2370
2371 if (eog_window_save_images (window, selected_images)) {
2372 g_signal_connect (priv->save_job,
2373 "finished",
2374 G_CALLBACK (eog_job_close_save_cb),
2375 window);
2376
2377 eog_job_scheduler_add_job (priv->save_job);
2378 }
2379
2380 break;
2381
2382 case EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVEAS:
2383 selected_images = eog_close_confirmation_dialog_get_selected_images (dlg);
2384 gtk_widget_destroy (GTK_WIDGET (dlg));
2385
2386 eog_thumb_view_set_current_image (EOG_THUMB_VIEW (priv->thumbview),
2387 g_list_first (selected_images)->data,
2388 TRUE);
2389
2390 action_save_as = g_action_map_lookup_action (G_ACTION_MAP (window),
2391 "save-as");
2392 eog_window_action_save_as (G_SIMPLE_ACTION (action_save_as), NULL, window);
2393 break;
2394
2395 case EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_CLOSE:
2396 gtk_widget_destroy (GTK_WIDGET (window));
2397 break;
2398
2399 case EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_CANCEL:
2400 gtk_widget_destroy (GTK_WIDGET (dlg));
2401 break;
2402 }
2403 }
2404
2405 static gboolean
eog_window_unsaved_images_confirm(EogWindow * window)2406 eog_window_unsaved_images_confirm (EogWindow *window)
2407 {
2408 EogWindowPrivate *priv;
2409 gboolean disabled;
2410 GtkWidget *dialog;
2411 GList *list;
2412 EogImage *image;
2413 GtkTreeIter iter;
2414
2415 priv = window->priv;
2416
2417 disabled = g_settings_get_boolean(priv->ui_settings,
2418 EOG_CONF_UI_DISABLE_CLOSE_CONFIRMATION);
2419 disabled |= window->priv->save_disabled;
2420
2421 if (disabled || !priv->store) {
2422 return FALSE;
2423 }
2424
2425 list = NULL;
2426 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter)) {
2427 do {
2428 gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
2429 EOG_LIST_STORE_EOG_IMAGE, &image,
2430 -1);
2431 if (!image)
2432 continue;
2433
2434 if (eog_image_is_modified (image)) {
2435 list = g_list_prepend (list, image);
2436 } else {
2437 g_object_unref (image);
2438 }
2439 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &iter));
2440 }
2441
2442 if (list) {
2443 list = g_list_reverse (list);
2444 dialog = eog_close_confirmation_dialog_new (GTK_WINDOW (window),
2445 list);
2446 g_list_free (list);
2447 g_signal_connect (dialog,
2448 "response",
2449 G_CALLBACK (close_confirmation_dialog_response_handler),
2450 window);
2451 gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
2452
2453 gtk_widget_show (dialog);
2454 return TRUE;
2455
2456 }
2457 return FALSE;
2458 }
2459
2460 static void
eog_window_action_close_window(GSimpleAction * action,GVariant * variant,gpointer user_data)2461 eog_window_action_close_window (GSimpleAction *action,
2462 GVariant *variant,
2463 gpointer user_data)
2464 {
2465 g_return_if_fail (EOG_IS_WINDOW (user_data));
2466
2467 eog_window_close (EOG_WINDOW (user_data));
2468 }
2469
2470 static void
eog_window_action_close_all_windows(GSimpleAction * action,GVariant * variant,gpointer user_data)2471 eog_window_action_close_all_windows (GSimpleAction *action,
2472 GVariant *variant,
2473 gpointer user_data)
2474 {
2475 g_return_if_fail (EOG_IS_WINDOW (user_data));
2476
2477 eog_application_close_all_windows (EOG_APP);
2478 }
2479
2480 static void
eog_window_action_preferences(GSimpleAction * action,GVariant * variant,gpointer user_data)2481 eog_window_action_preferences (GSimpleAction *action,
2482 GVariant *variant,
2483 gpointer user_data)
2484 {
2485 g_return_if_fail (EOG_IS_WINDOW (user_data));
2486
2487 eog_window_show_preferences_dialog (EOG_WINDOW (user_data));
2488 }
2489
2490 static void
eog_window_action_help(GSimpleAction * action,GVariant * variant,gpointer user_data)2491 eog_window_action_help (GSimpleAction *action,
2492 GVariant *variant,
2493 gpointer user_data)
2494 {
2495 EogWindow *window;
2496
2497 g_return_if_fail (EOG_IS_WINDOW (user_data));
2498
2499 window = EOG_WINDOW (user_data);
2500
2501 eog_util_show_help (NULL, GTK_WINDOW (window));
2502 }
2503
2504 static void
eog_window_action_about(GSimpleAction * action,GVariant * variant,gpointer user_data)2505 eog_window_action_about (GSimpleAction *action,
2506 GVariant *variant,
2507 gpointer user_data)
2508 {
2509 g_return_if_fail (EOG_IS_WINDOW (user_data));
2510
2511 eog_window_show_about_dialog (EOG_WINDOW (user_data));
2512 }
2513
2514 static void
eog_window_action_show_hide_bar(GSimpleAction * action,GVariant * state,gpointer user_data)2515 eog_window_action_show_hide_bar (GSimpleAction *action,
2516 GVariant *state,
2517 gpointer user_data)
2518 {
2519 EogWindow *window;
2520 EogWindowPrivate *priv;
2521 gboolean visible;
2522
2523 g_return_if_fail (EOG_IS_WINDOW (user_data));
2524
2525 window = EOG_WINDOW (user_data);
2526 priv = window->priv;
2527
2528 if (priv->mode != EOG_WINDOW_MODE_NORMAL &&
2529 priv->mode != EOG_WINDOW_MODE_FULLSCREEN) return;
2530
2531 visible = g_variant_get_boolean (state);
2532
2533 if (g_ascii_strcasecmp (g_action_get_name (G_ACTION (action)), "view-statusbar") == 0) {
2534 gtk_widget_set_visible (priv->statusbar, visible);
2535 g_simple_action_set_state (action, state);
2536
2537 if (priv->mode == EOG_WINDOW_MODE_NORMAL)
2538 g_settings_set_boolean (priv->ui_settings,
2539 EOG_CONF_UI_STATUSBAR, visible);
2540
2541 } else if (g_ascii_strcasecmp (g_action_get_name (G_ACTION (action)), "view-gallery") == 0) {
2542 if (visible) {
2543 /* Make sure the focus widget is realized to
2544 * avoid warnings on keypress events */
2545 if (!gtk_widget_get_realized (window->priv->thumbview))
2546 gtk_widget_realize (window->priv->thumbview);
2547
2548 gtk_widget_show (priv->nav);
2549 } else {
2550 /* Make sure the focus widget is realized to
2551 * avoid warnings on keypress events.
2552 * Don't do it during init phase or the view
2553 * will get a bogus allocation. */
2554 if (!gtk_widget_get_realized (priv->view)
2555 && priv->status == EOG_WINDOW_STATUS_NORMAL)
2556 gtk_widget_realize (priv->view);
2557
2558 gtk_widget_hide (priv->nav);
2559 }
2560 g_simple_action_set_state (action, state);
2561 g_settings_set_boolean (priv->ui_settings,
2562 EOG_CONF_UI_IMAGE_GALLERY, visible);
2563
2564 } else if (g_ascii_strcasecmp (g_action_get_name (G_ACTION (action)), "view-sidebar") == 0) {
2565 gtk_widget_set_visible (priv->sidebar, visible);
2566 g_simple_action_set_state (action, state);
2567 g_settings_set_boolean (priv->ui_settings, EOG_CONF_UI_SIDEBAR,
2568 visible);
2569 }
2570 }
2571
2572 static gboolean
in_desktop(const gchar * name)2573 in_desktop (const gchar *name)
2574 {
2575 const gchar *desktop_name_list;
2576 gchar **names;
2577 gboolean in_list = FALSE;
2578 gint i;
2579
2580 desktop_name_list = g_getenv ("XDG_CURRENT_DESKTOP");
2581 if (!desktop_name_list)
2582 return FALSE;
2583
2584 names = g_strsplit (desktop_name_list, ":", -1);
2585 for (i = 0; names[i] && !in_list; i++)
2586 if (strcmp (names[i], name) == 0) {
2587 in_list = TRUE;
2588 break;
2589 }
2590 g_strfreev (names);
2591
2592 return in_list;
2593 }
2594
2595 static void
wallpaper_info_bar_response(GtkInfoBar * bar,gint response,EogWindow * window)2596 wallpaper_info_bar_response (GtkInfoBar *bar, gint response, EogWindow *window)
2597 {
2598 if (response == GTK_RESPONSE_YES) {
2599 GAppInfo *app_info;
2600 gchar *path;
2601 GError *error = NULL;
2602
2603 path = g_find_program_in_path ("unity-control-center");
2604 if (path && in_desktop ("Unity"))
2605 app_info = g_app_info_create_from_commandline ("unity-control-center appearance",
2606 "System Settings",
2607 G_APP_INFO_CREATE_NONE,
2608 &error);
2609 else
2610 app_info = g_app_info_create_from_commandline ("gnome-control-center background",
2611 "System Settings",
2612 G_APP_INFO_CREATE_NONE,
2613 &error);
2614 g_free (path);
2615
2616 if (error != NULL) {
2617 g_warning ("%s%s", _("Error launching System Settings: "),
2618 error->message);
2619 g_error_free (error);
2620 error = NULL;
2621 }
2622
2623 if (app_info != NULL) {
2624 GdkAppLaunchContext *context;
2625 GdkDisplay *display;
2626
2627 display = gtk_widget_get_display (GTK_WIDGET (window));
2628 context = gdk_display_get_app_launch_context (display);
2629 g_app_info_launch (app_info, NULL, G_APP_LAUNCH_CONTEXT (context), &error);
2630
2631 if (error != NULL) {
2632 g_warning ("%s%s", _("Error launching System Settings: "),
2633 error->message);
2634 g_error_free (error);
2635 error = NULL;
2636 }
2637
2638 g_object_unref (context);
2639 g_object_unref (app_info);
2640 }
2641 }
2642
2643 /* Close message area on every response */
2644 eog_window_set_message_area (window, NULL);
2645 }
2646
2647 static void
eog_window_set_wallpaper(EogWindow * window,const gchar * filename,const gchar * visible_filename)2648 eog_window_set_wallpaper (EogWindow *window, const gchar *filename, const gchar *visible_filename)
2649 {
2650 GSettings *settings;
2651 GtkWidget *info_bar;
2652 GtkWidget *image;
2653 GtkWidget *label;
2654 GtkWidget *hbox;
2655 gchar *markup;
2656 gchar *text;
2657 gchar *basename;
2658 gchar *uri;
2659
2660 uri = g_filename_to_uri (filename, NULL, NULL);
2661 settings = g_settings_new (EOG_CONF_DESKTOP_WALLPAPER_SCHEMA);
2662 g_settings_set_string (settings, EOG_CONF_DESKTOP_WALLPAPER, uri);
2663 g_object_unref (settings);
2664 g_free (uri);
2665
2666 info_bar = gtk_info_bar_new_with_buttons (_("_Open Background Preferences"),
2667 GTK_RESPONSE_YES,
2668 C_("MessageArea","Hi_de"),
2669 GTK_RESPONSE_NO, NULL);
2670 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar),
2671 GTK_MESSAGE_QUESTION);
2672
2673 image = gtk_image_new_from_icon_name ("dialog-question",
2674 GTK_ICON_SIZE_DIALOG);
2675 label = gtk_label_new (NULL);
2676
2677 if (!visible_filename)
2678 basename = g_path_get_basename (filename);
2679
2680 text = g_strdup_printf (_("The image “%s” has been set as Desktop Background."
2681 " Would you like to modify its appearance?"),
2682 visible_filename ? visible_filename : basename);
2683 markup = g_markup_printf_escaped ("<b>%s</b>", text);
2684 gtk_label_set_markup (GTK_LABEL (label), markup);
2685 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2686 g_free (markup);
2687 g_free (text);
2688 if (!visible_filename)
2689 g_free (basename);
2690
2691 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
2692 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
2693 gtk_widget_set_valign (image, GTK_ALIGN_START);
2694 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
2695 gtk_widget_set_halign (label, GTK_ALIGN_START);
2696 gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar))), hbox, TRUE, TRUE, 0);
2697 gtk_widget_show_all (hbox);
2698 gtk_widget_show (info_bar);
2699
2700
2701 eog_window_set_message_area (window, info_bar);
2702 gtk_info_bar_set_default_response (GTK_INFO_BAR (info_bar),
2703 GTK_RESPONSE_YES);
2704 g_signal_connect (info_bar, "response",
2705 G_CALLBACK (wallpaper_info_bar_response), window);
2706 }
2707
2708 static void
eog_job_save_cb(EogJobSave * job,gpointer user_data)2709 eog_job_save_cb (EogJobSave *job, gpointer user_data)
2710 {
2711 EogWindow *window = EOG_WINDOW (user_data);
2712 GAction *action_save;
2713
2714 g_signal_handlers_disconnect_by_func (job,
2715 eog_job_save_cb,
2716 window);
2717
2718 g_signal_handlers_disconnect_by_func (job,
2719 eog_job_save_progress_cb,
2720 window);
2721
2722 /* clean the last save job */
2723 g_object_unref (window->priv->save_job);
2724 window->priv->save_job = NULL;
2725
2726 /* recover save action from actions group */
2727 action_save = g_action_map_lookup_action (G_ACTION_MAP (window),
2728 "save");
2729
2730 /* check if job contains any error */
2731 if (EOG_JOB (job)->error == NULL) {
2732 update_status_bar (window);
2733 gtk_window_set_title (GTK_WINDOW (window),
2734 eog_image_get_caption (job->current_image));
2735
2736 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_save), FALSE);
2737 } else {
2738 GtkWidget *message_area;
2739
2740 message_area = eog_image_save_error_message_area_new (
2741 eog_image_get_caption (job->current_image),
2742 EOG_JOB (job)->error);
2743
2744 g_signal_connect (message_area,
2745 "response",
2746 G_CALLBACK (eog_window_error_message_area_response),
2747 window);
2748
2749 gtk_window_set_icon (GTK_WINDOW (window), NULL);
2750 gtk_window_set_title (GTK_WINDOW (window),
2751 eog_image_get_caption (job->current_image));
2752
2753 eog_window_set_message_area (window, message_area);
2754
2755 gtk_info_bar_set_default_response (GTK_INFO_BAR (message_area),
2756 GTK_RESPONSE_CANCEL);
2757
2758 gtk_widget_show (message_area);
2759
2760 update_status_bar (window);
2761
2762 g_simple_action_set_enabled (G_SIMPLE_ACTION (action_save), TRUE);
2763 }
2764 }
2765
2766 static void
eog_job_copy_cb(EogJobCopy * job,gpointer user_data)2767 eog_job_copy_cb (EogJobCopy *job, gpointer user_data)
2768 {
2769 EogWindow *window = EOG_WINDOW (user_data);
2770 gchar *filepath, *basename, *filename, *extension;
2771 GAction *action;
2772 GFile *source_file, *dest_file;
2773 gint64 mtime;
2774
2775 /* Create source GFile */
2776 basename = g_file_get_basename (job->images->data);
2777 filepath = g_build_filename (job->destination, basename, NULL);
2778 source_file = g_file_new_for_path (filepath);
2779 g_free (filepath);
2780
2781 /* Create destination GFile */
2782 extension = eog_util_filename_get_extension (basename);
2783 filename = g_strdup_printf ("%s.%s", EOG_WALLPAPER_FILENAME, extension);
2784 filepath = g_build_filename (job->destination, filename, NULL);
2785 dest_file = g_file_new_for_path (filepath);
2786 g_free (filename);
2787 g_free (extension);
2788
2789 /* Move the file */
2790 g_file_move (source_file, dest_file, G_FILE_COPY_OVERWRITE,
2791 NULL, NULL, NULL, NULL);
2792
2793 /* Update mtime, see bug 664747 */
2794 mtime = g_get_real_time ();
2795 g_file_set_attribute_uint64 (dest_file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
2796 (guint64)(mtime / G_USEC_PER_SEC),
2797 G_FILE_QUERY_INFO_NONE, NULL, NULL);
2798 g_file_set_attribute_uint32 (dest_file,
2799 G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
2800 (guint32)(mtime % G_USEC_PER_SEC),
2801 G_FILE_QUERY_INFO_NONE, NULL, NULL);
2802
2803 /* Set the wallpaper */
2804 eog_window_set_wallpaper (window, filepath, basename);
2805 g_free (basename);
2806 g_free (filepath);
2807
2808 gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar),
2809 window->priv->copy_file_cid);
2810 action = g_action_map_lookup_action (G_ACTION_MAP (window),
2811 "set-wallpaper");
2812 g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
2813
2814 window->priv->copy_job = NULL;
2815
2816 g_object_unref (source_file);
2817 g_object_unref (dest_file);
2818 g_object_unref (job);
2819 }
2820
2821 static gboolean
eog_window_save_images(EogWindow * window,GList * images)2822 eog_window_save_images (EogWindow *window, GList *images)
2823 {
2824 EogWindowPrivate *priv;
2825
2826 priv = window->priv;
2827
2828 if (window->priv->save_job != NULL)
2829 return FALSE;
2830
2831 priv->save_job = eog_job_save_new (images);
2832
2833 g_signal_connect (priv->save_job,
2834 "finished",
2835 G_CALLBACK (eog_job_save_cb),
2836 window);
2837
2838 g_signal_connect (priv->save_job,
2839 "progress",
2840 G_CALLBACK (eog_job_save_progress_cb),
2841 window);
2842
2843 return TRUE;
2844 }
2845
2846 static void
eog_window_action_save(GSimpleAction * action,GVariant * variant,gpointer user_data)2847 eog_window_action_save (GSimpleAction *action,
2848 GVariant *variant,
2849 gpointer user_data)
2850 {
2851 EogWindowPrivate *priv;
2852 EogWindow *window;
2853 GList *images;
2854
2855 window = EOG_WINDOW (user_data);
2856 priv = window->priv;
2857
2858 if (window->priv->save_job != NULL)
2859 return;
2860
2861 images = eog_thumb_view_get_selected_images (EOG_THUMB_VIEW (priv->thumbview));
2862
2863 if (eog_window_save_images (window, images)) {
2864 eog_job_scheduler_add_job (priv->save_job);
2865 }
2866 }
2867
2868 static GFile*
eog_window_retrieve_save_as_file(EogWindow * window,EogImage * image)2869 eog_window_retrieve_save_as_file (EogWindow *window, EogImage *image)
2870 {
2871 GtkWidget *dialog;
2872 GFile *save_file = NULL;
2873 GFile *last_dest_folder;
2874 gint response;
2875
2876 g_assert (image != NULL);
2877
2878 dialog = eog_file_chooser_new (GTK_FILE_CHOOSER_ACTION_SAVE);
2879
2880 last_dest_folder = window->priv->last_save_as_folder;
2881
2882 if (last_dest_folder && g_file_query_exists (last_dest_folder, NULL)) {
2883 gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (dialog), last_dest_folder, NULL);
2884 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog),
2885 eog_image_get_caption (image));
2886 } else {
2887 GFile *image_file;
2888
2889 image_file = eog_image_get_file (image);
2890 /* Setting the file will also navigate to its parent folder */
2891 gtk_file_chooser_set_file (GTK_FILE_CHOOSER (dialog),
2892 image_file, NULL);
2893 g_object_unref (image_file);
2894 }
2895
2896 gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(window));
2897 response = gtk_dialog_run (GTK_DIALOG (dialog));
2898 gtk_widget_hide (dialog);
2899
2900 if (response == GTK_RESPONSE_OK) {
2901 save_file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
2902 if (window->priv->last_save_as_folder)
2903 g_object_unref (window->priv->last_save_as_folder);
2904 window->priv->last_save_as_folder = g_file_get_parent (save_file);
2905 }
2906 gtk_widget_destroy (dialog);
2907
2908 return save_file;
2909 }
2910
2911 static void
eog_window_action_save_as(GSimpleAction * action,GVariant * variant,gpointer user_data)2912 eog_window_action_save_as (GSimpleAction *action,
2913 GVariant *variant,
2914 gpointer user_data)
2915 {
2916 EogWindowPrivate *priv;
2917 EogWindow *window;
2918 GList *images;
2919 guint n_images;
2920
2921 window = EOG_WINDOW (user_data);
2922 priv = window->priv;
2923
2924 if (window->priv->save_job != NULL)
2925 return;
2926
2927 images = eog_thumb_view_get_selected_images (EOG_THUMB_VIEW (priv->thumbview));
2928 n_images = g_list_length (images);
2929
2930 if (n_images == 1) {
2931 GFile *file;
2932
2933 file = eog_window_retrieve_save_as_file (window, images->data);
2934
2935 if (!file) {
2936 g_list_free (images);
2937 return;
2938 }
2939
2940 priv->save_job = eog_job_save_as_new (images, NULL, file);
2941
2942 g_object_unref (file);
2943 } else if (n_images > 1) {
2944 GFile *base_file;
2945 GtkWidget *dialog;
2946 gchar *basedir;
2947 EogURIConverter *converter;
2948
2949 basedir = g_get_current_dir ();
2950 base_file = g_file_new_for_path (basedir);
2951 g_free (basedir);
2952
2953 dialog = eog_save_as_dialog_new (GTK_WINDOW (window),
2954 images,
2955 base_file);
2956
2957 gtk_widget_show_all (dialog);
2958
2959 if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK) {
2960 g_object_unref (base_file);
2961 g_list_free (images);
2962 gtk_widget_destroy (dialog);
2963
2964 return;
2965 }
2966
2967 converter = eog_save_as_dialog_get_converter (dialog);
2968
2969 g_assert (converter != NULL);
2970
2971 priv->save_job = eog_job_save_as_new (images, converter, NULL);
2972
2973 gtk_widget_destroy (dialog);
2974
2975 g_object_unref (converter);
2976 g_object_unref (base_file);
2977 } else {
2978 /* n_images = 0 -- No Image selected */
2979 return;
2980 }
2981
2982 g_signal_connect (priv->save_job,
2983 "finished",
2984 G_CALLBACK (eog_job_save_cb),
2985 window);
2986
2987 g_signal_connect (priv->save_job,
2988 "progress",
2989 G_CALLBACK (eog_job_save_progress_cb),
2990 window);
2991
2992 eog_job_scheduler_add_job (priv->save_job);
2993 }
2994
2995 static void
eog_window_action_open_containing_folder(GSimpleAction * action,GVariant * variant,gpointer user_data)2996 eog_window_action_open_containing_folder (GSimpleAction *action,
2997 GVariant *variant,
2998 gpointer user_data)
2999 {
3000 EogWindowPrivate *priv;
3001 GFile *file;
3002
3003 g_return_if_fail (EOG_IS_WINDOW (user_data));
3004
3005 priv = EOG_WINDOW (user_data)->priv;
3006
3007 g_return_if_fail (priv->image != NULL);
3008
3009 file = eog_image_get_file (priv->image);
3010
3011 g_return_if_fail (file != NULL);
3012
3013 eog_util_show_file_in_filemanager (file,
3014 GTK_WINDOW (user_data));
3015 }
3016
3017 static void
eog_window_action_print(GSimpleAction * action,GVariant * variant,gpointer user_data)3018 eog_window_action_print (GSimpleAction *action,
3019 GVariant *variant,
3020 gpointer user_data)
3021 {
3022 EogWindow *window = EOG_WINDOW (user_data);
3023
3024 eog_window_print (window);
3025 }
3026
3027 /**
3028 * eog_window_get_properties_dialog:
3029 * @window: a #EogWindow
3030 *
3031 * Gets the @window property dialog. The widget will be built on the first call to this function.
3032 *
3033 * Returns: (transfer none): a #GtkWidget.
3034 */
3035
3036 GtkWidget*
eog_window_get_properties_dialog(EogWindow * window)3037 eog_window_get_properties_dialog (EogWindow *window)
3038 {
3039 EogWindowPrivate *priv;
3040
3041 g_return_val_if_fail (EOG_IS_WINDOW (window), NULL);
3042
3043 priv = window->priv;
3044
3045 if (priv->properties_dlg == NULL) {
3046 priv->properties_dlg =
3047 eog_properties_dialog_new (GTK_WINDOW (window),
3048 EOG_THUMB_VIEW (priv->thumbview),
3049 "win.go-next",
3050 "win.go-previous");
3051
3052 eog_properties_dialog_update (EOG_PROPERTIES_DIALOG (priv->properties_dlg),
3053 priv->image);
3054 g_settings_bind (priv->ui_settings,
3055 EOG_CONF_UI_PROPSDIALOG_NETBOOK_MODE,
3056 priv->properties_dlg, "netbook-mode",
3057 G_SETTINGS_BIND_GET);
3058 }
3059
3060 return priv->properties_dlg;
3061 }
3062
3063 static void
eog_window_action_properties(GSimpleAction * action,GVariant * variant,gpointer user_data)3064 eog_window_action_properties (GSimpleAction *action,
3065 GVariant *variant,
3066 gpointer user_data)
3067 {
3068 EogWindow *window = EOG_WINDOW (user_data);
3069 GtkWidget *dialog;
3070
3071 dialog = eog_window_get_properties_dialog (window);
3072 gtk_widget_show (dialog);
3073 }
3074
3075 static void
eog_window_action_undo(GSimpleAction * action,GVariant * variant,gpointer user_data)3076 eog_window_action_undo (GSimpleAction *action,
3077 GVariant *variant,
3078 gpointer user_data)
3079 {
3080 g_return_if_fail (EOG_IS_WINDOW (user_data));
3081
3082 apply_transformation (EOG_WINDOW (user_data), NULL);
3083 }
3084
3085 static void
eog_window_action_flip_horizontal(GSimpleAction * action,GVariant * variant,gpointer user_data)3086 eog_window_action_flip_horizontal (GSimpleAction *action,
3087 GVariant *variant,
3088 gpointer user_data)
3089 {
3090 g_return_if_fail (EOG_IS_WINDOW (user_data));
3091
3092 apply_transformation (EOG_WINDOW (user_data),
3093 eog_transform_flip_new (EOG_TRANSFORM_FLIP_HORIZONTAL));
3094 }
3095
3096 static void
eog_window_action_flip_vertical(GSimpleAction * action,GVariant * variant,gpointer user_data)3097 eog_window_action_flip_vertical (GSimpleAction *action,
3098 GVariant *variant,
3099 gpointer user_data)
3100 {
3101 g_return_if_fail (EOG_IS_WINDOW (user_data));
3102
3103 apply_transformation (EOG_WINDOW (user_data),
3104 eog_transform_flip_new (EOG_TRANSFORM_FLIP_VERTICAL));
3105 }
3106
3107 static void
eog_window_action_rotate_90(GSimpleAction * action,GVariant * parameter,gpointer user_data)3108 eog_window_action_rotate_90 (GSimpleAction *action,
3109 GVariant *parameter,
3110 gpointer user_data)
3111 {
3112 g_return_if_fail (EOG_IS_WINDOW (user_data));
3113
3114 apply_transformation (EOG_WINDOW (user_data),
3115 eog_transform_rotate_new (90));
3116 }
3117
3118 static void
eog_window_action_rotate_270(GSimpleAction * action,GVariant * parameter,gpointer user_data)3119 eog_window_action_rotate_270 (GSimpleAction *action,
3120 GVariant *parameter,
3121 gpointer user_data)
3122 {
3123 g_return_if_fail (EOG_IS_WINDOW (user_data));
3124
3125 apply_transformation (EOG_WINDOW (user_data),
3126 eog_transform_rotate_new (270));
3127 }
3128
3129 static void
eog_window_action_wallpaper(GSimpleAction * action,GVariant * variant,gpointer user_data)3130 eog_window_action_wallpaper (GSimpleAction *action,
3131 GVariant *variant,
3132 gpointer user_data)
3133 {
3134 EogWindow *window;
3135 EogWindowPrivate *priv;
3136 EogImage *image;
3137 g_autoptr(GFile) file = NULL;
3138 g_autofree gchar *filename = NULL;
3139
3140 g_return_if_fail (EOG_IS_WINDOW (user_data));
3141
3142 window = EOG_WINDOW (user_data);
3143 priv = window->priv;
3144
3145 /* If currently copying an image to set it as wallpaper, return. */
3146 if (priv->copy_job != NULL)
3147 return;
3148
3149 image = eog_thumb_view_get_first_selected_image (EOG_THUMB_VIEW (priv->thumbview));
3150
3151 g_return_if_fail (EOG_IS_IMAGE (image));
3152
3153 file = eog_image_get_file (image);
3154
3155 filename = g_file_get_path (file);
3156
3157 /* Currently only local files can be set as wallpaper */
3158 if (filename == NULL || !eog_util_file_is_persistent (file))
3159 {
3160 GList *files = NULL;
3161
3162 g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
3163
3164 priv->copy_file_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar),
3165 "copy_file_cid");
3166 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar),
3167 priv->copy_file_cid,
3168 _("Saving image locally…"));
3169
3170 files = g_list_append (files, eog_image_get_file (image));
3171 priv->copy_job = eog_job_copy_new (files, g_get_user_data_dir ());
3172 g_signal_connect (priv->copy_job,
3173 "finished",
3174 G_CALLBACK (eog_job_copy_cb),
3175 window);
3176 g_signal_connect (priv->copy_job,
3177 "progress",
3178 G_CALLBACK (eog_job_progress_cb),
3179 window);
3180 eog_job_scheduler_add_job (priv->copy_job);
3181
3182 return;
3183 }
3184
3185 #ifdef HAVE_LIBPORTAL
3186 if (eog_util_is_running_inside_flatpak ()) {
3187 eog_util_set_wallpaper_with_portal (file, GTK_WINDOW (window));
3188
3189 return;
3190 }
3191 #endif
3192
3193 eog_window_set_wallpaper (window, filename, NULL);
3194
3195 }
3196
3197 static gboolean
eog_window_all_images_trasheable(GList * images)3198 eog_window_all_images_trasheable (GList *images)
3199 {
3200 GFile *file;
3201 GFileInfo *file_info;
3202 GList *iter;
3203 EogImage *image;
3204 gboolean can_trash = TRUE;
3205
3206 for (iter = images; iter != NULL; iter = g_list_next (iter)) {
3207 image = (EogImage *) iter->data;
3208 file = eog_image_get_file (image);
3209 file_info = g_file_query_info (file,
3210 G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
3211 0, NULL, NULL);
3212 can_trash = g_file_info_get_attribute_boolean (file_info,
3213 G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH);
3214
3215 g_object_unref (file_info);
3216 g_object_unref (file);
3217
3218 if (can_trash == FALSE)
3219 break;
3220 }
3221
3222 return can_trash;
3223 }
3224
3225 static gint
show_force_image_delete_confirm_dialog(EogWindow * window,GList * images)3226 show_force_image_delete_confirm_dialog (EogWindow *window,
3227 GList *images)
3228 {
3229 static gboolean dont_ask_again_force_delete = FALSE;
3230
3231 GtkWidget *dialog;
3232 GtkWidget *dont_ask_again_button;
3233 EogImage *image;
3234 gchar *prompt;
3235 guint n_images;
3236 gint response;
3237
3238 /* assume agreement, if the user doesn't want to be asked and deletion is available */
3239 if (dont_ask_again_force_delete)
3240 return GTK_RESPONSE_OK;
3241
3242 /* retrieve the selected images count */
3243 n_images = g_list_length (images);
3244
3245 /* make the dialog prompt message */
3246 if (n_images == 1) {
3247 image = EOG_IMAGE (images->data);
3248
3249 prompt = g_strdup_printf (_("Are you sure you want to remove\n“%s” permanently?"),
3250 eog_image_get_caption (image));
3251 } else {
3252 prompt = g_strdup_printf (ngettext ("Are you sure you want to remove\n"
3253 "the selected image permanently?",
3254 "Are you sure you want to remove\n"
3255 "the %d selected images permanently?",
3256 n_images),
3257 n_images);
3258 }
3259
3260 /* create the dialog */
3261 dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window),
3262 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3263 GTK_MESSAGE_WARNING,
3264 GTK_BUTTONS_NONE,
3265 "<span weight=\"bold\" size=\"larger\">%s</span>",
3266 prompt);
3267
3268 gtk_dialog_set_default_response (GTK_DIALOG (dialog),
3269 GTK_RESPONSE_OK);
3270
3271 /* add buttons to the dialog */
3272 if (n_images == 1) {
3273 gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Cancel"), GTK_RESPONSE_CANCEL);
3274 gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Delete"), GTK_RESPONSE_OK);
3275 } else {
3276 gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Cancel"), GTK_RESPONSE_CANCEL);
3277 gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Yes") , GTK_RESPONSE_OK);
3278 }
3279
3280 /* add 'dont ask again' button */
3281 dont_ask_again_button = gtk_check_button_new_with_mnemonic (_("Do _not ask again during this session"));
3282
3283 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dont_ask_again_button),
3284 FALSE);
3285
3286 gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
3287 dont_ask_again_button,
3288 TRUE,
3289 TRUE,
3290 0);
3291
3292 /* show dialog and get user response */
3293 gtk_widget_show_all (dialog);
3294 response = gtk_dialog_run (GTK_DIALOG (dialog));
3295
3296 /* only update the 'dont ask again' property if the user has accepted */
3297 if (response == GTK_RESPONSE_OK)
3298 dont_ask_again_force_delete = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dont_ask_again_button));
3299
3300 /* free resources */
3301 g_free (prompt);
3302 gtk_widget_destroy (dialog);
3303
3304 return response;
3305 }
3306
3307 static gboolean
force_image_delete_real(EogImage * image,GError ** error)3308 force_image_delete_real (EogImage *image,
3309 GError **error)
3310 {
3311 GFile *file;
3312 GFileInfo *file_info;
3313 gboolean can_delete;
3314 gboolean result;
3315
3316 g_return_val_if_fail (EOG_IS_IMAGE (image), FALSE);
3317
3318 /* retrieve image file */
3319 file = eog_image_get_file (image);
3320
3321 if (file == NULL) {
3322 g_set_error (error,
3323 EOG_WINDOW_ERROR,
3324 EOG_WINDOW_ERROR_IO,
3325 _("Couldn’t retrieve image file"));
3326
3327 return FALSE;
3328 }
3329
3330 /* retrieve some image file information */
3331 file_info = g_file_query_info (file,
3332 G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
3333 0,
3334 NULL,
3335 NULL);
3336
3337 if (file_info == NULL) {
3338 g_set_error (error,
3339 EOG_WINDOW_ERROR,
3340 EOG_WINDOW_ERROR_IO,
3341 _("Couldn’t retrieve image file information"));
3342
3343 /* free resources */
3344 g_object_unref (file);
3345
3346 return FALSE;
3347 }
3348
3349 /* check that image file can be deleted */
3350 can_delete = g_file_info_get_attribute_boolean (file_info,
3351 G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE);
3352
3353 if (!can_delete) {
3354 g_set_error (error,
3355 EOG_WINDOW_ERROR,
3356 EOG_WINDOW_ERROR_IO,
3357 _("Couldn’t delete file"));
3358
3359 /* free resources */
3360 g_object_unref (file_info);
3361 g_object_unref (file);
3362
3363 return FALSE;
3364 }
3365
3366 /* delete image file */
3367 result = g_file_delete (file,
3368 NULL,
3369 error);
3370
3371 /* free resources */
3372 g_object_unref (file_info);
3373 g_object_unref (file);
3374
3375 return result;
3376 }
3377
3378 static void
eog_window_force_image_delete(EogWindow * window,GList * images)3379 eog_window_force_image_delete (EogWindow *window,
3380 GList *images)
3381 {
3382 GList *item;
3383 gboolean success;
3384
3385 g_return_if_fail (EOG_WINDOW (window));
3386
3387 /* force delete of each image of the list */
3388 for (item = images; item != NULL; item = item->next) {
3389 GError *error;
3390 EogImage *image;
3391
3392 error = NULL;
3393 image = EOG_IMAGE (item->data);
3394
3395 success = force_image_delete_real (image, &error);
3396
3397 if (!success) {
3398 GtkWidget *dialog;
3399 gchar *header;
3400
3401 /* set dialog error message */
3402 header = g_strdup_printf (_("Error on deleting image %s"),
3403 eog_image_get_caption (image));
3404
3405 /* create dialog */
3406 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
3407 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3408 GTK_MESSAGE_ERROR,
3409 GTK_BUTTONS_OK,
3410 "%s", header);
3411
3412 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
3413 "%s",
3414 error->message);
3415
3416 /* show dialog */
3417 gtk_dialog_run (GTK_DIALOG (dialog));
3418
3419 /* free resources */
3420 gtk_widget_destroy (dialog);
3421 g_free (header);
3422
3423 return;
3424 }
3425
3426 /* remove image from store */
3427 eog_list_store_remove_image (window->priv->store, image);
3428 }
3429
3430 /* free list */
3431 g_list_foreach (images, (GFunc) g_object_unref, NULL);
3432 g_list_free (images);
3433 }
3434
3435 static void
eog_window_action_delete(GSimpleAction * action,GVariant * variant,gpointer user_data)3436 eog_window_action_delete (GSimpleAction *action,
3437 GVariant *variant,
3438 gpointer user_data)
3439 {
3440 EogWindow *window;
3441 GList *images;
3442 gint result;
3443
3444 window = EOG_WINDOW (user_data);
3445 images = eog_thumb_view_get_selected_images (EOG_THUMB_VIEW (window->priv->thumbview));
3446 if (G_LIKELY (g_list_length (images) > 0))
3447 {
3448 result = show_force_image_delete_confirm_dialog (window, images);
3449
3450 if (result == GTK_RESPONSE_OK)
3451 eog_window_force_image_delete (window, images);
3452 }
3453 }
3454
3455 static int
show_move_to_trash_confirm_dialog(EogWindow * window,GList * images,gboolean can_trash)3456 show_move_to_trash_confirm_dialog (EogWindow *window, GList *images, gboolean can_trash)
3457 {
3458 GtkWidget *dlg;
3459 char *prompt;
3460 int response;
3461 int n_images;
3462 EogImage *image;
3463 static gboolean dontaskagain = FALSE;
3464 gboolean neverask = FALSE;
3465 GtkWidget* dontask_cbutton = NULL;
3466
3467 /* Check if the user never wants to be bugged. */
3468 neverask = g_settings_get_boolean (window->priv->ui_settings,
3469 EOG_CONF_UI_DISABLE_TRASH_CONFIRMATION);
3470
3471 /* Assume agreement, if the user doesn't want to be
3472 * asked and the trash is available */
3473 if (can_trash && (dontaskagain || neverask))
3474 return GTK_RESPONSE_OK;
3475
3476 n_images = g_list_length (images);
3477
3478 if (n_images == 1) {
3479 image = EOG_IMAGE (images->data);
3480 if (can_trash) {
3481 prompt = g_strdup_printf (_("Are you sure you want to move\n“%s” to the trash?"),
3482 eog_image_get_caption (image));
3483 } else {
3484 prompt = g_strdup_printf (_("A trash for “%s” couldn’t be found. Do you want to remove "
3485 "this image permanently?"), eog_image_get_caption (image));
3486 }
3487 } else {
3488 if (can_trash) {
3489 prompt = g_strdup_printf (ngettext("Are you sure you want to move\n"
3490 "the selected image to the trash?",
3491 "Are you sure you want to move\n"
3492 "the %d selected images to the trash?", n_images), n_images);
3493 } else {
3494 prompt = g_strdup (_("Some of the selected images can’t be moved to the trash "
3495 "and will be removed permanently. Are you sure you want "
3496 "to proceed?"));
3497 }
3498 }
3499
3500 dlg = gtk_message_dialog_new_with_markup (GTK_WINDOW (window),
3501 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3502 GTK_MESSAGE_WARNING,
3503 GTK_BUTTONS_NONE,
3504 "<span weight=\"bold\" size=\"larger\">%s</span>",
3505 prompt);
3506 g_free (prompt);
3507
3508 gtk_dialog_add_button (GTK_DIALOG (dlg), _("_Cancel"), GTK_RESPONSE_CANCEL);
3509
3510 if (can_trash) {
3511 gtk_dialog_add_button (GTK_DIALOG (dlg), _("Move to _Trash"), GTK_RESPONSE_OK);
3512
3513 dontask_cbutton = gtk_check_button_new_with_mnemonic (_("Do _not ask again during this session"));
3514 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dontask_cbutton), FALSE);
3515
3516 gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), dontask_cbutton, TRUE, TRUE, 0);
3517 } else {
3518 if (n_images == 1) {
3519 gtk_dialog_add_button (GTK_DIALOG (dlg), _("_Delete"), GTK_RESPONSE_OK);
3520 } else {
3521 gtk_dialog_add_button (GTK_DIALOG (dlg), _("_Yes"), GTK_RESPONSE_OK);
3522 }
3523 }
3524
3525 gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK);
3526 gtk_window_set_title (GTK_WINDOW (dlg), "");
3527 gtk_widget_show_all (dlg);
3528
3529 response = gtk_dialog_run (GTK_DIALOG (dlg));
3530
3531 /* Only update the property if the user has accepted */
3532 if (can_trash && response == GTK_RESPONSE_OK)
3533 dontaskagain = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dontask_cbutton));
3534
3535 /* The checkbutton is destroyed together with the dialog */
3536 gtk_widget_destroy (dlg);
3537
3538 return response;
3539 }
3540
3541 static gboolean
move_to_trash_real(EogImage * image,GError ** error)3542 move_to_trash_real (EogImage *image, GError **error)
3543 {
3544 GFile *file;
3545 GFileInfo *file_info;
3546 gboolean can_trash, result;
3547
3548 g_return_val_if_fail (EOG_IS_IMAGE (image), FALSE);
3549
3550 file = eog_image_get_file (image);
3551 file_info = g_file_query_info (file,
3552 G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
3553 0, NULL, NULL);
3554 if (file_info == NULL) {
3555 g_set_error (error,
3556 EOG_WINDOW_ERROR,
3557 EOG_WINDOW_ERROR_TRASH_NOT_FOUND,
3558 _("Couldn’t access trash."));
3559 return FALSE;
3560 }
3561
3562 can_trash = g_file_info_get_attribute_boolean (file_info,
3563 G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH);
3564 g_object_unref (file_info);
3565 if (can_trash)
3566 {
3567 result = g_file_trash (file, NULL, NULL);
3568 if (result == FALSE) {
3569 g_set_error (error,
3570 EOG_WINDOW_ERROR,
3571 EOG_WINDOW_ERROR_TRASH_NOT_FOUND,
3572 _("Couldn’t access trash."));
3573 }
3574 } else {
3575 result = g_file_delete (file, NULL, NULL);
3576 if (result == FALSE) {
3577 g_set_error (error,
3578 EOG_WINDOW_ERROR,
3579 EOG_WINDOW_ERROR_IO,
3580 _("Couldn’t delete file"));
3581 }
3582 }
3583
3584 g_object_unref (file);
3585
3586 return result;
3587 }
3588
3589 static void
eog_window_action_copy_image(GSimpleAction * action,GVariant * variant,gpointer user_data)3590 eog_window_action_copy_image (GSimpleAction *action,
3591 GVariant *variant,
3592 gpointer user_data)
3593 {
3594 GtkClipboard *clipboard;
3595 EogWindow *window;
3596 EogWindowPrivate *priv;
3597 EogImage *image;
3598 EogClipboardHandler *cbhandler;
3599
3600 g_return_if_fail (EOG_IS_WINDOW (user_data));
3601
3602 window = EOG_WINDOW (user_data);
3603 priv = window->priv;
3604
3605 image = eog_thumb_view_get_first_selected_image (EOG_THUMB_VIEW (priv->thumbview));
3606
3607 g_return_if_fail (EOG_IS_IMAGE (image));
3608
3609 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
3610
3611 cbhandler = eog_clipboard_handler_new (image);
3612 // cbhandler will self-destruct when it's not needed anymore
3613 eog_clipboard_handler_copy_to_clipboard (cbhandler, clipboard);
3614 }
3615
3616 static void
eog_window_action_move_to_trash(GSimpleAction * action,GVariant * variant,gpointer user_data)3617 eog_window_action_move_to_trash (GSimpleAction *action,
3618 GVariant *variant,
3619 gpointer user_data)
3620 {
3621 GList *images;
3622 GList *it;
3623 EogWindowPrivate *priv;
3624 EogListStore *list;
3625 EogWindow *window;
3626 int response;
3627 int n_images;
3628 gboolean success;
3629 gboolean can_trash;
3630
3631 g_return_if_fail (EOG_IS_WINDOW (user_data));
3632
3633 window = EOG_WINDOW (user_data);
3634 priv = window->priv;
3635 list = priv->store;
3636
3637 n_images = eog_thumb_view_get_n_selected (EOG_THUMB_VIEW (priv->thumbview));
3638
3639 if (n_images < 1) return;
3640
3641 /* save position of selected image after the deletion */
3642 images = eog_thumb_view_get_selected_images (EOG_THUMB_VIEW (priv->thumbview));
3643
3644 g_assert (images != NULL);
3645
3646 /* HACK: eog_list_store_get_n_selected return list in reverse order */
3647 images = g_list_reverse (images);
3648
3649 can_trash = eog_window_all_images_trasheable (images);
3650
3651 if (g_ascii_strcasecmp (g_action_get_name (G_ACTION (action)), "Delete") == 0 ||
3652 can_trash == FALSE) {
3653 response = show_move_to_trash_confirm_dialog (window, images, can_trash);
3654
3655 if (response != GTK_RESPONSE_OK) return;
3656 }
3657
3658 /* FIXME: make a nice progress dialog */
3659 /* Do the work actually. First try to delete the image from the disk. If this
3660 * is successful, remove it from the screen. Otherwise show error dialog.
3661 */
3662 for (it = images; it != NULL; it = it->next) {
3663 GError *error = NULL;
3664 EogImage *image;
3665
3666 image = EOG_IMAGE (it->data);
3667
3668 success = move_to_trash_real (image, &error);
3669
3670 if (success) {
3671 eog_list_store_remove_image (list, image);
3672 } else {
3673 char *header;
3674 GtkWidget *dlg;
3675
3676 header = g_strdup_printf (_("Error on deleting image %s"),
3677 eog_image_get_caption (image));
3678
3679 dlg = gtk_message_dialog_new (GTK_WINDOW (window),
3680 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3681 GTK_MESSAGE_ERROR,
3682 GTK_BUTTONS_OK,
3683 "%s", header);
3684
3685 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg),
3686 "%s", error->message);
3687
3688 gtk_dialog_run (GTK_DIALOG (dlg));
3689
3690 gtk_widget_destroy (dlg);
3691
3692 g_free (header);
3693 }
3694 }
3695
3696 /* free list */
3697 g_list_foreach (images, (GFunc) g_object_unref, NULL);
3698 g_list_free (images);
3699 }
3700
3701 static void
eog_window_action_toggle_fullscreen(GSimpleAction * action,GVariant * state,gpointer user_data)3702 eog_window_action_toggle_fullscreen (GSimpleAction *action,
3703 GVariant *state,
3704 gpointer user_data)
3705 {
3706 EogWindow *window;
3707 gboolean fullscreen;
3708
3709 g_return_if_fail (EOG_IS_WINDOW (user_data));
3710
3711 eog_debug (DEBUG_WINDOW);
3712
3713 window = EOG_WINDOW (user_data);
3714
3715 fullscreen = g_variant_get_boolean (state);
3716
3717 if (fullscreen) {
3718 eog_window_run_fullscreen (window, FALSE);
3719 } else {
3720 eog_window_stop_fullscreen (window, FALSE);
3721 }
3722 }
3723
3724 static void
eog_window_action_toggle_slideshow(GSimpleAction * action,GVariant * state,gpointer user_data)3725 eog_window_action_toggle_slideshow (GSimpleAction *action,
3726 GVariant *state,
3727 gpointer user_data)
3728 {
3729 EogWindow *window;
3730 gboolean slideshow;
3731
3732 g_return_if_fail (EOG_IS_WINDOW (user_data));
3733
3734 eog_debug (DEBUG_WINDOW);
3735
3736 window = EOG_WINDOW (user_data);
3737
3738 slideshow = g_variant_get_boolean (state);
3739
3740 if (slideshow) {
3741 eog_window_run_fullscreen (window, TRUE);
3742 } else {
3743 eog_window_stop_fullscreen (window, TRUE);
3744 }
3745 }
3746
3747 static void
eog_window_action_pause_slideshow(GSimpleAction * action,GVariant * variant,gpointer user_data)3748 eog_window_action_pause_slideshow (GSimpleAction *action,
3749 GVariant *variant,
3750 gpointer user_data)
3751 {
3752 EogWindow *window;
3753 gboolean slideshow;
3754
3755 g_return_if_fail (EOG_IS_WINDOW (user_data));
3756
3757 eog_debug (DEBUG_WINDOW);
3758
3759 window = EOG_WINDOW (user_data);
3760
3761 slideshow = window->priv->mode == EOG_WINDOW_MODE_SLIDESHOW;
3762
3763 if (!slideshow && window->priv->mode != EOG_WINDOW_MODE_FULLSCREEN)
3764 return;
3765
3766 eog_window_run_fullscreen (window, !slideshow);
3767 }
3768
3769 static void
eog_window_action_zoom_in(GSimpleAction * action,GVariant * parameter,gpointer user_data)3770 eog_window_action_zoom_in (GSimpleAction *action,
3771 GVariant *parameter,
3772 gpointer user_data)
3773 {
3774 EogWindowPrivate *priv;
3775
3776 g_return_if_fail (EOG_IS_WINDOW (user_data));
3777
3778 eog_debug (DEBUG_WINDOW);
3779
3780 priv = EOG_WINDOW (user_data)->priv;
3781
3782 if (priv->view) {
3783 eog_scroll_view_zoom_in (EOG_SCROLL_VIEW (priv->view), FALSE);
3784 }
3785 }
3786
3787 static void
eog_window_action_zoom_out(GSimpleAction * action,GVariant * parameter,gpointer user_data)3788 eog_window_action_zoom_out (GSimpleAction *action,
3789 GVariant *parameter,
3790 gpointer user_data)
3791 {
3792 EogWindowPrivate *priv;
3793
3794 g_return_if_fail (EOG_IS_WINDOW (user_data));
3795
3796 eog_debug (DEBUG_WINDOW);
3797
3798 priv = EOG_WINDOW (user_data)->priv;
3799
3800 if (priv->view) {
3801 eog_scroll_view_zoom_out (EOG_SCROLL_VIEW (priv->view), FALSE);
3802 }
3803 }
3804
3805 static void
eog_window_action_zoom_normal(GSimpleAction * action,GVariant * variant,gpointer user_data)3806 eog_window_action_zoom_normal (GSimpleAction *action,
3807 GVariant *variant,
3808 gpointer user_data)
3809 {
3810 EogWindowPrivate *priv;
3811
3812 g_return_if_fail (EOG_IS_WINDOW (user_data));
3813
3814 eog_debug (DEBUG_WINDOW);
3815
3816 priv = EOG_WINDOW (user_data)->priv;
3817
3818 if (priv->view) {
3819 eog_scroll_view_set_zoom (EOG_SCROLL_VIEW (priv->view), 1.0);
3820 }
3821 }
3822
3823 static void
eog_window_action_toggle_zoom_fit(GSimpleAction * action,GVariant * state,gpointer user_data)3824 eog_window_action_toggle_zoom_fit (GSimpleAction *action,
3825 GVariant *state,
3826 gpointer user_data)
3827 {
3828 EogWindowPrivate *priv;
3829 EogZoomMode mode;
3830
3831 g_return_if_fail (EOG_IS_WINDOW (user_data));
3832
3833 eog_debug (DEBUG_WINDOW);
3834
3835 priv = EOG_WINDOW (user_data)->priv;
3836
3837 mode = g_variant_get_boolean (state)
3838 ? EOG_ZOOM_MODE_SHRINK_TO_FIT : EOG_ZOOM_MODE_FREE;
3839
3840 if (priv->view) {
3841 eog_scroll_view_set_zoom_mode (EOG_SCROLL_VIEW (priv->view),
3842 mode);
3843 }
3844 }
3845
3846 static void
eog_window_action_go_prev(GSimpleAction * action,GVariant * parameter,gpointer user_data)3847 eog_window_action_go_prev (GSimpleAction *action,
3848 GVariant *parameter,
3849 gpointer user_data)
3850 {
3851 EogWindowPrivate *priv;
3852
3853 g_return_if_fail (EOG_IS_WINDOW (user_data));
3854
3855 eog_debug (DEBUG_WINDOW);
3856
3857 priv = EOG_WINDOW (user_data)->priv;
3858
3859 eog_thumb_view_select_single (EOG_THUMB_VIEW (priv->thumbview),
3860 EOG_THUMB_VIEW_SELECT_LEFT);
3861 }
3862
3863 static void
eog_window_action_go_next(GSimpleAction * action,GVariant * parameter,gpointer user_data)3864 eog_window_action_go_next (GSimpleAction *action,
3865 GVariant *parameter,
3866 gpointer user_data)
3867 {
3868 EogWindowPrivate *priv;
3869
3870 g_return_if_fail (EOG_IS_WINDOW (user_data));
3871
3872 eog_debug (DEBUG_WINDOW);
3873
3874 priv = EOG_WINDOW (user_data)->priv;
3875
3876 eog_thumb_view_select_single (EOG_THUMB_VIEW (priv->thumbview),
3877 EOG_THUMB_VIEW_SELECT_RIGHT);
3878 }
3879
3880 static void
eog_window_action_go_first(GSimpleAction * action,GVariant * parameter,gpointer user_data)3881 eog_window_action_go_first (GSimpleAction *action,
3882 GVariant *parameter,
3883 gpointer user_data)
3884 {
3885 EogWindowPrivate *priv;
3886
3887 g_return_if_fail (EOG_IS_WINDOW (user_data));
3888
3889 eog_debug (DEBUG_WINDOW);
3890
3891 priv = EOG_WINDOW (user_data)->priv;
3892
3893 eog_thumb_view_select_single (EOG_THUMB_VIEW (priv->thumbview),
3894 EOG_THUMB_VIEW_SELECT_FIRST);
3895 }
3896
3897 static void
eog_window_action_go_last(GSimpleAction * action,GVariant * parameter,gpointer user_data)3898 eog_window_action_go_last (GSimpleAction *action,
3899 GVariant *parameter,
3900 gpointer user_data)
3901 {
3902 EogWindowPrivate *priv;
3903
3904 g_return_if_fail (EOG_IS_WINDOW (user_data));
3905
3906 eog_debug (DEBUG_WINDOW);
3907
3908 priv = EOG_WINDOW (user_data)->priv;
3909
3910 eog_thumb_view_select_single (EOG_THUMB_VIEW (priv->thumbview),
3911 EOG_THUMB_VIEW_SELECT_LAST);
3912 }
3913
3914 static void
eog_window_action_go_random(GSimpleAction * action,GVariant * parameter,gpointer user_data)3915 eog_window_action_go_random (GSimpleAction *action,
3916 GVariant *parameter,
3917 gpointer user_data)
3918 {
3919 EogWindowPrivate *priv;
3920
3921 g_return_if_fail (EOG_IS_WINDOW (user_data));
3922
3923 eog_debug (DEBUG_WINDOW);
3924
3925 priv = EOG_WINDOW (user_data)->priv;
3926
3927 eog_thumb_view_select_single (EOG_THUMB_VIEW (priv->thumbview),
3928 EOG_THUMB_VIEW_SELECT_RANDOM);
3929 }
3930
3931 static void
eog_window_action_set_zoom(GSimpleAction * action,GVariant * parameter,gpointer user_data)3932 eog_window_action_set_zoom (GSimpleAction *action,
3933 GVariant *parameter,
3934 gpointer user_data)
3935 {
3936 EogWindow *window;
3937 double zoom;
3938
3939 g_return_if_fail (EOG_IS_WINDOW (user_data));
3940 g_return_if_fail (g_variant_is_of_type (parameter, G_VARIANT_TYPE_DOUBLE));
3941
3942 window = EOG_WINDOW (user_data);
3943
3944 zoom = g_variant_get_double (parameter);
3945
3946 eog_debug_message (DEBUG_WINDOW, "Set zoom factor to %.4lf", zoom);
3947
3948 if (window->priv->view) {
3949 eog_scroll_view_set_zoom (EOG_SCROLL_VIEW (window->priv->view),
3950 zoom);
3951 }
3952 }
3953
3954 static void
readonly_state_handler(GSimpleAction * action,GVariant * value,gpointer user_data)3955 readonly_state_handler (GSimpleAction *action,
3956 GVariant *value,
3957 gpointer user_data)
3958 {
3959 g_warning ("The state of action \"%s\" is read-only! Ignoring request!",
3960 g_action_get_name (G_ACTION (action)));
3961 }
3962
3963 static const GActionEntry window_actions[] = {
3964 /* Stateless actions on the window. */
3965 { "open", eog_window_action_file_open },
3966 { "open-with", eog_window_action_open_with },
3967 { "open-folder", eog_window_action_open_containing_folder },
3968 { "save", eog_window_action_save },
3969 { "save-as", eog_window_action_save_as },
3970 { "close", eog_window_action_close_window },
3971 { "close-all", eog_window_action_close_all_windows },
3972 { "print", eog_window_action_print },
3973 { "properties", eog_window_action_properties },
3974 { "set-wallpaper", eog_window_action_wallpaper },
3975 { "preferences", eog_window_action_preferences },
3976 { "manual", eog_window_action_help },
3977 { "about", eog_window_action_about },
3978
3979 /* Stateless actions on the image. */
3980 { "go-previous", eog_window_action_go_prev },
3981 { "go-next", eog_window_action_go_next },
3982 { "go-first", eog_window_action_go_first },
3983 { "go-last", eog_window_action_go_last },
3984 { "go-random", eog_window_action_go_random },
3985 { "rotate-90", eog_window_action_rotate_90 },
3986 { "rotate-270", eog_window_action_rotate_270 },
3987 { "flip-horizontal", eog_window_action_flip_horizontal },
3988 { "flip-vertical", eog_window_action_flip_vertical },
3989 { "move-trash", eog_window_action_move_to_trash },
3990 { "delete", eog_window_action_delete },
3991 { "copy", eog_window_action_copy_image },
3992 { "undo", eog_window_action_undo },
3993 { "zoom-in", eog_window_action_zoom_in },
3994 { "zoom-out", eog_window_action_zoom_out },
3995 { "zoom-normal", eog_window_action_zoom_normal },
3996 { "zoom-set", eog_window_action_set_zoom, "d" },
3997
3998 /* Stateful actions. */
3999 { "current-image", NULL, NULL, "@(ii) (0, 0)", readonly_state_handler },
4000 { "view-statusbar", NULL, NULL, "true", eog_window_action_show_hide_bar },
4001 { "view-gallery", NULL, NULL, "true", eog_window_action_show_hide_bar },
4002 { "view-sidebar", NULL, NULL, "true", eog_window_action_show_hide_bar },
4003 { "view-slideshow", NULL, NULL, "false", eog_window_action_toggle_slideshow },
4004 { "view-fullscreen", NULL, NULL, "false", eog_window_action_toggle_fullscreen },
4005 { "pause-slideshow", NULL, NULL, "false", eog_window_action_pause_slideshow },
4006 { "toggle-zoom-fit", NULL, NULL, "true", eog_window_action_toggle_zoom_fit },
4007 };
4008
4009 static void
eog_window_ui_settings_changed_cb(GSettings * settings,gchar * key,gpointer user_data)4010 eog_window_ui_settings_changed_cb (GSettings *settings,
4011 gchar *key,
4012 gpointer user_data)
4013 {
4014 GVariant *new_state = NULL;
4015 GVariant *old_state;
4016 GAction *action;
4017
4018 g_return_if_fail (G_IS_ACTION (user_data));
4019
4020 action = G_ACTION (user_data);
4021
4022 new_state = g_settings_get_value (settings, key);
4023 g_assert (new_state != NULL);
4024
4025 old_state = g_action_get_state (action);
4026
4027 if (g_variant_get_boolean (new_state) != g_variant_get_boolean (old_state))
4028 g_action_change_state (action, new_state);
4029
4030 g_variant_unref (new_state);
4031 }
4032
4033 static void
eog_window_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)4034 eog_window_drag_data_received (GtkWidget *widget,
4035 GdkDragContext *context,
4036 gint x, gint y,
4037 GtkSelectionData *selection_data,
4038 guint info, guint time)
4039 {
4040 GSList *file_list;
4041 EogWindow *window;
4042 GdkAtom target;
4043 GtkWidget *src;
4044
4045 target = gtk_selection_data_get_target (selection_data);
4046
4047 if (!gtk_targets_include_uri (&target, 1))
4048 return;
4049
4050 /* if the request is from another process this will return NULL */
4051 src = gtk_drag_get_source_widget (context);
4052
4053 /* if the drag request originates from the current eog instance, ignore
4054 the request if the source window is the same as the dest window */
4055 if (src &&
4056 gtk_widget_get_toplevel (src) == gtk_widget_get_toplevel (widget))
4057 {
4058 gdk_drag_status (context, 0, time);
4059 return;
4060 }
4061
4062 if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY)
4063 {
4064 window = EOG_WINDOW (widget);
4065
4066 file_list = eog_util_parse_uri_string_list_to_file_list ((const gchar *) gtk_selection_data_get_data (selection_data));
4067
4068 eog_window_open_file_list (window, file_list);
4069 }
4070 }
4071
4072 static void
eog_window_set_drag_dest(EogWindow * window)4073 eog_window_set_drag_dest (EogWindow *window)
4074 {
4075 gtk_drag_dest_set (GTK_WIDGET (window),
4076 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4077 NULL, 0,
4078 GDK_ACTION_COPY | GDK_ACTION_ASK);
4079 gtk_drag_dest_add_uri_targets (GTK_WIDGET (window));
4080 }
4081
4082 static void
eog_window_sidebar_visibility_changed(GtkWidget * widget,EogWindow * window)4083 eog_window_sidebar_visibility_changed (GtkWidget *widget, EogWindow *window)
4084 {
4085 GAction *action;
4086 GVariant *state;
4087 gboolean visible;
4088 gboolean active;
4089
4090 visible = gtk_widget_get_visible (window->priv->sidebar);
4091
4092 action = g_action_map_lookup_action (G_ACTION_MAP (window),
4093 "view-sidebar");
4094
4095 state = g_action_get_state (action);
4096 active = g_variant_get_boolean (state);
4097 if (active != visible)
4098 g_action_change_state (action,
4099 g_variant_new_boolean (visible));
4100 g_variant_unref (state);
4101
4102 /* Focus the image */
4103 if (!visible && window->priv->image != NULL)
4104 gtk_widget_grab_focus (window->priv->view);
4105 }
4106
4107 static void
eog_window_sidebar_page_added(EogSidebar * sidebar,GtkWidget * main_widget,EogWindow * window)4108 eog_window_sidebar_page_added (EogSidebar *sidebar,
4109 GtkWidget *main_widget,
4110 EogWindow *window)
4111 {
4112 if (eog_sidebar_get_n_pages (sidebar) == 1) {
4113 GAction *action;
4114 GVariant *state;
4115 gboolean show;
4116
4117 action = g_action_map_lookup_action (G_ACTION_MAP (window),
4118 "view-sidebar");
4119
4120 g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
4121
4122 state = g_action_get_state (action);
4123 show = g_variant_get_boolean (state);
4124
4125 if (show)
4126 gtk_widget_show (GTK_WIDGET (sidebar));
4127
4128 g_variant_unref (state);
4129 }
4130 }
4131
4132 static void
eog_window_sidebar_page_removed(EogSidebar * sidebar,GtkWidget * main_widget,EogWindow * window)4133 eog_window_sidebar_page_removed (EogSidebar *sidebar,
4134 GtkWidget *main_widget,
4135 EogWindow *window)
4136 {
4137 if (eog_sidebar_is_empty (sidebar)) {
4138 GAction *action;
4139
4140 gtk_widget_hide (GTK_WIDGET (sidebar));
4141
4142 action = g_action_map_lookup_action (G_ACTION_MAP (window),
4143 "view-sidebar");
4144
4145 g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
4146 }
4147 }
4148
4149 static void
eog_window_finish_saving(EogWindow * window)4150 eog_window_finish_saving (EogWindow *window)
4151 {
4152 EogWindowPrivate *priv = window->priv;
4153
4154 gtk_widget_set_sensitive (GTK_WIDGET (window), FALSE);
4155
4156 do {
4157 gtk_main_iteration ();
4158 } while (priv->save_job != NULL);
4159 }
4160
4161 static void
eog_window_view_rotation_changed_cb(EogScrollView * view,gdouble degrees,EogWindow * window)4162 eog_window_view_rotation_changed_cb (EogScrollView *view,
4163 gdouble degrees,
4164 EogWindow *window)
4165 {
4166 apply_transformation (window, eog_transform_rotate_new (degrees));
4167 }
4168
4169 static void
eog_window_view_next_image_cb(EogScrollView * view,EogWindow * window)4170 eog_window_view_next_image_cb (EogScrollView *view,
4171 EogWindow *window)
4172 {
4173 eog_window_action_go_next (NULL, NULL, window);
4174 }
4175
4176 static void
eog_window_view_previous_image_cb(EogScrollView * view,EogWindow * window)4177 eog_window_view_previous_image_cb (EogScrollView *view,
4178 EogWindow *window)
4179 {
4180 eog_window_action_go_prev (NULL, NULL, window);
4181 }
4182
4183 static void
eog_window_construct_ui(EogWindow * window)4184 eog_window_construct_ui (EogWindow *window)
4185 {
4186 EogWindowPrivate *priv;
4187
4188 GtkBuilder *builder;
4189 GAction *action = NULL;
4190 GObject *builder_object;
4191
4192 GtkWidget *popup_menu;
4193 GtkWidget *hpaned;
4194 GtkWidget *headerbar;
4195 GtkWidget *zoom_entry;
4196 GtkWidget *menu_button;
4197 GtkWidget *menu_image;
4198 GtkWidget *fullscreen_button;
4199
4200 g_return_if_fail (EOG_IS_WINDOW (window));
4201
4202 priv = window->priv;
4203
4204 priv->box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
4205 gtk_container_add (GTK_CONTAINER (window), priv->box);
4206 gtk_widget_show (priv->box);
4207
4208 headerbar = gtk_header_bar_new ();
4209 gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (headerbar), TRUE);
4210 gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar),
4211 g_get_application_name ());
4212 gtk_window_set_titlebar (GTK_WINDOW (window), headerbar);
4213 gtk_widget_show (headerbar);
4214
4215 #if 0
4216 zoom_button = gtk_toggle_button_new ();
4217 zoom_image = gtk_image_new_from_icon_name ("zoom-in-symbolic",
4218 GTK_ICON_SIZE_BUTTON);
4219 gtk_button_set_image (GTK_BUTTON (zoom_button), zoom_image);
4220 gtk_widget_set_tooltip_text (zoom_button,
4221 _("Shrink or enlarge the current image"));
4222 g_signal_connect (zoom_button, "toggled",
4223 G_CALLBACK (eog_window_zoom_button_toggled_cb),
4224 window);
4225 gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar), zoom_button);
4226 /* disable zoom button if no image is loaded */
4227 g_object_bind_property (g_action_map_lookup_action (G_ACTION_MAP(window),
4228 "zoom-normal"),
4229 "enabled", zoom_button, "sensitive",
4230 G_BINDING_DEFAULT);
4231 gtk_widget_show (zoom_button);
4232
4233 priv->zoom_revealer = gtk_revealer_new ();
4234 gtk_revealer_set_transition_type (GTK_REVEALER (priv->zoom_revealer),
4235 GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT);
4236 gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar),
4237 priv->zoom_revealer);
4238 gtk_widget_show (priv->zoom_revealer);
4239
4240 priv->zoom_scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL,
4241 0.3, 3.0, 0.1);
4242 gtk_scale_set_draw_value (GTK_SCALE (priv->zoom_scale), FALSE);
4243 /* Add a stop mark at 100% */
4244 gtk_scale_add_mark (GTK_SCALE (priv->zoom_scale),
4245 1.0, GTK_POS_BOTTOM, NULL);
4246 /* TODO: the scale by itself does not take any width, so we manually
4247 * set it here. Decide on the optimal value. */
4248 gtk_widget_set_size_request (priv->zoom_scale, 200, -1);
4249 g_signal_connect (priv->zoom_scale, "value-changed",
4250 G_CALLBACK (eog_window_zoom_scale_value_changed_cb),
4251 window);
4252 gtk_container_add (GTK_CONTAINER (priv->zoom_revealer),
4253 priv->zoom_scale);
4254 gtk_widget_show (priv->zoom_scale);
4255 #endif
4256
4257 menu_button = gtk_menu_button_new ();
4258 menu_image = gtk_image_new_from_icon_name ("open-menu-symbolic",
4259 GTK_ICON_SIZE_BUTTON);
4260 gtk_button_set_image (GTK_BUTTON (menu_button), menu_image);
4261
4262 builder = gtk_builder_new_from_resource ("/org/gnome/eog/ui/eog-gear-menu.ui");
4263 builder_object = gtk_builder_get_object (builder, "gear-menu");
4264 gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (menu_button),
4265 G_MENU_MODEL (builder_object));
4266
4267 gtk_header_bar_pack_end (GTK_HEADER_BAR (headerbar), menu_button);
4268 gtk_widget_show (menu_button);
4269
4270 action = G_ACTION (g_property_action_new ("toggle-gear-menu",
4271 menu_button, "active"));
4272 g_action_map_add_action (G_ACTION_MAP (window), action);
4273 g_object_unref (action);
4274
4275 fullscreen_button = gtk_button_new_from_icon_name ("view-fullscreen-symbolic",
4276 GTK_ICON_SIZE_BUTTON);
4277 gtk_actionable_set_action_name (GTK_ACTIONABLE (fullscreen_button),
4278 "win.view-fullscreen");
4279 gtk_widget_set_tooltip_text(fullscreen_button,
4280 _("Show the current image in fullscreen mode"));
4281 gtk_header_bar_pack_end (GTK_HEADER_BAR (headerbar), fullscreen_button);
4282 gtk_widget_show (fullscreen_button);
4283
4284 priv->gear_menu_builder = builder;
4285 builder = NULL;
4286
4287 priv->cbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
4288 gtk_box_pack_start (GTK_BOX (priv->box), priv->cbox, TRUE, TRUE, 0);
4289 gtk_widget_show (priv->cbox);
4290
4291 priv->statusbar = eog_statusbar_new ();
4292 gtk_box_pack_end (GTK_BOX (priv->box),
4293 GTK_WIDGET (priv->statusbar),
4294 FALSE, FALSE, 0);
4295 gtk_widget_show (priv->statusbar);
4296
4297 priv->image_info_message_cid =
4298 gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar),
4299 "image_info_message");
4300 priv->tip_message_cid =
4301 gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar),
4302 "tip_message");
4303
4304 hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
4305
4306 priv->sidebar = eog_sidebar_new ();
4307 /* The sidebar shouldn't be shown automatically on show_all(),
4308 but only when the user actually wants it. */
4309 gtk_widget_set_no_show_all (priv->sidebar, TRUE);
4310
4311 gtk_widget_set_size_request (priv->sidebar, 210, -1);
4312
4313 g_signal_connect_after (priv->sidebar,
4314 "show",
4315 G_CALLBACK (eog_window_sidebar_visibility_changed),
4316 window);
4317
4318 g_signal_connect_after (priv->sidebar,
4319 "hide",
4320 G_CALLBACK (eog_window_sidebar_visibility_changed),
4321 window);
4322
4323 g_signal_connect_after (priv->sidebar,
4324 "page-added",
4325 G_CALLBACK (eog_window_sidebar_page_added),
4326 window);
4327
4328 g_signal_connect_after (priv->sidebar,
4329 "page-removed",
4330 G_CALLBACK (eog_window_sidebar_page_removed),
4331 window);
4332 priv->overlay = gtk_overlay_new();
4333
4334 priv->view = eog_scroll_view_new ();
4335 g_signal_connect (priv->view,
4336 "rotation-changed",
4337 G_CALLBACK (eog_window_view_rotation_changed_cb),
4338 window);
4339 g_signal_connect (priv->view,
4340 "next-image",
4341 G_CALLBACK (eog_window_view_next_image_cb),
4342 window);
4343 g_signal_connect (priv->view,
4344 "previous-image",
4345 G_CALLBACK (eog_window_view_previous_image_cb),
4346 window);
4347
4348 gtk_container_add (GTK_CONTAINER(priv->overlay), priv->view);
4349
4350 eog_sidebar_add_page (EOG_SIDEBAR (priv->sidebar),
4351 _("Properties"),
4352 GTK_WIDGET (eog_metadata_sidebar_new (window)));
4353
4354 gtk_widget_set_size_request (GTK_WIDGET (priv->view), 100, 100);
4355 g_signal_connect (G_OBJECT (priv->view),
4356 "zoom_changed",
4357 G_CALLBACK (view_zoom_changed_cb),
4358 window);
4359 action = g_action_map_lookup_action (G_ACTION_MAP (window),
4360 "toggle-zoom-fit");
4361 if (action != NULL) {
4362 /* Binding will be destroyed when the objects finalize */
4363 g_object_bind_property_full (priv->view, "zoom-mode",
4364 action, "state",
4365 G_BINDING_SYNC_CREATE,
4366 _eog_zoom_shrink_to_boolean,
4367 NULL, NULL, NULL);
4368 }
4369 g_settings_bind (priv->view_settings, EOG_CONF_VIEW_SCROLL_WHEEL_ZOOM,
4370 priv->view, "scrollwheel-zoom", G_SETTINGS_BIND_GET);
4371 g_settings_bind (priv->view_settings, EOG_CONF_VIEW_ZOOM_MULTIPLIER,
4372 priv->view, "zoom-multiplier", G_SETTINGS_BIND_GET);
4373
4374 builder = gtk_builder_new_from_resource ("/org/gnome/eog/ui/popup-menus.ui");
4375 builder_object = gtk_builder_get_object (builder, "view-popup-menu");
4376
4377 popup_menu = gtk_menu_new_from_model (G_MENU_MODEL (builder_object));
4378
4379 eog_scroll_view_set_popup (EOG_SCROLL_VIEW (priv->view),
4380 GTK_MENU (popup_menu));
4381
4382 g_object_unref (popup_menu);
4383
4384 gtk_paned_pack1 (GTK_PANED (hpaned),
4385 priv->overlay,
4386 TRUE,
4387 FALSE);
4388 gtk_paned_pack2 (GTK_PANED (hpaned),
4389 priv->sidebar,
4390 FALSE,
4391 FALSE);
4392
4393 gtk_widget_show_all (hpaned);
4394
4395 zoom_entry = eog_zoom_entry_new (EOG_SCROLL_VIEW (priv->view),
4396 G_MENU (gtk_builder_get_object (builder,
4397 "zoom-menu")));
4398 gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar), zoom_entry);
4399
4400 priv->thumbview = g_object_ref (eog_thumb_view_new ());
4401
4402 /* giving shape to the view */
4403 gtk_icon_view_set_margin (GTK_ICON_VIEW (priv->thumbview), 4);
4404 gtk_icon_view_set_row_spacing (GTK_ICON_VIEW (priv->thumbview), 0);
4405
4406 g_signal_connect (G_OBJECT (priv->thumbview), "selection_changed",
4407 G_CALLBACK (handle_image_selection_changed_cb), window);
4408
4409 priv->nav = eog_thumb_nav_new (priv->thumbview,
4410 EOG_THUMB_NAV_MODE_ONE_ROW,
4411 g_settings_get_boolean (priv->ui_settings,
4412 EOG_CONF_UI_SCROLL_BUTTONS));
4413
4414 // Bind the scroll buttons to their GSettings key
4415 g_settings_bind (priv->ui_settings, EOG_CONF_UI_SCROLL_BUTTONS,
4416 priv->nav, "show-buttons", G_SETTINGS_BIND_GET);
4417
4418 // Re-use the scroll view's popup menu model for the thumb view
4419 popup_menu = gtk_menu_new_from_model (G_MENU_MODEL(builder_object));
4420 eog_thumb_view_set_thumbnail_popup (EOG_THUMB_VIEW (priv->thumbview),
4421 GTK_MENU (popup_menu));
4422
4423 g_object_unref (popup_menu);
4424 g_clear_object (&builder);
4425
4426 // Setup priv->layout
4427 eog_window_set_gallery_mode (window, priv->gallery_position, priv->gallery_resizable);
4428
4429 g_settings_bind (priv->ui_settings, EOG_CONF_UI_IMAGE_GALLERY_POSITION,
4430 window, "gallery-position", G_SETTINGS_BIND_GET);
4431 g_settings_bind (priv->ui_settings, EOG_CONF_UI_IMAGE_GALLERY_RESIZABLE,
4432 window, "gallery-resizable", G_SETTINGS_BIND_GET);
4433
4434 g_signal_connect (priv->lockdown_settings,
4435 "changed::" EOG_CONF_DESKTOP_CAN_SAVE,
4436 G_CALLBACK (eog_window_can_save_changed_cb), window);
4437 // Call callback once to have the value set
4438 eog_window_can_save_changed_cb (priv->lockdown_settings,
4439 EOG_CONF_DESKTOP_CAN_SAVE, window);
4440
4441 update_action_groups_state (window);
4442
4443 if ((priv->flags & EOG_STARTUP_FULLSCREEN) ||
4444 (priv->flags & EOG_STARTUP_SLIDE_SHOW)) {
4445 eog_window_run_fullscreen (window, (priv->flags & EOG_STARTUP_SLIDE_SHOW));
4446 } else {
4447 priv->mode = EOG_WINDOW_MODE_NORMAL;
4448 update_ui_visibility (window);
4449 }
4450
4451 eog_window_set_drag_dest (window);
4452 }
4453
4454 static void
eog_window_init(EogWindow * window)4455 eog_window_init (EogWindow *window)
4456 {
4457 GdkGeometry hints;
4458 EogWindowPrivate *priv;
4459 GAction* action;
4460
4461 eog_debug (DEBUG_WINDOW);
4462
4463 hints.min_width = EOG_WINDOW_MIN_WIDTH;
4464 hints.min_height = EOG_WINDOW_MIN_HEIGHT;
4465
4466 priv = window->priv = eog_window_get_instance_private (window);
4467
4468 priv->fullscreen_settings = g_settings_new (EOG_CONF_FULLSCREEN);
4469 priv->ui_settings = g_settings_new (EOG_CONF_UI);
4470 priv->view_settings = g_settings_new (EOG_CONF_VIEW);
4471 priv->lockdown_settings = g_settings_new (EOG_CONF_DESKTOP_LOCKDOWN_SCHEMA);
4472
4473 window->priv->file_list = NULL;
4474 window->priv->store = NULL;
4475 window->priv->image = NULL;
4476
4477 window->priv->fullscreen_popup = NULL;
4478 window->priv->fullscreen_timeout_source = NULL;
4479 window->priv->slideshow_loop = FALSE;
4480 window->priv->slideshow_switch_timeout = 0;
4481 window->priv->slideshow_switch_source = NULL;
4482 window->priv->fullscreen_idle_inhibit_cookie = 0;
4483
4484 gtk_window_set_geometry_hints (GTK_WINDOW (window),
4485 GTK_WIDGET (window),
4486 &hints,
4487 GDK_HINT_MIN_SIZE);
4488
4489 gtk_window_set_default_size (GTK_WINDOW (window),
4490 EOG_WINDOW_DEFAULT_WIDTH,
4491 EOG_WINDOW_DEFAULT_HEIGHT);
4492
4493 gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
4494
4495 window->priv->mode = EOG_WINDOW_MODE_UNKNOWN;
4496 window->priv->status = EOG_WINDOW_STATUS_UNKNOWN;
4497
4498 #if defined(HAVE_LCMS) && defined(GDK_WINDOWING_X11)
4499 window->priv->display_profile =
4500 eog_window_get_display_profile (GTK_WIDGET (window));
4501 #endif
4502
4503 window->priv->gallery_position = EOG_WINDOW_GALLERY_POS_BOTTOM;
4504 window->priv->gallery_resizable = FALSE;
4505
4506 window->priv->save_disabled = FALSE;
4507
4508 window->priv->page_setup = NULL;
4509
4510 gtk_window_set_application (GTK_WINDOW (window), GTK_APPLICATION (EOG_APP));
4511
4512 g_action_map_add_action_entries (G_ACTION_MAP (window),
4513 window_actions, G_N_ELEMENTS (window_actions),
4514 window);
4515
4516 /* Creating a binding between the ui settings and the related GActions does
4517 * not trigger the state changed handler since the state is updated directly
4518 * via the "state" property. Requesting a state change via these callbacks,
4519 * however, works. */
4520 g_signal_connect (priv->ui_settings, "changed::"EOG_CONF_UI_IMAGE_GALLERY,
4521 G_CALLBACK (eog_window_ui_settings_changed_cb),
4522 g_action_map_lookup_action (G_ACTION_MAP (window), "view-gallery"));
4523
4524 g_signal_connect (priv->ui_settings, "changed::"EOG_CONF_UI_SIDEBAR,
4525 G_CALLBACK (eog_window_ui_settings_changed_cb),
4526 g_action_map_lookup_action (G_ACTION_MAP (window), "view-sidebar"));
4527
4528 g_signal_connect (priv->ui_settings, "changed::"EOG_CONF_UI_STATUSBAR,
4529 G_CALLBACK (eog_window_ui_settings_changed_cb),
4530 g_action_map_lookup_action (G_ACTION_MAP (window), "view-statusbar"));
4531
4532 action = g_action_map_lookup_action (G_ACTION_MAP (window),
4533 "current-image");
4534 if (G_LIKELY (action != NULL))
4535 g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
4536
4537 if (g_strcmp0 (PROFILE, "") != 0) {
4538 GtkStyleContext *style_context;
4539
4540 style_context = gtk_widget_get_style_context (GTK_WIDGET (window));
4541 gtk_style_context_add_class (style_context, "devel");
4542 }
4543 }
4544
4545 static void
eog_window_dispose(GObject * object)4546 eog_window_dispose (GObject *object)
4547 {
4548 EogWindow *window;
4549 EogWindowPrivate *priv;
4550
4551 g_return_if_fail (object != NULL);
4552 g_return_if_fail (EOG_IS_WINDOW (object));
4553
4554 eog_debug (DEBUG_WINDOW);
4555
4556 window = EOG_WINDOW (object);
4557 priv = window->priv;
4558
4559 peas_engine_garbage_collect (PEAS_ENGINE (EOG_APP->priv->plugin_engine));
4560
4561 if (priv->extensions != NULL) {
4562 g_object_unref (priv->extensions);
4563 priv->extensions = NULL;
4564 peas_engine_garbage_collect (PEAS_ENGINE (EOG_APP->priv->plugin_engine));
4565 }
4566
4567 if (priv->store != NULL) {
4568 g_signal_handlers_disconnect_by_func (priv->store,
4569 eog_window_list_store_image_added,
4570 window);
4571 g_signal_handlers_disconnect_by_func (priv->store,
4572 eog_window_list_store_image_removed,
4573 window);
4574 g_object_unref (priv->store);
4575 priv->store = NULL;
4576 }
4577
4578 if (priv->image != NULL) {
4579 g_signal_handlers_disconnect_by_func (priv->image,
4580 image_thumb_changed_cb,
4581 window);
4582 g_signal_handlers_disconnect_by_func (priv->image,
4583 image_file_changed_cb,
4584 window);
4585 g_object_unref (priv->image);
4586 priv->image = NULL;
4587 }
4588
4589 fullscreen_clear_timeout (window);
4590
4591 if (window->priv->fullscreen_popup != NULL) {
4592 gtk_widget_destroy (priv->fullscreen_popup);
4593 priv->fullscreen_popup = NULL;
4594 }
4595
4596 slideshow_clear_timeout (window);
4597 eog_window_uninhibit_screensaver (window);
4598
4599 eog_window_clear_load_job (window);
4600
4601 eog_window_clear_transform_job (window);
4602
4603 if (priv->view_settings) {
4604 g_object_unref (priv->view_settings);
4605 priv->view_settings = NULL;
4606 }
4607
4608 if (priv->ui_settings) {
4609 g_object_unref (priv->ui_settings);
4610 priv->ui_settings = NULL;
4611 }
4612
4613 if (priv->fullscreen_settings) {
4614 g_object_unref (priv->fullscreen_settings);
4615 priv->fullscreen_settings = NULL;
4616 }
4617
4618 if (priv->lockdown_settings) {
4619 g_object_unref (priv->lockdown_settings);
4620 priv->lockdown_settings = NULL;
4621 }
4622
4623 if (priv->file_list != NULL) {
4624 g_slist_foreach (priv->file_list, (GFunc) g_object_unref, NULL);
4625 g_slist_free (priv->file_list);
4626 priv->file_list = NULL;
4627 }
4628
4629 #ifdef HAVE_LCMS
4630 if (priv->display_profile != NULL) {
4631 cmsCloseProfile (priv->display_profile);
4632 priv->display_profile = NULL;
4633 }
4634 #endif
4635
4636 if (priv->last_save_as_folder != NULL) {
4637 g_object_unref (priv->last_save_as_folder);
4638 priv->last_save_as_folder = NULL;
4639 }
4640
4641 if (priv->page_setup != NULL) {
4642 g_object_unref (priv->page_setup);
4643 priv->page_setup = NULL;
4644 }
4645
4646 if (priv->thumbview)
4647 {
4648 /* Disconnect so we don't get any unwanted callbacks
4649 * when the thumb view is disposed. */
4650 g_signal_handlers_disconnect_by_func (priv->thumbview,
4651 G_CALLBACK (handle_image_selection_changed_cb),
4652 window);
4653 g_clear_object (&priv->thumbview);
4654 }
4655
4656 g_clear_object (&priv->gear_menu_builder);
4657
4658 peas_engine_garbage_collect (PEAS_ENGINE (EOG_APP->priv->plugin_engine));
4659
4660 G_OBJECT_CLASS (eog_window_parent_class)->dispose (object);
4661 }
4662
4663 static gint
eog_window_delete(GtkWidget * widget,GdkEventAny * event)4664 eog_window_delete (GtkWidget *widget, GdkEventAny *event)
4665 {
4666 EogWindow *window;
4667 EogWindowPrivate *priv;
4668
4669 g_return_val_if_fail (EOG_IS_WINDOW (widget), FALSE);
4670
4671 window = EOG_WINDOW (widget);
4672 priv = window->priv;
4673
4674 if (priv->save_job != NULL) {
4675 eog_window_finish_saving (window);
4676 }
4677
4678 if (eog_window_unsaved_images_confirm (window)) {
4679 return TRUE;
4680 }
4681
4682 gtk_widget_destroy (widget);
4683
4684 return TRUE;
4685 }
4686
4687 /*
4688 * Imported from gedit-window.c:
4689 * GtkWindow catches keybindings for the menu items _before_ passing them to
4690 * the focused widget. This is unfortunate and means that trying to use
4691 * Return to activate a menu entry will instead skip to the next image.
4692 * Here we override GtkWindow's handler to do the same things that it
4693 * does, but in the opposite order and then we chain up to the grand
4694 * parent handler, skipping gtk_window_key_press_event.
4695 */
4696 static gint
eog_window_key_press(GtkWidget * widget,GdkEventKey * event)4697 eog_window_key_press (GtkWidget *widget, GdkEventKey *event)
4698 {
4699 static gpointer grand_parent_class = NULL;
4700
4701 gint result = FALSE;
4702 gboolean handle_selection = FALSE;
4703 GdkModifierType modifiers;
4704
4705 if (grand_parent_class == NULL)
4706 {
4707 grand_parent_class = g_type_class_peek_parent (eog_window_parent_class);
4708 }
4709
4710 /* handle focus widget key events */
4711 if (!handle_selection) {
4712 handle_selection = gtk_window_propagate_key_event (GTK_WINDOW (widget), event);
4713 }
4714
4715 /* handle mnemonics and accelerators */
4716 if (!handle_selection) {
4717 handle_selection = gtk_window_activate_key (GTK_WINDOW (widget), event);
4718 }
4719
4720 /* This part is disabled for now as it overrides the arrow key handlers
4721 * below which are still needed in RTL scenarios */
4722 #if 0
4723 /* Chain up, invokes binding set on window */
4724 if (!handle_selection) {
4725 handle_selection = GTK_WIDGET_CLASS (grand_parent_class)->key_press_event (widget, event);
4726 }
4727 #endif
4728
4729 /* If the workaround already handled the key event return early */
4730 if(handle_selection)
4731 return TRUE;
4732
4733 modifiers = gtk_accelerator_get_default_mod_mask ();
4734
4735 switch (event->keyval) {
4736 #if 0
4737 case GDK_KEY_space:
4738 if ((event->state & modifiers) == GDK_CONTROL_MASK) {
4739 handle_selection = TRUE;
4740 break;
4741 }
4742 case GDK_KEY_Return:
4743 /* Image properties dialog case */
4744 if ((event->state & modifiers) == GDK_MOD1_MASK) {
4745 result = FALSE;
4746 break;
4747 }
4748
4749 if ((event->state & modifiers) == GDK_SHIFT_MASK) {
4750 eog_window_action_go_prev (NULL, NULL, EOG_WINDOW (widget));
4751 } else {
4752 eog_window_action_go_next (NULL, NULL, EOG_WINDOW (widget));
4753 }
4754 result = TRUE;
4755 break;
4756 #endif
4757 case GDK_KEY_Escape:
4758 if (EOG_WINDOW (widget)->priv->mode == EOG_WINDOW_MODE_FULLSCREEN) {
4759 eog_window_stop_fullscreen (EOG_WINDOW (widget), FALSE);
4760 } else if (EOG_WINDOW (widget)->priv->mode == EOG_WINDOW_MODE_SLIDESHOW) {
4761 eog_window_stop_fullscreen (EOG_WINDOW (widget), TRUE);
4762 } else {
4763 eog_window_action_close_window (NULL, NULL, EOG_WINDOW (widget));
4764 return TRUE;
4765 }
4766 break;
4767 case GDK_KEY_Left:
4768 /* case GDK_KEY_Up: */
4769 if ((event->state & modifiers) == 0) {
4770 /* Left and Up move to previous image */
4771 if (is_rtl) { /* move to next in RTL mode */
4772 eog_window_action_go_next (NULL, NULL, EOG_WINDOW (widget));
4773 } else {
4774 eog_window_action_go_prev (NULL, NULL, EOG_WINDOW (widget));
4775 }
4776 result = TRUE;
4777 }
4778 break;
4779 case GDK_KEY_Right:
4780 /* case GDK_KEY_Down: */
4781 if ((event->state & modifiers) == 0) {
4782 /* Right and Down move to next image */
4783 if (is_rtl) { /* move to previous in RTL mode */
4784 eog_window_action_go_prev (NULL, NULL, EOG_WINDOW (widget));
4785 } else {
4786 eog_window_action_go_next (NULL, NULL, EOG_WINDOW (widget));
4787 }
4788 result = TRUE;
4789 }
4790 break;
4791 case GDK_KEY_Page_Up:
4792 if ((event->state & modifiers) == 0) {
4793 if (!eog_scroll_view_scrollbars_visible (EOG_SCROLL_VIEW (EOG_WINDOW (widget)->priv->view))) {
4794 if (!gtk_widget_get_visible (EOG_WINDOW (widget)->priv->nav)) {
4795 /* If the iconview is not visible skip to the
4796 * previous image manually as it won't handle
4797 * the keypress then. */
4798 eog_window_action_go_prev (NULL, NULL, EOG_WINDOW (widget));
4799 result = TRUE;
4800 } else
4801 handle_selection = TRUE;
4802 }
4803 }
4804 break;
4805 case GDK_KEY_Page_Down:
4806 if ((event->state & modifiers) == 0) {
4807 if (!eog_scroll_view_scrollbars_visible (EOG_SCROLL_VIEW (EOG_WINDOW (widget)->priv->view))) {
4808 if (!gtk_widget_get_visible (EOG_WINDOW (widget)->priv->nav)) {
4809 /* If the iconview is not visible skip to the
4810 * next image manually as it won't handle
4811 * the keypress then. */
4812 eog_window_action_go_next (NULL, NULL, EOG_WINDOW (widget));
4813 result = TRUE;
4814 } else
4815 handle_selection = TRUE;
4816 }
4817 }
4818 break;
4819 }
4820
4821 /* Update slideshow timeout */
4822 if (result && (EOG_WINDOW (widget)->priv->mode == EOG_WINDOW_MODE_SLIDESHOW)) {
4823 slideshow_set_timeout (EOG_WINDOW (widget));
4824 }
4825
4826 if (handle_selection == TRUE && result == FALSE) {
4827 gtk_widget_grab_focus (GTK_WIDGET (EOG_WINDOW (widget)->priv->thumbview));
4828
4829 result = gtk_widget_event (GTK_WIDGET (EOG_WINDOW (widget)->priv->thumbview),
4830 (GdkEvent *) event);
4831 }
4832
4833 /* If we still haven't handled the event, give the scrollview a chance to do it. */
4834 if (result == FALSE &&
4835 gtk_widget_get_realized (GTK_WIDGET (EOG_WINDOW (widget)->priv->view))) {
4836 result = gtk_widget_event (GTK_WIDGET (EOG_WINDOW (widget)->priv->view),
4837 (GdkEvent *) event);
4838 }
4839
4840 if (result == FALSE && GTK_WIDGET_CLASS (eog_window_parent_class)->key_press_event) {
4841 result = (* GTK_WIDGET_CLASS (eog_window_parent_class)->key_press_event) (widget, event);
4842 }
4843
4844 return result;
4845 }
4846
4847 static gint
eog_window_button_press(GtkWidget * widget,GdkEventButton * event)4848 eog_window_button_press (GtkWidget *widget, GdkEventButton *event)
4849 {
4850 EogWindow *window = EOG_WINDOW (widget);
4851 gint result = FALSE;
4852
4853 /* We currently can't tell whether the old button codes (6, 7) are
4854 * still in use. So we keep them in addition to the new ones (8, 9)
4855 */
4856 if (event->type == GDK_BUTTON_PRESS) {
4857 switch (event->button) {
4858 case 6:
4859 case 8:
4860 eog_thumb_view_select_single (EOG_THUMB_VIEW (window->priv->thumbview),
4861 EOG_THUMB_VIEW_SELECT_LEFT);
4862 result = TRUE;
4863 break;
4864 case 7:
4865 case 9:
4866 eog_thumb_view_select_single (EOG_THUMB_VIEW (window->priv->thumbview),
4867 EOG_THUMB_VIEW_SELECT_RIGHT);
4868 result = TRUE;
4869 break;
4870 }
4871 }
4872
4873 if (result == FALSE && GTK_WIDGET_CLASS (eog_window_parent_class)->button_press_event) {
4874 result = (* GTK_WIDGET_CLASS (eog_window_parent_class)->button_press_event) (widget, event);
4875 }
4876
4877 return result;
4878 }
4879
4880 static gboolean
eog_window_focus_out_event(GtkWidget * widget,GdkEventFocus * event)4881 eog_window_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
4882 {
4883 EogWindow *window = EOG_WINDOW (widget);
4884 EogWindowPrivate *priv = window->priv;
4885 gboolean fullscreen;
4886
4887 eog_debug (DEBUG_WINDOW);
4888
4889 fullscreen = priv->mode == EOG_WINDOW_MODE_FULLSCREEN ||
4890 priv->mode == EOG_WINDOW_MODE_SLIDESHOW;
4891
4892 if (fullscreen) {
4893 gtk_widget_hide (priv->fullscreen_popup);
4894 }
4895
4896 return GTK_WIDGET_CLASS (eog_window_parent_class)->focus_out_event (widget, event);
4897 }
4898
4899 static void
eog_window_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)4900 eog_window_set_property (GObject *object,
4901 guint property_id,
4902 const GValue *value,
4903 GParamSpec *pspec)
4904 {
4905 EogWindow *window;
4906 EogWindowPrivate *priv;
4907
4908 g_return_if_fail (EOG_IS_WINDOW (object));
4909
4910 window = EOG_WINDOW (object);
4911 priv = window->priv;
4912
4913 switch (property_id) {
4914 case PROP_GALLERY_POS:
4915 eog_window_set_gallery_mode (window, g_value_get_enum (value),
4916 priv->gallery_resizable);
4917 break;
4918 case PROP_GALLERY_RESIZABLE:
4919 eog_window_set_gallery_mode (window, priv->gallery_position,
4920 g_value_get_boolean (value));
4921 break;
4922 case PROP_STARTUP_FLAGS:
4923 priv->flags = g_value_get_flags (value);
4924 break;
4925
4926 default:
4927 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
4928 }
4929 }
4930
4931 static void
eog_window_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)4932 eog_window_get_property (GObject *object,
4933 guint property_id,
4934 GValue *value,
4935 GParamSpec *pspec)
4936 {
4937 EogWindow *window;
4938 EogWindowPrivate *priv;
4939
4940 g_return_if_fail (EOG_IS_WINDOW (object));
4941
4942 window = EOG_WINDOW (object);
4943 priv = window->priv;
4944
4945 switch (property_id) {
4946 case PROP_GALLERY_POS:
4947 g_value_set_enum (value, priv->gallery_position);
4948 break;
4949 case PROP_GALLERY_RESIZABLE:
4950 g_value_set_boolean (value, priv->gallery_resizable);
4951 break;
4952 case PROP_STARTUP_FLAGS:
4953 g_value_set_flags (value, priv->flags);
4954 break;
4955
4956 default:
4957 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
4958 }
4959 }
4960
4961 static void
on_extension_added(PeasExtensionSet * set,PeasPluginInfo * info,PeasExtension * exten,GtkWindow * window)4962 on_extension_added (PeasExtensionSet *set,
4963 PeasPluginInfo *info,
4964 PeasExtension *exten,
4965 GtkWindow *window)
4966 {
4967 peas_extension_call (exten, "activate", window);
4968 }
4969
4970 static void
on_extension_removed(PeasExtensionSet * set,PeasPluginInfo * info,PeasExtension * exten,GtkWindow * window)4971 on_extension_removed (PeasExtensionSet *set,
4972 PeasPluginInfo *info,
4973 PeasExtension *exten,
4974 GtkWindow *window)
4975 {
4976 peas_extension_call (exten, "deactivate", window);
4977 }
4978
4979 static GObject *
eog_window_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_params)4980 eog_window_constructor (GType type,
4981 guint n_construct_properties,
4982 GObjectConstructParam *construct_params)
4983 {
4984 GObject *object;
4985 EogWindowPrivate *priv;
4986
4987 object = G_OBJECT_CLASS (eog_window_parent_class)->constructor
4988 (type, n_construct_properties, construct_params);
4989
4990 priv = EOG_WINDOW (object)->priv;
4991
4992 eog_window_construct_ui (EOG_WINDOW (object));
4993
4994 priv->extensions = peas_extension_set_new (PEAS_ENGINE (EOG_APP->priv->plugin_engine),
4995 EOG_TYPE_WINDOW_ACTIVATABLE,
4996 "window",
4997 EOG_WINDOW (object), NULL);
4998 peas_extension_set_call (priv->extensions, "activate");
4999 g_signal_connect (priv->extensions, "extension-added",
5000 G_CALLBACK (on_extension_added), object);
5001 g_signal_connect (priv->extensions, "extension-removed",
5002 G_CALLBACK (on_extension_removed), object);
5003
5004 return object;
5005 }
5006
5007 static void
eog_window_class_init(EogWindowClass * class)5008 eog_window_class_init (EogWindowClass *class)
5009 {
5010 GObjectClass *g_object_class = (GObjectClass *) class;
5011 GtkWidgetClass *widget_class = (GtkWidgetClass *) class;
5012
5013 g_object_class->constructor = eog_window_constructor;
5014 g_object_class->dispose = eog_window_dispose;
5015 g_object_class->set_property = eog_window_set_property;
5016 g_object_class->get_property = eog_window_get_property;
5017
5018 widget_class->delete_event = eog_window_delete;
5019 widget_class->key_press_event = eog_window_key_press;
5020 widget_class->button_press_event = eog_window_button_press;
5021 widget_class->drag_data_received = eog_window_drag_data_received;
5022 widget_class->focus_out_event = eog_window_focus_out_event;
5023
5024 /**
5025 * EogWindow:gallery-position:
5026 *
5027 * Determines the position of the image gallery in the window
5028 * relative to the image.
5029 */
5030 g_object_class_install_property (
5031 g_object_class, PROP_GALLERY_POS,
5032 g_param_spec_enum ("gallery-position", NULL, NULL,
5033 EOG_TYPE_WINDOW_GALLERY_POS,
5034 EOG_WINDOW_GALLERY_POS_BOTTOM,
5035 G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
5036
5037 /**
5038 * EogWindow:gallery-resizable:
5039 *
5040 * If %TRUE the gallery will be resizable by the user otherwise it will be
5041 * in single column/row mode.
5042 */
5043 g_object_class_install_property (
5044 g_object_class, PROP_GALLERY_RESIZABLE,
5045 g_param_spec_boolean ("gallery-resizable", NULL, NULL, FALSE,
5046 G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
5047
5048 /**
5049 * EogWindow:startup-flags:
5050 *
5051 * A bitwise OR of #EogStartupFlags elements, indicating how the window
5052 * should behave upon creation.
5053 */
5054 g_object_class_install_property (g_object_class,
5055 PROP_STARTUP_FLAGS,
5056 g_param_spec_flags ("startup-flags",
5057 NULL,
5058 NULL,
5059 EOG_TYPE_STARTUP_FLAGS,
5060 0,
5061 G_PARAM_READWRITE |
5062 G_PARAM_CONSTRUCT_ONLY));
5063
5064 /**
5065 * EogWindow::prepared:
5066 * @window: the object which received the signal.
5067 *
5068 * The #EogWindow::prepared signal is emitted when the @window is ready
5069 * to be shown.
5070 */
5071 signals [SIGNAL_PREPARED] =
5072 g_signal_new ("prepared",
5073 EOG_TYPE_WINDOW,
5074 G_SIGNAL_RUN_LAST,
5075 G_STRUCT_OFFSET (EogWindowClass, prepared),
5076 NULL, NULL,
5077 g_cclosure_marshal_VOID__VOID,
5078 G_TYPE_NONE, 0);
5079 }
5080
5081 /**
5082 * eog_window_new:
5083 * @flags: the initialization parameters for the new window.
5084 *
5085 *
5086 * Creates a new and empty #EogWindow. Use @flags to indicate
5087 * if the window should be initialized fullscreen, in slideshow mode,
5088 * and/or without the thumbnails gallery visible. See #EogStartupFlags.
5089 *
5090 * Returns: a newly created #EogWindow.
5091 **/
5092 GtkWidget*
eog_window_new(EogStartupFlags flags)5093 eog_window_new (EogStartupFlags flags)
5094 {
5095 EogWindow *window;
5096
5097 eog_debug (DEBUG_WINDOW);
5098
5099 window = EOG_WINDOW (g_object_new (EOG_TYPE_WINDOW,
5100 "type", GTK_WINDOW_TOPLEVEL,
5101 "application", EOG_APP,
5102 "startup-flags", flags,
5103 NULL));
5104
5105 return GTK_WIDGET (window);
5106 }
5107
5108 static void
eog_window_list_store_image_added(GtkTreeModel * tree_model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)5109 eog_window_list_store_image_added (GtkTreeModel *tree_model,
5110 GtkTreePath *path,
5111 GtkTreeIter *iter,
5112 gpointer user_data)
5113 {
5114 EogWindow *window = EOG_WINDOW (user_data);
5115
5116 update_image_pos (window);
5117 update_action_groups_state (window);
5118 }
5119
5120 static void
eog_window_list_store_image_removed(GtkTreeModel * tree_model,GtkTreePath * path,gpointer user_data)5121 eog_window_list_store_image_removed (GtkTreeModel *tree_model,
5122 GtkTreePath *path,
5123 gpointer user_data)
5124 {
5125 EogWindow *window = EOG_WINDOW (user_data);
5126 EogWindowPrivate *priv = window->priv;
5127 gint n_images = eog_list_store_length (priv->store);
5128
5129 if (eog_thumb_view_get_n_selected (EOG_THUMB_VIEW (priv->thumbview)) == 0
5130 && n_images > 0) {
5131 gint pos = MIN (gtk_tree_path_get_indices (path)[0],
5132 n_images - 1);
5133 EogImage *image = eog_list_store_get_image_by_pos (priv->store, pos);
5134
5135 if (image != NULL) {
5136 eog_thumb_view_set_current_image (EOG_THUMB_VIEW (priv->thumbview),
5137 image, TRUE);
5138 g_object_unref (image);
5139 }
5140 } else if (!n_images) {
5141 eog_window_clear_load_job (window);
5142 }
5143
5144 update_image_pos (window);
5145 update_action_groups_state (window);
5146 }
5147
5148 static void
eog_job_model_cb(EogJobModel * job,gpointer data)5149 eog_job_model_cb (EogJobModel *job, gpointer data)
5150 {
5151 EogWindow *window;
5152 EogWindowPrivate *priv;
5153 gint n_images;
5154
5155 eog_debug (DEBUG_WINDOW);
5156
5157 #ifdef HAVE_EXIF
5158 int i;
5159 EogImage *image;
5160 #endif
5161
5162 g_return_if_fail (EOG_IS_WINDOW (data));
5163
5164 window = EOG_WINDOW (data);
5165 priv = window->priv;
5166
5167 if (priv->store != NULL) {
5168 g_object_unref (priv->store);
5169 priv->store = NULL;
5170 }
5171
5172 priv->store = g_object_ref (job->store);
5173
5174 n_images = eog_list_store_length (EOG_LIST_STORE (priv->store));
5175
5176 #ifdef HAVE_EXIF
5177 if (g_settings_get_boolean (priv->view_settings, EOG_CONF_VIEW_AUTOROTATE)) {
5178 for (i = 0; i < n_images; i++) {
5179 image = eog_list_store_get_image_by_pos (priv->store, i);
5180 eog_image_autorotate (image);
5181 g_object_unref (image);
5182 }
5183 }
5184 #endif
5185
5186 eog_thumb_view_set_model (EOG_THUMB_VIEW (priv->thumbview), priv->store);
5187
5188 g_signal_connect (G_OBJECT (priv->store),
5189 "row-inserted",
5190 G_CALLBACK (eog_window_list_store_image_added),
5191 window);
5192
5193 g_signal_connect (G_OBJECT (priv->store),
5194 "row-deleted",
5195 G_CALLBACK (eog_window_list_store_image_removed),
5196 window);
5197
5198 if (n_images == 0) {
5199 gint n_files;
5200
5201 /* Avoid starting up fullscreen with an empty model as
5202 * fullscreen controls might end up disabled */
5203 if (priv->status == EOG_WINDOW_STATUS_INIT &&
5204 (priv->mode == EOG_WINDOW_MODE_FULLSCREEN
5205 || priv->mode == EOG_WINDOW_MODE_SLIDESHOW)) {
5206 eog_window_stop_fullscreen (window,
5207 priv->mode == EOG_WINDOW_MODE_SLIDESHOW);
5208 }
5209 priv->status = EOG_WINDOW_STATUS_NORMAL;
5210 update_action_groups_state (window);
5211
5212 n_files = g_slist_length (priv->file_list);
5213
5214 if (n_files > 0) {
5215 GtkWidget *message_area;
5216 GFile *file = NULL;
5217
5218 if (n_files == 1) {
5219 file = (GFile *) priv->file_list->data;
5220 }
5221
5222 message_area = eog_no_images_error_message_area_new (file);
5223
5224 eog_window_set_message_area (window, message_area);
5225
5226 gtk_widget_show (message_area);
5227 }
5228
5229 g_signal_emit (window, signals[SIGNAL_PREPARED], 0);
5230 }
5231 }
5232
5233 /**
5234 * eog_window_open_file_list:
5235 * @window: An #EogWindow.
5236 * @file_list: (element-type GFile): A %NULL-terminated list of #GFile's.
5237 *
5238 * Opens a list of files, adding them to the gallery in @window.
5239 * Files will be checked to be readable and later filtered according
5240 * with eog_list_store_add_files().
5241 **/
5242 void
eog_window_open_file_list(EogWindow * window,GSList * file_list)5243 eog_window_open_file_list (EogWindow *window, GSList *file_list)
5244 {
5245 EogJob *job;
5246
5247 eog_debug (DEBUG_WINDOW);
5248
5249 window->priv->status = EOG_WINDOW_STATUS_INIT;
5250
5251 /* Free the list to avoid memory leak
5252 * when using flag EOG_STARTUP_SINGLE_WINDOW
5253 */
5254 if (window->priv->file_list != NULL) {
5255 g_slist_foreach (window->priv->file_list, (GFunc) g_object_unref, NULL);
5256 g_slist_free (window->priv->file_list);
5257 }
5258
5259 g_slist_foreach (file_list, (GFunc) g_object_ref, NULL);
5260 window->priv->file_list = file_list;
5261
5262 job = eog_job_model_new (file_list);
5263
5264 g_signal_connect (job,
5265 "finished",
5266 G_CALLBACK (eog_job_model_cb),
5267 window);
5268
5269 eog_job_scheduler_add_job (job);
5270 g_object_unref (job);
5271 }
5272
5273 /**
5274 * eog_window_get_gear_menu_section:
5275 * @window: an #EogWindow.
5276 * @id: the ID for the menu section to look up
5277 *
5278 * Return value: (transfer none): a #GMenu or %NULL on failure
5279 **/
5280 GMenu *
eog_window_get_gear_menu_section(EogWindow * window,const gchar * id)5281 eog_window_get_gear_menu_section (EogWindow *window, const gchar *id)
5282 {
5283 GObject *object;
5284 g_return_val_if_fail (EOG_IS_WINDOW (window), NULL);
5285
5286 object = gtk_builder_get_object (window->priv->gear_menu_builder, id);
5287 if (object == NULL || !G_IS_MENU (object))
5288 return NULL;
5289
5290 return G_MENU (object);
5291 }
5292
5293 /**
5294 * eog_window_get_mode:
5295 * @window: An #EogWindow.
5296 *
5297 * Gets the mode of @window. See #EogWindowMode for details.
5298 *
5299 * Returns: An #EogWindowMode.
5300 **/
5301 EogWindowMode
eog_window_get_mode(EogWindow * window)5302 eog_window_get_mode (EogWindow *window)
5303 {
5304 g_return_val_if_fail (EOG_IS_WINDOW (window), EOG_WINDOW_MODE_UNKNOWN);
5305
5306 return window->priv->mode;
5307 }
5308
5309 /**
5310 * eog_window_set_mode:
5311 * @window: an #EogWindow.
5312 * @mode: an #EogWindowMode value.
5313 *
5314 * Changes the mode of @window to normal, fullscreen, or slideshow.
5315 * See #EogWindowMode for details.
5316 **/
5317 void
eog_window_set_mode(EogWindow * window,EogWindowMode mode)5318 eog_window_set_mode (EogWindow *window, EogWindowMode mode)
5319 {
5320 g_return_if_fail (EOG_IS_WINDOW (window));
5321
5322 if (window->priv->mode == mode)
5323 return;
5324
5325 switch (mode) {
5326 case EOG_WINDOW_MODE_NORMAL:
5327 eog_window_stop_fullscreen (window,
5328 window->priv->mode == EOG_WINDOW_MODE_SLIDESHOW);
5329 break;
5330 case EOG_WINDOW_MODE_FULLSCREEN:
5331 eog_window_run_fullscreen (window, FALSE);
5332 break;
5333 case EOG_WINDOW_MODE_SLIDESHOW:
5334 eog_window_run_fullscreen (window, TRUE);
5335 break;
5336 case EOG_WINDOW_MODE_UNKNOWN:
5337 break;
5338 }
5339 }
5340
5341 /**
5342 * eog_window_get_store:
5343 * @window: An #EogWindow.
5344 *
5345 * Gets the #EogListStore that contains the images in the gallery
5346 * of @window.
5347 *
5348 * Returns: (transfer none): an #EogListStore.
5349 **/
5350 EogListStore *
eog_window_get_store(EogWindow * window)5351 eog_window_get_store (EogWindow *window)
5352 {
5353 g_return_val_if_fail (EOG_IS_WINDOW (window), NULL);
5354
5355 return EOG_LIST_STORE (window->priv->store);
5356 }
5357
5358 /**
5359 * eog_window_get_view:
5360 * @window: An #EogWindow.
5361 *
5362 * Gets the #EogScrollView in the window.
5363 *
5364 * Returns: (transfer none): the #EogScrollView.
5365 **/
5366 GtkWidget *
eog_window_get_view(EogWindow * window)5367 eog_window_get_view (EogWindow *window)
5368 {
5369 g_return_val_if_fail (EOG_IS_WINDOW (window), NULL);
5370
5371 return window->priv->view;
5372 }
5373
5374 /**
5375 * eog_window_get_sidebar:
5376 * @window: An #EogWindow.
5377 *
5378 * Gets the sidebar widget of @window.
5379 *
5380 * Returns: (transfer none): the #EogSidebar.
5381 **/
5382 GtkWidget *
eog_window_get_sidebar(EogWindow * window)5383 eog_window_get_sidebar (EogWindow *window)
5384 {
5385 g_return_val_if_fail (EOG_IS_WINDOW (window), NULL);
5386
5387 return window->priv->sidebar;
5388 }
5389
5390 /**
5391 * eog_window_get_thumb_view:
5392 * @window: an #EogWindow.
5393 *
5394 * Gets the thumbnails view in @window.
5395 *
5396 * Returns: (transfer none): an #EogThumbView.
5397 **/
5398 GtkWidget *
eog_window_get_thumb_view(EogWindow * window)5399 eog_window_get_thumb_view (EogWindow *window)
5400 {
5401 g_return_val_if_fail (EOG_IS_WINDOW (window), NULL);
5402
5403 return window->priv->thumbview;
5404 }
5405
5406 /**
5407 * eog_window_get_thumb_nav:
5408 * @window: an #EogWindow.
5409 *
5410 * Gets the thumbnails navigation pane in @window.
5411 *
5412 * Returns: (transfer none): an #EogThumbNav.
5413 **/
5414 GtkWidget *
eog_window_get_thumb_nav(EogWindow * window)5415 eog_window_get_thumb_nav (EogWindow *window)
5416 {
5417 g_return_val_if_fail (EOG_IS_WINDOW (window), NULL);
5418
5419 return window->priv->nav;
5420 }
5421
5422 /**
5423 * eog_window_get_statusbar:
5424 * @window: an #EogWindow.
5425 *
5426 * Gets the statusbar in @window.
5427 *
5428 * Returns: (transfer none): a #EogStatusbar.
5429 **/
5430 GtkWidget *
eog_window_get_statusbar(EogWindow * window)5431 eog_window_get_statusbar (EogWindow *window)
5432 {
5433 g_return_val_if_fail (EOG_IS_WINDOW (window), NULL);
5434
5435 return window->priv->statusbar;
5436 }
5437
5438 /**
5439 * eog_window_get_image:
5440 * @window: an #EogWindow.
5441 *
5442 * Gets the image currently displayed in @window or %NULL if
5443 * no image is being displayed.
5444 *
5445 * Returns: (transfer none): an #EogImage.
5446 **/
5447 EogImage *
eog_window_get_image(EogWindow * window)5448 eog_window_get_image (EogWindow *window)
5449 {
5450 g_return_val_if_fail (EOG_IS_WINDOW (window), NULL);
5451
5452 return window->priv->image;
5453 }
5454
5455 /**
5456 * eog_window_is_empty:
5457 * @window: an #EogWindow.
5458 *
5459 * Tells whether @window is currently empty or not.
5460 *
5461 * Returns: %TRUE if @window has no images, %FALSE otherwise.
5462 **/
5463 gboolean
eog_window_is_empty(EogWindow * window)5464 eog_window_is_empty (EogWindow *window)
5465 {
5466 EogWindowPrivate *priv;
5467 gboolean empty = TRUE;
5468
5469 eog_debug (DEBUG_WINDOW);
5470
5471 g_return_val_if_fail (EOG_IS_WINDOW (window), FALSE);
5472
5473 priv = window->priv;
5474
5475 if (priv->store != NULL) {
5476 empty = (eog_list_store_length (EOG_LIST_STORE (priv->store)) == 0);
5477 }
5478
5479 return empty;
5480 }
5481
5482 void
eog_window_reload_image(EogWindow * window)5483 eog_window_reload_image (EogWindow *window)
5484 {
5485 GtkWidget *view;
5486
5487 g_return_if_fail (EOG_IS_WINDOW (window));
5488
5489 if (window->priv->image == NULL)
5490 return;
5491
5492 g_object_unref (window->priv->image);
5493 window->priv->image = NULL;
5494
5495 view = eog_window_get_view (window);
5496 eog_scroll_view_set_image (EOG_SCROLL_VIEW (view), NULL);
5497
5498 eog_thumb_view_select_single (EOG_THUMB_VIEW (window->priv->thumbview),
5499 EOG_THUMB_VIEW_SELECT_CURRENT);
5500 }
5501
5502 gboolean
eog_window_is_not_initializing(const EogWindow * window)5503 eog_window_is_not_initializing (const EogWindow *window)
5504 {
5505 g_return_val_if_fail (EOG_IS_WINDOW (window), FALSE);
5506
5507 return window->priv->status != EOG_WINDOW_STATUS_INIT;
5508 }
5509
5510 void
eog_window_show_about_dialog(EogWindow * window)5511 eog_window_show_about_dialog (EogWindow *window)
5512 {
5513 g_return_if_fail (EOG_IS_WINDOW (window));
5514
5515 static const char *authors[] = {
5516 "Felix Riemann <friemann@gnome.org> (maintainer)",
5517 "",
5518 "Claudio Saavedra <csaavedra@igalia.com>",
5519 "Lucas Rocha <lucasr@gnome.org>",
5520 "Tim Gerla <tim+gnomebugs@gerla.net>",
5521 "Philip Van Hoof <pvanhoof@gnome.org>",
5522 "Paolo Borelli <pborelli@katamail.com>",
5523 "Jens Finke <jens@triq.net>",
5524 "Martin Baulig <martin@home-of-linux.org>",
5525 "Arik Devens <arik@gnome.org>",
5526 "Michael Meeks <mmeeks@gnu.org>",
5527 "Federico Mena-Quintero <federico@gnome.org>",
5528 "Lutz M\xc3\xbcller <urc8@rz.uni-karlsruhe.de>",
5529 NULL
5530 };
5531
5532 static const char *documenters[] = {
5533 "Eliot Landrum <eliot@landrum.cx>",
5534 "Federico Mena-Quintero <federico@gnome.org>",
5535 "Sun GNOME Documentation Team <gdocteam@sun.com>",
5536 "Tiffany Antopolski <tiffany@antopolski.com>",
5537 NULL
5538 };
5539
5540 gtk_show_about_dialog (GTK_WINDOW (window),
5541 "program-name", _("Image Viewer"),
5542 "version", VERSION,
5543 "copyright", "Copyright \xc2\xa9 2000-2010 Free Software Foundation, Inc.",
5544 "comments",_("The GNOME image viewer."),
5545 "authors", authors,
5546 "documenters", documenters,
5547 "translator-credits", _("translator-credits"),
5548 "website", "https://wiki.gnome.org/Apps/EyeOfGnome",
5549 "logo-icon-name", APPLICATION_ID,
5550 "wrap-license", TRUE,
5551 "license-type", GTK_LICENSE_GPL_2_0,
5552 NULL);
5553 }
5554
5555 void
eog_window_show_preferences_dialog(EogWindow * window)5556 eog_window_show_preferences_dialog (EogWindow *window)
5557 {
5558 GtkWidget *pref_dlg;
5559
5560 g_return_if_fail (window != NULL);
5561
5562 pref_dlg = eog_preferences_dialog_get_instance (GTK_WINDOW (window));
5563
5564 gtk_widget_show (pref_dlg);
5565 }
5566
5567 void
eog_window_close(EogWindow * window)5568 eog_window_close (EogWindow *window)
5569 {
5570 EogWindowPrivate *priv;
5571
5572 g_return_if_fail (EOG_IS_WINDOW (window));
5573
5574 priv = window->priv;
5575
5576 if (priv->save_job != NULL) {
5577 eog_window_finish_saving (window);
5578 }
5579
5580 if (!eog_window_unsaved_images_confirm (window)) {
5581 gtk_widget_destroy (GTK_WIDGET (window));
5582 }
5583 }
5584