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