1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  GThumb
5  *
6  *  Copyright (C) 2001-2011 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 <stdlib.h>
24 #include <math.h>
25 #include <glib/gi18n.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <gtk/gtk.h>
28 #include "cairo-utils.h"
29 #include "gth-enum-types.h"
30 #include "gth-image-dragger.h"
31 #include "gth-image-viewer.h"
32 #include "gth-marshal.h"
33 #include "gtk-utils.h"
34 #include "glib-utils.h"
35 #include "pixbuf-utils.h"
36 
37 
38 #define DRAG_THRESHOLD  1     /* When dragging the image ignores movements
39 			       * smaller than this value. */
40 #define MINIMUM_DELAY   10    /* When an animation frame has a 0 milli seconds
41 			       * delay use this delay instead. */
42 #define STEP_INCREMENT  20.0  /* Scroll increment. */
43 #define GRAY_VALUE 0.2
44 #define CHECKED_PATTERN_SIZE 20
45 
46 
47 enum {
48 	PROP_0,
49 	PROP_HADJUSTMENT,
50 	PROP_VADJUSTMENT,
51 	PROP_HSCROLL_POLICY,
52 	PROP_VSCROLL_POLICY
53 };
54 
55 
56 enum {
57 	CLICKED,
58 	ZOOM_IN,
59 	ZOOM_OUT,
60 	SET_ZOOM,
61 	SET_FIT_MODE,
62 	IMAGE_CHANGED,
63 	ZOOM_CHANGED,
64 	BETTER_QUALITY,
65 	SCROLL,
66 	LAST_SIGNAL
67 };
68 
69 
70 struct _GthImageViewerPrivate {
71 	GtkScrollablePolicy     hscroll_policy;
72 	GtkScrollablePolicy     vscroll_policy;
73 
74 	GthImage               *image;
75 	cairo_surface_t        *surface;
76 	cairo_pattern_t        *background_pattern;
77 	GdkPixbufAnimation     *animation;
78 	int                     original_width;
79 	int                     original_height;
80 	int                     requested_size;
81 
82 	GdkPixbufAnimationIter *iter;
83 	GTimeVal                time;               /* Timer used to get the current frame. */
84 	guint                   anim_id;
85 	cairo_surface_t        *iter_surface;
86 
87 	gboolean                is_animation;
88 	gboolean                play_animation;
89 	gboolean                cursor_visible;
90 
91 	gboolean                frame_visible;
92 	int                     frame_border;
93 
94 	GthImageViewerTool     *tool;
95 
96 	GdkCursor              *cursor;
97 	GdkCursor              *cursor_void;
98 
99 	gboolean                zoom_enabled;
100 	gboolean                enable_key_bindings;
101 	double                  zoom_level;
102 	guint                   zoom_quality : 1;   /* A ZoomQualityType value. */
103 	guint                   zoom_change : 3;    /* A ZoomChangeType value. */
104 
105 	GthFit                  fit;
106 	gboolean                doing_zoom_fit;     /* Whether we are performing
107 					             * a zoom to fit the window. */
108 	gboolean                is_void;            /* If TRUE do not show anything.
109 					             * It is reset to FALSE we an
110 					             * image is loaded. */
111 	gboolean                double_click;
112 	gboolean                just_focused;
113 	gboolean                skip_zoom_change;
114 	gboolean                update_image_after_zoom;
115 	gboolean                reset_scrollbars;
116 	GthTransparencyStyle    transparency_style;
117 	GList                  *painters;
118 };
119 
120 
121 static guint gth_image_viewer_signals[LAST_SIGNAL] = { 0 };
122 
123 
124 G_DEFINE_TYPE_WITH_CODE (GthImageViewer,
125 			 gth_image_viewer,
126 			 GTK_TYPE_WIDGET,
127 			 G_ADD_PRIVATE (GthImageViewer)
128 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
129 
130 
131 typedef struct {
132 	GthImageViewerPaintFunc func;
133 	gpointer                user_data;
134 } PainterData;
135 
136 
137 static void
painter_data_free(PainterData * painter_data)138 painter_data_free (PainterData *painter_data)
139 {
140 	g_free (painter_data);
141 }
142 
143 
144 static void
gth_image_viewer_finalize(GObject * object)145 gth_image_viewer_finalize (GObject *object)
146 {
147 	GthImageViewer *self;
148 
149 	g_return_if_fail (GTH_IS_IMAGE_VIEWER (object));
150 
151 	self = GTH_IMAGE_VIEWER (object);
152 
153 	if (self->priv->anim_id != 0)
154 		g_source_remove (self->priv->anim_id);
155 
156 	if (self->priv->cursor != NULL)
157 		g_object_unref (self->priv->cursor);
158 
159 	if (self->priv->cursor_void != NULL)
160 		g_object_unref (self->priv->cursor_void);
161 
162 	if (self->hadj != NULL) {
163 		_g_signal_handlers_disconnect_by_data (G_OBJECT (self->hadj), self);
164 		g_object_unref (self->hadj);
165 	}
166 
167 	if (self->vadj != NULL) {
168 		_g_signal_handlers_disconnect_by_data (G_OBJECT (self->vadj), self);
169 		g_object_unref (self->vadj);
170 	}
171 
172 	g_list_foreach (self->priv->painters, (GFunc) painter_data_free, NULL);
173 	_g_object_unref (self->priv->tool);
174 
175 	_g_clear_object (&self->priv->image);
176 	_g_clear_object (&self->priv->animation);
177 	_g_clear_object (&self->priv->iter);
178 	_cairo_clear_surface (&self->priv->iter_surface);
179 	_cairo_clear_surface (&self->priv->surface);
180 	cairo_pattern_destroy (self->priv->background_pattern);
181 
182 	G_OBJECT_CLASS (gth_image_viewer_parent_class)->finalize (object);
183 }
184 
185 
186 static double zooms[] = {                  0.05, 0.07, 0.10,
187 			 0.15, 0.20, 0.30, 0.50, 0.75, 1.0,
188 			 1.5 , 2.0 , 3.0 , 5.0 , 7.5,  10.0,
189 			 15.0, 20.0, 30.0, 50.0, 75.0, 100.0};
190 
191 static const int nzooms = sizeof (zooms) / sizeof (gdouble);
192 
193 
194 static double
get_next_zoom(gdouble zoom)195 get_next_zoom (gdouble zoom)
196 {
197 	int i;
198 
199 	i = 0;
200 	while ((i < nzooms) && (zooms[i] <= zoom))
201 		i++;
202 	i = CLAMP (i, 0, nzooms - 1);
203 
204 	return zooms[i];
205 }
206 
207 
208 static double
get_prev_zoom(double zoom)209 get_prev_zoom (double zoom)
210 {
211 	int i;
212 
213 	i = nzooms - 1;
214 	while ((i >= 0) && (zooms[i] >= zoom))
215 		i--;
216 	i = CLAMP (i, 0, nzooms - 1);
217 
218 	return zooms[i];
219 }
220 
221 
222 static double
_gth_image_viewer_get_quality_zoom(GthImageViewer * self)223 _gth_image_viewer_get_quality_zoom (GthImageViewer *self)
224 {
225 	cairo_surface_t *image;
226 
227 	image = gth_image_viewer_get_current_image (self);
228 	if (self->priv->original_width <= 0)
229 		return 1.0;
230 
231 	return (double) cairo_image_surface_get_width (image) / self->priv->original_width;
232 }
233 
234 
235 static int
_gth_image_viewer_get_frame_border(GthImageViewer * self)236 _gth_image_viewer_get_frame_border (GthImageViewer *self)
237 {
238 	if (! self->priv->frame_visible)
239 		return 0;
240 
241 	return self->priv->frame_border;
242 }
243 
244 
245 static void
_gth_image_viewer_get_zoomed_size_for_zoom(GthImageViewer * self,int * width,int * height,double zoom_level)246 _gth_image_viewer_get_zoomed_size_for_zoom (GthImageViewer *self,
247 					    int            *width,
248 					    int            *height,
249 					    double          zoom_level)
250 {
251 	if (gth_image_viewer_get_current_image (self) == NULL) {
252 		if (width != NULL) *width = 0;
253 		if (height != NULL) *height = 0;
254 	}
255 	else {
256 		int frame_border = _gth_image_viewer_get_frame_border (self);
257 		if (width != NULL) *width  = (int) round ((double) self->priv->original_width * zoom_level) + (frame_border * 2);
258 		if (height != NULL) *height = (int) round ((double) self->priv->original_height * zoom_level) + (frame_border * 2);
259 	}
260 }
261 
262 
263 static void
_gth_image_viewer_get_zoomed_size(GthImageViewer * self,int * width,int * height)264 _gth_image_viewer_get_zoomed_size (GthImageViewer *self,
265 		 	 	   int            *width,
266 		 	 	   int            *height)
267 {
268 	_gth_image_viewer_get_zoomed_size_for_zoom (self, width, height, self->priv->zoom_level);
269 }
270 
271 
272 static void
_gth_image_viewer_update_visible_area(GthImageViewer * self,GtkAllocation * allocation)273 _gth_image_viewer_update_visible_area (GthImageViewer *self,
274 				       GtkAllocation  *allocation)
275 {
276 	GtkAllocation local_allocation;
277 
278 	if (allocation != NULL)
279 		local_allocation = *allocation;
280 	else
281 		gtk_widget_get_allocation (GTK_WIDGET (self), &local_allocation);
282 
283 	self->visible_area.width = local_allocation.width;
284 	self->visible_area.height = local_allocation.height;
285 }
286 
287 
288 static void
_gth_image_viewer_update_image_area(GthImageViewer * self)289 _gth_image_viewer_update_image_area (GthImageViewer *self)
290 {
291 	int zoomed_width;
292 	int zoomed_height;
293 
294 	_gth_image_viewer_get_zoomed_size (self, &zoomed_width, &zoomed_height);
295 
296 	self->frame_area.x = MAX (0, (self->visible_area.width - zoomed_width) / 2);
297 	self->frame_area.y = MAX (0, (self->visible_area.height - zoomed_height) / 2);
298 	self->frame_area.width = zoomed_width;
299 	self->frame_area.height = zoomed_height;
300 
301 	self->image_area.x = self->frame_area.x + self->priv->frame_border;
302 	self->image_area.y = self->frame_area.y + self->priv->frame_border;
303 	self->image_area.width = self->frame_area.width - (self->priv->frame_border * 2);
304 	self->image_area.height = self->frame_area.height - (self->priv->frame_border * 2);
305 }
306 
307 
308 static void _set_surface (GthImageViewer  *self,
309 			  cairo_surface_t *surface,
310 			  int              original_width,
311 			  int              original_height,
312 			  gboolean         better_quality);
313 
314 
315 static void
set_zoom(GthImageViewer * self,gdouble zoom_level,int center_x,int center_y)316 set_zoom (GthImageViewer *self,
317 	  gdouble         zoom_level,
318 	  int             center_x,
319 	  int             center_y)
320 {
321 	g_return_if_fail (self != NULL);
322 
323 	if (gth_image_get_is_zoomable (self->priv->image)) {
324 		int original_width;
325 		int original_height;
326 
327 		if (gth_image_set_zoom (self->priv->image,
328 				        zoom_level,
329 				        &original_width,
330 				        &original_height))
331 		{
332 			cairo_surface_t *surface;
333 
334 			surface = gth_image_get_cairo_surface (self->priv->image);
335 			if (surface != NULL) {
336 				_set_surface (self,
337 					      surface,
338 					      original_width,
339 					      original_height,
340 					      TRUE);
341 				cairo_surface_destroy (surface);
342 			}
343 		}
344 	}
345 
346 	/* try to keep the center of the view visible. */
347 
348 	{
349 		cairo_surface_t *image;
350 
351 		image = gth_image_viewer_get_current_image (self);
352 		if (image != NULL) {
353 			double quality_zoom;
354 			double old_zoom_level;
355 			int    x, y;
356 			int    frame_border;
357 			int    new_center_x, new_center_y;
358 
359 			quality_zoom = (double) self->priv->original_width / cairo_image_surface_get_width (image);
360 			old_zoom_level = self->priv->zoom_level * quality_zoom;
361 			x = (center_x + self->visible_area.x - self->image_area.x) / old_zoom_level;
362 			y = (center_y + self->visible_area.y - self->image_area.y) / old_zoom_level;
363 			frame_border = _gth_image_viewer_get_frame_border (self);
364 			new_center_x = x * zoom_level * quality_zoom + frame_border + 1;
365 			new_center_y = y * zoom_level * quality_zoom + frame_border + 1;
366 
367 			self->visible_area.x = new_center_x - (self->visible_area.width / 2);
368 			self->visible_area.y = new_center_y - (self->visible_area.height / 2);
369 		}
370 	}
371 
372 	self->priv->zoom_level = zoom_level;
373 
374 	_gth_image_viewer_update_image_area (self);
375 	if (self->priv->update_image_after_zoom) {
376 		g_signal_emit (G_OBJECT (self), gth_image_viewer_signals[IMAGE_CHANGED], 0);
377 		gth_image_viewer_tool_image_changed (self->priv->tool);
378 		self->priv->update_image_after_zoom = FALSE;
379 		self->priv->skip_zoom_change = TRUE;
380 	}
381 	else
382 		gth_image_viewer_tool_zoom_changed (self->priv->tool);
383 
384 	if (! self->priv->skip_zoom_change)
385 		g_signal_emit (G_OBJECT (self), gth_image_viewer_signals[ZOOM_CHANGED], 0);
386 	else
387 		self->priv->skip_zoom_change = FALSE;
388 }
389 
390 
391 static void
set_zoom_centered_at(GthImageViewer * self,double zoom_level,gboolean zoom_to_fit,int center_x,int center_y)392 set_zoom_centered_at (GthImageViewer *self,
393 		      double          zoom_level,
394 		      gboolean        zoom_to_fit,
395 		      int             center_x,
396 		      int             center_y)
397 {
398 	/* reset zoom_fit unless we are performing a zoom to fit. */
399 	if (! zoom_to_fit)
400 		self->priv->fit = GTH_FIT_NONE;
401 	set_zoom (self, zoom_level, center_x, center_y);
402 }
403 
404 
405 static void
set_zoom_centered(GthImageViewer * self,gdouble zoom_level,gboolean zoom_to_fit)406 set_zoom_centered (GthImageViewer *self,
407 		   gdouble         zoom_level,
408 		   gboolean        zoom_to_fit)
409 {
410 	set_zoom_centered_at (self,
411 			      zoom_level,
412 			      zoom_to_fit,
413 			      self->visible_area.width / 2,
414 			      self->visible_area.height / 2);
415 }
416 
417 
418 static void
gth_image_viewer_realize(GtkWidget * widget)419 gth_image_viewer_realize (GtkWidget *widget)
420 {
421 	GthImageViewer *self;
422 	GtkAllocation   allocation;
423 	GdkWindowAttr   attributes;
424 	int             attributes_mask;
425 	GdkWindow      *window;
426 
427 	g_return_if_fail (GTH_IS_IMAGE_VIEWER (widget));
428 
429 	self = GTH_IMAGE_VIEWER (widget);
430 	gtk_widget_set_realized (widget, TRUE);
431 	gtk_widget_get_allocation (widget, &allocation);
432 
433 	attributes.window_type = GDK_WINDOW_CHILD;
434 	attributes.x           = allocation.x;
435 	attributes.y           = allocation.y;
436 	attributes.width       = allocation.width;
437 	attributes.height      = allocation.height;
438 	attributes.wclass      = GDK_INPUT_OUTPUT;
439 	attributes.visual      = gtk_widget_get_visual (widget);
440 	attributes.event_mask  = (gtk_widget_get_events (widget)
441 				  | GDK_EXPOSURE_MASK
442 				  | GDK_BUTTON_PRESS_MASK
443 				  | GDK_BUTTON_RELEASE_MASK
444 				  | GDK_POINTER_MOTION_MASK
445 				  | GDK_POINTER_MOTION_HINT_MASK
446 				  | GDK_BUTTON_MOTION_MASK
447 				  | GDK_SCROLL_MASK
448 				  | GDK_STRUCTURE_MASK);
449 	attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_WMCLASS;
450 	window = gdk_window_new (gtk_widget_get_parent_window (widget),
451 				 &attributes,
452 				 attributes_mask);
453 	gtk_widget_register_window (widget, window);
454 	gtk_widget_set_window (widget, window);
455 	gtk_style_context_set_background (gtk_widget_get_style_context (widget), window);
456 
457 	self->priv->cursor = _gdk_cursor_new_for_widget (widget, GDK_LEFT_PTR);
458 	self->priv->cursor_void = _gdk_cursor_new_for_widget (widget, GDK_BLANK_CURSOR);
459 	if (self->priv->cursor_visible)
460 		gdk_window_set_cursor (window, self->priv->cursor);
461 	else
462 		gdk_window_set_cursor (window, self->priv->cursor_void);
463 
464 	gth_image_viewer_tool_realize (self->priv->tool);
465 }
466 
467 
468 static void
gth_image_viewer_unrealize(GtkWidget * widget)469 gth_image_viewer_unrealize (GtkWidget *widget)
470 {
471 	GthImageViewer *self;
472 
473 	g_return_if_fail (GTH_IS_IMAGE_VIEWER (widget));
474 
475 	self = GTH_IMAGE_VIEWER (widget);
476 
477 	if (self->priv->cursor) {
478 		g_object_unref (self->priv->cursor);
479 		self->priv->cursor = NULL;
480 	}
481 	if (self->priv->cursor_void) {
482 		g_object_unref (self->priv->cursor_void);
483 		self->priv->cursor_void = NULL;
484 	}
485 
486 	gth_image_viewer_tool_unrealize (self->priv->tool);
487 
488 	GTK_WIDGET_CLASS (gth_image_viewer_parent_class)->unrealize (widget);
489 }
490 
491 
492 static void
gth_image_viewer_map(GtkWidget * widget)493 gth_image_viewer_map (GtkWidget *widget)
494 {
495 	GthImageViewer *self;
496 
497 	g_return_if_fail (GTH_IS_IMAGE_VIEWER (widget));
498 
499 	GTK_WIDGET_CLASS (gth_image_viewer_parent_class)->map (widget);
500 
501 	self = GTH_IMAGE_VIEWER (widget);
502 	gth_image_viewer_tool_map (self->priv->tool);
503 }
504 
505 
506 static void
gth_image_viewer_unmap(GtkWidget * widget)507 gth_image_viewer_unmap (GtkWidget *widget)
508 {
509 	GthImageViewer *self;
510 
511 	g_return_if_fail (GTH_IS_IMAGE_VIEWER (widget));
512 
513 	self = GTH_IMAGE_VIEWER (widget);
514 	gth_image_viewer_tool_unmap (self->priv->tool);
515 
516 	GTK_WIDGET_CLASS (gth_image_viewer_parent_class)->unmap (widget);
517 }
518 
519 
520 static void
_gth_image_viewer_configure_hadjustment(GthImageViewer * self)521 _gth_image_viewer_configure_hadjustment (GthImageViewer *self)
522 {
523 	int zoomed_width;
524 
525 	_gth_image_viewer_get_zoomed_size (self, &zoomed_width, NULL);
526 	gtk_adjustment_configure (self->hadj,
527 				  self->visible_area.x,
528 				  0.0,
529 				  zoomed_width,
530 				  STEP_INCREMENT,
531 				  self->visible_area.width / 2,
532 				  self->visible_area.width);
533 }
534 
535 
536 static void
_gth_image_viewer_configure_vadjustment(GthImageViewer * self)537 _gth_image_viewer_configure_vadjustment (GthImageViewer *self)
538 {
539 	int zoomed_height;
540 
541 	_gth_image_viewer_get_zoomed_size (self, NULL, &zoomed_height);
542 	gtk_adjustment_configure (self->vadj,
543 				  self->visible_area.y,
544 				  0.0,
545 				  zoomed_height,
546 				  STEP_INCREMENT,
547 				  self->visible_area.height / 2,
548 				  self->visible_area.height);
549 }
550 
551 
552 static GtkSizeRequestMode
gth_image_viewer_get_request_mode(GtkWidget * widget)553 gth_image_viewer_get_request_mode (GtkWidget *widget)
554 {
555 	return GTK_SIZE_REQUEST_CONSTANT_SIZE;
556 }
557 
558 
559 static void
gth_image_viewer_get_preferred_width(GtkWidget * widget,int * minimum_width,int * natural_width)560 gth_image_viewer_get_preferred_width (GtkWidget *widget,
561 				      int       *minimum_width,
562 				      int       *natural_width)
563 {
564 	GthImageViewer *self = GTH_IMAGE_VIEWER (widget);
565 	int             zoomed_width;
566 
567 	_gth_image_viewer_get_zoomed_size (self, &zoomed_width, NULL);
568 	*minimum_width = 0;
569 	*natural_width = zoomed_width;
570 }
571 
572 
573 static void
gth_image_viewer_get_preferred_height(GtkWidget * widget,int * minimum_height,int * natural_height)574 gth_image_viewer_get_preferred_height (GtkWidget *widget,
575 				       int       *minimum_height,
576 				       int       *natural_height)
577 {
578 	GthImageViewer *self = GTH_IMAGE_VIEWER (widget);
579 	int             zoomed_height;
580 
581 	_gth_image_viewer_get_zoomed_size (self, NULL, &zoomed_height);
582 	*minimum_height = 0;
583 	*natural_height = zoomed_height;
584 }
585 
586 
587 static double
get_zoom_to_fit(GthImageViewer * self,GtkAllocation * allocation)588 get_zoom_to_fit (GthImageViewer *self,
589 		 GtkAllocation  *allocation)
590 {
591 	int    original_width;
592 	int    original_height;
593 	int    frame_border_2;
594 	double x_level;
595 	double y_level;
596 
597 	gth_image_viewer_get_original_size (self, &original_width, &original_height);
598 	frame_border_2 = _gth_image_viewer_get_frame_border (self) * 2;
599 
600 	x_level = (double) (allocation->width - frame_border_2) / original_width;
601 	y_level = (double) (allocation->height - frame_border_2) / original_height;
602 
603 	return (x_level < y_level) ? x_level : y_level;
604 }
605 
606 
607 static double
get_zoom_to_fit_width(GthImageViewer * self,GtkAllocation * allocation)608 get_zoom_to_fit_width (GthImageViewer *self,
609 		       GtkAllocation  *allocation)
610 {
611 	int original_width;
612 	int frame_border_2;
613 
614 	gth_image_viewer_get_original_size (self, &original_width, NULL);
615 	frame_border_2 = _gth_image_viewer_get_frame_border (self) * 2;
616 
617 	return (double) (allocation->width - frame_border_2) / original_width;
618 }
619 
620 
621 static double
get_zoom_to_fit_height(GthImageViewer * self,GtkAllocation * allocation)622 get_zoom_to_fit_height (GthImageViewer *self,
623 		        GtkAllocation  *allocation)
624 {
625 	int original_height;
626 	int frame_border_2;
627 
628 	gth_image_viewer_get_original_size (self, NULL, &original_height);
629 	frame_border_2 = _gth_image_viewer_get_frame_border (self) * 2;
630 
631 	return (double) (allocation->height - frame_border_2) / original_height;
632 }
633 
634 
635 static double
get_zoom_level_for_allocation(GthImageViewer * self,GtkAllocation * allocation)636 get_zoom_level_for_allocation (GthImageViewer *self,
637 			       GtkAllocation  *allocation)
638 {
639 	double           zoom_level;
640 	cairo_surface_t *current_image;
641 	int              original_width;
642 	int              original_height;
643 	int              frame_border_2;
644 
645 	zoom_level = self->priv->zoom_level;
646 	current_image = gth_image_viewer_get_current_image (self);
647 	if (self->priv->is_void || (current_image == NULL))
648 		return zoom_level;
649 
650 	gth_image_viewer_get_original_size (self, &original_width, &original_height);
651 	frame_border_2 = _gth_image_viewer_get_frame_border (self) * 2;
652 
653 	switch (self->priv->fit) {
654 	case GTH_FIT_SIZE:
655 		zoom_level = get_zoom_to_fit (self, allocation);
656 		break;
657 	case GTH_FIT_SIZE_IF_LARGER:
658 		if ((allocation->width < original_width + frame_border_2) || (allocation->height < original_height + frame_border_2))
659 			zoom_level = get_zoom_to_fit (self, allocation);
660 		else
661 			zoom_level = 1.0;
662 		break;
663 	case GTH_FIT_WIDTH:
664 		zoom_level = get_zoom_to_fit_width (self, allocation);
665 		break;
666 	case GTH_FIT_WIDTH_IF_LARGER:
667 		if (allocation->width < original_width + frame_border_2)
668 			zoom_level = get_zoom_to_fit_width (self, allocation);
669 		else
670 			zoom_level = 1.0;
671 		break;
672 	case GTH_FIT_HEIGHT:
673 		zoom_level = get_zoom_to_fit_height (self, allocation);
674 		break;
675 	case GTH_FIT_HEIGHT_IF_LARGER:
676 		if (allocation->height < original_height + frame_border_2)
677 			zoom_level = get_zoom_to_fit_height (self, allocation);
678 		else
679 			zoom_level = 1.0;
680 		break;
681 	default:
682 		break;
683 	}
684 
685 	return zoom_level;
686 }
687 
688 
689 static void
gth_image_viewer_size_allocate(GtkWidget * widget,GtkAllocation * allocation)690 gth_image_viewer_size_allocate (GtkWidget     *widget,
691 				GtkAllocation *allocation)
692 {
693 	GthImageViewer  *self = GTH_IMAGE_VIEWER (widget);
694 	double           zoom_level;
695 	int              zoomed_width;
696 	int              zoomed_height;
697 	cairo_surface_t *current_image;
698 
699 	_gth_image_viewer_update_visible_area (self, allocation);
700 
701 	gtk_widget_set_allocation (widget, allocation);
702 	if (gtk_widget_get_realized (widget))
703 		gdk_window_move_resize (gtk_widget_get_window (widget),
704 					allocation->x,
705 					allocation->y,
706 					allocation->width,
707 					allocation->height);
708 
709 	/* update the zoom level if the automatic fit mode is active */
710 
711 	zoom_level = get_zoom_level_for_allocation (self, allocation);
712 	if (self->priv->fit != GTH_FIT_NONE)
713 		set_zoom_centered (self, zoom_level, TRUE);
714 
715 	/* Keep the scrollbars offset in a valid range */
716 
717 	current_image = gth_image_viewer_get_current_image (self);
718 	if (current_image != NULL) {
719 		_gth_image_viewer_get_zoomed_size (self, &zoomed_width, &zoomed_height);
720 		self->visible_area.x = (zoomed_width <= self->visible_area.width) ? 0 : CLAMP (self->visible_area.x, 0, zoomed_width - self->visible_area.width);
721 		self->visible_area.y = (zoomed_height <= self->visible_area.height) ? 0 : CLAMP (self->visible_area.y, 0, zoomed_height - self->visible_area.height);
722 	}
723 
724 	_gth_image_viewer_configure_hadjustment (self);
725 	_gth_image_viewer_configure_vadjustment (self);
726 	_gth_image_viewer_update_image_area (self);
727 	gth_image_viewer_tool_size_allocate (self->priv->tool, allocation);
728 }
729 
730 
731 static gboolean
gth_image_viewer_focus_in(GtkWidget * widget,GdkEventFocus * event)732 gth_image_viewer_focus_in (GtkWidget     *widget,
733 			   GdkEventFocus *event)
734 {
735 	gtk_widget_queue_draw (widget);
736 	return TRUE;
737 }
738 
739 
740 static gboolean
gth_image_viewer_focus_out(GtkWidget * widget,GdkEventFocus * event)741 gth_image_viewer_focus_out (GtkWidget     *widget,
742 			    GdkEventFocus *event)
743 {
744 	gtk_widget_queue_draw (widget);
745 	return TRUE;
746 }
747 
748 
749 static int
gth_image_viewer_key_press(GtkWidget * widget,GdkEventKey * event)750 gth_image_viewer_key_press (GtkWidget   *widget,
751 			    GdkEventKey *event)
752 {
753 	gboolean handled;
754 
755 	handled = gtk_bindings_activate (G_OBJECT (widget),
756 					 event->keyval,
757 					 event->state);
758 	if (handled)
759 		return TRUE;
760 
761 	if (GTK_WIDGET_CLASS (gth_image_viewer_parent_class)->key_press_event &&
762 	    GTK_WIDGET_CLASS (gth_image_viewer_parent_class)->key_press_event (widget, event))
763 		return TRUE;
764 
765 	return FALSE;
766 }
767 
768 
769 static gboolean
change_animation_frame(gpointer data)770 change_animation_frame (gpointer data)
771 {
772 	GthImageViewer *self = data;
773 
774 	if (self->priv->anim_id != 0) {
775 		g_source_remove (self->priv->anim_id);
776 		self->priv->anim_id = 0;
777 	}
778 
779 	if (self->priv->is_void
780 	    || ! self->priv->is_animation
781 	    || (self->priv->iter == NULL))
782 	{
783 		return FALSE;
784 	}
785 
786 	g_time_val_add (&self->priv->time, (glong) gdk_pixbuf_animation_iter_get_delay_time (self->priv->iter) * 1000);
787 	gdk_pixbuf_animation_iter_advance (self->priv->iter, &self->priv->time);
788 
789 	_cairo_clear_surface (&self->priv->iter_surface);
790 	gtk_widget_queue_draw (GTK_WIDGET (self));
791 
792 	return FALSE;
793 }
794 
795 
796 static void
queue_animation_frame_change(GthImageViewer * self)797 queue_animation_frame_change (GthImageViewer *self)
798 {
799 	if (! self->priv->is_void
800 	    && self->priv->is_animation
801 	    && self->priv->play_animation
802 	    && (self->priv->anim_id == 0)
803 	    && (self->priv->iter != NULL))
804 	{
805 		self->priv->anim_id = g_timeout_add (MAX (gdk_pixbuf_animation_iter_get_delay_time (self->priv->iter), MINIMUM_DELAY),
806 						     change_animation_frame,
807 						     self);
808 	}
809 }
810 
811 
812 static gboolean
gth_image_viewer_draw(GtkWidget * widget,cairo_t * cr)813 gth_image_viewer_draw (GtkWidget *widget,
814 		       cairo_t   *cr)
815 {
816 	GthImageViewer *self = GTH_IMAGE_VIEWER (widget);
817 
818 	cairo_save (cr);
819 
820 	/* set the default values of the cairo context */
821 
822 	cairo_set_line_width (cr, 0.5);
823 	cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
824 
825 	/* delegate the rest to the tool  */
826 
827 	gth_image_viewer_tool_draw (self->priv->tool, cr);
828 
829 	cairo_restore (cr);
830 
831 	queue_animation_frame_change (self);
832 
833 	return TRUE;
834 }
835 
836 
837 static gboolean
gth_image_viewer_button_press(GtkWidget * widget,GdkEventButton * event)838 gth_image_viewer_button_press (GtkWidget      *widget,
839 			       GdkEventButton *event)
840 {
841 	GthImageViewer *self = GTH_IMAGE_VIEWER (widget);
842 	int             retval;
843 
844 	if (! gtk_widget_has_focus (widget)) {
845 		gtk_widget_grab_focus (widget);
846 		self->priv->just_focused = TRUE;
847 	}
848 
849 	if (self->dragging)
850 		return FALSE;
851 
852 	self->priv->double_click = ((event->type == GDK_2BUTTON_PRESS) || (event->type == GDK_3BUTTON_PRESS));
853 
854 	retval = gth_image_viewer_tool_button_press (self->priv->tool, event);
855 
856 	if (self->pressed && ! self->priv->double_click) {
857 		self->event_x_start = self->event_x_prev = event->x;
858 		self->event_y_start = self->event_y_prev = event->y;
859 		self->drag_x = self->drag_x_start = self->drag_x_prev = event->x + self->visible_area.x;
860 		self->drag_y = self->drag_y_start = self->drag_y_prev = event->y + self->visible_area.y;
861 	}
862 
863 	return retval;
864 }
865 
866 static void
_gth_image_viewer_button_release(GthImageViewer * self,GdkEventButton * event)867 _gth_image_viewer_button_release (GthImageViewer *self,
868 		 	 	  GdkEventButton *event)
869 {
870 	gth_image_viewer_tool_button_release (self->priv->tool, event);
871 
872 	self->priv->just_focused = FALSE;
873 	self->pressed = FALSE;
874 	self->dragging = FALSE;
875 }
876 
877 
878 static gboolean
gth_image_viewer_button_release(GtkWidget * widget,GdkEventButton * event)879 gth_image_viewer_button_release (GtkWidget      *widget,
880 				 GdkEventButton *event)
881 {
882 	GthImageViewer *self = GTH_IMAGE_VIEWER (widget);
883 
884 	if ((event->button == 1)
885 	    && ! self->dragging
886 	    && ! self->priv->double_click
887 	    && ! self->priv->just_focused)
888 	{
889 		g_signal_emit (G_OBJECT (self),
890 			       gth_image_viewer_signals[CLICKED],
891 			       0);
892 	}
893 
894 	_gth_image_viewer_button_release (self, event);
895 
896 	return FALSE;
897 }
898 
899 
900 static void
scroll_to(GthImageViewer * self,int x_offset,int y_offset)901 scroll_to (GthImageViewer *self,
902 	   int             x_offset,
903 	   int             y_offset)
904 {
905 	int        delta_x, delta_y;
906 	GdkWindow *window;
907 
908 	g_return_if_fail (self != NULL);
909 
910 	if (gth_image_viewer_get_current_image (self) == NULL)
911 		return;
912 
913 	if (self->frame_area.width > self->visible_area.width)
914 		x_offset = CLAMP (x_offset, 0, self->frame_area.width - self->visible_area.width);
915 	else
916 		x_offset = self->visible_area.x;
917 
918 	if (self->frame_area.height > self->visible_area.height)
919 		y_offset = CLAMP (y_offset, 0, self->frame_area.height - self->visible_area.height);
920 	else
921 		y_offset = self->visible_area.y;
922 
923 	if ((x_offset == self->visible_area.x) && (y_offset == self->visible_area.y))
924 		return;
925 
926 	delta_x = x_offset - self->visible_area.x;
927 	delta_y = y_offset - self->visible_area.y;
928 
929 	self->visible_area.x = x_offset;
930 	self->visible_area.y = y_offset;
931 
932 	window = gtk_widget_get_window (GTK_WIDGET (self));
933 	gdk_window_scroll (window, -delta_x, -delta_y);
934 }
935 
936 
937 static gboolean
gth_image_viewer_motion_notify(GtkWidget * widget,GdkEventMotion * event)938 gth_image_viewer_motion_notify (GtkWidget      *widget,
939 				GdkEventMotion *event)
940 {
941 	GthImageViewer *self = GTH_IMAGE_VIEWER (widget);
942 
943 	if (self->pressed) {
944 		self->drag_x = event->x + self->visible_area.x;
945 		self->drag_y = event->y + self->visible_area.y;
946 	}
947 
948 	gth_image_viewer_tool_motion_notify (self->priv->tool, event);
949 
950 	if (self->pressed) {
951 		self->event_x_prev = event->x;
952 		self->event_y_prev = event->y;
953 		self->drag_x_prev = self->drag_x;
954 		self->drag_y_prev = self->drag_y;
955 	}
956 
957 	return FALSE;
958 }
959 
960 
961 static gboolean
gth_image_viewer_scroll_event(GtkWidget * widget,GdkEventScroll * event)962 gth_image_viewer_scroll_event (GtkWidget      *widget,
963 			       GdkEventScroll *event)
964 {
965 	GthImageViewer *self = GTH_IMAGE_VIEWER (widget);
966 	gdouble         new_value = 0.0;
967 	gboolean        retval = FALSE;
968 
969 	g_return_val_if_fail (GTH_IS_IMAGE_VIEWER (widget), FALSE);
970 	g_return_val_if_fail (event != NULL, FALSE);
971 
972 	/* Control + Scroll-Up / Control + Scroll-Down ==> Zoom In / Zoom Out */
973 
974 	if (event->state & GDK_CONTROL_MASK) {
975 		if (self->priv->zoom_enabled && gth_image_viewer_zoom_from_scroll (self, event))
976 			return TRUE;
977 	}
978 
979 	/* Scroll Left / Scroll Right ==> Scroll the image horizontally */
980 
981 	switch (event->direction) {
982 	case GDK_SCROLL_LEFT:
983 	case GDK_SCROLL_RIGHT:
984 		if (event->direction == GDK_SCROLL_LEFT)
985 			new_value = gtk_adjustment_get_value (self->hadj) - gtk_adjustment_get_page_increment (self->hadj) / 2;
986 		else
987 			new_value = gtk_adjustment_get_value (self->hadj) + gtk_adjustment_get_page_increment (self->hadj) / 2;
988 		new_value = CLAMP (new_value, gtk_adjustment_get_lower (self->hadj), gtk_adjustment_get_upper (self->hadj) - gtk_adjustment_get_page_size (self->hadj));
989 		gtk_adjustment_set_value (self->hadj, new_value);
990 		retval = TRUE;
991 		break;
992 
993 	default:
994 		break;
995 	}
996 
997 	return retval;
998 }
999 
1000 
1001 static void
gth_image_viewer_drag_end(GtkWidget * widget,GdkDragContext * context)1002 gth_image_viewer_drag_end (GtkWidget      *widget,
1003 			   GdkDragContext *context)
1004 {
1005 	GthImageViewer *self;
1006 
1007 	g_return_if_fail (GTH_IS_IMAGE_VIEWER (widget));
1008 
1009 	self = GTH_IMAGE_VIEWER (widget);
1010 	_gth_image_viewer_button_release (self, NULL);
1011 }
1012 
1013 
1014 static void
scroll_relative(GthImageViewer * self,int delta_x,int delta_y)1015 scroll_relative (GthImageViewer *self,
1016 		 int             delta_x,
1017 		 int             delta_y)
1018 {
1019 	gth_image_viewer_scroll_to (self,
1020 				    self->visible_area.x + delta_x,
1021 				    self->visible_area.y + delta_y);
1022 }
1023 
1024 
1025 static void
scroll_signal(GtkWidget * widget,GtkScrollType xscroll_type,GtkScrollType yscroll_type)1026 scroll_signal (GtkWidget     *widget,
1027 	       GtkScrollType  xscroll_type,
1028 	       GtkScrollType  yscroll_type)
1029 {
1030 	GthImageViewer *self = GTH_IMAGE_VIEWER (widget);
1031 	int             xstep, ystep;
1032 
1033 	if (! self->priv->enable_key_bindings)
1034 		return;
1035 
1036 	switch (xscroll_type) {
1037 	case GTK_SCROLL_STEP_LEFT:
1038 		xstep = - gtk_adjustment_get_step_increment (self->hadj);
1039 		break;
1040 	case GTK_SCROLL_STEP_RIGHT:
1041 		xstep = gtk_adjustment_get_step_increment (self->hadj);
1042 		break;
1043 	case GTK_SCROLL_PAGE_LEFT:
1044 		xstep = - gtk_adjustment_get_page_increment (self->hadj);
1045 		break;
1046 	case GTK_SCROLL_PAGE_RIGHT:
1047 		xstep = gtk_adjustment_get_page_increment (self->hadj);
1048 		break;
1049 	default:
1050 		xstep = 0;
1051 		break;
1052 	}
1053 
1054 	switch (yscroll_type) {
1055 	case GTK_SCROLL_STEP_UP:
1056 		ystep = - gtk_adjustment_get_step_increment (self->vadj);
1057 		break;
1058 	case GTK_SCROLL_STEP_DOWN:
1059 		ystep = gtk_adjustment_get_step_increment (self->vadj);
1060 		break;
1061 	case GTK_SCROLL_PAGE_UP:
1062 		ystep = - gtk_adjustment_get_page_increment (self->vadj);
1063 		break;
1064 	case GTK_SCROLL_PAGE_DOWN:
1065 		ystep = gtk_adjustment_get_page_increment (self->vadj);
1066 		break;
1067 	default:
1068 		ystep = 0;
1069 		break;
1070 	}
1071 
1072 	scroll_relative (self, xstep, ystep);
1073 }
1074 
1075 
1076 static gboolean
hadj_value_changed(GtkAdjustment * adj,GthImageViewer * self)1077 hadj_value_changed (GtkAdjustment  *adj,
1078 		    GthImageViewer *self)
1079 {
1080 	scroll_to (self, (int) gtk_adjustment_get_value (adj), self->visible_area.y);
1081 	return FALSE;
1082 }
1083 
1084 
1085 static gboolean
vadj_value_changed(GtkAdjustment * adj,GthImageViewer * self)1086 vadj_value_changed (GtkAdjustment  *adj,
1087 		    GthImageViewer *self)
1088 {
1089 	scroll_to (self, self->visible_area.x, (int) gtk_adjustment_get_value (adj));
1090 	return FALSE;
1091 }
1092 
1093 
1094 static void
_gth_image_viewer_set_hadjustment(GthImageViewer * self,GtkAdjustment * hadj)1095 _gth_image_viewer_set_hadjustment (GthImageViewer *self,
1096 				   GtkAdjustment  *hadj)
1097 {
1098 	if (hadj != NULL)
1099 		g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
1100 	else
1101 		hadj = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
1102 
1103 	if ((self->hadj != NULL) && (self->hadj != hadj)) {
1104 		_g_signal_handlers_disconnect_by_data (G_OBJECT (self->hadj), self);
1105 		g_object_unref (self->hadj);
1106 		self->hadj = NULL;
1107 	}
1108 
1109 	if (self->hadj != hadj) {
1110 		self->hadj = hadj;
1111 		g_object_ref (self->hadj);
1112 		g_object_ref_sink (self->hadj);
1113 
1114 		_gth_image_viewer_configure_hadjustment (self);
1115 
1116 		g_signal_connect (G_OBJECT (self->hadj),
1117 				  "value-changed",
1118 				  G_CALLBACK (hadj_value_changed),
1119 				  self);
1120 	}
1121 }
1122 
1123 
1124 static void
_gth_image_viewer_set_vadjustment(GthImageViewer * self,GtkAdjustment * vadj)1125 _gth_image_viewer_set_vadjustment (GthImageViewer *self,
1126 				   GtkAdjustment  *vadj)
1127 {
1128 	if (vadj != NULL)
1129 		g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
1130 	else
1131 		vadj = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
1132 
1133 	if ((self->vadj != NULL) && (self->vadj != vadj)) {
1134 		_g_signal_handlers_disconnect_by_data (G_OBJECT (self->vadj), self);
1135 		g_object_unref (self->vadj);
1136 		self->vadj = NULL;
1137 	}
1138 
1139 	if (self->vadj != vadj) {
1140 		self->vadj = vadj;
1141 		g_object_ref (self->vadj);
1142 		g_object_ref_sink (self->vadj);
1143 
1144 		_gth_image_viewer_configure_vadjustment (self);
1145 
1146 		g_signal_connect (G_OBJECT (self->vadj),
1147 				  "value-changed",
1148 				  G_CALLBACK (vadj_value_changed),
1149 				  self);
1150 	}
1151 }
1152 
1153 
1154 static void
gth_image_viewer_zoom_in__key_binding(GthImageViewer * self)1155 gth_image_viewer_zoom_in__key_binding (GthImageViewer *self)
1156 {
1157 	if (self->priv->enable_key_bindings)
1158 		gth_image_viewer_zoom_in (self);
1159 }
1160 
1161 
1162 static void
gth_image_viewer_zoom_out__key_binding(GthImageViewer * self)1163 gth_image_viewer_zoom_out__key_binding (GthImageViewer *self)
1164 {
1165 	if (self->priv->enable_key_bindings)
1166 		gth_image_viewer_zoom_out (self);
1167 }
1168 
1169 static void
gth_image_viewer_set_fit_mode__key_binding(GthImageViewer * self,GthFit fit_mode)1170 gth_image_viewer_set_fit_mode__key_binding (GthImageViewer *self,
1171 					    GthFit          fit_mode)
1172 {
1173 	if (self->priv->enable_key_bindings)
1174 		gth_image_viewer_set_fit_mode (self, fit_mode);
1175 }
1176 
1177 
1178 static void
gth_image_viewer_set_zoom__key_binding(GthImageViewer * self,gdouble zoom_level)1179 gth_image_viewer_set_zoom__key_binding (GthImageViewer *self,
1180 					gdouble         zoom_level)
1181 {
1182 	if (self->priv->enable_key_bindings)
1183 		gth_image_viewer_set_zoom (self, zoom_level);
1184 }
1185 
1186 
1187 static void
gth_image_viewer_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1188 gth_image_viewer_get_property (GObject     *object,
1189 			       guint        prop_id,
1190 			       GValue      *value,
1191 			       GParamSpec  *pspec)
1192 {
1193 	GthImageViewer *self = GTH_IMAGE_VIEWER (object);
1194 
1195 	switch (prop_id) {
1196 	case PROP_HADJUSTMENT:
1197 		g_value_set_object (value, self->hadj);
1198 		break;
1199 	case PROP_VADJUSTMENT:
1200 		g_value_set_object (value, self->vadj);
1201 		break;
1202 	case PROP_HSCROLL_POLICY:
1203 		g_value_set_enum (value, self->priv->hscroll_policy);
1204 		break;
1205 	case PROP_VSCROLL_POLICY:
1206 		g_value_set_enum (value, self->priv->vscroll_policy);
1207 		break;
1208 	default:
1209 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1210 		break;
1211 	}
1212 }
1213 
1214 
1215 static void
gth_image_viewer_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1216 gth_image_viewer_set_property (GObject      *object,
1217 			       guint         prop_id,
1218 			       const GValue *value,
1219 			       GParamSpec   *pspec)
1220 {
1221 	GthImageViewer *self = GTH_IMAGE_VIEWER (object);
1222 
1223 	switch (prop_id) {
1224 	case PROP_HADJUSTMENT:
1225 		_gth_image_viewer_set_hadjustment (self, (GtkAdjustment *) g_value_get_object (value));
1226 		break;
1227 	case PROP_VADJUSTMENT:
1228 		_gth_image_viewer_set_vadjustment (self, (GtkAdjustment *) g_value_get_object (value));
1229 		break;
1230 	case PROP_HSCROLL_POLICY:
1231 		self->priv->hscroll_policy = g_value_get_enum (value);
1232 		break;
1233 	case PROP_VSCROLL_POLICY:
1234 		self->priv->vscroll_policy = g_value_get_enum (value);
1235 		break;
1236 	default:
1237 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1238 		break;
1239 	}
1240 }
1241 
1242 
1243 static void
gth_image_viewer_class_init(GthImageViewerClass * class)1244 gth_image_viewer_class_init (GthImageViewerClass *class)
1245 {
1246 	GObjectClass   *gobject_class;
1247 	GtkWidgetClass *widget_class;
1248 	GtkBindingSet  *binding_set;
1249 
1250 	/* signals */
1251 
1252 	gth_image_viewer_signals[CLICKED] =
1253 		g_signal_new ("clicked",
1254 			      G_TYPE_FROM_CLASS (class),
1255 			      G_SIGNAL_RUN_LAST,
1256 			      G_STRUCT_OFFSET (GthImageViewerClass, clicked),
1257 			      NULL, NULL,
1258 			      g_cclosure_marshal_VOID__VOID,
1259 			      G_TYPE_NONE,
1260 			      0);
1261 	gth_image_viewer_signals[ZOOM_IN] =
1262 		g_signal_new ("zoom_in",
1263 			      G_TYPE_FROM_CLASS (class),
1264 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1265 			      G_STRUCT_OFFSET (GthImageViewerClass, zoom_in),
1266 			      NULL, NULL,
1267 			      g_cclosure_marshal_VOID__VOID,
1268 			      G_TYPE_NONE,
1269 			      0);
1270 	gth_image_viewer_signals[ZOOM_OUT] =
1271 		g_signal_new ("zoom_out",
1272 			      G_TYPE_FROM_CLASS (class),
1273 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1274 			      G_STRUCT_OFFSET (GthImageViewerClass, zoom_out),
1275 			      NULL, NULL,
1276 			      g_cclosure_marshal_VOID__VOID,
1277 			      G_TYPE_NONE,
1278 			      0);
1279 	gth_image_viewer_signals[SET_ZOOM] =
1280 		g_signal_new ("set-zoom",
1281 			      G_TYPE_FROM_CLASS (class),
1282 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1283 			      G_STRUCT_OFFSET (GthImageViewerClass, set_zoom),
1284 			      NULL, NULL,
1285 			      g_cclosure_marshal_VOID__DOUBLE,
1286 			      G_TYPE_NONE,
1287 			      1,
1288 			      G_TYPE_DOUBLE);
1289 	gth_image_viewer_signals[SET_FIT_MODE] =
1290 		g_signal_new ("set-fit-mode",
1291 			      G_TYPE_FROM_CLASS (class),
1292 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1293 			      G_STRUCT_OFFSET (GthImageViewerClass, set_fit_mode),
1294 			      NULL, NULL,
1295 			      g_cclosure_marshal_VOID__ENUM,
1296 			      G_TYPE_NONE,
1297 			      1,
1298 			      GTH_TYPE_FIT);
1299 	gth_image_viewer_signals[IMAGE_CHANGED] =
1300 		g_signal_new ("image-changed",
1301 			      G_TYPE_FROM_CLASS (class),
1302 			      G_SIGNAL_RUN_LAST,
1303 			      G_STRUCT_OFFSET (GthImageViewerClass, image_changed),
1304 			      NULL, NULL,
1305 			      g_cclosure_marshal_VOID__VOID,
1306 			      G_TYPE_NONE,
1307 			      0);
1308 	gth_image_viewer_signals[ZOOM_CHANGED] =
1309 		g_signal_new ("zoom-changed",
1310 			      G_TYPE_FROM_CLASS (class),
1311 			      G_SIGNAL_RUN_LAST,
1312 			      G_STRUCT_OFFSET (GthImageViewerClass, zoom_changed),
1313 			      NULL, NULL,
1314 			      g_cclosure_marshal_VOID__VOID,
1315 			      G_TYPE_NONE,
1316 			      0);
1317 	gth_image_viewer_signals[BETTER_QUALITY] =
1318 		g_signal_new ("better-quality",
1319 			      G_TYPE_FROM_CLASS (class),
1320 			      G_SIGNAL_RUN_LAST,
1321 			      G_STRUCT_OFFSET (GthImageViewerClass, better_quality),
1322 			      NULL, NULL,
1323 			      g_cclosure_marshal_VOID__VOID,
1324 			      G_TYPE_NONE,
1325 			      0);
1326 	gth_image_viewer_signals[SCROLL] =
1327 		g_signal_new ("scroll",
1328 			      G_TYPE_FROM_CLASS (class),
1329 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1330 			      G_STRUCT_OFFSET (GthImageViewerClass, scroll),
1331 			      NULL, NULL,
1332 			      gth_marshal_VOID__ENUM_ENUM,
1333 			      G_TYPE_NONE,
1334 			      2,
1335 			      GTK_TYPE_SCROLL_TYPE,
1336 			      GTK_TYPE_SCROLL_TYPE);
1337 
1338 	/**/
1339 
1340 	gobject_class = (GObjectClass*) class;
1341 	gobject_class->finalize = gth_image_viewer_finalize;
1342 	gobject_class->set_property = gth_image_viewer_set_property;
1343 	gobject_class->get_property = gth_image_viewer_get_property;
1344 
1345 	widget_class = (GtkWidgetClass*) class;
1346 	widget_class->realize = gth_image_viewer_realize;
1347 	widget_class->unrealize = gth_image_viewer_unrealize;
1348 	widget_class->map = gth_image_viewer_map;
1349 	widget_class->unmap = gth_image_viewer_unmap;
1350 	widget_class->get_request_mode = gth_image_viewer_get_request_mode;
1351 	widget_class->get_preferred_width = gth_image_viewer_get_preferred_width;
1352 	widget_class->get_preferred_height = gth_image_viewer_get_preferred_height;
1353 	widget_class->size_allocate = gth_image_viewer_size_allocate;
1354 	widget_class->focus_in_event = gth_image_viewer_focus_in;
1355 	widget_class->focus_out_event = gth_image_viewer_focus_out;
1356 	widget_class->key_press_event = gth_image_viewer_key_press;
1357 	widget_class->draw = gth_image_viewer_draw;
1358 	widget_class->button_press_event = gth_image_viewer_button_press;
1359 	widget_class->button_release_event = gth_image_viewer_button_release;
1360 	widget_class->motion_notify_event = gth_image_viewer_motion_notify;
1361 	widget_class->scroll_event = gth_image_viewer_scroll_event;
1362 	widget_class->drag_end = gth_image_viewer_drag_end;
1363 
1364 	class->clicked      = NULL;
1365 	class->zoom_changed = NULL;
1366 	class->scroll       = scroll_signal;
1367 	class->zoom_in      = gth_image_viewer_zoom_in__key_binding;
1368 	class->zoom_out     = gth_image_viewer_zoom_out__key_binding;
1369 	class->set_zoom     = gth_image_viewer_set_zoom__key_binding;
1370 	class->set_fit_mode = gth_image_viewer_set_fit_mode__key_binding;
1371 
1372 	/* GtkScrollable interface */
1373 
1374 	g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment");
1375 	g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment");
1376 	g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
1377 	g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
1378 
1379 	/* Add key bindings */
1380 
1381 	binding_set = gtk_binding_set_by_class (class);
1382 
1383 	/* For scrolling */
1384 
1385 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, 0,
1386 			      "scroll", 2,
1387 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_RIGHT,
1388 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE);
1389 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, 0,
1390 			      "scroll", 2,
1391 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_LEFT,
1392 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE);
1393 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0,
1394 			      "scroll", 2,
1395 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE,
1396 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
1397 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0,
1398 			      "scroll", 2,
1399 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE,
1400 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
1401 
1402 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, GDK_SHIFT_MASK,
1403 			      "scroll", 2,
1404 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_RIGHT,
1405 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE);
1406 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, GDK_SHIFT_MASK,
1407 			      "scroll", 2,
1408 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_LEFT,
1409 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE);
1410 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_SHIFT_MASK,
1411 			      "scroll", 2,
1412 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE,
1413 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
1414 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_SHIFT_MASK,
1415 			      "scroll", 2,
1416 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE,
1417 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
1418 
1419 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
1420 			      "scroll", 2,
1421 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE,
1422 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
1423 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
1424 			      "scroll", 2,
1425 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE,
1426 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
1427 
1428 	/* For scrolling (Keypad) */
1429 
1430 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, 0,
1431 			      "scroll", 2,
1432 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_RIGHT,
1433 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE);
1434 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, 0,
1435 			      "scroll", 2,
1436 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_LEFT,
1437 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE);
1438 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, 0,
1439 			      "scroll", 2,
1440 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE,
1441 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
1442 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, 0,
1443 			      "scroll", 2,
1444 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_NONE,
1445 			      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
1446 
1447 	/* Zoom in */
1448 
1449 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_plus, 0,
1450 				      "zoom_in", 0);
1451 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_equal, 0,
1452 				      "zoom_in", 0);
1453 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Add, 0,
1454 				      "zoom_in", 0);
1455 
1456 	/* Zoom out */
1457 
1458 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_minus, 0,
1459 				      "zoom_out", 0);
1460 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Subtract, 0,
1461 				      "zoom_out", 0);
1462 
1463 	/* Set zoom */
1464 
1465 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Divide, 0,
1466 				      "set_zoom", 1,
1467 				      G_TYPE_DOUBLE, 1.0);
1468 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_1, 0,
1469 				      "set_zoom", 1,
1470 				      G_TYPE_DOUBLE, 1.0);
1471 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_z, 0,
1472 				      "set_zoom", 1,
1473 				      G_TYPE_DOUBLE, 1.0);
1474 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_2, 0,
1475 				      "set_zoom", 1,
1476 				      G_TYPE_DOUBLE, 2.0);
1477 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_3, 0,
1478 				      "set_zoom", 1,
1479 				      G_TYPE_DOUBLE, 3.0);
1480 
1481 	/* Set fit mode */
1482 
1483 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_x, 0,
1484 				      "set_fit_mode", 1,
1485 				      GTH_TYPE_FIT, GTH_FIT_SIZE_IF_LARGER);
1486 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Multiply, 0,
1487 				      "set_fit_mode", 1,
1488 				      GTH_TYPE_FIT, GTH_FIT_SIZE_IF_LARGER);
1489 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_x, GDK_SHIFT_MASK,
1490 				      "set_fit_mode", 1,
1491 				      GTH_TYPE_FIT, GTH_FIT_SIZE);
1492 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_w, 0,
1493 				      "set_fit_mode", 1,
1494 				      GTH_TYPE_FIT, GTH_FIT_WIDTH_IF_LARGER);
1495 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_w, GDK_SHIFT_MASK,
1496 				      "set_fit_mode", 1,
1497 				      GTH_TYPE_FIT, GTH_FIT_WIDTH);
1498 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_h, 0,
1499 				      "set_fit_mode", 1,
1500 				      GTH_TYPE_FIT, GTH_FIT_HEIGHT_IF_LARGER);
1501 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_h, GDK_SHIFT_MASK,
1502 				      "set_fit_mode", 1,
1503 				      GTH_TYPE_FIT, GTH_FIT_HEIGHT);
1504 }
1505 
1506 
1507 static void
halt_animation(GthImageViewer * self)1508 halt_animation (GthImageViewer *self)
1509 {
1510 	g_return_if_fail (self != NULL);
1511 
1512 	if (self->priv->anim_id != 0) {
1513 		g_source_remove (self->priv->anim_id);
1514 		self->priv->anim_id = 0;
1515 	}
1516 }
1517 
1518 
1519 static void
gth_image_viewer_init(GthImageViewer * self)1520 gth_image_viewer_init (GthImageViewer *self)
1521 {
1522 	gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
1523 	gtk_widget_set_has_window (GTK_WIDGET (self), TRUE);
1524 
1525 	/* Initialize data. */
1526 
1527 	self->priv = gth_image_viewer_get_instance_private (self);
1528 
1529 	self->priv->background_pattern = _cairo_create_checked_pattern (CHECKED_PATTERN_SIZE);
1530 	self->priv->is_animation = FALSE;
1531 	self->priv->play_animation = TRUE;
1532 	self->priv->cursor_visible = TRUE;
1533 
1534 	self->priv->frame_visible = FALSE;
1535 	self->priv->frame_border = 0;
1536 
1537 	self->priv->anim_id = 0;
1538 	self->priv->iter = NULL;
1539 	self->priv->iter_surface = NULL;
1540 
1541 	self->priv->zoom_enabled = TRUE;
1542 	self->priv->enable_key_bindings = TRUE;
1543 	self->priv->zoom_level = 1.0;
1544 	self->priv->zoom_quality = GTH_ZOOM_QUALITY_HIGH;
1545 	self->priv->zoom_change = GTH_ZOOM_CHANGE_KEEP_PREV;
1546 	self->priv->fit = GTH_FIT_SIZE_IF_LARGER;
1547 	self->priv->doing_zoom_fit = FALSE;
1548 
1549 	self->priv->skip_zoom_change = FALSE;
1550 	self->priv->update_image_after_zoom = FALSE;
1551 
1552 	self->priv->is_void = TRUE;
1553 	self->visible_area.x = 0;
1554 	self->visible_area.y = 0;
1555 	self->dragging = FALSE;
1556 	self->priv->double_click = FALSE;
1557 	self->priv->just_focused = FALSE;
1558 
1559 	self->priv->cursor = NULL;
1560 	self->priv->cursor_void = NULL;
1561 
1562 	self->priv->reset_scrollbars = TRUE;
1563 	self->priv->transparency_style = GTH_TRANSPARENCY_STYLE_CHECKERED;
1564 
1565 	gth_image_viewer_set_tool (self, NULL);
1566 
1567 	/* Create the widget. */
1568 
1569 	self->hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 1.0, 0.0, 0.1, 1.0, 1.0));
1570 	self->vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 1.0, 0.0, 0.1, 1.0, 1.0));
1571 
1572 	g_object_ref (self->hadj);
1573 	g_object_ref_sink (self->hadj);
1574 	g_object_ref (self->vadj);
1575 	g_object_ref_sink (self->vadj);
1576 
1577 	g_signal_connect (G_OBJECT (self->hadj),
1578 			  "value_changed",
1579 			  G_CALLBACK (hadj_value_changed),
1580 			  self);
1581 	g_signal_connect (G_OBJECT (self->vadj),
1582 			  "value_changed",
1583 			  G_CALLBACK (vadj_value_changed),
1584 			  self);
1585 
1586 	/* do not use the rgba visual on the drawing area */
1587 	{
1588 		GdkVisual *visual;
1589 		visual = gdk_screen_get_system_visual (gtk_widget_get_screen (GTK_WIDGET (self)));
1590 		if (visual != NULL)
1591 			gtk_widget_set_visual (GTK_WIDGET (self), visual);
1592 	}
1593 }
1594 
1595 
1596 GtkWidget *
gth_image_viewer_new(void)1597 gth_image_viewer_new (void)
1598 {
1599 	return GTK_WIDGET (g_object_new (GTH_TYPE_IMAGE_VIEWER, NULL));
1600 }
1601 
1602 
1603 static void
_gth_image_viewer_set_original_size(GthImageViewer * self,int original_width,int original_height)1604 _gth_image_viewer_set_original_size (GthImageViewer *self,
1605 				     int             original_width,
1606 				     int             original_height)
1607 {
1608 	cairo_surface_t *image;
1609 	int              image_width;
1610 	int              image_height;
1611 
1612 	image = gth_image_viewer_get_current_image (self);
1613 	image_width = cairo_image_surface_get_width (image);
1614 	image_height = cairo_image_surface_get_height (image);
1615 
1616 	if (original_width <= 0 || original_height <= 0)
1617 		_cairo_image_surface_get_original_size (image, &original_width, &original_height);
1618 
1619 	if (original_width > 0)
1620 		self->priv->original_width = original_width;
1621 	else
1622 		self->priv->original_width = (image != NULL) ? image_width : 0;
1623 
1624 	if (original_height > 0)
1625 		self->priv->original_height = original_height;
1626 	else
1627 		self->priv->original_height = (image != NULL) ? image_height : 0;
1628 
1629 	if ((self->priv->original_width > image_width)
1630 	    && (self->priv->original_height > image_height))
1631 	{
1632 		self->priv->requested_size = MAX (image_width, image_height);
1633 	}
1634 	else
1635 		self->priv->requested_size = -1;
1636 }
1637 
1638 
1639 static void
_gth_image_viewer_content_changed(GthImageViewer * self,gboolean better_quality)1640 _gth_image_viewer_content_changed (GthImageViewer *self,
1641 				   gboolean        better_quality)
1642 {
1643 	halt_animation (self);
1644 
1645 	if (better_quality)
1646 		g_signal_emit (G_OBJECT (self), gth_image_viewer_signals[BETTER_QUALITY], 0);
1647 	else if (! self->priv->zoom_enabled || (self->priv->zoom_change == GTH_ZOOM_CHANGE_KEEP_PREV))
1648 		g_signal_emit (G_OBJECT (self), gth_image_viewer_signals[IMAGE_CHANGED], 0);
1649 
1650 	if (! better_quality && self->priv->reset_scrollbars) {
1651 		self->visible_area.x = 0;
1652 		self->visible_area.y = 0;
1653 	}
1654 
1655 	if (better_quality || ! self->priv->zoom_enabled) {
1656 		gth_image_viewer_tool_image_changed (self->priv->tool);
1657 		return;
1658 	}
1659 
1660 	self->priv->update_image_after_zoom = TRUE;
1661 
1662 	switch (self->priv->zoom_change) {
1663 	case GTH_ZOOM_CHANGE_ACTUAL_SIZE:
1664 		gth_image_viewer_set_zoom (self, 1.0);
1665 		queue_animation_frame_change (self);
1666 		break;
1667 
1668 	case GTH_ZOOM_CHANGE_KEEP_PREV:
1669 		gth_image_viewer_tool_image_changed (self->priv->tool);
1670 		gtk_widget_queue_resize (GTK_WIDGET (self));
1671 		break;
1672 
1673 	case GTH_ZOOM_CHANGE_FIT_SIZE:
1674 		gth_image_viewer_set_fit_mode (self, GTH_FIT_SIZE);
1675 		queue_animation_frame_change (self);
1676 		break;
1677 
1678 	case GTH_ZOOM_CHANGE_FIT_SIZE_IF_LARGER:
1679 		gth_image_viewer_set_fit_mode (self, GTH_FIT_SIZE_IF_LARGER);
1680 		queue_animation_frame_change (self);
1681 		break;
1682 
1683 	case GTH_ZOOM_CHANGE_FIT_WIDTH:
1684 		gth_image_viewer_set_fit_mode (self, GTH_FIT_WIDTH);
1685 		queue_animation_frame_change (self);
1686 		break;
1687 
1688 	case GTH_ZOOM_CHANGE_FIT_WIDTH_IF_LARGER:
1689 		gth_image_viewer_set_fit_mode (self, GTH_FIT_WIDTH_IF_LARGER);
1690 		queue_animation_frame_change (self);
1691 		break;
1692 
1693 	case GTH_ZOOM_CHANGE_FIT_HEIGHT:
1694 		gth_image_viewer_set_fit_mode (self, GTH_FIT_HEIGHT);
1695 		queue_animation_frame_change (self);
1696 		break;
1697 
1698 	case GTH_ZOOM_CHANGE_FIT_HEIGHT_IF_LARGER:
1699 		gth_image_viewer_set_fit_mode (self, GTH_FIT_HEIGHT_IF_LARGER);
1700 		queue_animation_frame_change (self);
1701 		break;
1702 	}
1703 }
1704 
1705 
1706 static void
_set_animation(GthImageViewer * self,GdkPixbufAnimation * animation,int original_width,int original_height,gboolean better_quality)1707 _set_animation (GthImageViewer     *self,
1708 		GdkPixbufAnimation *animation,
1709 		int                 original_width,
1710 		int                 original_height,
1711 		gboolean            better_quality)
1712 {
1713 	g_return_if_fail (self != NULL);
1714 
1715 	_cairo_clear_surface (&self->priv->surface);
1716 	_cairo_clear_surface (&self->priv->iter_surface);
1717 	_g_clear_object (&self->priv->animation);
1718 	_g_clear_object (&self->priv->iter);
1719 	_g_clear_object (&self->priv->image);
1720 
1721 	self->priv->animation = _g_object_ref (animation);
1722 	self->priv->is_void = (self->priv->animation == NULL);
1723 	self->priv->is_animation = (self->priv->animation != NULL) ? ! gdk_pixbuf_animation_is_static_image (self->priv->animation) : FALSE;
1724 	if (self->priv->animation != NULL) {
1725 		g_get_current_time (&self->priv->time);
1726 		_g_object_unref (self->priv->iter);
1727 		self->priv->iter = gdk_pixbuf_animation_get_iter (self->priv->animation, &self->priv->time);
1728 	}
1729 	_gth_image_viewer_set_original_size (self, original_width, original_height);
1730 
1731 	_gth_image_viewer_content_changed (self, better_quality);
1732 }
1733 
1734 
1735 void
gth_image_viewer_set_animation(GthImageViewer * self,GdkPixbufAnimation * animation,int original_width,int original_height)1736 gth_image_viewer_set_animation (GthImageViewer     *self,
1737 				GdkPixbufAnimation *animation,
1738 				int                 original_width,
1739 				int                 original_height)
1740 {
1741 	_set_animation (self, animation, original_width, original_height, FALSE);
1742 }
1743 
1744 
1745 static void
_set_surface(GthImageViewer * self,cairo_surface_t * surface,int original_width,int original_height,gboolean better_quality)1746 _set_surface (GthImageViewer  *self,
1747 	      cairo_surface_t *surface,
1748 	      int              original_width,
1749 	      int              original_height,
1750 	      gboolean         better_quality)
1751 {
1752 	if (self->priv->surface != surface) {
1753 		_cairo_clear_surface (&self->priv->surface);
1754 		self->priv->surface = cairo_surface_reference (surface);
1755 	}
1756 
1757 	_cairo_clear_surface (&self->priv->iter_surface);
1758 	_g_clear_object (&self->priv->animation);
1759 	_g_clear_object (&self->priv->iter);
1760 
1761 	self->priv->is_void = (self->priv->surface == NULL);
1762 	self->priv->is_animation = FALSE;
1763 	_gth_image_viewer_set_original_size (self, original_width, original_height);
1764 
1765 	_gth_image_viewer_content_changed (self, better_quality);
1766 }
1767 
1768 
1769 void
gth_image_viewer_set_better_quality(GthImageViewer * self,GthImage * image,int original_width,int original_height)1770 gth_image_viewer_set_better_quality (GthImageViewer *self,
1771 				     GthImage       *image,
1772 				     int             original_width,
1773 				     int             original_height)
1774 {
1775 	if (gth_image_get_is_animation (image)) {
1776 		GdkPixbufAnimation *animation;
1777 
1778 		animation = gth_image_get_pixbuf_animation (image);
1779 		_set_animation (self, animation, original_width, original_height, TRUE);
1780 
1781 		g_object_unref (animation);
1782 	}
1783 	else {
1784 		cairo_surface_t *surface;
1785 
1786 		surface = gth_image_get_cairo_surface (image);
1787 		_set_surface (self, surface, original_width, original_height, TRUE);
1788 
1789 		cairo_surface_destroy (surface);
1790 	}
1791 }
1792 
1793 
1794 void
gth_image_viewer_set_pixbuf(GthImageViewer * self,GdkPixbuf * pixbuf,int original_width,int original_height)1795 gth_image_viewer_set_pixbuf (GthImageViewer *self,
1796 			     GdkPixbuf      *pixbuf,
1797 			     int             original_width,
1798 			     int             original_height)
1799 {
1800 	cairo_surface_t *image;
1801 
1802 	g_return_if_fail (self != NULL);
1803 
1804 	image = _cairo_image_surface_create_from_pixbuf (pixbuf);
1805 	gth_image_viewer_set_surface (self, image, original_width, original_height);
1806 
1807 	cairo_surface_destroy (image);
1808 }
1809 
1810 
1811 void
gth_image_viewer_set_surface(GthImageViewer * self,cairo_surface_t * surface,int original_width,int original_height)1812 gth_image_viewer_set_surface (GthImageViewer  *self,
1813 			      cairo_surface_t *surface,
1814 			      int              original_width,
1815 			      int              original_height)
1816 {
1817 	_g_clear_object (&self->priv->image);
1818 	_set_surface (self, surface, original_width, original_height, FALSE);
1819 }
1820 
1821 
1822 void
gth_image_viewer_set_image(GthImageViewer * self,GthImage * image,int original_width,int original_height)1823 gth_image_viewer_set_image (GthImageViewer *self,
1824 			    GthImage       *image,
1825 			    int             original_width,
1826 			    int             original_height)
1827 {
1828 	if (self->priv->image != image)
1829 		_g_clear_object (&self->priv->image);
1830 
1831 	self->priv->image = g_object_ref (image);
1832 
1833 	if (gth_image_get_is_animation (image)) {
1834 		GdkPixbufAnimation *animation;
1835 
1836 		animation = gth_image_get_pixbuf_animation (image);
1837 		gth_image_viewer_set_animation (self,	animation, original_width, original_height);
1838 
1839 		g_object_unref (animation);
1840 	}
1841 	else {
1842 		cairo_surface_t *surface;
1843 
1844 		if (gth_image_get_is_zoomable (image)) {
1845 			gth_image_set_zoom (self->priv->image,
1846 					    self->priv->zoom_level,
1847 					    &original_width,
1848 					    &original_height);
1849 		}
1850 		surface = gth_image_get_cairo_surface (image);
1851 		_set_surface (self, surface, original_width, original_height, FALSE);
1852 
1853 		cairo_surface_destroy (surface);
1854 	}
1855 }
1856 
1857 
1858 void
gth_image_viewer_set_void(GthImageViewer * self)1859 gth_image_viewer_set_void (GthImageViewer *self)
1860 {
1861 	g_return_if_fail (self != NULL);
1862 
1863 	_cairo_clear_surface (&self->priv->surface);
1864 	_cairo_clear_surface (&self->priv->iter_surface);
1865 	_g_clear_object (&self->priv->animation);
1866 	_g_clear_object (&self->priv->iter);
1867 	_g_clear_object (&self->priv->image);
1868 
1869 	self->priv->is_void = TRUE;
1870 	self->priv->is_animation = FALSE;
1871 
1872 	_gth_image_viewer_content_changed (self, FALSE);
1873 }
1874 
1875 
1876 gboolean
gth_image_viewer_is_void(GthImageViewer * self)1877 gth_image_viewer_is_void (GthImageViewer *self)
1878 {
1879 	return self->priv->is_void;
1880 }
1881 
1882 
1883 void
gth_image_viewer_add_painter(GthImageViewer * self,GthImageViewerPaintFunc func,gpointer user_data)1884 gth_image_viewer_add_painter (GthImageViewer          *self,
1885 			      GthImageViewerPaintFunc  func,
1886 			      gpointer                 user_data)
1887 {
1888 	GList       *link;
1889 	GList       *scan;
1890 	PainterData *painter_data;
1891 
1892 	g_return_if_fail (self != NULL);
1893 
1894 	link = NULL;
1895 	for (scan = self->priv->painters; scan; scan = scan->next) {
1896 		PainterData *painter_data = scan->data;
1897 		if ((painter_data->func == func) && (painter_data->user_data == user_data)) {
1898 			link = scan;
1899 			break;
1900 		}
1901 	}
1902 
1903 	if (link != NULL)
1904 		return;
1905 
1906 	painter_data = g_new0 (PainterData, 1);
1907 	painter_data->func = func;
1908 	painter_data->user_data = user_data;
1909 	self->priv->painters = g_list_append (self->priv->painters, painter_data);
1910 }
1911 
1912 
1913 void
gth_image_viewer_remove_painter(GthImageViewer * self,GthImageViewerPaintFunc func,gpointer user_data)1914 gth_image_viewer_remove_painter (GthImageViewer          *self,
1915 				 GthImageViewerPaintFunc  func,
1916 				 gpointer                 user_data)
1917 {
1918 	GList *link;
1919 	GList *scan;
1920 
1921 	link = NULL;
1922 	for (scan = self->priv->painters; scan; scan = scan->next) {
1923 		PainterData *painter_data = scan->data;
1924 		if ((painter_data->func == func) && (painter_data->user_data == user_data)) {
1925 			link = scan;
1926 			break;
1927 		}
1928 	}
1929 
1930 	if (link == NULL)
1931 		return;
1932 
1933 	self->priv->painters = g_list_remove_link (self->priv->painters, link);
1934 	painter_data_free (link->data);
1935 	g_list_free (link);
1936 }
1937 
1938 
1939 int
gth_image_viewer_get_image_width(GthImageViewer * self)1940 gth_image_viewer_get_image_width (GthImageViewer *self)
1941 {
1942 	cairo_surface_t *image;
1943 
1944 	g_return_val_if_fail (self != NULL, 0);
1945 
1946 	image = gth_image_viewer_get_current_image (self);
1947 	if (image != NULL)
1948 		return cairo_image_surface_get_width (image);
1949 
1950 	return 0;
1951 }
1952 
1953 
1954 int
gth_image_viewer_get_image_height(GthImageViewer * self)1955 gth_image_viewer_get_image_height (GthImageViewer *self)
1956 {
1957 	cairo_surface_t *image;
1958 
1959 	g_return_val_if_fail (self != NULL, 0);
1960 
1961 	image = gth_image_viewer_get_current_image (self);
1962 	if (image != NULL)
1963 		return cairo_image_surface_get_height (image);
1964 
1965 	return 0;
1966 }
1967 
1968 
1969 void
gth_image_viewer_get_original_size(GthImageViewer * self,int * width,int * height)1970 gth_image_viewer_get_original_size (GthImageViewer *self,
1971 				    int            *width,
1972 				    int            *height)
1973 {
1974 	g_return_if_fail (self != NULL);
1975 
1976 	if (width != NULL)
1977 		*width = self->priv->original_width;
1978 	if (height != NULL)
1979 		*height = self->priv->original_height;
1980 }
1981 
1982 
1983 void
gth_image_viewer_set_requested_size(GthImageViewer * self,int requested_size)1984 gth_image_viewer_set_requested_size (GthImageViewer *self,
1985 				     int             requested_size)
1986 {
1987 	self->priv->requested_size = requested_size;
1988 }
1989 
1990 
1991 int
gth_image_viewer_get_requested_size(GthImageViewer * self)1992 gth_image_viewer_get_requested_size (GthImageViewer *self)
1993 {
1994 	return self->priv->requested_size;
1995 }
1996 
1997 
1998 gboolean
gth_image_viewer_get_has_alpha(GthImageViewer * self)1999 gth_image_viewer_get_has_alpha (GthImageViewer *self)
2000 {
2001 	return _cairo_image_surface_get_has_alpha (self->priv->surface);
2002 }
2003 
2004 
2005 GthImage *
gth_image_viewer_get_image(GthImageViewer * self)2006 gth_image_viewer_get_image (GthImageViewer *self)
2007 {
2008 	return self->priv->image;
2009 }
2010 
2011 
2012 GdkPixbuf *
gth_image_viewer_get_current_pixbuf(GthImageViewer * self)2013 gth_image_viewer_get_current_pixbuf (GthImageViewer *self)
2014 {
2015 	g_return_val_if_fail (self != NULL, NULL);
2016 
2017 	if (self->priv->is_void)
2018 		return NULL;
2019 
2020 	if (self->priv->surface != NULL)
2021 		return _gdk_pixbuf_new_from_cairo_surface (self->priv->surface);
2022 
2023 	if (self->priv->iter != NULL)
2024 		return gdk_pixbuf_copy (gdk_pixbuf_animation_iter_get_pixbuf (self->priv->iter));
2025 
2026 	return NULL;
2027 }
2028 
2029 
2030 cairo_surface_t *
gth_image_viewer_get_current_image(GthImageViewer * self)2031 gth_image_viewer_get_current_image (GthImageViewer *self)
2032 {
2033 	g_return_val_if_fail (self != NULL, NULL);
2034 
2035 	if (self->priv->is_void)
2036 		return NULL;
2037 
2038 	if (self->priv->surface != NULL)
2039 		return self->priv->surface;
2040 
2041 	if (self->priv->iter != NULL) {
2042 		if (self->priv->iter_surface == NULL)
2043 			self->priv->iter_surface = _cairo_image_surface_create_from_pixbuf (gdk_pixbuf_animation_iter_get_pixbuf (self->priv->iter));
2044 		return self->priv->iter_surface;
2045 	}
2046 
2047 	return NULL;
2048 }
2049 
2050 
2051 void
gth_image_viewer_start_animation(GthImageViewer * self)2052 gth_image_viewer_start_animation (GthImageViewer *self)
2053 {
2054 	g_return_if_fail (self != NULL);
2055 
2056 	self->priv->play_animation = TRUE;
2057 	gtk_widget_queue_resize (GTK_WIDGET (self));
2058 }
2059 
2060 
2061 void
gth_image_viewer_stop_animation(GthImageViewer * self)2062 gth_image_viewer_stop_animation (GthImageViewer *self)
2063 {
2064 	g_return_if_fail (self != NULL);
2065 
2066 	self->priv->play_animation = FALSE;
2067 	halt_animation (self);
2068 }
2069 
2070 
2071 void
gth_image_viewer_step_animation(GthImageViewer * self)2072 gth_image_viewer_step_animation (GthImageViewer *self)
2073 {
2074 	g_return_if_fail (self != NULL);
2075 
2076 	if (! self->priv->is_animation)
2077 		return;
2078 	if (self->priv->play_animation)
2079 		return;
2080 
2081 	change_animation_frame (self);
2082 }
2083 
2084 
2085 gboolean
gth_image_viewer_is_animation(GthImageViewer * self)2086 gth_image_viewer_is_animation (GthImageViewer *self)
2087 {
2088 	g_return_val_if_fail (self != NULL, FALSE);
2089 
2090 	return self->priv->is_animation;
2091 }
2092 
2093 
2094 gboolean
gth_image_viewer_is_playing_animation(GthImageViewer * self)2095 gth_image_viewer_is_playing_animation (GthImageViewer *self)
2096 {
2097 	g_return_val_if_fail (self != NULL, FALSE);
2098 
2099 	return self->priv->is_animation && self->priv->play_animation;
2100 }
2101 
2102 
2103 void
gth_image_viewer_set_zoom(GthImageViewer * self,gdouble zoom_level)2104 gth_image_viewer_set_zoom (GthImageViewer *self,
2105 			   gdouble         zoom_level)
2106 {
2107 	if (! self->priv->zoom_enabled)
2108 		return;
2109 
2110 	set_zoom_centered (self, zoom_level, FALSE);
2111 	gtk_widget_queue_resize (GTK_WIDGET (self));
2112 }
2113 
2114 
2115 gdouble
gth_image_viewer_get_zoom(GthImageViewer * self)2116 gth_image_viewer_get_zoom (GthImageViewer *self)
2117 {
2118 	return self->priv->zoom_level;
2119 }
2120 
2121 
2122 void
gth_image_viewer_set_zoom_quality(GthImageViewer * self,GthZoomQuality quality)2123 gth_image_viewer_set_zoom_quality (GthImageViewer *self,
2124 				   GthZoomQuality  quality)
2125 {
2126 	self->priv->zoom_quality = quality;
2127 
2128 	/* to update the zoom filter */
2129 
2130 	if (self->priv->tool != NULL)
2131 		gth_image_viewer_tool_zoom_changed (self->priv->tool);
2132 }
2133 
2134 
2135 GthZoomQuality
gth_image_viewer_get_zoom_quality(GthImageViewer * self)2136 gth_image_viewer_get_zoom_quality (GthImageViewer *self)
2137 {
2138 	return self->priv->zoom_quality;
2139 }
2140 
2141 
2142 cairo_filter_t
gth_image_viewer_get_zoom_quality_filter(GthImageViewer * viewer)2143 gth_image_viewer_get_zoom_quality_filter (GthImageViewer *viewer)
2144 {
2145 	cairo_filter_t filter;
2146 
2147 	if (gth_image_viewer_get_zoom_quality (viewer) == GTH_ZOOM_QUALITY_LOW)
2148 		filter = CAIRO_FILTER_FAST;
2149 	else
2150 		filter = CAIRO_FILTER_BEST;
2151 
2152 	if (viewer->priv->zoom_level >= 1.0)
2153 		filter = CAIRO_FILTER_FAST;
2154 
2155 	return filter;
2156 }
2157 
2158 
2159 void
gth_image_viewer_set_zoom_change(GthImageViewer * self,GthZoomChange zoom_change)2160 gth_image_viewer_set_zoom_change (GthImageViewer *self,
2161 				  GthZoomChange   zoom_change)
2162 {
2163 	self->priv->zoom_change = zoom_change;
2164 }
2165 
2166 
2167 GthZoomChange
gth_image_viewer_get_zoom_change(GthImageViewer * self)2168 gth_image_viewer_get_zoom_change (GthImageViewer *self)
2169 {
2170 	return self->priv->zoom_change;
2171 }
2172 
2173 
2174 void
gth_image_viewer_zoom_in(GthImageViewer * self)2175 gth_image_viewer_zoom_in (GthImageViewer *self)
2176 {
2177 	if (! self->priv->is_void)
2178 		gth_image_viewer_set_zoom (self, get_next_zoom (self->priv->zoom_level));
2179 }
2180 
2181 
2182 void
gth_image_viewer_zoom_out(GthImageViewer * self)2183 gth_image_viewer_zoom_out (GthImageViewer *self)
2184 {
2185 	if (! self->priv->is_void)
2186 		gth_image_viewer_set_zoom (self, get_prev_zoom (self->priv->zoom_level));
2187 }
2188 
2189 
2190 gboolean
gth_image_viewer_zoom_from_scroll(GthImageViewer * self,GdkEventScroll * event)2191 gth_image_viewer_zoom_from_scroll (GthImageViewer *self,
2192 				   GdkEventScroll *event)
2193 {
2194 	gboolean handled;
2195 	double   new_zoom_level;
2196 
2197 	handled = FALSE;
2198 	switch (event->direction) {
2199 	case GDK_SCROLL_UP:
2200 	case GDK_SCROLL_DOWN:
2201 		if (event->direction == GDK_SCROLL_UP)
2202 			new_zoom_level = get_next_zoom (self->priv->zoom_level);
2203 		else
2204 			new_zoom_level = get_prev_zoom (self->priv->zoom_level);
2205 		set_zoom_centered_at (self, new_zoom_level, FALSE, (int) event->x, (int) event->y);
2206 		gtk_widget_queue_resize (GTK_WIDGET (self));
2207 		handled = TRUE;
2208 		break;
2209 
2210 	default:
2211 		break;
2212 	}
2213 
2214 	return handled;
2215 }
2216 
2217 
2218 void
gth_image_viewer_set_fit_mode(GthImageViewer * self,GthFit fit_mode)2219 gth_image_viewer_set_fit_mode (GthImageViewer *self,
2220 			       GthFit          fit_mode)
2221 {
2222 	if (! self->priv->zoom_enabled)
2223 		return;
2224 
2225 	self->priv->fit = fit_mode;
2226 	if (! self->priv->is_void)
2227 		gtk_widget_queue_resize (GTK_WIDGET (self));
2228 }
2229 
2230 
2231 GthFit
gth_image_viewer_get_fit_mode(GthImageViewer * self)2232 gth_image_viewer_get_fit_mode (GthImageViewer *self)
2233 {
2234 	return self->priv->fit;
2235 }
2236 
2237 
2238 void
gth_image_viewer_set_zoom_enabled(GthImageViewer * self,gboolean value)2239 gth_image_viewer_set_zoom_enabled (GthImageViewer *self,
2240 				   gboolean        value)
2241 {
2242 	g_return_if_fail (GTH_IS_IMAGE_VIEWER (self));
2243 
2244 	self->priv->zoom_enabled = value;
2245 }
2246 
2247 
2248 gboolean
gth_image_viewer_get_zoom_enabled(GthImageViewer * self)2249 gth_image_viewer_get_zoom_enabled (GthImageViewer *self)
2250 {
2251 	return self->priv->zoom_enabled;
2252 }
2253 
2254 
2255 void
gth_image_viewer_clicked(GthImageViewer * self)2256 gth_image_viewer_clicked (GthImageViewer *self)
2257 {
2258 	g_signal_emit (G_OBJECT (self), gth_image_viewer_signals[CLICKED], 0);
2259 }
2260 
2261 
2262 void
gth_image_viewer_set_tool(GthImageViewer * self,GthImageViewerTool * tool)2263 gth_image_viewer_set_tool (GthImageViewer     *self,
2264 			   GthImageViewerTool *tool)
2265 {
2266 	if (self->priv->tool != NULL) {
2267 		gth_image_viewer_tool_unset_viewer (self->priv->tool, self);
2268 		g_object_unref (self->priv->tool);
2269 	}
2270 	if (tool == NULL)
2271 		self->priv->tool = gth_image_dragger_new (FALSE);
2272 	else
2273 		self->priv->tool = g_object_ref (tool);
2274 	gth_image_viewer_tool_set_viewer (self->priv->tool, self);
2275 	if (gtk_widget_get_realized (GTK_WIDGET (self)))
2276 		gth_image_viewer_tool_realize (self->priv->tool);
2277 	gth_image_viewer_tool_image_changed (self->priv->tool);
2278 	gtk_widget_queue_resize (GTK_WIDGET (self));
2279 }
2280 
2281 
2282 GthImageViewerTool *
gth_image_viewer_get_tool(GthImageViewer * self)2283 gth_image_viewer_get_tool (GthImageViewer *self)
2284 {
2285 	return self->priv->tool;
2286 }
2287 
2288 
2289 void
gth_image_viewer_set_transparency_style(GthImageViewer * self,GthTransparencyStyle style)2290 gth_image_viewer_set_transparency_style (GthImageViewer       *self,
2291 					 GthTransparencyStyle  style)
2292 {
2293 	g_return_if_fail (GTH_IS_IMAGE_VIEWER (self));
2294 
2295 	self->priv->transparency_style = style;
2296 	gtk_widget_queue_draw (GTK_WIDGET (self));
2297 }
2298 
2299 
2300 GthTransparencyStyle
gth_image_viewer_get_transparency_style(GthImageViewer * self)2301 gth_image_viewer_get_transparency_style (GthImageViewer *self)
2302 {
2303 	g_return_val_if_fail (GTH_IS_IMAGE_VIEWER (self), 0);
2304 
2305 	return self->priv->transparency_style;
2306 }
2307 
2308 
2309 void
gth_image_viewer_enable_key_bindings(GthImageViewer * self,gboolean value)2310 gth_image_viewer_enable_key_bindings (GthImageViewer *self,
2311 				      gboolean        value)
2312 {
2313 	g_return_if_fail (GTH_IS_IMAGE_VIEWER (self));
2314 
2315 	self->priv->enable_key_bindings = value;
2316 }
2317 
2318 
2319 void
gth_image_viewer_scroll_to(GthImageViewer * self,int x_offset,int y_offset)2320 gth_image_viewer_scroll_to (GthImageViewer *self,
2321 			    int             x_offset,
2322 			    int             y_offset)
2323 {
2324 	g_return_if_fail (self != NULL);
2325 
2326 	if (gth_image_viewer_get_current_image (self) == NULL)
2327 		return;
2328 
2329 	scroll_to (self, x_offset, y_offset);
2330 
2331 	/* update the adjustments value */
2332 
2333 	_g_signal_handlers_block_by_data (G_OBJECT (self->hadj), self);
2334 	_g_signal_handlers_block_by_data (G_OBJECT (self->vadj), self);
2335 	gtk_adjustment_set_value (self->hadj, self->visible_area.x);
2336 	gtk_adjustment_set_value (self->vadj, self->visible_area.y);
2337 	_g_signal_handlers_unblock_by_data (G_OBJECT (self->hadj), self);
2338 	_g_signal_handlers_unblock_by_data (G_OBJECT (self->vadj), self);
2339 }
2340 
2341 
2342 void
gth_image_viewer_scroll_to_center(GthImageViewer * self)2343 gth_image_viewer_scroll_to_center (GthImageViewer *self)
2344 {
2345 	int zoomed_width;
2346 	int zoomed_height;
2347 
2348 	_gth_image_viewer_get_zoomed_size (self, &zoomed_width, &zoomed_height);
2349 
2350 	gth_image_viewer_scroll_to (self,
2351 				    (zoomed_width - self->visible_area.width) / 2,
2352 				    (zoomed_height - self->visible_area.height) / 2);
2353 }
2354 
2355 
2356 void
gth_image_viewer_scroll_step_x(GthImageViewer * self,gboolean increment)2357 gth_image_viewer_scroll_step_x (GthImageViewer *self,
2358 				gboolean        increment)
2359 {
2360 	scroll_relative (self,
2361 			 (increment ? 1 : -1) * gtk_adjustment_get_step_increment (self->hadj),
2362 			 0);
2363 }
2364 
2365 
2366 void
gth_image_viewer_scroll_step_y(GthImageViewer * self,gboolean increment)2367 gth_image_viewer_scroll_step_y (GthImageViewer *self,
2368 				gboolean        increment)
2369 {
2370 	scroll_relative (self,
2371 			 0,
2372 			 (increment ? 1 : -1) * gtk_adjustment_get_step_increment (self->vadj));
2373 }
2374 
2375 
2376 void
gth_image_viewer_scroll_page_x(GthImageViewer * self,gboolean increment)2377 gth_image_viewer_scroll_page_x (GthImageViewer *self,
2378 				gboolean        increment)
2379 {
2380 	scroll_relative (self,
2381 			 (increment ? 1 : -1) * gtk_adjustment_get_page_increment (self->hadj),
2382 			 0);
2383 }
2384 
2385 
2386 void
gth_image_viewer_scroll_page_y(GthImageViewer * self,gboolean increment)2387 gth_image_viewer_scroll_page_y (GthImageViewer *self,
2388 				gboolean        increment)
2389 {
2390 	scroll_relative (self,
2391 			 0,
2392 			 (increment ? 1 : -1) * gtk_adjustment_get_page_increment (self->vadj));
2393 }
2394 
2395 
2396 void
gth_image_viewer_set_scroll_offset(GthImageViewer * self,int x,int y)2397 gth_image_viewer_set_scroll_offset (GthImageViewer *self,
2398 				    int             x,
2399 				    int             y)
2400 {
2401 	double quality_zoom;
2402 
2403 	quality_zoom = _gth_image_viewer_get_quality_zoom (self);
2404 	gth_image_viewer_scroll_to (self, x / quality_zoom, y / quality_zoom);
2405 }
2406 
2407 
2408 void
gth_image_viewer_get_scroll_offset(GthImageViewer * self,int * x,int * y)2409 gth_image_viewer_get_scroll_offset (GthImageViewer *self,
2410 				    int            *x,
2411 				    int            *y)
2412 {
2413 	double quality_zoom;
2414 
2415 	quality_zoom = _gth_image_viewer_get_quality_zoom (self);
2416 	*x = self->visible_area.x * quality_zoom;
2417 	*y = self->visible_area.y * quality_zoom;
2418 }
2419 
2420 
2421 void
gth_image_viewer_set_reset_scrollbars(GthImageViewer * self,gboolean reset)2422 gth_image_viewer_set_reset_scrollbars (GthImageViewer *self,
2423 				       gboolean        reset)
2424 {
2425 	self->priv->reset_scrollbars = reset;
2426 }
2427 
2428 
2429 gboolean
gth_image_viewer_get_reset_scrollbars(GthImageViewer * self)2430 gth_image_viewer_get_reset_scrollbars (GthImageViewer *self)
2431 {
2432 	return self->priv->reset_scrollbars;
2433 }
2434 
2435 
2436 void
gth_image_viewer_needs_scrollbars(GthImageViewer * self,GtkAllocation * allocation,GtkWidget * hscrollbar,GtkWidget * vscrollbar,gboolean * hscrollbar_visible_p,gboolean * vscrollbar_visible_p)2437 gth_image_viewer_needs_scrollbars (GthImageViewer *self,
2438 				   GtkAllocation  *allocation,
2439 				   GtkWidget      *hscrollbar,
2440 				   GtkWidget      *vscrollbar,
2441 				   gboolean       *hscrollbar_visible_p,
2442 				   gboolean       *vscrollbar_visible_p)
2443 {
2444 	double   zoom_level;
2445 	int      zoomed_width;
2446 	int      zoomed_height;
2447 	int      visible_width;
2448 	int      visible_height;
2449 	gboolean hscrollbar_visible;
2450 	gboolean vscrollbar_visible;
2451 
2452 	zoom_level = get_zoom_level_for_allocation (self, allocation);
2453 	_gth_image_viewer_get_zoomed_size_for_zoom (self, &zoomed_width, &zoomed_height, zoom_level);
2454 	visible_width = allocation->width;
2455 	visible_height = allocation->height;
2456 
2457 	hscrollbar_visible = (zoomed_width > visible_width);
2458 	vscrollbar_visible = (zoomed_height > visible_height);
2459 
2460 	switch (self->priv->fit) {
2461 	case GTH_FIT_SIZE:
2462 	case GTH_FIT_SIZE_IF_LARGER:
2463 		hscrollbar_visible = FALSE;
2464 		vscrollbar_visible = FALSE;
2465 		break;
2466 
2467 	case GTH_FIT_WIDTH:
2468 	case GTH_FIT_WIDTH_IF_LARGER:
2469 		hscrollbar_visible = FALSE;
2470 		break;
2471 
2472 	case GTH_FIT_HEIGHT:
2473 	case GTH_FIT_HEIGHT_IF_LARGER:
2474 		vscrollbar_visible = FALSE;
2475 		break;
2476 
2477 	case GTH_FIT_NONE:
2478 		if (hscrollbar_visible != vscrollbar_visible) {
2479 			if (vscrollbar_visible) {
2480 				GtkRequisition vscrollbar_requisition;
2481 
2482 				gtk_widget_get_preferred_size (vscrollbar, &vscrollbar_requisition, NULL);
2483 				visible_width -= vscrollbar_requisition.width;
2484 				hscrollbar_visible = (zoomed_width > visible_width);
2485 			}
2486 			else if (hscrollbar_visible) {
2487 				GtkRequisition hscrollbar_requisition;
2488 
2489 				gtk_widget_get_preferred_size (hscrollbar, &hscrollbar_requisition, NULL);
2490 				visible_height -= hscrollbar_requisition.height;
2491 				vscrollbar_visible = (zoomed_height > visible_height);
2492 			}
2493 		}
2494 		break;
2495 	}
2496 
2497 	if (hscrollbar_visible_p != NULL)
2498 		*hscrollbar_visible_p = hscrollbar_visible;
2499 	if (vscrollbar_visible_p != NULL)
2500 		*vscrollbar_visible_p = vscrollbar_visible;
2501 }
2502 
2503 
2504 gboolean
gth_image_viewer_has_scrollbars(GthImageViewer * self)2505 gth_image_viewer_has_scrollbars (GthImageViewer *self)
2506 {
2507 	int zoomed_width, zoomed_height;
2508 	_gth_image_viewer_get_zoomed_size (self, &zoomed_width, &zoomed_height);
2509 	return (self->visible_area.width < zoomed_width) || (self->visible_area.height < zoomed_height);
2510 }
2511 
2512 
2513 void
gth_image_viewer_show_cursor(GthImageViewer * self)2514 gth_image_viewer_show_cursor (GthImageViewer *self)
2515 {
2516 	if (self->priv->cursor_visible)
2517 		return;
2518 
2519 	self->priv->cursor_visible = TRUE;
2520 	if (self->priv->cursor != NULL)
2521 		gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (self)), self->priv->cursor);
2522 }
2523 
2524 
2525 void
gth_image_viewer_hide_cursor(GthImageViewer * self)2526 gth_image_viewer_hide_cursor (GthImageViewer *self)
2527 {
2528 	if (! self->priv->cursor_visible)
2529 		return;
2530 
2531 	self->priv->cursor_visible = FALSE;
2532 	if (self->priv->cursor_void != NULL)
2533 		gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (self)), self->priv->cursor_void);
2534 }
2535 
2536 
2537 void
gth_image_viewer_set_cursor(GthImageViewer * self,GdkCursor * cursor)2538 gth_image_viewer_set_cursor (GthImageViewer *self,
2539 			     GdkCursor      *cursor)
2540 {
2541 	if (cursor != NULL)
2542 		g_object_ref (cursor);
2543 
2544 	if (self->priv->cursor != NULL) {
2545 		g_object_unref (self->priv->cursor);
2546 		self->priv->cursor = NULL;
2547 	}
2548 	if (cursor != NULL)
2549 		self->priv->cursor = cursor;
2550 	else
2551 		self->priv->cursor = g_object_ref (self->priv->cursor_void);
2552 
2553 	if (! gtk_widget_get_realized (GTK_WIDGET (self)))
2554 		return;
2555 
2556 	if (self->priv->cursor_visible)
2557 		gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (self)), self->priv->cursor);
2558 }
2559 
2560 
2561 gboolean
gth_image_viewer_is_cursor_visible(GthImageViewer * self)2562 gth_image_viewer_is_cursor_visible (GthImageViewer *self)
2563 {
2564 	return self->priv->cursor_visible;
2565 }
2566 
2567 
2568 void
gth_image_viewer_show_frame(GthImageViewer * self,int frame_border)2569 gth_image_viewer_show_frame (GthImageViewer *self,
2570 			     int             frame_border)
2571 {
2572 	self->priv->frame_visible = TRUE;
2573 	self->priv->frame_border = frame_border;
2574 
2575 	gtk_widget_queue_resize (GTK_WIDGET (self));
2576 }
2577 
2578 
2579 void
gth_image_viewer_hide_frame(GthImageViewer * self)2580 gth_image_viewer_hide_frame (GthImageViewer *self)
2581 {
2582 	self->priv->frame_visible = FALSE;
2583 	self->priv->frame_border = 0;
2584 
2585 	gtk_widget_queue_resize (GTK_WIDGET (self));
2586 }
2587 
2588 
2589 gboolean
gth_image_viewer_is_frame_visible(GthImageViewer * self)2590 gth_image_viewer_is_frame_visible (GthImageViewer *self)
2591 {
2592 	return self->priv->frame_visible;
2593 }
2594 
2595 
2596 int
gth_image_viewer_get_frame_border(GthImageViewer * self)2597 gth_image_viewer_get_frame_border (GthImageViewer *self)
2598 {
2599 	return _gth_image_viewer_get_frame_border (self);
2600 }
2601 
2602 
2603 void
gth_image_viewer_paint(GthImageViewer * self,cairo_t * cr,cairo_surface_t * surface,int src_x,int src_y,int dest_x,int dest_y,int width,int height,cairo_filter_t filter)2604 gth_image_viewer_paint (GthImageViewer  *self,
2605 			cairo_t         *cr,
2606 			cairo_surface_t *surface,
2607 			int              src_x,
2608 			int              src_y,
2609 			int              dest_x,
2610 			int              dest_y,
2611 			int              width,
2612 			int              height,
2613 			cairo_filter_t   filter)
2614 {
2615 	int    original_width;
2616 	int    surface_width;
2617 	double zoom_level;
2618 	double src_dx;
2619 	double src_dy;
2620 	double dest_dx;
2621 	double dest_dy;
2622 	double dwidth;
2623 	double dheight;
2624 
2625 	surface_width = cairo_image_surface_get_width (surface);
2626 	if (surface_width <= 0)
2627 		return;
2628 
2629 	gth_image_viewer_get_original_size (self, &original_width, NULL);
2630 	zoom_level = self->priv->zoom_level * ((double) original_width / surface_width);
2631 	src_dx = (double) src_x / zoom_level;
2632 	src_dy = (double) src_y / zoom_level;
2633 	dest_dx = (double) dest_x / zoom_level;
2634 	dest_dy = (double) dest_y / zoom_level;
2635 	dwidth = (double) width / zoom_level;
2636 	dheight = (double) height / zoom_level;
2637 
2638 	if ((dwidth < 1) || (dheight < 1))
2639 		return;
2640 
2641 	cairo_save (cr);
2642 
2643 	cairo_rectangle (cr, 0, 0, self->visible_area.width, self->visible_area.height);
2644 	cairo_clip (cr);
2645 	cairo_scale (cr, zoom_level, zoom_level);
2646 	cairo_set_source_surface (cr, surface, dest_dx - src_dx, dest_dy - src_dy);
2647 	cairo_pattern_set_filter (cairo_get_source (cr), filter);
2648 	cairo_rectangle (cr, dest_dx, dest_dy, dwidth, dheight);
2649 	cairo_fill (cr);
2650 
2651   	cairo_restore (cr);
2652 }
2653 
2654 
2655 void
gth_image_viewer_paint_region(GthImageViewer * self,cairo_t * cr,cairo_surface_t * surface,int dest_x,int dest_y,cairo_rectangle_int_t * paint_area,cairo_filter_t filter)2656 gth_image_viewer_paint_region (GthImageViewer        *self,
2657 			       cairo_t               *cr,
2658 			       cairo_surface_t       *surface,
2659 			       int                    dest_x,
2660 			       int                    dest_y,
2661 			       cairo_rectangle_int_t *paint_area,
2662 			       cairo_filter_t         filter)
2663 {
2664 	int frame_border;
2665 
2666 	frame_border = _gth_image_viewer_get_frame_border (self);
2667 	gth_image_viewer_paint (self,
2668 				cr,
2669 				surface,
2670 				paint_area->x - frame_border,
2671 				paint_area->y - frame_border,
2672 				dest_x - frame_border,
2673 				dest_y - frame_border,
2674 				MIN (paint_area->width, self->image_area.width + frame_border),
2675 				MIN (paint_area->height, self->image_area.height + frame_border),
2676 				filter);
2677 }
2678 
2679 
2680 void
gth_image_viewer_paint_background(GthImageViewer * self,cairo_t * cr)2681 gth_image_viewer_paint_background (GthImageViewer *self,
2682 				   cairo_t        *cr)
2683 {
2684 	GtkAllocation allocation;
2685 
2686 	cairo_save (cr);
2687 	gtk_widget_get_allocation (GTK_WIDGET (self), &allocation);
2688 	cairo_set_source_rgb (cr, GRAY_VALUE, GRAY_VALUE, GRAY_VALUE);
2689 	cairo_rectangle (cr,
2690 			 0,
2691 			 0,
2692 			 allocation.width,
2693 			 allocation.height);
2694 	cairo_fill (cr);
2695 	cairo_restore (cr);
2696 }
2697 
2698 
2699 #define FRAME_SHADOW_OFS 1
2700 
2701 
2702 void
gth_image_viewer_paint_frame(GthImageViewer * self,cairo_t * cr)2703 gth_image_viewer_paint_frame (GthImageViewer *self,
2704 			      cairo_t        *cr)
2705 {
2706 	gboolean background_only;
2707 
2708 	background_only = ! gth_image_viewer_is_frame_visible (self);
2709 
2710 	cairo_save (cr);
2711 
2712 	cairo_translate (cr, -self->visible_area.x, -self->visible_area.y);
2713 
2714 	if (! background_only) {
2715 		/* drop shadow */
2716 
2717 		cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
2718 		cairo_rectangle (cr,
2719 				 self->image_area.x + FRAME_SHADOW_OFS + 0.5,
2720 				 self->image_area.y + FRAME_SHADOW_OFS + 0.5,
2721 				 self->image_area.width + 2 + FRAME_SHADOW_OFS,
2722 				 self->image_area.height + 2 + FRAME_SHADOW_OFS);
2723 		cairo_fill (cr);
2724 	}
2725 
2726 	/* background */
2727 
2728 	switch (self->priv->transparency_style) {
2729 	case GTH_TRANSPARENCY_STYLE_CHECKERED:
2730 		cairo_set_source (cr, self->priv->background_pattern);
2731 		break;
2732 	case GTH_TRANSPARENCY_STYLE_WHITE:
2733 		cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
2734 		break;
2735 	case GTH_TRANSPARENCY_STYLE_GRAY:
2736 		cairo_set_source_rgb (cr, GRAY_VALUE, GRAY_VALUE, GRAY_VALUE);
2737 		break;
2738 	case GTH_TRANSPARENCY_STYLE_BLACK:
2739 		cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2740 		break;
2741 	}
2742 
2743 	cairo_rectangle (cr,
2744 			 self->image_area.x,
2745 			 self->image_area.y,
2746 			 self->image_area.width,
2747 			 self->image_area.height);
2748 	cairo_fill (cr);
2749 
2750 	if (! background_only) {
2751 		/* frame */
2752 
2753 		cairo_set_line_width (cr, 2.0);
2754 		cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
2755 		cairo_rectangle (cr,
2756 				 self->image_area.x - 1,
2757 				 self->image_area.y - 1,
2758 				 self->image_area.width + 2,
2759 				 self->image_area.height + 2);
2760 		cairo_stroke (cr);
2761 
2762 		cairo_set_line_width (cr, 1.0);
2763 		cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2764 		cairo_rectangle (cr,
2765 				 self->image_area.x - 2,
2766 				 self->image_area.y - 2,
2767 				 self->image_area.width + 5,
2768 				 self->image_area.height + 5);
2769 		cairo_stroke (cr);
2770 	}
2771 
2772 	cairo_restore (cr);
2773 }
2774 
2775 
2776 void
gth_image_viewer_apply_painters(GthImageViewer * self,cairo_t * cr)2777 gth_image_viewer_apply_painters (GthImageViewer *self,
2778 				 cairo_t        *cr)
2779 {
2780 	GList *scan;
2781 
2782 	for (scan = self->priv->painters; scan; scan = scan->next) {
2783 		PainterData *painter_data = scan->data;
2784 
2785 		cairo_save (cr);
2786 		painter_data->func (self, cr, painter_data->user_data);
2787 		cairo_restore (cr);
2788 	}
2789 }
2790 
2791 
2792 void
gth_image_viewer_crop_area(GthImageViewer * self,cairo_rectangle_int_t * area)2793 gth_image_viewer_crop_area (GthImageViewer        *self,
2794 			    cairo_rectangle_int_t *area)
2795 {
2796 	area->width = MIN (area->width, self->visible_area.width);
2797 	area->width = MIN (area->height, self->visible_area.height);
2798 }
2799