1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2009 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <math.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <gthumb.h>
26 #include "actions.h"
27 #include "gth-image-viewer-page.h"
28 #include "preferences.h"
29
30
31 #define UPDATE_QUALITY_DELAY 200
32 #define UPDATE_VISIBILITY_DELAY 100
33 #define N_HEADER_BAR_BUTTONS 7
34 #define HIDE_OVERVIEW_TIMEOUT 2 /* in seconds */
35 #define OVERLAY_MARGIN 10
36 #define ZOOM_BUTTON 2
37 #define APPLY_ICC_PROFILE_BUTTON 3
38 #define TOGGLE_ANIMATION_BUTTON 4
39 #define STEP_ANIMATION_BUTTON 5
40 #define TRANSPARENCY_STYLE_BUTTON 6
41 #undef ALWAYS_LOAD_ORIGINAL_SIZE
42 #define N_FORWARD_PRELOADERS 2
43 #define N_BACKWARD_PRELOADERS 2
44
45
46 static void gth_viewer_page_interface_init (GthViewerPageInterface *iface);
47
48
49 static const GActionEntry actions[] = {
50 { "image-zoom-in", gth_browser_activate_image_zoom_in },
51 { "image-zoom-out", gth_browser_activate_image_zoom_out },
52 { "image-zoom-100", gth_browser_activate_image_zoom_100 },
53 { "image-zoom-200", gth_browser_activate_image_zoom_200 },
54 { "image-zoom-300", gth_browser_activate_image_zoom_300 },
55 { "image-zoom-fit", gth_browser_activate_image_zoom_fit },
56 { "image-zoom-fit-if-larger", gth_browser_activate_image_zoom_fit_if_larger },
57 { "image-zoom-fit-width", gth_browser_activate_image_zoom_fit_width },
58 { "image-zoom-fit-width-if-larger", gth_browser_activate_image_zoom_fit_width_if_larger },
59 { "image-zoom-fit-height", gth_browser_activate_image_zoom_fit_height },
60 { "image-zoom-fit-height-if-larger", gth_browser_activate_image_zoom_fit_height_if_larger },
61 { "image-undo", gth_browser_activate_image_undo },
62 { "image-redo", gth_browser_activate_image_redo },
63 { "copy-image", gth_browser_activate_copy_image },
64 { "paste-image", gth_browser_activate_paste_image },
65 { "apply-icc-profile", toggle_action_activated, NULL, "true", gth_browser_activate_apply_icc_profile },
66 { "toggle-animation", toggle_action_activated, NULL, "true", gth_browser_activate_toggle_animation },
67 { "step-animation", gth_browser_activate_step_animation },
68 { "image-zoom", gth_browser_activate_image_zoom, "s", "''", NULL },
69 { "transparency-style", gth_browser_activate_transparency_style, "s", "''", NULL },
70 { "scroll-step-left", gth_browser_activate_scroll_step_left },
71 { "scroll-step-right", gth_browser_activate_scroll_step_right },
72 { "scroll-step-up", gth_browser_activate_scroll_step_up },
73 { "scroll-step-down", gth_browser_activate_scroll_step_down },
74 { "scroll-page-left", gth_browser_activate_scroll_page_left },
75 { "scroll-page-right", gth_browser_activate_scroll_page_right },
76 { "scroll-page-up", gth_browser_activate_scroll_page_up },
77 { "scroll-page-down", gth_browser_activate_scroll_page_down },
78 { "scroll-to-center", gth_browser_activate_scroll_to_center },
79 };
80
81
82 static const GthMenuEntry file_popup_entries[] = {
83 { N_("Copy Image"), "win.copy-image" },
84 { N_("Paste Image"), "win.paste-image" },
85 };
86
87
88 struct _GthImageViewerPagePrivate {
89 GthBrowser *browser;
90 GSettings *settings;
91 GtkWidget *image_navigator;
92 GtkWidget *overview_revealer;
93 GtkWidget *overview;
94 GtkWidget *viewer;
95 GthImagePreloader *preloader;
96 guint file_popup_merge_id;
97 GthImageHistory *history;
98 GthFileData *file_data;
99 GFileInfo *updated_info;
100 gboolean active;
101 gboolean image_changed;
102 gboolean loading_image;
103 GFile *last_loaded;
104 gboolean can_paste;
105 guint update_quality_id;
106 guint update_visibility_id;
107 GtkWidget *buttons[N_HEADER_BAR_BUTTONS];
108 GtkBuilder *builder;
109 gboolean pointer_on_viewer;
110 gboolean pointer_on_overview;
111 guint hide_overview_id;
112 gboolean apply_icc_profile;
113 GthFileData *next_file_data[N_FORWARD_PRELOADERS];
114 GthFileData *prev_file_data[N_BACKWARD_PRELOADERS];
115 gulong drag_data_get_event;
116 };
117
118
G_DEFINE_TYPE_WITH_CODE(GthImageViewerPage,gth_image_viewer_page,G_TYPE_OBJECT,G_ADD_PRIVATE (GthImageViewerPage)G_IMPLEMENT_INTERFACE (GTH_TYPE_VIEWER_PAGE,gth_viewer_page_interface_init))119 G_DEFINE_TYPE_WITH_CODE (GthImageViewerPage,
120 gth_image_viewer_page,
121 G_TYPE_OBJECT,
122 G_ADD_PRIVATE (GthImageViewerPage)
123 G_IMPLEMENT_INTERFACE (GTH_TYPE_VIEWER_PAGE,
124 gth_viewer_page_interface_init))
125
126
127 static void
128 gth_image_viewer_page_file_loaded (GthImageViewerPage *self,
129 gboolean success)
130 {
131 if (_g_file_equal (self->priv->last_loaded, self->priv->file_data->file))
132 return;
133
134 _g_object_unref (self->priv->last_loaded);
135 self->priv->last_loaded = g_object_ref (self->priv->file_data->file);
136
137 gth_viewer_page_file_loaded (GTH_VIEWER_PAGE (self),
138 self->priv->file_data,
139 self->priv->updated_info,
140 success);
141 }
142
143
144 static int
get_viewer_size(GthImageViewerPage * self)145 get_viewer_size (GthImageViewerPage *self)
146 {
147 GtkAllocation allocation;
148 int size;
149
150 gtk_widget_get_allocation (GTK_WIDGET (self->priv->viewer), &allocation);
151 size = MAX (allocation.width, allocation.height);
152 if (size <= 1) {
153 int window_width;
154 int window_height;
155 gtk_window_get_size (GTK_WINDOW (self->priv->browser),
156 &window_width,
157 &window_height);
158 size = MAX (window_width, window_height);;
159 }
160
161 return size;
162 }
163
164
165 static int
_gth_image_preloader_get_requested_size_for_next_images(GthImageViewerPage * self)166 _gth_image_preloader_get_requested_size_for_next_images (GthImageViewerPage *self)
167 {
168 int requested_size;
169
170 requested_size = -1;
171
172 switch (gth_image_viewer_get_zoom_change (GTH_IMAGE_VIEWER (self->priv->viewer))) {
173 case GTH_ZOOM_CHANGE_ACTUAL_SIZE:
174 requested_size = -1;
175 break;
176 default:
177 requested_size = (int) floor ((double) get_viewer_size (self) * 0.5);
178 break;
179 }
180
181 return requested_size * gtk_widget_get_scale_factor (GTK_WIDGET (self->priv->viewer));
182 }
183
184
185 static int
_gth_image_preloader_get_requested_size_for_current_image(GthImageViewerPage * self)186 _gth_image_preloader_get_requested_size_for_current_image (GthImageViewerPage *self)
187 {
188 int requested_size;
189 double zoom;
190
191 requested_size = -1;
192
193 switch (gth_image_viewer_get_fit_mode (GTH_IMAGE_VIEWER (self->priv->viewer))) {
194 case GTH_FIT_NONE:
195 zoom = gth_image_viewer_get_zoom (GTH_IMAGE_VIEWER (self->priv->viewer));
196 if (zoom < 1.0) {
197 int original_width;
198 int original_height;
199
200 gth_image_viewer_get_original_size (GTH_IMAGE_VIEWER (self->priv->viewer), &original_width, &original_height);
201 requested_size = MAX (original_width * zoom, original_height * zoom);
202 }
203 else
204 requested_size = -1;
205 break;
206
207 default:
208 requested_size = get_viewer_size (self);
209 break;
210 }
211
212 return requested_size * gtk_widget_get_scale_factor (GTK_WIDGET (self->priv->viewer));
213 }
214
215
216 /* -- _gth_image_viewer_page_load_with_preloader -- */
217
218
219 typedef struct {
220 GthImageViewerPage *self;
221 GthFileData *file_data;
222 int requested_size;
223 GCancellable *cancellable;
224 GAsyncReadyCallback callback;
225 gpointer user_data;
226 } ProfileData;
227
228
229 static void
profile_data_free(ProfileData * profile_data)230 profile_data_free (ProfileData *profile_data)
231 {
232 _g_object_unref (profile_data->cancellable);
233 _g_object_unref (profile_data->file_data);
234 _g_object_unref (profile_data->self);
235 g_free (profile_data);
236 }
237
238
239 static void
_gth_image_viewer_page_load_with_preloader_step2(GthImageViewerPage * self,GthFileData * file_data,int requested_size,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)240 _gth_image_viewer_page_load_with_preloader_step2 (GthImageViewerPage *self,
241 GthFileData *file_data,
242 int requested_size,
243 GCancellable *cancellable,
244 GAsyncReadyCallback callback,
245 gpointer user_data)
246 {
247 g_object_ref (self);
248
249 gth_image_preloader_load (self->priv->preloader,
250 file_data,
251 requested_size,
252 cancellable,
253 callback,
254 user_data,
255 N_FORWARD_PRELOADERS + N_BACKWARD_PRELOADERS,
256 self->priv->next_file_data[0],
257 self->priv->next_file_data[1],
258 self->priv->prev_file_data[0],
259 self->priv->prev_file_data[1]);
260 }
261
262
263 static void
profile_ready_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)264 profile_ready_cb (GObject *source_object,
265 GAsyncResult *res,
266 gpointer user_data)
267 {
268 ProfileData *profile_data = user_data;
269 GthImageViewerPage *self = profile_data->self;
270
271 if (self->priv->active && ! self->priv->image_changed && _g_file_equal (self->priv->file_data->file, profile_data->file_data->file)) {
272 GthICCProfile *profile;
273
274 profile = gth_color_manager_get_profile_finish (GTH_COLOR_MANAGER (source_object), res, NULL);
275 if (profile == NULL)
276 profile = _g_object_ref (gth_browser_get_monitor_profile (self->priv->browser));
277 gth_image_preloader_set_out_profile (self->priv->preloader, profile);
278
279 _gth_image_viewer_page_load_with_preloader_step2 (profile_data->self,
280 profile_data->file_data,
281 profile_data->requested_size,
282 profile_data->cancellable,
283 profile_data->callback,
284 profile_data->user_data);
285
286 _g_object_unref (profile);
287 }
288
289 profile_data_free (profile_data);
290 }
291
292
293 static void
_gth_image_viewer_page_load_with_preloader(GthImageViewerPage * self,GthFileData * file_data,int requested_size,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)294 _gth_image_viewer_page_load_with_preloader (GthImageViewerPage *self,
295 GthFileData *file_data,
296 int requested_size,
297 GCancellable *cancellable,
298 GAsyncReadyCallback callback,
299 gpointer user_data)
300 {
301 if ((file_data != NULL) && self->priv->apply_icc_profile) {
302 char *monitor_name = NULL;
303
304 if (_gtk_window_get_monitor_info (GTK_WINDOW (self->priv->browser), NULL, NULL, &monitor_name)) {
305 ProfileData *profile_data;
306
307 profile_data = g_new (ProfileData, 1);
308 profile_data->self = g_object_ref (self);
309 profile_data->file_data = g_object_ref (file_data);
310 profile_data->requested_size = requested_size;
311 profile_data->cancellable = _g_object_ref (cancellable);
312 profile_data->callback = callback;
313 profile_data->user_data = user_data;
314
315 gth_color_manager_get_profile_async (gth_main_get_default_color_manager(),
316 monitor_name,
317 cancellable,
318 profile_ready_cb,
319 profile_data);
320
321 return;
322 }
323 }
324
325 gth_image_preloader_set_out_profile (self->priv->preloader, NULL);
326 _gth_image_viewer_page_load_with_preloader_step2 (self, file_data, requested_size, cancellable, callback, user_data);
327 }
328
329
330 static gboolean
_gth_image_viewer_page_load_with_preloader_finish(GthImageViewerPage * self)331 _gth_image_viewer_page_load_with_preloader_finish (GthImageViewerPage *self)
332 {
333 gboolean active = self->priv->active;
334 g_object_unref (self);
335
336 return active;
337 }
338
339
340 static void
different_quality_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)341 different_quality_ready_cb (GObject *source_object,
342 GAsyncResult *result,
343 gpointer user_data)
344 {
345 GthImageViewerPage *self = user_data;
346 GthFileData *requested;
347 GthImage *image;
348 int requested_size;
349 int original_width;
350 int original_height;
351 GError *error = NULL;
352 cairo_surface_t *s1 = NULL;
353 cairo_surface_t *s2;
354 int w1, h1, w2, h2;
355 gboolean got_better_quality;
356
357 if (! _gth_image_viewer_page_load_with_preloader_finish (self))
358 return;
359
360 if (! gth_image_preloader_load_finish (GTH_IMAGE_PRELOADER (source_object),
361 result,
362 &requested,
363 &image,
364 &requested_size,
365 &original_width,
366 &original_height,
367 &error))
368 {
369 g_clear_error (&error);
370 return;
371 }
372
373 if (! (self->priv->image_changed && requested == NULL) && ! _g_file_equal (requested->file, self->priv->file_data->file))
374 goto clear_data;
375
376 if (image == NULL)
377 goto clear_data;
378
379 /* check whether the image is of different quality */
380
381 s1 = gth_image_get_cairo_surface (image);
382 if (s1 == NULL)
383 goto clear_data;
384
385 s2 = gth_image_viewer_get_current_image (GTH_IMAGE_VIEWER (self->priv->viewer));
386 if (s2 == NULL) {
387 got_better_quality = TRUE;
388 }
389 else {
390 w1 = cairo_image_surface_get_width (s1);
391 h1 = cairo_image_surface_get_height (s1);
392 w2 = cairo_image_surface_get_width (s2);
393 h2 = cairo_image_surface_get_height (s2);
394 got_better_quality = ((w1 > w2) || (h1 > h2));
395 }
396
397 if (got_better_quality) {
398 gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
399 gth_image_viewer_set_better_quality (GTH_IMAGE_VIEWER (self->priv->viewer),
400 image,
401 original_width,
402 original_height);
403 gth_image_viewer_set_requested_size (GTH_IMAGE_VIEWER (self->priv->viewer), requested_size);
404 gtk_widget_queue_draw (self->priv->viewer);
405 }
406
407 clear_data:
408
409 if (s1 != NULL)
410 cairo_surface_destroy (s1);
411 _g_object_unref (requested);
412 _g_object_unref (image);
413 g_clear_error (&error);
414
415 return;
416 }
417
418
419 static gboolean
_g_mime_type_can_load_different_quality(const char * mime_type)420 _g_mime_type_can_load_different_quality (const char *mime_type)
421 {
422 static const char *supported[] = {
423 "image/jpeg",
424 "image/x-portable-pixmap"
425 };
426
427 int i;
428 for (i = 0; i < G_N_ELEMENTS (supported); i++)
429 if (g_strcmp0 (mime_type, supported[i]) == 0)
430 return TRUE;
431
432 if (_g_mime_type_is_raw (mime_type))
433 return TRUE;
434
435 return FALSE;
436 }
437
438
439 typedef struct {
440 GthImageViewerPage *self;
441 GthFileData *file_data;
442 } UpdateQualityData;
443
444
445 static void
update_quality_data_free(UpdateQualityData * data)446 update_quality_data_free (UpdateQualityData *data)
447 {
448 _g_object_unref (data->file_data);
449 g_free (data);
450 }
451
452
453 static gboolean
update_quality_cb(gpointer user_data)454 update_quality_cb (gpointer user_data)
455 {
456 UpdateQualityData *data = user_data;
457 GthImageViewerPage *self = data->self;
458 gboolean file_changed;
459
460 if (! _gth_image_viewer_page_load_with_preloader_finish (self)) {
461 update_quality_data_free (data);
462 return FALSE;
463 }
464
465 if (self->priv->update_quality_id != 0) {
466 g_source_remove (self->priv->update_quality_id);
467 self->priv->update_quality_id = 0;
468 }
469
470 file_changed = ! _g_file_equal (data->file_data->file, self->priv->file_data->file);
471 update_quality_data_free (data);
472
473 if (file_changed)
474 return FALSE;
475
476 if (! self->priv->active)
477 return FALSE;
478
479 if (self->priv->viewer == NULL)
480 return FALSE;
481
482 if (self->priv->loading_image)
483 return FALSE;
484
485 if (! self->priv->image_changed && ! _g_mime_type_can_load_different_quality (gth_file_data_get_mime_type (self->priv->file_data)))
486 return FALSE;
487
488 _gth_image_viewer_page_load_with_preloader (self,
489 self->priv->image_changed ? GTH_MODIFIED_IMAGE : self->priv->file_data,
490 _gth_image_preloader_get_requested_size_for_current_image (self),
491 NULL,
492 different_quality_ready_cb,
493 self);
494
495 return FALSE;
496 }
497
498
499 static void
update_image_quality_if_required(GthImageViewerPage * self)500 update_image_quality_if_required (GthImageViewerPage *self)
501 {
502 #ifndef ALWAYS_LOAD_ORIGINAL_SIZE
503 GthImage *image;
504
505 if (self->priv->loading_image || gth_sidebar_tool_is_active (GTH_SIDEBAR (gth_browser_get_viewer_sidebar (self->priv->browser))))
506 return;
507
508 image = gth_image_viewer_get_image (GTH_IMAGE_VIEWER (self->priv->viewer));
509 if ((image != NULL) && (gth_image_get_is_zoomable (image) || gth_image_get_is_animation (image)))
510 return;
511
512 if (self->priv->update_quality_id != 0) {
513 g_source_remove (self->priv->update_quality_id);
514 self->priv->update_quality_id = 0;
515 }
516
517 UpdateQualityData *data;
518
519 data = g_new0 (UpdateQualityData, 1);
520 data->self = self;
521 data->file_data = _g_object_ref (self->priv->file_data);
522
523 _g_object_ref (self);
524 self->priv->update_quality_id = g_timeout_add (UPDATE_QUALITY_DELAY,
525 update_quality_cb,
526 data);
527 #endif
528 }
529
530
531 static gboolean
hide_overview_after_timeout(gpointer data)532 hide_overview_after_timeout (gpointer data)
533 {
534 GthImageViewerPage *self = data;
535
536 if (self->priv->hide_overview_id != 0)
537 g_source_remove (self->priv->hide_overview_id);
538 self->priv->hide_overview_id = 0;
539
540 if (! self->priv->pointer_on_overview)
541 gtk_revealer_set_reveal_child (GTK_REVEALER (self->priv->overview_revealer), FALSE);
542
543 return FALSE;
544 }
545
546
547 static gboolean
update_overview_visibility_now(gpointer user_data)548 update_overview_visibility_now (gpointer user_data)
549 {
550 GthImageViewerPage *self;
551 gboolean overview_visible;
552
553 self = GTH_IMAGE_VIEWER_PAGE (user_data);
554
555 if (self->priv->update_visibility_id != 0) {
556 g_source_remove (self->priv->update_visibility_id);
557 self->priv->update_visibility_id = 0;
558 }
559
560 if (! self->priv->active)
561 return FALSE;
562
563 overview_visible = self->priv->pointer_on_overview || (self->priv->pointer_on_viewer && gth_image_viewer_has_scrollbars (GTH_IMAGE_VIEWER (self->priv->viewer)));
564 gtk_revealer_set_reveal_child (GTK_REVEALER (self->priv->overview_revealer), overview_visible);
565
566 if (overview_visible) {
567 if (self->priv->hide_overview_id != 0)
568 g_source_remove (self->priv->hide_overview_id);
569 self->priv->hide_overview_id = g_timeout_add_seconds (HIDE_OVERVIEW_TIMEOUT, hide_overview_after_timeout, self);
570 }
571
572 return FALSE;
573 }
574
575
576 static void
update_overview_visibility(GthImageViewerPage * self)577 update_overview_visibility (GthImageViewerPage *self)
578 {
579 if (self->priv->update_visibility_id != 0) {
580 g_source_remove (self->priv->update_visibility_id);
581 self->priv->update_visibility_id = 0;
582 }
583
584 self->priv->update_visibility_id = g_timeout_add (UPDATE_VISIBILITY_DELAY,
585 update_overview_visibility_now,
586 self);
587 }
588
589
590 #define MIN_ZOOM_LEVEL 0.3
591 #define MAX_ZOOM_LEVEL 3.0
592
593
594 static void
zoom_scale_value_changed_cb(GtkScale * scale,gpointer user_data)595 zoom_scale_value_changed_cb (GtkScale *scale,
596 gpointer user_data)
597 {
598 GthImageViewerPage *self = user_data;
599 double x, zoom;
600
601 x = gtk_range_get_value (GTK_RANGE (scale));
602 zoom = MIN_ZOOM_LEVEL + (x / 100.0 * (MAX_ZOOM_LEVEL - MIN_ZOOM_LEVEL));
603 gth_image_viewer_set_zoom (GTH_IMAGE_VIEWER (self->priv->viewer), zoom);
604 }
605
606
607 static void
zoom_button_toggled_cb(GtkToggleButton * togglebutton,gpointer user_data)608 zoom_button_toggled_cb (GtkToggleButton *togglebutton,
609 gpointer user_data)
610 {
611 GthImageViewerPage *self = user_data;
612
613 if (! gtk_toggle_button_get_active (togglebutton))
614 return;
615
616 gth_browser_keep_mouse_visible (self->priv->browser, TRUE);
617 }
618
619
620 static void
zoom_popover_closed_cb(GtkPopover * popover,gpointer user_data)621 zoom_popover_closed_cb (GtkPopover *popover,
622 gpointer user_data)
623 {
624 GthImageViewerPage *self = user_data;
625
626 gth_browser_keep_mouse_visible (self->priv->browser, FALSE);
627 call_when_idle ((DataFunc) gth_viewer_page_focus, self);
628 }
629
630
631 #define ZOOM_EQUAL(a,b) (fabs (a - b) < 1e-3)
632
633
634 static void
update_zoom_info(GthImageViewerPage * self)635 update_zoom_info (GthImageViewerPage *self)
636 {
637 double zoom;
638 char *text;
639 double x;
640
641 /* status bar */
642
643 zoom = gth_image_viewer_get_zoom (GTH_IMAGE_VIEWER (self->priv->viewer));
644 text = g_strdup_printf (" %d%% ", (int) (zoom * 100));
645 gth_statusbar_set_secondary_text (GTH_STATUSBAR (gth_browser_get_statusbar (self->priv->browser)), text);
646 g_free (text);
647
648 /* zoom menu */
649
650 gboolean zoom_enabled;
651 GthFit fit_mode;
652 GAction *action;
653 const char *state;
654
655 zoom_enabled = gth_image_viewer_get_zoom_enabled (GTH_IMAGE_VIEWER (self->priv->viewer));
656 fit_mode = gth_image_viewer_get_fit_mode (GTH_IMAGE_VIEWER (self->priv->viewer));
657
658 gth_window_enable_action (GTH_WINDOW (self->priv->browser), "image-zoom", zoom_enabled);
659
660 state = "";
661 if (fit_mode == GTH_FIT_SIZE)
662 state = "fit";
663 else if (fit_mode == GTH_FIT_WIDTH)
664 state = "fit-width";
665 else if (fit_mode == GTH_FIT_HEIGHT)
666 state = "fit-height";
667 else if (fit_mode == GTH_FIT_SIZE_IF_LARGER)
668 state = "automatic";
669 else if (ZOOM_EQUAL (zoom, 0.5))
670 state = "50";
671 else if (ZOOM_EQUAL (zoom, 1.0))
672 state = "100";
673 else if (ZOOM_EQUAL (zoom, 2.0))
674 state = "200";
675 else if (ZOOM_EQUAL (zoom, 3.0))
676 state = "300";
677
678 action = g_action_map_lookup_action (G_ACTION_MAP (self->priv->browser), "image-zoom");
679 g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_string (state));
680
681 gth_window_enable_action (GTH_WINDOW (self->priv->browser), "image-zoom-100", ! ZOOM_EQUAL (zoom, 1.0));
682 gth_window_enable_action (GTH_WINDOW (self->priv->browser), "image-zoom-fit-if-larger", fit_mode != GTH_FIT_SIZE_IF_LARGER);
683
684 /* zoom menu scale */
685
686 GtkWidget *scale = _gtk_builder_get_widget (self->priv->builder, "zoom_level_scale");
687
688 _g_signal_handlers_block_by_data (scale, self);
689 x = (zoom - MIN_ZOOM_LEVEL) / (MAX_ZOOM_LEVEL - MIN_ZOOM_LEVEL) * 100.0;
690 gtk_range_set_value (GTK_RANGE (scale), CLAMP (x, 0, 100));
691 _g_signal_handlers_unblock_by_data (scale, self);
692 }
693
694
695 static void
viewer_zoom_changed_cb(GtkWidget * widget,GthImageViewerPage * self)696 viewer_zoom_changed_cb (GtkWidget *widget,
697 GthImageViewerPage *self)
698 {
699 update_image_quality_if_required (self);
700 self->priv->pointer_on_viewer = TRUE;
701 update_overview_visibility (self);
702 update_zoom_info (self);
703 }
704
705
706 static void
viewer_image_changed_cb(GtkWidget * widget,GthImageViewerPage * self)707 viewer_image_changed_cb (GtkWidget *widget,
708 GthImageViewerPage *self)
709 {
710 gth_viewer_page_update_sensitivity (GTH_VIEWER_PAGE (self));
711 update_image_quality_if_required (self);
712 update_overview_visibility (self);
713 update_zoom_info (self);
714 }
715
716
717 static gboolean
viewer_button_press_event_cb(GtkWidget * widget,GdkEventButton * event,GthImageViewerPage * self)718 viewer_button_press_event_cb (GtkWidget *widget,
719 GdkEventButton *event,
720 GthImageViewerPage *self)
721 {
722 return gth_browser_viewer_button_press_cb (self->priv->browser, event);
723 }
724
725
726 static gboolean
viewer_popup_menu_cb(GtkWidget * widget,GthImageViewerPage * self)727 viewer_popup_menu_cb (GtkWidget *widget,
728 GthImageViewerPage *self)
729 {
730 gth_browser_file_menu_popup (self->priv->browser, NULL);
731 return TRUE;
732 }
733
734
735 static gboolean
viewer_scroll_event_cb(GtkWidget * widget,GdkEventScroll * event,GthImageViewerPage * self)736 viewer_scroll_event_cb (GtkWidget *widget,
737 GdkEventScroll *event,
738 GthImageViewerPage *self)
739 {
740 return gth_browser_viewer_scroll_event_cb (self->priv->browser, event);
741 }
742
743
744 static gboolean
viewer_image_map_event_cb(GtkWidget * widget,GdkEvent * event,GthImageViewerPage * self)745 viewer_image_map_event_cb (GtkWidget *widget,
746 GdkEvent *event,
747 GthImageViewerPage *self)
748 {
749 gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
750 return FALSE;
751 }
752
753
754 static void
clipboard_targets_received_cb(GtkClipboard * clipboard,GdkAtom * atoms,int n_atoms,gpointer user_data)755 clipboard_targets_received_cb (GtkClipboard *clipboard,
756 GdkAtom *atoms,
757 int n_atoms,
758 gpointer user_data)
759 {
760 GthImageViewerPage *self = user_data;
761 int i;
762
763 self->priv->can_paste = FALSE;
764 for (i = 0; ! self->priv->can_paste && (i < n_atoms); i++)
765 if (atoms[i] == gdk_atom_intern_static_string ("image/png"))
766 self->priv->can_paste = TRUE;
767
768 gth_window_enable_action (GTH_WINDOW (self->priv->browser), "paste-image", self->priv->can_paste);
769
770 g_object_unref (self);
771 }
772
773
774 static void
_gth_image_viewer_page_update_paste_command_sensitivity(GthImageViewerPage * self,GtkClipboard * clipboard)775 _gth_image_viewer_page_update_paste_command_sensitivity (GthImageViewerPage *self,
776 GtkClipboard *clipboard)
777 {
778 self->priv->can_paste = FALSE;
779 gth_window_enable_action (GTH_WINDOW (self->priv->browser), "paste-image", self->priv->can_paste);
780
781 if (clipboard == NULL)
782 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (self->priv->viewer), GDK_SELECTION_CLIPBOARD);
783 gtk_clipboard_request_targets (clipboard,
784 clipboard_targets_received_cb,
785 g_object_ref (self));
786 }
787
788
789 static void
clipboard_owner_change_cb(GtkClipboard * clipboard,GdkEvent * event,gpointer user_data)790 clipboard_owner_change_cb (GtkClipboard *clipboard,
791 GdkEvent *event,
792 gpointer user_data)
793 {
794 _gth_image_viewer_page_update_paste_command_sensitivity ((GthImageViewerPage *) user_data, clipboard);
795 }
796
797
798 static void
viewer_realize_cb(GtkWidget * widget,gpointer user_data)799 viewer_realize_cb (GtkWidget *widget,
800 gpointer user_data)
801 {
802 GthImageViewerPage *self = user_data;
803 GtkClipboard *clipboard;
804
805 clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
806 g_signal_connect (clipboard,
807 "owner_change",
808 G_CALLBACK (clipboard_owner_change_cb),
809 self);
810 }
811
812
813 static void
viewer_unrealize_cb(GtkWidget * widget,gpointer user_data)814 viewer_unrealize_cb (GtkWidget *widget,
815 gpointer user_data)
816 {
817 GthImageViewerPage *self = user_data;
818 GtkClipboard *clipboard;
819
820 clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
821 g_signal_handlers_disconnect_by_func (clipboard,
822 G_CALLBACK (clipboard_owner_change_cb),
823 self);
824 }
825
826
827 static gboolean
image_navigator_get_child_position_cb(GtkOverlay * overlay,GtkWidget * widget,GdkRectangle * allocation,gpointer user_data)828 image_navigator_get_child_position_cb (GtkOverlay *overlay,
829 GtkWidget *widget,
830 GdkRectangle *allocation,
831 gpointer user_data)
832 {
833 GthImageViewerPage *self = GTH_IMAGE_VIEWER_PAGE (user_data);
834 GtkAllocation main_alloc;
835 gboolean allocation_filled = FALSE;
836
837 gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN (overlay)), &main_alloc);
838 gtk_widget_get_preferred_width (widget, NULL, &allocation->width);
839 gtk_widget_get_preferred_height (widget, NULL, &allocation->height);
840
841 if (widget == self->priv->overview_revealer) {
842 allocation->x = main_alloc.width - allocation->width - OVERLAY_MARGIN;
843 allocation->y = OVERLAY_MARGIN;
844 if (gth_browser_get_is_fullscreen (self->priv->browser))
845 allocation->y += gtk_widget_get_allocated_height (gth_browser_get_fullscreen_headerbar (self->priv->browser));
846 allocation_filled = TRUE;
847 }
848
849 return allocation_filled;
850 }
851
852
853 static gboolean
overview_motion_notify_event_cb(GtkWidget * widget,GdkEventMotion * event,gpointer data)854 overview_motion_notify_event_cb (GtkWidget *widget,
855 GdkEventMotion *event,
856 gpointer data)
857 {
858 GthImageViewerPage *self = data;
859
860 if (self->priv->hide_overview_id != 0) {
861 g_source_remove (self->priv->hide_overview_id);
862 self->priv->hide_overview_id = 0;
863 }
864
865 self->priv->pointer_on_viewer = TRUE;
866 if (widget == self->priv->overview)
867 self->priv->pointer_on_overview = TRUE;
868 update_overview_visibility (data);
869
870 return FALSE;
871 }
872
873
874 static gboolean
overview_leave_notify_event_cb(GtkWidget * widget,GdkEvent * event,gpointer data)875 overview_leave_notify_event_cb (GtkWidget *widget,
876 GdkEvent *event,
877 gpointer data)
878 {
879 GthImageViewerPage *self = data;
880
881 if (widget == self->priv->overview)
882 self->priv->pointer_on_overview = gth_image_overview_get_scrolling_is_active (GTH_IMAGE_OVERVIEW (self->priv->overview));
883
884 return FALSE;
885 }
886
887
888 static void
pref_zoom_quality_changed(GSettings * settings,char * key,gpointer user_data)889 pref_zoom_quality_changed (GSettings *settings,
890 char *key,
891 gpointer user_data)
892 {
893 GthImageViewerPage *self = user_data;
894
895 if (! self->priv->active || (self->priv->viewer == NULL))
896 return;
897
898 gth_image_viewer_set_zoom_quality (GTH_IMAGE_VIEWER (self->priv->viewer),
899 g_settings_get_enum (self->priv->settings, PREF_IMAGE_VIEWER_ZOOM_QUALITY));
900 gtk_widget_queue_draw (self->priv->viewer);
901 }
902
903
904 static void
pref_zoom_change_changed(GSettings * settings,char * key,gpointer user_data)905 pref_zoom_change_changed (GSettings *settings,
906 char *key,
907 gpointer user_data)
908 {
909 GthImageViewerPage *self = user_data;
910
911 if (! self->priv->active || (self->priv->viewer == NULL))
912 return;
913
914 gth_image_viewer_set_zoom_change (GTH_IMAGE_VIEWER (self->priv->viewer),
915 g_settings_get_enum (self->priv->settings, PREF_IMAGE_VIEWER_ZOOM_CHANGE));
916 gtk_widget_queue_draw (self->priv->viewer);
917 }
918
919
920 static void
pref_reset_scrollbars_changed(GSettings * settings,char * key,gpointer user_data)921 pref_reset_scrollbars_changed (GSettings *settings,
922 char *key,
923 gpointer user_data)
924 {
925 GthImageViewerPage *self = user_data;
926
927 if (! self->priv->active || (self->priv->viewer == NULL))
928 return;
929
930 gth_image_viewer_set_reset_scrollbars (GTH_IMAGE_VIEWER (self->priv->viewer),
931 g_settings_get_boolean (self->priv->settings, PREF_IMAGE_VIEWER_RESET_SCROLLBARS));
932 }
933
934
935 static void
pref_transparency_style_changed(GSettings * settings,char * key,gpointer user_data)936 pref_transparency_style_changed (GSettings *settings,
937 char *key,
938 gpointer user_data)
939 {
940 GthImageViewerPage *self = user_data;
941 GthTransparencyStyle style;
942 GAction *action;
943 const char *state;
944
945 if (! self->priv->active || (self->priv->viewer == NULL))
946 return;
947
948 style = g_settings_get_enum (self->priv->settings, PREF_IMAGE_VIEWER_TRANSPARENCY_STYLE);
949 state = "";
950 switch (style) {
951 case GTH_TRANSPARENCY_STYLE_CHECKERED:
952 state = "checkered";
953 break;
954 case GTH_TRANSPARENCY_STYLE_WHITE:
955 state = "white";
956 break;
957 case GTH_TRANSPARENCY_STYLE_GRAY:
958 state = "gray";
959 break;
960 case GTH_TRANSPARENCY_STYLE_BLACK:
961 state = "black";
962 break;
963 }
964 action = g_action_map_lookup_action (G_ACTION_MAP (self->priv->browser), "transparency-style");
965 if (action != NULL)
966 g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_string (state));
967
968 gth_image_viewer_set_transparency_style (GTH_IMAGE_VIEWER (self->priv->viewer), style);
969 }
970
971
972 static void
paint_comment_over_image_func(GthImageViewer * image_viewer,cairo_t * cr,gpointer user_data)973 paint_comment_over_image_func (GthImageViewer *image_viewer,
974 cairo_t *cr,
975 gpointer user_data)
976 {
977 GthImageViewerPage *self = user_data;
978 GthFileData *file_data = self->priv->file_data;
979 GString *file_info;
980 char *comment;
981 const char *file_date;
982 const char *file_size;
983 int current_position;
984 int n_visibles;
985 int width;
986 int height;
987 GthMetadata *metadata;
988 PangoLayout *layout;
989 PangoAttrList *attr_list = NULL;
990 GError *error = NULL;
991 char *text;
992 static GdkPixbuf *icon = NULL;
993 int icon_width;
994 int icon_height;
995 int image_width;
996 int image_height;
997 const int x_padding = 20;
998 const int y_padding = 20;
999 int max_text_width;
1000 PangoRectangle bounds;
1001 int text_x;
1002 int text_y;
1003 int icon_x;
1004 int icon_y;
1005
1006 file_info = g_string_new ("");
1007
1008 comment = gth_file_data_get_attribute_as_string (file_data, "general::description");
1009 if (comment != NULL) {
1010 g_string_append_printf (file_info, "<b>%s</b>\n\n", comment);
1011 g_free (comment);
1012 }
1013
1014 metadata = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "general::datetime");
1015 if (metadata != NULL)
1016 file_date = gth_metadata_get_formatted (metadata);
1017 else
1018 file_date = g_file_info_get_attribute_string (file_data->info, "gth::file::display-mtime");
1019 file_size = g_file_info_get_attribute_string (file_data->info, "gth::file::display-size");
1020
1021 gth_browser_get_file_list_info (self->priv->browser, ¤t_position, &n_visibles);
1022 gth_image_viewer_get_original_size (GTH_IMAGE_VIEWER (self->priv->viewer), &width, &height);
1023
1024 g_string_append_printf (file_info,
1025 "<small><i>%s - %dx%d (%d%%) - %s</i>\n<tt>%d/%d - %s</tt></small>",
1026 file_date,
1027 width,
1028 height,
1029 (int) (gth_image_viewer_get_zoom (GTH_IMAGE_VIEWER (self->priv->viewer)) * 100),
1030 file_size,
1031 current_position + 1,
1032 n_visibles,
1033 g_file_info_get_attribute_string (file_data->info, "standard::display-name"));
1034
1035 layout = gtk_widget_create_pango_layout (GTK_WIDGET (self->priv->viewer), NULL);
1036 pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
1037 pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
1038
1039 if (! pango_parse_markup (file_info->str,
1040 -1,
1041 0,
1042 &attr_list,
1043 &text,
1044 NULL,
1045 &error))
1046 {
1047 g_warning ("Failed to set text from markup due to error parsing markup: %s\nThis is the text that caused the error: %s", error->message, file_info->str);
1048 g_error_free (error);
1049 g_object_unref (layout);
1050 g_string_free (file_info, TRUE);
1051 return;
1052 }
1053
1054 pango_layout_set_attributes (layout, attr_list);
1055 pango_layout_set_text (layout, text, strlen (text));
1056
1057 if (icon == NULL) {
1058 GIcon *gicon;
1059
1060 gicon = g_themed_icon_new ("dialog-information-symbolic");
1061 icon = _g_icon_get_pixbuf (gicon, 24, _gtk_widget_get_icon_theme (GTK_WIDGET (image_viewer)));
1062
1063 g_object_unref (gicon);
1064 }
1065 icon_width = gdk_pixbuf_get_width (icon);
1066 icon_height = gdk_pixbuf_get_height (icon);
1067
1068 image_width = gdk_window_get_width (gtk_widget_get_window (self->priv->viewer));
1069 image_height = gdk_window_get_height (gtk_widget_get_window (self->priv->viewer));
1070 max_text_width = ((image_width * 3 / 4) - icon_width - (x_padding * 3) - (x_padding * 2));
1071
1072 pango_layout_set_width (layout, max_text_width * PANGO_SCALE);
1073 pango_layout_get_pixel_extents (layout, NULL, &bounds);
1074
1075 bounds.width += (2 * x_padding) + (icon_width + x_padding);
1076 bounds.height = MIN (image_height - icon_height - (y_padding * 2), bounds.height + (2 * y_padding));
1077 bounds.x = MAX ((image_width - bounds.width) / 2, 0);
1078 bounds.y = MAX (image_height - bounds.height - (y_padding * 3), 0);
1079
1080 text_x = bounds.x + x_padding + icon_width + x_padding;
1081 text_y = bounds.y + y_padding;
1082 icon_x = bounds.x + x_padding;
1083 icon_y = bounds.y + (bounds.height - icon_height) / 2;
1084
1085 cairo_save (cr);
1086
1087 /* background */
1088
1089 _cairo_draw_rounded_box (cr, bounds.x, bounds.y, bounds.width, bounds.height, 8.0);
1090 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.80);
1091 cairo_fill (cr);
1092 cairo_set_line_width (cr, 1.0);
1093 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
1094 cairo_stroke (cr);
1095
1096 /* icon */
1097
1098 gdk_cairo_set_source_pixbuf (cr, icon, icon_x, icon_y);
1099 cairo_rectangle (cr, icon_x, icon_y, icon_width, icon_height);
1100 cairo_fill (cr);
1101
1102 /* text */
1103
1104 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
1105 pango_cairo_update_layout (cr, layout);
1106 cairo_move_to (cr, text_x, text_y);
1107 pango_cairo_show_layout (cr, layout);
1108
1109 cairo_restore (cr);
1110
1111 g_free (text);
1112 pango_attr_list_unref (attr_list);
1113 g_object_unref (layout);
1114 g_string_free (file_info, TRUE);
1115 }
1116
1117
1118 static void
gth_image_viewer_page_real_activate(GthViewerPage * base,GthBrowser * browser)1119 gth_image_viewer_page_real_activate (GthViewerPage *base,
1120 GthBrowser *browser)
1121 {
1122 GthImageViewerPage *self;
1123
1124 self = (GthImageViewerPage*) base;
1125
1126 self->priv->browser = browser;
1127 self->priv->active = TRUE;
1128
1129 g_action_map_add_action_entries (G_ACTION_MAP (browser),
1130 actions,
1131 G_N_ELEMENTS (actions),
1132 browser);
1133
1134 self->priv->buttons[0] =
1135 gth_browser_add_header_bar_button (browser,
1136 GTH_BROWSER_HEADER_SECTION_VIEWER_ZOOM,
1137 "view-zoom-original-symbolic",
1138 _("Set to actual size"),
1139 "win.image-zoom-100",
1140 NULL);
1141 self->priv->buttons[1] =
1142 gth_browser_add_header_bar_button (browser,
1143 GTH_BROWSER_HEADER_SECTION_VIEWER_ZOOM,
1144 "view-zoom-fit-symbolic",
1145 _("Fit to window if larger"),
1146 "win.image-zoom-fit-if-larger",
1147 NULL);
1148
1149 self->priv->builder = gtk_builder_new_from_resource ("/org/gnome/gThumb/image_viewer/data/ui/toolbar-zoom-menu.ui");
1150 self->priv->buttons[ZOOM_BUTTON] =
1151 gth_browser_add_header_bar_menu_button (browser,
1152 GTH_BROWSER_HEADER_SECTION_VIEWER_ZOOM,
1153 "view-zoom-in-symbolic",
1154 NULL,
1155 _gtk_builder_get_widget (self->priv->builder, "zoom_popover"));
1156 g_signal_connect (_gtk_builder_get_widget (self->priv->builder, "zoom_level_scale"),
1157 "value-changed",
1158 G_CALLBACK (zoom_scale_value_changed_cb),
1159 self);
1160 g_signal_connect (self->priv->buttons[ZOOM_BUTTON],
1161 "toggled",
1162 G_CALLBACK (zoom_button_toggled_cb),
1163 self);
1164 g_signal_connect (_gtk_builder_get_widget (self->priv->builder, "zoom_popover"),
1165 "closed",
1166 G_CALLBACK (zoom_popover_closed_cb),
1167 self);
1168
1169 self->priv->buttons[APPLY_ICC_PROFILE_BUTTON] =
1170 gth_browser_add_header_bar_toggle_button (browser,
1171 GTH_BROWSER_HEADER_SECTION_VIEWER_OTHER_COMMANDS,
1172 "color-profile",
1173 _("Apply the embedded color profile"),
1174 "win.apply-icc-profile",
1175 NULL);
1176
1177 self->priv->buttons[TOGGLE_ANIMATION_BUTTON] =
1178 gth_browser_add_header_bar_toggle_button (browser,
1179 GTH_BROWSER_HEADER_SECTION_VIEWER_COMMANDS,
1180 "media-playback-start-symbolic",
1181 _("Play"),
1182 "win.toggle-animation",
1183 NULL);
1184 self->priv->buttons[STEP_ANIMATION_BUTTON] =
1185 gth_browser_add_header_bar_button (browser,
1186 GTH_BROWSER_HEADER_SECTION_VIEWER_COMMANDS,
1187 "media-skip-forward-symbolic",
1188 _("Next frame"),
1189 "win.step-animation",
1190 NULL);
1191 self->priv->buttons[TRANSPARENCY_STYLE_BUTTON] =
1192 gth_browser_add_header_bar_menu_button (browser,
1193 GTH_BROWSER_HEADER_SECTION_VIEWER_OTHER_COMMANDS,
1194 "transparency-symbolic",
1195 _("Transparency"),
1196 _gtk_builder_get_widget (self->priv->builder, "transparency_popover"));
1197
1198 self->priv->preloader = gth_browser_get_image_preloader (browser);
1199
1200 self->priv->viewer = gth_image_viewer_new ();
1201 g_object_add_weak_pointer (G_OBJECT (self->priv->viewer), (gpointer *) &self->priv->viewer);
1202 gtk_widget_add_events (self->priv->viewer, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1203 gth_image_viewer_page_reset_viewer_tool (self);
1204 gtk_widget_show (self->priv->viewer);
1205
1206 g_signal_connect (G_OBJECT (self->priv->viewer),
1207 "zoom-changed",
1208 G_CALLBACK (viewer_zoom_changed_cb),
1209 self);
1210 g_signal_connect (G_OBJECT (self->priv->viewer),
1211 "image-changed",
1212 G_CALLBACK (viewer_image_changed_cb),
1213 self);
1214 g_signal_connect (G_OBJECT (self->priv->viewer),
1215 "popup-menu",
1216 G_CALLBACK (viewer_popup_menu_cb),
1217 self);
1218 g_signal_connect_after (G_OBJECT (self->priv->viewer),
1219 "button_press_event",
1220 G_CALLBACK (viewer_button_press_event_cb),
1221 self);
1222 g_signal_connect_after (G_OBJECT (self->priv->viewer),
1223 "scroll_event",
1224 G_CALLBACK (viewer_scroll_event_cb),
1225 self);
1226 g_signal_connect_after (G_OBJECT (self->priv->viewer),
1227 "map_event",
1228 G_CALLBACK (viewer_image_map_event_cb),
1229 self);
1230 g_signal_connect (G_OBJECT (self->priv->viewer),
1231 "realize",
1232 G_CALLBACK (viewer_realize_cb),
1233 self);
1234 g_signal_connect (G_OBJECT (self->priv->viewer),
1235 "unrealize",
1236 G_CALLBACK (viewer_unrealize_cb),
1237 self);
1238
1239 self->priv->image_navigator = gtk_overlay_new ();
1240 g_signal_connect (self->priv->image_navigator,
1241 "get-child-position",
1242 G_CALLBACK (image_navigator_get_child_position_cb),
1243 self);
1244 gtk_container_add (GTK_CONTAINER (self->priv->image_navigator), self->priv->viewer);
1245 gtk_widget_show (self->priv->image_navigator);
1246
1247 self->priv->overview_revealer = gtk_revealer_new ();
1248 gtk_revealer_set_transition_duration (GTK_REVEALER (self->priv->overview_revealer), 200);
1249 gtk_revealer_set_transition_type (GTK_REVEALER (self->priv->overview_revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN);
1250 gtk_widget_show (self->priv->overview_revealer);
1251 gtk_overlay_add_overlay (GTK_OVERLAY (self->priv->image_navigator), self->priv->overview_revealer);
1252
1253 self->priv->overview = gth_image_overview_new (GTH_IMAGE_VIEWER (self->priv->viewer));
1254 gtk_widget_add_events (self->priv->overview, GDK_POINTER_MOTION_HINT_MASK | GDK_LEAVE_NOTIFY_MASK);
1255 gtk_widget_show (self->priv->overview);
1256 gtk_container_add (GTK_CONTAINER (self->priv->overview_revealer), self->priv->overview);
1257
1258 g_signal_connect_after (G_OBJECT (self->priv->overview),
1259 "motion-notify-event",
1260 G_CALLBACK (overview_motion_notify_event_cb),
1261 self);
1262 g_signal_connect_after (G_OBJECT (self->priv->overview),
1263 "leave-notify-event",
1264 G_CALLBACK (overview_leave_notify_event_cb),
1265 self);
1266
1267 gth_browser_set_viewer_widget (browser, self->priv->image_navigator);
1268 gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
1269 }
1270
1271
1272 static void
gth_image_viewer_page_real_deactivate(GthViewerPage * base)1273 gth_image_viewer_page_real_deactivate (GthViewerPage *base)
1274 {
1275 GthImageViewerPage *self;
1276 int i;
1277
1278 self = (GthImageViewerPage*) base;
1279
1280 for (i = 0; i < N_HEADER_BAR_BUTTONS; i++) {
1281 if (self->priv->buttons[i] != NULL) {
1282 gtk_widget_destroy (self->priv->buttons[i]);
1283 self->priv->buttons[i] = NULL;
1284 }
1285 }
1286
1287 _g_object_unref (self->priv->builder);
1288 self->priv->builder = NULL;
1289 _g_object_unref (self->priv->preloader);
1290 self->priv->preloader = NULL;
1291 self->priv->active = FALSE;
1292
1293 gth_browser_set_viewer_widget (self->priv->browser, NULL);
1294 }
1295
1296
1297 static void
gth_image_viewer_page_real_show(GthViewerPage * base)1298 gth_image_viewer_page_real_show (GthViewerPage *base)
1299 {
1300 GthImageViewerPage *self = (GthImageViewerPage*) base;
1301
1302 if (self->priv->file_popup_merge_id == 0)
1303 self->priv->file_popup_merge_id =
1304 gth_menu_manager_append_entries (gth_browser_get_menu_manager (self->priv->browser, GTH_BROWSER_MENU_MANAGER_FILE_EDIT_ACTIONS),
1305 file_popup_entries,
1306 G_N_ELEMENTS (file_popup_entries));
1307 gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
1308 }
1309
1310
1311 static void
gth_image_viewer_page_real_hide(GthViewerPage * base)1312 gth_image_viewer_page_real_hide (GthViewerPage *base)
1313 {
1314 GthImageViewerPage *self = (GthImageViewerPage*) base;
1315
1316 gth_menu_manager_remove_entries (gth_browser_get_menu_manager (self->priv->browser, GTH_BROWSER_MENU_MANAGER_FILE_EDIT_ACTIONS), self->priv->file_popup_merge_id);
1317 self->priv->file_popup_merge_id = 0;
1318 }
1319
1320
1321 static gboolean
gth_image_viewer_page_real_can_view(GthViewerPage * base,GthFileData * file_data)1322 gth_image_viewer_page_real_can_view (GthViewerPage *base,
1323 GthFileData *file_data)
1324 {
1325 g_return_val_if_fail (file_data != NULL, FALSE);
1326 return _g_mime_type_is_image (gth_file_data_get_mime_type (file_data));
1327 }
1328
1329
1330 static void
preloader_load_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1331 preloader_load_ready_cb (GObject *source_object,
1332 GAsyncResult *result,
1333 gpointer user_data)
1334 {
1335 GthImageViewerPage *self = user_data;
1336 GthFileData *requested;
1337 GthImage *image;
1338 int requested_size;
1339 int original_width;
1340 int original_height;
1341 GError *error = NULL;
1342
1343 self->priv->loading_image = FALSE;
1344 if (! _gth_image_viewer_page_load_with_preloader_finish (self))
1345 return;
1346
1347 if (! gth_image_preloader_load_finish (GTH_IMAGE_PRELOADER (source_object),
1348 result,
1349 &requested,
1350 &image,
1351 &requested_size,
1352 &original_width,
1353 &original_height,
1354 &error))
1355 {
1356 if (! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1357 gth_image_viewer_page_file_loaded (self, FALSE);
1358 g_clear_error (&error);
1359 return;
1360 }
1361
1362 if (! _g_file_equal (requested->file, self->priv->file_data->file))
1363 goto clear_data;
1364
1365 if (image == NULL) {
1366 gth_image_viewer_page_file_loaded (self, FALSE);
1367 goto clear_data;
1368 }
1369
1370 gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
1371
1372 gth_image_viewer_set_image (GTH_IMAGE_VIEWER (self->priv->viewer),
1373 image,
1374 original_width,
1375 original_height);
1376 gth_image_viewer_set_requested_size (GTH_IMAGE_VIEWER (self->priv->viewer), requested_size);
1377 gtk_widget_queue_draw (self->priv->viewer);
1378
1379 gth_image_history_clear (self->priv->history);
1380 gth_image_history_add_image (self->priv->history,
1381 image,
1382 requested_size,
1383 FALSE);
1384
1385 if ((original_width == -1) || (original_height == -1))
1386 /* In this case the image was loaded at its original size,
1387 * so we get the original size from the image surface size. */
1388 gth_image_viewer_get_original_size (GTH_IMAGE_VIEWER (self->priv->viewer),
1389 &original_width,
1390 &original_height);
1391 g_file_info_set_attribute_int32 (self->priv->updated_info,
1392 "frame::width",
1393 original_width);
1394 g_file_info_set_attribute_int32 (self->priv->updated_info,
1395 "frame::height",
1396 original_height);
1397
1398 {
1399 GthICCProfile *profile;
1400
1401 profile = gth_image_get_icc_profile (image);
1402 if (profile != NULL) {
1403 char *desc = gth_icc_profile_get_description (profile);
1404
1405 if (desc != NULL) {
1406 g_file_info_set_attribute_string (self->priv->updated_info,
1407 "Loaded::Image::ColorProfile",
1408 desc);
1409 g_free (desc);
1410 }
1411 }
1412 }
1413
1414 gth_image_viewer_page_file_loaded (self, TRUE);
1415 update_image_quality_if_required (self);
1416
1417 clear_data:
1418
1419 _g_object_unref (requested);
1420 _g_object_unref (image);
1421 g_clear_error (&error);
1422
1423 return;
1424 }
1425
1426
1427 static void
_gth_image_viewer_page_load(GthImageViewerPage * self,GthFileData * file_data)1428 _gth_image_viewer_page_load (GthImageViewerPage *self,
1429 GthFileData *file_data)
1430 {
1431 GthFileStore *file_store;
1432 GtkTreeIter iter;
1433 int i;
1434
1435 if (self->priv->file_data != file_data) {
1436 _g_object_unref (self->priv->file_data);
1437 self->priv->file_data = gth_file_data_dup (file_data);
1438 _g_object_unref (self->priv->updated_info);
1439 self->priv->updated_info = g_file_info_new ();
1440 }
1441 self->priv->image_changed = FALSE;
1442 self->priv->loading_image = TRUE;
1443
1444 for (i = 0; i < N_FORWARD_PRELOADERS; i++)
1445 _g_clear_object (&self->priv->next_file_data[i]);
1446 for (i = 0; i < N_BACKWARD_PRELOADERS; i++)
1447 _g_clear_object (&self->priv->prev_file_data[i]);
1448
1449 file_store = gth_browser_get_file_store (self->priv->browser);
1450 if (gth_file_store_find_visible (file_store, self->priv->file_data->file, &iter)) {
1451 GtkTreeIter next_iter;
1452
1453 next_iter = iter;
1454 for (i = 0; i < N_FORWARD_PRELOADERS; i++) {
1455 if (! gth_file_store_get_next_visible (file_store, &next_iter))
1456 break;
1457 self->priv->next_file_data[i] = g_object_ref (gth_file_store_get_file (file_store, &next_iter));
1458 }
1459
1460 next_iter = iter;
1461 for (i = 0; i < N_BACKWARD_PRELOADERS; i++) {
1462 if (! gth_file_store_get_prev_visible (file_store, &next_iter))
1463 break;
1464 self->priv->prev_file_data[i] = g_object_ref (gth_file_store_get_file (file_store, &next_iter));
1465 }
1466
1467 gth_image_viewer_set_void (GTH_IMAGE_VIEWER (self->priv->viewer));
1468 }
1469
1470 _gth_image_viewer_page_load_with_preloader (self,
1471 self->priv->file_data,
1472 #ifdef ALWAYS_LOAD_ORIGINAL_SIZE
1473 GTH_ORIGINAL_SIZE,
1474 #else
1475 _gth_image_preloader_get_requested_size_for_next_images (self),
1476 #endif
1477 NULL,
1478 preloader_load_ready_cb,
1479 self);
1480 }
1481
1482
1483 static void
gth_image_viewer_page_real_view(GthViewerPage * base,GthFileData * file_data)1484 gth_image_viewer_page_real_view (GthViewerPage *base,
1485 GthFileData *file_data)
1486 {
1487 GthImageViewerPage *self;
1488
1489 self = (GthImageViewerPage*) base;
1490 g_return_if_fail (file_data != NULL);
1491 g_return_if_fail (self->priv->active);
1492
1493 gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
1494
1495 _g_clear_object (&self->priv->last_loaded);
1496
1497 if ((self->priv->file_data != NULL)
1498 && g_file_equal (file_data->file, self->priv->file_data->file)
1499 && (gth_file_data_get_mtime (file_data) == gth_file_data_get_mtime (self->priv->file_data))
1500 && ! self->priv->image_changed)
1501 {
1502 gth_image_viewer_page_file_loaded (self, TRUE);
1503 return;
1504 }
1505
1506 _gth_image_viewer_page_load (self, file_data);
1507 }
1508
1509
1510 static void
gth_image_viewer_page_real_focus(GthViewerPage * base)1511 gth_image_viewer_page_real_focus (GthViewerPage *base)
1512 {
1513 GtkWidget *widget;
1514
1515 widget = GTH_IMAGE_VIEWER_PAGE (base)->priv->viewer;
1516 if (gtk_widget_get_realized (widget) && gtk_widget_get_mapped (widget))
1517 gtk_widget_grab_focus (widget);
1518 }
1519
1520
1521 static void
gth_image_viewer_page_real_fullscreen(GthViewerPage * base,gboolean active)1522 gth_image_viewer_page_real_fullscreen (GthViewerPage *base,
1523 gboolean active)
1524 {
1525 GthImageViewerPage *self;
1526 GthImageViewerTool *tool;
1527
1528 self = (GthImageViewerPage *) base;
1529 tool = gth_image_viewer_get_tool (GTH_IMAGE_VIEWER (self->priv->viewer));
1530
1531 if (! GTH_IS_IMAGE_DRAGGER (tool))
1532 return;
1533
1534 g_object_set (tool, "show-frame", ! active, NULL);
1535 }
1536
1537
1538 static void
gth_image_viewer_page_real_show_pointer(GthViewerPage * base,gboolean show)1539 gth_image_viewer_page_real_show_pointer (GthViewerPage *base,
1540 gboolean show)
1541 {
1542 GthImageViewerPage *self;
1543
1544 self = (GthImageViewerPage *) base;
1545
1546 if (show)
1547 gth_image_viewer_show_cursor (GTH_IMAGE_VIEWER (self->priv->viewer));
1548 else if (gth_browser_get_is_fullscreen (self->priv->browser))
1549 gth_image_viewer_hide_cursor (GTH_IMAGE_VIEWER (self->priv->viewer));
1550
1551 if (self->priv->hide_overview_id != 0) {
1552 g_source_remove (self->priv->hide_overview_id);
1553 self->priv->hide_overview_id = 0;
1554 }
1555
1556 self->priv->pointer_on_viewer = show;
1557 update_overview_visibility (self);
1558 }
1559
1560
1561 static void
gth_image_viewer_page_real_update_sensitivity(GthViewerPage * base)1562 gth_image_viewer_page_real_update_sensitivity (GthViewerPage *base)
1563 {
1564 GthImageViewerPage *self;
1565 GthImage *image;
1566 gboolean enable_action;
1567 gboolean is_animation;
1568
1569 self = (GthImageViewerPage*) base;
1570
1571 gth_window_enable_action (GTH_WINDOW (self->priv->browser), "image-undo", gth_image_history_can_undo (self->priv->history));
1572 gth_window_enable_action (GTH_WINDOW (self->priv->browser), "image-redo", gth_image_history_can_redo (self->priv->history));
1573
1574 image = gth_image_viewer_get_image (GTH_IMAGE_VIEWER (self->priv->viewer));
1575 enable_action = (image != NULL) && (gth_image_get_icc_profile (image) != NULL);
1576 gtk_widget_set_visible (self->priv->buttons[APPLY_ICC_PROFILE_BUTTON], enable_action);
1577 gth_window_enable_action (GTH_WINDOW (self->priv->browser), "apply-icc-profile", enable_action);
1578
1579 enable_action = (self->priv->file_data != NULL) && _g_mime_type_has_transparency (gth_file_data_get_mime_type (self->priv->file_data));
1580 gtk_widget_set_visible (self->priv->buttons[TRANSPARENCY_STYLE_BUTTON], enable_action);
1581 gth_window_enable_action (GTH_WINDOW (self->priv->browser), "transparency-style", enable_action);
1582
1583 is_animation = gth_image_viewer_is_animation (GTH_IMAGE_VIEWER (self->priv->viewer));
1584 gtk_widget_set_visible (self->priv->buttons[TOGGLE_ANIMATION_BUTTON], is_animation);
1585 gtk_widget_set_visible (self->priv->buttons[STEP_ANIMATION_BUTTON], is_animation);
1586 gth_window_enable_action (GTH_WINDOW (self->priv->browser), "step-animation", ! gth_image_viewer_is_playing_animation (GTH_IMAGE_VIEWER (self->priv->viewer)));
1587
1588 _gth_image_viewer_page_update_paste_command_sensitivity (self, NULL);
1589
1590 update_zoom_info (self);
1591 }
1592
1593
1594 typedef struct {
1595 GthImageViewerPage *self;
1596 GthFileData *file_to_save;
1597 GthFileData *original_file;
1598 FileSavedFunc func;
1599 gpointer user_data;
1600 } SaveData;
1601
1602
1603 static void
save_data_free(SaveData * data)1604 save_data_free (SaveData *data)
1605 {
1606 g_object_unref (data->file_to_save);
1607 g_object_unref (data->original_file);
1608 g_free (data);
1609 }
1610
1611
1612 static void
save_image_task_completed_cb(GthTask * task,GError * error,gpointer user_data)1613 save_image_task_completed_cb (GthTask *task,
1614 GError *error,
1615 gpointer user_data)
1616 {
1617 SaveData *data = user_data;
1618 GthImageViewerPage *self = data->self;
1619 gboolean error_occurred;
1620
1621 error_occurred = error != NULL;
1622
1623 if (error_occurred) {
1624 gth_file_data_set_file (data->file_to_save, data->original_file->file);
1625 g_file_info_set_attribute_boolean (data->file_to_save->info, "gth::file::is-modified", FALSE);
1626 }
1627
1628 if (data->func != NULL)
1629 (data->func) ((GthViewerPage *) self, data->file_to_save, error, data->user_data);
1630 else if (error != NULL)
1631 _gtk_error_dialog_from_gerror_show (GTK_WINDOW (self->priv->browser), _("Could not save the file"), error);
1632
1633 if (! error_occurred) {
1634 GFile *folder;
1635 GList *file_list;
1636
1637 folder = g_file_get_parent (data->file_to_save->file);
1638 file_list = g_list_prepend (NULL, g_object_ref (data->file_to_save->file));
1639 gth_monitor_folder_changed (gth_main_get_default_monitor (),
1640 folder,
1641 file_list,
1642 GTH_MONITOR_EVENT_CHANGED);
1643
1644 _g_object_list_unref (file_list);
1645 g_object_unref (folder);
1646 }
1647
1648 save_data_free (data);
1649 _g_object_unref (task);
1650 }
1651
1652
1653 static void
_gth_image_viewer_page_real_save(GthViewerPage * base,GFile * file,const char * mime_type,FileSavedFunc func,gpointer user_data)1654 _gth_image_viewer_page_real_save (GthViewerPage *base,
1655 GFile *file,
1656 const char *mime_type,
1657 FileSavedFunc func,
1658 gpointer user_data)
1659 {
1660 GthImageViewerPage *self;
1661 SaveData *data;
1662 GthFileData *current_file;
1663 GthTask *task;
1664
1665 self = (GthImageViewerPage *) base;
1666
1667 data = g_new0 (SaveData, 1);
1668 data->self = self;
1669 data->func = func;
1670 data->user_data = user_data;
1671
1672 if (mime_type == NULL)
1673 mime_type = gth_file_data_get_mime_type (self->priv->file_data);
1674
1675 current_file = gth_browser_get_current_file (self->priv->browser);
1676 if (current_file == NULL)
1677 return;
1678
1679 data->file_to_save = g_object_ref (current_file);
1680 data->original_file = gth_file_data_dup (current_file);
1681 if (file != NULL)
1682 gth_file_data_set_file (data->file_to_save, file);
1683
1684 /* save the value of 'gth::file::is-modified' into 'gth::file::image-changed'
1685 * to allow the exiv2 metadata writer to not change some fields if the
1686 * content wasn't modified. */
1687 g_file_info_set_attribute_boolean (data->file_to_save->info,
1688 "gth::file::image-changed",
1689 g_file_info_get_attribute_boolean (data->file_to_save->info, "gth::file::is-modified"));
1690
1691 /* the 'gth::file::is-modified' attribute must be set to false before
1692 * saving the file to avoid a scenario where the user is asked whether
1693 * he wants to save the file after saving it.
1694 * This is because when a file is modified in the current folder the
1695 * folder_changed_cb function in gth-browser.c is called automatically
1696 * and if the current file has been modified it is reloaded
1697 * (see file_attributes_ready_cb in gth-browser.c) and if it has been
1698 * modified ('gth::file::is-modified' is TRUE) the user is asked if he
1699 * wants to save (see load_file_delayed_cb in gth-browser.c). */
1700 g_file_info_set_attribute_boolean (data->file_to_save->info, "gth::file::is-modified", FALSE);
1701
1702 task = gth_image_task_chain_new (_("Saving"),
1703 gth_original_image_task_new (self),
1704 gth_save_image_task_new (NULL, mime_type, data->file_to_save, GTH_OVERWRITE_RESPONSE_YES),
1705 NULL);
1706 g_signal_connect (task,
1707 "completed",
1708 G_CALLBACK (save_image_task_completed_cb),
1709 data);
1710 gth_browser_exec_task (GTH_BROWSER (self->priv->browser), task, GTH_TASK_FLAGS_DEFAULT);
1711 }
1712
1713
1714 static gboolean
gth_image_viewer_page_real_can_save(GthViewerPage * base)1715 gth_image_viewer_page_real_can_save (GthViewerPage *base)
1716 {
1717 GArray *savers;
1718
1719 savers = gth_main_get_type_set ("image-saver");
1720 return (savers != NULL) && (savers->len > 0);
1721 }
1722
1723
1724 static void
gth_image_viewer_page_real_save(GthViewerPage * base,GFile * file,FileSavedFunc func,gpointer user_data)1725 gth_image_viewer_page_real_save (GthViewerPage *base,
1726 GFile *file,
1727 FileSavedFunc func,
1728 gpointer user_data)
1729 {
1730 _gth_image_viewer_page_real_save (base, file, NULL, func, user_data);
1731 }
1732
1733
1734 /* -- gth_image_viewer_page_real_save_as -- */
1735
1736
1737 typedef struct {
1738 GthImageViewerPage *self;
1739 FileSavedFunc func;
1740 gpointer user_data;
1741 GthFileData *file_data;
1742 GtkWidget *file_sel;
1743 } SaveAsData;
1744
1745
1746 static void
save_as_destroy_cb(GtkWidget * w,SaveAsData * data)1747 save_as_destroy_cb (GtkWidget *w,
1748 SaveAsData *data)
1749 {
1750 g_object_unref (data->file_data);
1751 g_free (data);
1752 }
1753
1754
1755 static void
save_as_response_cb(GtkDialog * file_sel,int response,SaveAsData * data)1756 save_as_response_cb (GtkDialog *file_sel,
1757 int response,
1758 SaveAsData *data)
1759 {
1760 GFile *file;
1761 const char *mime_type;
1762
1763 if (response != GTK_RESPONSE_OK) {
1764 if (data->func != NULL) {
1765 (*data->func) ((GthViewerPage *) data->self,
1766 data->file_data,
1767 g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, ""),
1768 data->user_data);
1769 }
1770 gtk_widget_destroy (GTK_WIDGET (file_sel));
1771 return;
1772 }
1773
1774 if (! gth_file_chooser_dialog_get_file (GTH_FILE_CHOOSER_DIALOG (file_sel), &file, &mime_type))
1775 return;
1776
1777 gtk_widget_hide (GTK_WIDGET (data->file_sel));
1778
1779 gth_file_data_set_file (data->file_data, file);
1780 _gth_image_viewer_page_real_save ((GthViewerPage *) data->self,
1781 file,
1782 mime_type,
1783 data->func,
1784 data->user_data);
1785
1786 gtk_widget_destroy (GTK_WIDGET (data->file_sel));
1787
1788 g_object_unref (file);
1789 }
1790
1791
1792 static void
gth_image_viewer_page_real_save_as(GthViewerPage * base,FileSavedFunc func,gpointer user_data)1793 gth_image_viewer_page_real_save_as (GthViewerPage *base,
1794 FileSavedFunc func,
1795 gpointer user_data)
1796 {
1797 GthImageViewerPage *self;
1798 GtkWidget *file_sel;
1799 char *uri;
1800 SaveAsData *data;
1801
1802 self = GTH_IMAGE_VIEWER_PAGE (base);
1803 file_sel = gth_file_chooser_dialog_new (_("Save Image"),
1804 GTK_WINDOW (self->priv->browser),
1805 "image-saver");
1806 _gtk_dialog_add_class_to_response (GTK_DIALOG (file_sel), GTK_RESPONSE_OK, GTK_STYLE_CLASS_SUGGESTED_ACTION);
1807
1808 uri = g_file_get_uri (self->priv->file_data->file);
1809 gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (file_sel), uri);
1810
1811 data = g_new0 (SaveAsData, 1);
1812 data->self = self;
1813 data->func = func;
1814 data->file_data = gth_file_data_dup (self->priv->file_data);
1815 data->user_data = user_data;
1816 data->file_sel = file_sel;
1817
1818 g_signal_connect (GTK_DIALOG (file_sel),
1819 "response",
1820 G_CALLBACK (save_as_response_cb),
1821 data);
1822 g_signal_connect (G_OBJECT (file_sel),
1823 "destroy",
1824 G_CALLBACK (save_as_destroy_cb),
1825 data);
1826
1827 gtk_window_set_transient_for (GTK_WINDOW (file_sel), GTK_WINDOW (self->priv->browser));
1828 gtk_window_set_modal (GTK_WINDOW (file_sel), TRUE);
1829 gtk_widget_show (file_sel);
1830
1831 g_free (uri);
1832 }
1833
1834
1835 static void
_gth_image_viewer_page_set_image(GthImageViewerPage * self,GthImage * image,int requested_size,gboolean modified)1836 _gth_image_viewer_page_set_image (GthImageViewerPage *self,
1837 GthImage *image,
1838 int requested_size,
1839 gboolean modified)
1840 {
1841 GthFileData *file_data;
1842 int width;
1843 int height;
1844
1845 if (image == NULL)
1846 return;
1847
1848 if (modified)
1849 gth_image_preloader_set_modified_image (self->priv->preloader, image);
1850 gth_image_viewer_set_image (GTH_IMAGE_VIEWER (self->priv->viewer), image, -1, -1);
1851 gth_image_viewer_set_requested_size (GTH_IMAGE_VIEWER (self->priv->viewer), requested_size);
1852
1853 file_data = gth_browser_get_current_file (GTH_BROWSER (self->priv->browser));
1854
1855 self->priv->image_changed = modified;
1856 g_file_info_set_attribute_boolean (file_data->info, "gth::file::is-modified", modified);
1857
1858 if (gth_image_get_original_size (image, &width, &height)) {
1859 char *size;
1860
1861 g_file_info_set_attribute_int32 (file_data->info, "image::width", width);
1862 g_file_info_set_attribute_int32 (file_data->info, "image::height", height);
1863
1864 size = g_strdup_printf (_("%d × %d"), width, height);
1865 g_file_info_set_attribute_string (file_data->info, "general::dimensions", size);
1866 g_free (size);
1867 }
1868
1869 gth_monitor_metadata_changed (gth_main_get_default_monitor (), file_data);
1870
1871 update_image_quality_if_required (self);
1872 }
1873
1874
1875 static void
_gth_image_viewer_page_set_surface(GthImageViewerPage * self,cairo_surface_t * surface,int requested_size,gboolean modified)1876 _gth_image_viewer_page_set_surface (GthImageViewerPage *self,
1877 cairo_surface_t *surface,
1878 int requested_size,
1879 gboolean modified)
1880 {
1881 GthImage *image;
1882
1883 image = gth_image_new_for_surface (surface);
1884 _gth_image_viewer_page_set_image (self, image, requested_size, modified);
1885
1886 g_object_unref (image);
1887 }
1888
1889
1890 static void
gth_image_viewer_page_real_revert(GthViewerPage * base)1891 gth_image_viewer_page_real_revert (GthViewerPage *base)
1892 {
1893 GthImageViewerPage *self = GTH_IMAGE_VIEWER_PAGE (base);
1894 GthImageData *last_saved;
1895
1896 last_saved = gth_image_history_revert (self->priv->history);
1897 if (last_saved == NULL)
1898 return;
1899
1900 gth_image_history_add_image (self->priv->history,
1901 last_saved->image,
1902 last_saved->requested_size,
1903 last_saved->unsaved);
1904 _gth_image_viewer_page_set_image (self,
1905 last_saved->image,
1906 last_saved->requested_size,
1907 last_saved->unsaved);
1908
1909 gth_image_data_unref (last_saved);
1910 }
1911
1912
1913 static void
gth_image_viewer_page_real_update_info(GthViewerPage * base,GthFileData * file_data)1914 gth_image_viewer_page_real_update_info (GthViewerPage *base,
1915 GthFileData *file_data)
1916 {
1917 GthImageViewerPage *self = GTH_IMAGE_VIEWER_PAGE (base);
1918
1919 if (! _g_file_equal (self->priv->file_data->file, file_data->file))
1920 return;
1921 _g_object_unref (self->priv->file_data);
1922 self->priv->file_data = gth_file_data_dup (file_data);
1923
1924 if (self->priv->viewer == NULL)
1925 return;
1926
1927 gtk_widget_queue_draw (self->priv->viewer);
1928 }
1929
1930
1931 static gboolean
gth_image_viewer_page_real_zoom_from_scroll(GthViewerPage * base,GdkEventScroll * event)1932 gth_image_viewer_page_real_zoom_from_scroll (GthViewerPage *base,
1933 GdkEventScroll *event)
1934 {
1935 GthImageViewerPage *self = GTH_IMAGE_VIEWER_PAGE (base);
1936 return gth_image_viewer_zoom_from_scroll (GTH_IMAGE_VIEWER (self->priv->viewer), event);
1937 }
1938
1939
1940 static void
gth_image_viewer_page_real_show_properties(GthViewerPage * base,gboolean show)1941 gth_image_viewer_page_real_show_properties (GthViewerPage *base,
1942 gboolean show)
1943 {
1944 GthImageViewerPage *self;
1945
1946 self = GTH_IMAGE_VIEWER_PAGE (base);
1947
1948 if (show)
1949 gth_image_viewer_add_painter (GTH_IMAGE_VIEWER (self->priv->viewer), paint_comment_over_image_func, self);
1950 else
1951 gth_image_viewer_remove_painter (GTH_IMAGE_VIEWER (self->priv->viewer), paint_comment_over_image_func, self);
1952 gtk_widget_queue_draw (self->priv->viewer);
1953 }
1954
1955
1956 static void
gth_image_viewer_page_finalize(GObject * obj)1957 gth_image_viewer_page_finalize (GObject *obj)
1958 {
1959 GthImageViewerPage *self;
1960 int i;
1961
1962 self = GTH_IMAGE_VIEWER_PAGE (obj);
1963
1964 if (self->priv->update_quality_id != 0) {
1965 g_source_remove (self->priv->update_quality_id);
1966 self->priv->update_quality_id = 0;
1967 }
1968 if (self->priv->update_visibility_id != 0) {
1969 g_source_remove (self->priv->update_visibility_id);
1970 self->priv->update_visibility_id = 0;
1971 }
1972 if (self->priv->hide_overview_id != 0) {
1973 g_source_remove (self->priv->hide_overview_id);
1974 self->priv->hide_overview_id = 0;
1975 }
1976
1977 g_object_unref (self->priv->settings);
1978 g_object_unref (self->priv->history);
1979 _g_object_unref (self->priv->file_data);
1980 _g_object_unref (self->priv->last_loaded);
1981
1982 for (i = 0; i < N_FORWARD_PRELOADERS; i++)
1983 _g_clear_object (&self->priv->next_file_data[i]);
1984 for (i = 0; i < N_BACKWARD_PRELOADERS; i++)
1985 _g_clear_object (&self->priv->prev_file_data[i]);
1986
1987 G_OBJECT_CLASS (gth_image_viewer_page_parent_class)->finalize (obj);
1988 }
1989
1990
1991 static void
gth_image_viewer_page_class_init(GthImageViewerPageClass * klass)1992 gth_image_viewer_page_class_init (GthImageViewerPageClass *klass)
1993 {
1994 G_OBJECT_CLASS (klass)->finalize = gth_image_viewer_page_finalize;
1995 }
1996
1997
1998 static void
gth_viewer_page_interface_init(GthViewerPageInterface * iface)1999 gth_viewer_page_interface_init (GthViewerPageInterface *iface)
2000 {
2001 iface->activate = gth_image_viewer_page_real_activate;
2002 iface->deactivate = gth_image_viewer_page_real_deactivate;
2003 iface->show = gth_image_viewer_page_real_show;
2004 iface->hide = gth_image_viewer_page_real_hide;
2005 iface->can_view = gth_image_viewer_page_real_can_view;
2006 iface->view = gth_image_viewer_page_real_view;
2007 iface->focus = gth_image_viewer_page_real_focus;
2008 iface->fullscreen = gth_image_viewer_page_real_fullscreen;
2009 iface->show_pointer = gth_image_viewer_page_real_show_pointer;
2010 iface->update_sensitivity = gth_image_viewer_page_real_update_sensitivity;
2011 iface->can_save = gth_image_viewer_page_real_can_save;
2012 iface->save = gth_image_viewer_page_real_save;
2013 iface->save_as = gth_image_viewer_page_real_save_as;
2014 iface->revert = gth_image_viewer_page_real_revert;
2015 iface->update_info = gth_image_viewer_page_real_update_info;
2016 iface->zoom_from_scroll = gth_image_viewer_page_real_zoom_from_scroll;
2017 iface->show_properties = gth_image_viewer_page_real_show_properties;
2018 }
2019
2020
2021 static void
gth_image_viewer_page_init(GthImageViewerPage * self)2022 gth_image_viewer_page_init (GthImageViewerPage *self)
2023 {
2024 int i;
2025
2026 self->priv = gth_image_viewer_page_get_instance_private (self);
2027 self->priv->settings = g_settings_new (GTHUMB_IMAGE_VIEWER_SCHEMA);
2028 self->priv->preloader = NULL;
2029 self->priv->file_popup_merge_id = 0;
2030 self->priv->history = gth_image_history_new ();
2031 self->priv->file_data = NULL;
2032 self->priv->updated_info = NULL;
2033 self->priv->active = FALSE;
2034 self->priv->image_changed = FALSE;
2035 self->priv->loading_image = FALSE;
2036 self->priv->last_loaded = NULL;
2037 self->priv->can_paste = FALSE;
2038 self->priv->update_quality_id = 0;
2039 self->priv->update_visibility_id = 0;
2040 for (i = 0; i < N_HEADER_BAR_BUTTONS; i++)
2041 self->priv->buttons[i] = NULL;
2042 self->priv->builder = NULL;
2043 self->priv->pointer_on_overview = FALSE;
2044 self->priv->pointer_on_viewer = FALSE;
2045 self->priv->hide_overview_id = 0;
2046 self->priv->apply_icc_profile = TRUE;
2047
2048 for (i = 0; i < N_FORWARD_PRELOADERS; i++)
2049 self->priv->next_file_data[i] = NULL;
2050 for (i = 0; i < N_BACKWARD_PRELOADERS; i++)
2051 self->priv->prev_file_data[i] = NULL;
2052
2053 self->priv->drag_data_get_event = 0;
2054
2055 /* settings notifications */
2056
2057 g_signal_connect (self->priv->settings,
2058 "changed::" PREF_IMAGE_VIEWER_ZOOM_QUALITY,
2059 G_CALLBACK (pref_zoom_quality_changed),
2060 self);
2061 g_signal_connect (self->priv->settings,
2062 "changed::" PREF_IMAGE_VIEWER_ZOOM_CHANGE,
2063 G_CALLBACK (pref_zoom_change_changed),
2064 self);
2065 g_signal_connect (self->priv->settings,
2066 "changed::" PREF_IMAGE_VIEWER_RESET_SCROLLBARS,
2067 G_CALLBACK (pref_reset_scrollbars_changed),
2068 self);
2069 g_signal_connect (self->priv->settings,
2070 "changed::" PREF_IMAGE_VIEWER_TRANSPARENCY_STYLE,
2071 G_CALLBACK (pref_transparency_style_changed),
2072 self);
2073 }
2074
2075
2076 GtkWidget *
gth_image_viewer_page_get_image_viewer(GthImageViewerPage * self)2077 gth_image_viewer_page_get_image_viewer (GthImageViewerPage *self)
2078 {
2079 return self->priv->viewer;
2080 }
2081
2082
2083 GdkPixbuf *
gth_image_viewer_page_get_pixbuf(GthImageViewerPage * self)2084 gth_image_viewer_page_get_pixbuf (GthImageViewerPage *self)
2085 {
2086 return gth_image_viewer_get_current_pixbuf (GTH_IMAGE_VIEWER (self->priv->viewer));
2087 }
2088
2089
2090 void
gth_image_viewer_page_set_pixbuf(GthImageViewerPage * self,GdkPixbuf * pixbuf,gboolean add_to_history)2091 gth_image_viewer_page_set_pixbuf (GthImageViewerPage *self,
2092 GdkPixbuf *pixbuf,
2093 gboolean add_to_history)
2094 {
2095 cairo_surface_t *image;
2096
2097 image = _cairo_image_surface_create_from_pixbuf (pixbuf);
2098 gth_image_viewer_page_set_image (self, image, add_to_history);
2099
2100 cairo_surface_destroy (image);
2101 }
2102
2103
2104 cairo_surface_t *
gth_image_viewer_page_get_current_image(GthImageViewerPage * self)2105 gth_image_viewer_page_get_current_image (GthImageViewerPage *self)
2106 {
2107 return gth_image_viewer_get_current_image (GTH_IMAGE_VIEWER (self->priv->viewer));
2108 }
2109
2110
2111 cairo_surface_t *
gth_image_viewer_page_get_modified_image(GthImageViewerPage * self)2112 gth_image_viewer_page_get_modified_image (GthImageViewerPage *self)
2113 {
2114 return gth_image_preloader_get_modified_image (self->priv->preloader);
2115 }
2116
2117
2118 void
gth_image_viewer_page_set_image(GthImageViewerPage * self,cairo_surface_t * image,gboolean add_to_history)2119 gth_image_viewer_page_set_image (GthImageViewerPage *self,
2120 cairo_surface_t *image,
2121 gboolean add_to_history)
2122 {
2123 if (gth_image_viewer_page_get_current_image (self) == image)
2124 return;
2125
2126 if (add_to_history)
2127 gth_image_history_add_surface (self->priv->history, image, -1, TRUE);
2128
2129 _gth_image_viewer_page_set_surface (self, image, -1, TRUE);
2130
2131 if (add_to_history)
2132 gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
2133 }
2134
2135
2136 void
gth_image_viewer_page_undo(GthImageViewerPage * self)2137 gth_image_viewer_page_undo (GthImageViewerPage *self)
2138 {
2139 GthImageData *idata;
2140
2141 idata = gth_image_history_undo (self->priv->history);
2142 if (idata != NULL)
2143 _gth_image_viewer_page_set_image (self, idata->image, idata->requested_size, idata->unsaved);
2144 }
2145
2146
2147 void
gth_image_viewer_page_redo(GthImageViewerPage * self)2148 gth_image_viewer_page_redo (GthImageViewerPage *self)
2149 {
2150 GthImageData *idata;
2151
2152 idata = gth_image_history_redo (self->priv->history);
2153 if (idata != NULL)
2154 _gth_image_viewer_page_set_image (self, idata->image, idata->requested_size, idata->unsaved);
2155 }
2156
2157
2158 GthImageHistory *
gth_image_viewer_page_get_history(GthImageViewerPage * self)2159 gth_image_viewer_page_get_history (GthImageViewerPage *self)
2160 {
2161 return self->priv->history;
2162 }
2163
2164
2165 void
gth_image_viewer_page_reset(GthImageViewerPage * self)2166 gth_image_viewer_page_reset (GthImageViewerPage *self)
2167 {
2168 GthImageData *last_image;
2169
2170 last_image = gth_image_history_get_last (self->priv->history);
2171 if (last_image == NULL)
2172 return;
2173
2174 _gth_image_viewer_page_set_image (self, last_image->image, last_image->requested_size, last_image->unsaved);
2175 }
2176
2177
2178 static void
viewer_drag_data_get_cb(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * data,guint info,guint time,gpointer user_data)2179 viewer_drag_data_get_cb (GtkWidget *widget,
2180 GdkDragContext *context,
2181 GtkSelectionData *data,
2182 guint info,
2183 guint time,
2184 gpointer user_data)
2185 {
2186 GthImageViewerPage *self = user_data;
2187 char *uris[2];
2188
2189 if (self->priv->file_data == NULL)
2190 return;
2191
2192 uris[0] = g_file_get_uri (self->priv->file_data->file);
2193 uris[1] = NULL;
2194 gtk_selection_data_set_uris (data, (char **) uris);
2195
2196 g_free (uris[0]);
2197 }
2198
2199
2200 static void
_gth_image_viewer_page_enable_drag_source(GthImageViewerPage * self,gboolean enable)2201 _gth_image_viewer_page_enable_drag_source (GthImageViewerPage *self,
2202 gboolean enable)
2203 {
2204 GthImageViewerTool *dragger;
2205 GtkTargetList *source_target_list;
2206 GtkTargetEntry *source_targets;
2207 int n_source_targets;
2208
2209 dragger = gth_image_viewer_get_tool (GTH_IMAGE_VIEWER (self->priv->viewer));
2210 if (! GTH_IS_IMAGE_DRAGGER (dragger))
2211 return;
2212
2213 if (! enable) {
2214 if (self->priv->drag_data_get_event > 0) {
2215 g_signal_handler_disconnect (self->priv->viewer, self->priv->drag_data_get_event);
2216 self->priv->drag_data_get_event = 0;
2217 }
2218 gth_image_dragger_disable_drag_source (GTH_IMAGE_DRAGGER (dragger));
2219 return;
2220 }
2221
2222 source_target_list = gtk_target_list_new (NULL, 0);
2223 gtk_target_list_add_uri_targets (source_target_list, 0);
2224 gtk_target_list_add_text_targets (source_target_list, 0);
2225 source_targets = gtk_target_table_new_from_list (source_target_list, &n_source_targets);
2226 gth_image_dragger_enable_drag_source (GTH_IMAGE_DRAGGER (dragger),
2227 GDK_BUTTON1_MASK,
2228 source_targets,
2229 n_source_targets,
2230 GDK_ACTION_COPY | GDK_ACTION_MOVE);
2231 gtk_target_table_free (source_targets, n_source_targets);
2232 gtk_target_list_unref (source_target_list);
2233
2234 if (self->priv->drag_data_get_event == 0)
2235 self->priv->drag_data_get_event = g_signal_connect (self->priv->viewer,
2236 "drag-data-get",
2237 G_CALLBACK (viewer_drag_data_get_cb),
2238 self);
2239 }
2240
2241
2242 void
gth_image_viewer_page_reset_viewer_tool(GthImageViewerPage * self)2243 gth_image_viewer_page_reset_viewer_tool (GthImageViewerPage *self)
2244 {
2245 GthImageViewerTool *dragger;
2246
2247 dragger = gth_image_dragger_new (TRUE);
2248 gth_image_viewer_set_tool (GTH_IMAGE_VIEWER (self->priv->viewer), dragger);
2249 g_object_unref (dragger);
2250
2251 gth_image_viewer_set_fit_mode (GTH_IMAGE_VIEWER (self->priv->viewer), GTH_FIT_SIZE_IF_LARGER);
2252 gth_image_viewer_set_zoom_quality (GTH_IMAGE_VIEWER (self->priv->viewer),
2253 g_settings_get_enum (self->priv->settings, PREF_IMAGE_VIEWER_ZOOM_QUALITY));
2254 gth_image_viewer_set_zoom_change (GTH_IMAGE_VIEWER (self->priv->viewer),
2255 g_settings_get_enum (self->priv->settings, PREF_IMAGE_VIEWER_ZOOM_CHANGE));
2256 gth_image_viewer_set_reset_scrollbars (GTH_IMAGE_VIEWER (self->priv->viewer),
2257 g_settings_get_boolean (self->priv->settings, PREF_IMAGE_VIEWER_RESET_SCROLLBARS));
2258 gth_image_viewer_enable_key_bindings (GTH_IMAGE_VIEWER (self->priv->viewer), FALSE);
2259 pref_transparency_style_changed (self->priv->settings, NULL, self);
2260 _gth_image_viewer_page_enable_drag_source (self, TRUE);
2261 }
2262
2263
2264 gboolean
gth_image_viewer_page_get_is_modified(GthImageViewerPage * self)2265 gth_image_viewer_page_get_is_modified (GthImageViewerPage *self)
2266 {
2267 return self->priv->image_changed;
2268 }
2269
2270
2271 /* -- gth_image_viewer_page_copy_image -- */
2272
2273
2274 static void
copy_image_original_image_ready_cb(GthTask * task,GError * error,gpointer user_data)2275 copy_image_original_image_ready_cb (GthTask *task,
2276 GError *error,
2277 gpointer user_data)
2278 {
2279 GthImageViewerPage *self = user_data;
2280 cairo_surface_t *image;
2281
2282 image = gth_original_image_task_get_image (task);
2283 if (image != NULL) {
2284 GtkClipboard *clipboard;
2285 GdkPixbuf *pixbuf;
2286
2287 clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (self->priv->viewer), GDK_SELECTION_CLIPBOARD);
2288 pixbuf = _gdk_pixbuf_new_from_cairo_surface (image);
2289 gtk_clipboard_set_image (clipboard, pixbuf);
2290
2291 g_object_unref (pixbuf);
2292 }
2293
2294 cairo_surface_destroy (image);
2295 g_object_unref (task);
2296 }
2297
2298
2299 void
gth_image_viewer_page_copy_image(GthImageViewerPage * self)2300 gth_image_viewer_page_copy_image (GthImageViewerPage *self)
2301 {
2302 GthTask *task;
2303
2304 task = gth_original_image_task_new (self);
2305 g_signal_connect (task,
2306 "completed",
2307 G_CALLBACK (copy_image_original_image_ready_cb),
2308 self);
2309 gth_browser_exec_task (self->priv->browser, task, GTH_TASK_FLAGS_DEFAULT);
2310 }
2311
2312
2313 static void
clipboard_image_received_cb(GtkClipboard * clipboard,GdkPixbuf * pixbuf,gpointer user_data)2314 clipboard_image_received_cb (GtkClipboard *clipboard,
2315 GdkPixbuf *pixbuf,
2316 gpointer user_data)
2317 {
2318 GthImageViewerPage *self = user_data;
2319
2320 if (pixbuf != NULL)
2321 gth_image_viewer_page_set_pixbuf (self, pixbuf, TRUE);
2322 g_object_unref (self);
2323 }
2324
2325
2326 void
gth_image_viewer_page_paste_image(GthImageViewerPage * self)2327 gth_image_viewer_page_paste_image (GthImageViewerPage *self)
2328 {
2329 GtkClipboard *clipboard;
2330
2331 clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (self->priv->viewer), GDK_SELECTION_CLIPBOARD);
2332 gtk_clipboard_request_image (clipboard,
2333 clipboard_image_received_cb,
2334 g_object_ref (self));
2335 }
2336
2337
2338 /* -- gth_image_viewer_page_get_original -- */
2339
2340
2341 typedef struct {
2342 GthImageViewerPage *viewer_page;
2343 GTask *task;
2344 GCancellable *cancellable;
2345 } OriginalImageData;
2346
2347
2348 static OriginalImageData *
get_original_data_new(void)2349 get_original_data_new (void)
2350 {
2351 OriginalImageData *data;
2352
2353 data = g_new0 (OriginalImageData, 1);
2354 data->cancellable = NULL;
2355 data->task = NULL;
2356
2357 return data;
2358 }
2359
2360
2361 static void
get_original_data_free(OriginalImageData * data)2362 get_original_data_free (OriginalImageData *data)
2363 {
2364 if (data == NULL)
2365 return;
2366
2367 _g_object_unref (data->viewer_page);
2368 _g_object_unref (data->cancellable);
2369 _g_object_unref (data->task);
2370 g_free (data);
2371 }
2372
2373
2374 static void
original_image_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2375 original_image_ready_cb (GObject *source_object,
2376 GAsyncResult *result,
2377 gpointer user_data)
2378 {
2379 OriginalImageData *data = user_data;
2380 GthImage *image = NULL;
2381 GError *error = NULL;
2382
2383 if (! _gth_image_viewer_page_load_with_preloader_finish (data->viewer_page)) {
2384 g_task_return_error (data->task, g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, ""));
2385 get_original_data_free (data);
2386 return;
2387 }
2388
2389 if (! gth_image_preloader_load_finish (GTH_IMAGE_PRELOADER (source_object),
2390 result,
2391 NULL,
2392 &image,
2393 NULL,
2394 NULL,
2395 NULL,
2396 &error))
2397 {
2398 g_task_return_error (data->task, error);
2399 }
2400 else
2401 g_task_return_pointer (data->task, image, (GDestroyNotify) g_object_unref);
2402
2403 get_original_data_free (data);
2404 }
2405
2406
2407 void
gth_image_viewer_page_get_original(GthImageViewerPage * self,GCancellable * cancellable,GAsyncReadyCallback ready_callback,gpointer user_data)2408 gth_image_viewer_page_get_original (GthImageViewerPage *self,
2409 GCancellable *cancellable,
2410 GAsyncReadyCallback ready_callback,
2411 gpointer user_data)
2412 {
2413 OriginalImageData *data;
2414
2415 data = get_original_data_new ();
2416 data->viewer_page = g_object_ref (self);
2417 data->cancellable = (cancellable != NULL) ? g_object_ref (cancellable) : g_cancellable_new ();
2418 data->task = g_task_new (G_OBJECT (self),
2419 data->cancellable,
2420 ready_callback,
2421 user_data);
2422
2423 if (gth_image_viewer_is_animation (GTH_IMAGE_VIEWER (self->priv->viewer))) {
2424 GthImage *image;
2425
2426 image = gth_image_new_for_surface (gth_image_viewer_get_current_image (GTH_IMAGE_VIEWER (self->priv->viewer)));
2427 g_task_return_pointer (data->task, image, (GDestroyNotify) g_object_unref);
2428
2429 get_original_data_free (data);
2430 }
2431 else {
2432 _gth_image_viewer_page_load_with_preloader (self,
2433 self->priv->image_changed ? GTH_MODIFIED_IMAGE : self->priv->file_data,
2434 GTH_ORIGINAL_SIZE,
2435 data->cancellable,
2436 original_image_ready_cb,
2437 data);
2438 }
2439 }
2440
2441
2442 gboolean
gth_image_viewer_page_get_original_finish(GthImageViewerPage * self,GAsyncResult * result,cairo_surface_t ** image_p,GError ** error)2443 gth_image_viewer_page_get_original_finish (GthImageViewerPage *self,
2444 GAsyncResult *result,
2445 cairo_surface_t **image_p,
2446 GError **error)
2447 {
2448 GthImage *image;
2449
2450 g_return_val_if_fail (g_task_is_valid (G_TASK (result), G_OBJECT (self)), FALSE);
2451
2452 image = g_task_propagate_pointer (G_TASK (result), error);
2453 if (image == NULL)
2454 return FALSE;
2455
2456 if (image_p != NULL)
2457 *image_p = gth_image_get_cairo_surface (image);
2458
2459 g_object_unref (image);
2460
2461 return TRUE;
2462 }
2463
2464
2465 /* -- GthOriginalImageTask -- */
2466
2467
2468 #define GTH_TYPE_ORIGINAL_IMAGE_TASK (gth_original_image_task_get_type ())
2469 #define GTH_ORIGINAL_IMAGE_TASK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTH_TYPE_ORIGINAL_IMAGE_TASK, GthOriginalImageTask))
2470
2471
2472 typedef struct _GthOriginalImageTask GthOriginalImageTask;
2473 typedef struct _GthOriginalImageTaskClass GthOriginalImageTaskClass;
2474
2475
2476 struct _GthOriginalImageTask {
2477 GthImageTask __parent;
2478 GthImageViewerPage *viewer_page;
2479 };
2480
2481
2482 struct _GthOriginalImageTaskClass {
2483 GthImageTaskClass __parent;
2484 };
2485
2486 GType gth_original_image_task_get_type (void);
2487
G_DEFINE_TYPE(GthOriginalImageTask,gth_original_image_task,GTH_TYPE_IMAGE_TASK)2488 G_DEFINE_TYPE (GthOriginalImageTask, gth_original_image_task, GTH_TYPE_IMAGE_TASK)
2489
2490
2491 static void
2492 get_original_image_ready_cb (GObject *source_object,
2493 GAsyncResult *result,
2494 gpointer user_data)
2495 {
2496 GthOriginalImageTask *self = user_data;
2497 cairo_surface_t *image = NULL;
2498 GError *error = NULL;
2499
2500 if (gth_image_viewer_page_get_original_finish (self->viewer_page,
2501 result,
2502 &image,
2503 &error))
2504 {
2505 gth_image_task_set_destination_surface (GTH_IMAGE_TASK (self), image);
2506 }
2507 gth_task_completed (GTH_TASK (self), error);
2508
2509 cairo_surface_destroy (image);
2510 _g_error_free (error);
2511 }
2512
2513
2514 static void
gth_original_image_task_exec(GthTask * base)2515 gth_original_image_task_exec (GthTask *base)
2516 {
2517 GthOriginalImageTask *self = GTH_ORIGINAL_IMAGE_TASK (base);
2518
2519 gth_task_progress (base, _("Loading the original image"), NULL, TRUE, 0.0);
2520 gth_image_viewer_page_get_original (self->viewer_page,
2521 gth_task_get_cancellable (base),
2522 get_original_image_ready_cb,
2523 self);
2524 }
2525
2526
2527 static void
gth_original_image_task_class_init(GthOriginalImageTaskClass * class)2528 gth_original_image_task_class_init (GthOriginalImageTaskClass *class)
2529 {
2530 GthTaskClass *task_class;
2531
2532 task_class = GTH_TASK_CLASS (class);
2533 task_class->exec = gth_original_image_task_exec;
2534 }
2535
2536
2537 static void
gth_original_image_task_init(GthOriginalImageTask * self)2538 gth_original_image_task_init (GthOriginalImageTask *self)
2539 {
2540 self->viewer_page = NULL;
2541 }
2542
2543
2544 GthTask *
gth_original_image_task_new(GthImageViewerPage * self)2545 gth_original_image_task_new (GthImageViewerPage *self)
2546 {
2547 GthOriginalImageTask *task;
2548
2549 task = g_object_new (GTH_TYPE_ORIGINAL_IMAGE_TASK, NULL);
2550 task->viewer_page = self;
2551
2552 return GTH_TASK (task);
2553 }
2554
2555
2556 cairo_surface_t *
gth_original_image_task_get_image(GthTask * task)2557 gth_original_image_task_get_image (GthTask *task)
2558 {
2559 return gth_image_task_get_destination_surface (GTH_IMAGE_TASK (task));
2560 }
2561
2562
2563 void
gth_image_viewer_page_apply_icc_profile(GthImageViewerPage * self,gboolean apply)2564 gth_image_viewer_page_apply_icc_profile (GthImageViewerPage *self,
2565 gboolean apply)
2566 {
2567 GthFileData *file_data;
2568
2569 g_return_if_fail (self->priv->active);
2570
2571 self->priv->apply_icc_profile = apply;
2572 gth_image_preloader_clear_cache (self->priv->preloader);
2573
2574 file_data = gth_browser_get_current_file (self->priv->browser);
2575 if (file_data == NULL)
2576 return;
2577
2578 /* force a complete reload */
2579 _g_object_unref (self->priv->last_loaded);
2580 self->priv->last_loaded = NULL;
2581
2582 g_object_ref (file_data);
2583 _gth_image_viewer_page_load (self, file_data);
2584
2585 g_object_unref (file_data);
2586 }
2587