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, &current_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