1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpview.c
5 * Copyright (C) 2001-2006 Michael Natterer <mitch@gimp.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <string.h>
24
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27
28 #include "libgimpcolor/gimpcolor.h"
29 #include "libgimpmath/gimpmath.h"
30 #include "libgimpbase/gimpbase.h"
31 #include "libgimpwidgets/gimpwidgets.h"
32
33 #include "widgets-types.h"
34
35 #include "core/gimpcontext.h"
36 #include "core/gimpmarshal.h"
37 #include "core/gimpviewable.h"
38
39 #include "gimpdnd.h"
40 #include "gimpview.h"
41 #include "gimpview-popup.h"
42 #include "gimpviewrenderer.h"
43 #include "gimpviewrenderer-utils.h"
44
45
46 enum
47 {
48 SET_VIEWABLE,
49 CLICKED,
50 DOUBLE_CLICKED,
51 CONTEXT,
52 LAST_SIGNAL
53 };
54
55
56 static void gimp_view_dispose (GObject *object);
57
58 static void gimp_view_realize (GtkWidget *widget);
59 static void gimp_view_unrealize (GtkWidget *widget);
60 static void gimp_view_map (GtkWidget *widget);
61 static void gimp_view_unmap (GtkWidget *widget);
62 static void gimp_view_size_request (GtkWidget *widget,
63 GtkRequisition *requisition);
64 static void gimp_view_size_allocate (GtkWidget *widget,
65 GtkAllocation *allocation);
66 static void gimp_view_style_set (GtkWidget *widget,
67 GtkStyle *prev_style);
68 static gboolean gimp_view_expose_event (GtkWidget *widget,
69 GdkEventExpose *event);
70 static gboolean gimp_view_button_press_event (GtkWidget *widget,
71 GdkEventButton *bevent);
72 static gboolean gimp_view_button_release_event (GtkWidget *widget,
73 GdkEventButton *bevent);
74 static gboolean gimp_view_enter_notify_event (GtkWidget *widget,
75 GdkEventCrossing *event);
76 static gboolean gimp_view_leave_notify_event (GtkWidget *widget,
77 GdkEventCrossing *event);
78
79 static void gimp_view_real_set_viewable (GimpView *view,
80 GimpViewable *old,
81 GimpViewable *viewable);
82
83 static void gimp_view_update_callback (GimpViewRenderer *renderer,
84 GimpView *view);
85
86 static void gimp_view_monitor_changed (GimpView *view);
87
88 static GimpViewable * gimp_view_drag_viewable (GtkWidget *widget,
89 GimpContext **context,
90 gpointer data);
91 static GdkPixbuf * gimp_view_drag_pixbuf (GtkWidget *widget,
92 gpointer data);
93
94
95 G_DEFINE_TYPE (GimpView, gimp_view, GTK_TYPE_WIDGET)
96
97 #define parent_class gimp_view_parent_class
98
99 static guint view_signals[LAST_SIGNAL] = { 0 };
100
101
102 static void
gimp_view_class_init(GimpViewClass * klass)103 gimp_view_class_init (GimpViewClass *klass)
104 {
105 GObjectClass *object_class = G_OBJECT_CLASS (klass);
106 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
107
108 view_signals[SET_VIEWABLE] =
109 g_signal_new ("set-viewable",
110 G_TYPE_FROM_CLASS (klass),
111 G_SIGNAL_RUN_FIRST,
112 G_STRUCT_OFFSET (GimpViewClass, set_viewable),
113 NULL, NULL,
114 gimp_marshal_VOID__OBJECT_OBJECT,
115 G_TYPE_NONE, 2,
116 GIMP_TYPE_VIEWABLE, GIMP_TYPE_VIEWABLE);
117
118 view_signals[CLICKED] =
119 g_signal_new ("clicked",
120 G_TYPE_FROM_CLASS (klass),
121 G_SIGNAL_RUN_FIRST,
122 G_STRUCT_OFFSET (GimpViewClass, clicked),
123 NULL, NULL,
124 gimp_marshal_VOID__FLAGS,
125 G_TYPE_NONE, 1,
126 GDK_TYPE_MODIFIER_TYPE);
127
128 view_signals[DOUBLE_CLICKED] =
129 g_signal_new ("double-clicked",
130 G_TYPE_FROM_CLASS (klass),
131 G_SIGNAL_RUN_FIRST,
132 G_STRUCT_OFFSET (GimpViewClass, double_clicked),
133 NULL, NULL,
134 gimp_marshal_VOID__VOID,
135 G_TYPE_NONE, 0);
136
137 view_signals[CONTEXT] =
138 g_signal_new ("context",
139 G_TYPE_FROM_CLASS (klass),
140 G_SIGNAL_RUN_FIRST,
141 G_STRUCT_OFFSET (GimpViewClass, context),
142 NULL, NULL,
143 gimp_marshal_VOID__VOID,
144 G_TYPE_NONE, 0);
145
146 object_class->dispose = gimp_view_dispose;
147
148 widget_class->activate_signal = view_signals[CLICKED];
149 widget_class->realize = gimp_view_realize;
150 widget_class->unrealize = gimp_view_unrealize;
151 widget_class->map = gimp_view_map;
152 widget_class->unmap = gimp_view_unmap;
153 widget_class->size_request = gimp_view_size_request;
154 widget_class->size_allocate = gimp_view_size_allocate;
155 widget_class->style_set = gimp_view_style_set;
156 widget_class->expose_event = gimp_view_expose_event;
157 widget_class->button_press_event = gimp_view_button_press_event;
158 widget_class->button_release_event = gimp_view_button_release_event;
159 widget_class->enter_notify_event = gimp_view_enter_notify_event;
160 widget_class->leave_notify_event = gimp_view_leave_notify_event;
161
162 klass->set_viewable = gimp_view_real_set_viewable;
163 klass->clicked = NULL;
164 klass->double_clicked = NULL;
165 klass->context = NULL;
166 }
167
168 static void
gimp_view_init(GimpView * view)169 gimp_view_init (GimpView *view)
170 {
171 gtk_widget_set_has_window (GTK_WIDGET (view), FALSE);
172 gtk_widget_add_events (GTK_WIDGET (view),
173 GDK_BUTTON_PRESS_MASK |
174 GDK_BUTTON_RELEASE_MASK |
175 GDK_ENTER_NOTIFY_MASK |
176 GDK_LEAVE_NOTIFY_MASK);
177
178 view->clickable = FALSE;
179 view->eat_button_events = TRUE;
180 view->show_popup = FALSE;
181 view->expand = FALSE;
182
183 view->in_button = FALSE;
184 view->has_grab = FALSE;
185 view->press_state = 0;
186
187 gimp_widget_track_monitor (GTK_WIDGET (view),
188 G_CALLBACK (gimp_view_monitor_changed),
189 NULL);
190 }
191
192 static void
gimp_view_dispose(GObject * object)193 gimp_view_dispose (GObject *object)
194 {
195 GimpView *view = GIMP_VIEW (object);
196
197 if (view->viewable)
198 gimp_view_set_viewable (view, NULL);
199
200 g_clear_object (&view->renderer);
201
202 G_OBJECT_CLASS (parent_class)->dispose (object);
203 }
204
205 static void
gimp_view_realize(GtkWidget * widget)206 gimp_view_realize (GtkWidget *widget)
207 {
208 GimpView *view = GIMP_VIEW (widget);
209 GtkAllocation allocation;
210 GdkWindowAttr attributes;
211 gint attributes_mask;
212
213 GTK_WIDGET_CLASS (parent_class)->realize (widget);
214
215 gtk_widget_get_allocation (widget, &allocation);
216
217 attributes.window_type = GDK_WINDOW_CHILD;
218 attributes.x = allocation.x;
219 attributes.y = allocation.y;
220 attributes.width = allocation.width;
221 attributes.height = allocation.height;
222 attributes.wclass = GDK_INPUT_ONLY;
223 attributes.event_mask = gtk_widget_get_events (widget);
224
225 attributes_mask = GDK_WA_X | GDK_WA_Y;
226
227 view->event_window = gdk_window_new (gtk_widget_get_window (widget),
228 &attributes, attributes_mask);
229 gdk_window_set_user_data (view->event_window, view);
230 }
231
232 static void
gimp_view_unrealize(GtkWidget * widget)233 gimp_view_unrealize (GtkWidget *widget)
234 {
235 GimpView *view = GIMP_VIEW (widget);
236
237 if (view->event_window)
238 {
239 gdk_window_set_user_data (view->event_window, NULL);
240 gdk_window_destroy (view->event_window);
241 view->event_window = NULL;
242 }
243
244 GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
245 }
246
247 static void
gimp_view_map(GtkWidget * widget)248 gimp_view_map (GtkWidget *widget)
249 {
250 GimpView *view = GIMP_VIEW (widget);
251
252 GTK_WIDGET_CLASS (parent_class)->map (widget);
253
254 if (view->event_window)
255 gdk_window_show (view->event_window);
256 }
257
258 static void
gimp_view_unmap(GtkWidget * widget)259 gimp_view_unmap (GtkWidget *widget)
260 {
261 GimpView *view = GIMP_VIEW (widget);
262
263 if (view->has_grab)
264 {
265 gtk_grab_remove (widget);
266 view->has_grab = FALSE;
267 }
268
269 if (view->event_window)
270 gdk_window_hide (view->event_window);
271
272 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
273 }
274
275 static void
gimp_view_size_request(GtkWidget * widget,GtkRequisition * requisition)276 gimp_view_size_request (GtkWidget *widget,
277 GtkRequisition *requisition)
278 {
279 GimpView *view = GIMP_VIEW (widget);
280
281 if (view->expand)
282 {
283 requisition->width = 2 * view->renderer->border_width + 1;
284 requisition->height = 2 * view->renderer->border_width + 1;
285 }
286 else
287 {
288 requisition->width = (view->renderer->width +
289 2 * view->renderer->border_width);
290 requisition->height = (view->renderer->height +
291 2 * view->renderer->border_width);
292 }
293 }
294
295 static void
gimp_view_size_allocate(GtkWidget * widget,GtkAllocation * allocation)296 gimp_view_size_allocate (GtkWidget *widget,
297 GtkAllocation *allocation)
298 {
299 GimpView *view = GIMP_VIEW (widget);
300 gint width;
301 gint height;
302
303 if (view->expand)
304 {
305 width = MIN (GIMP_VIEWABLE_MAX_PREVIEW_SIZE,
306 allocation->width - 2 * view->renderer->border_width);
307 height = MIN (GIMP_VIEWABLE_MAX_PREVIEW_SIZE,
308 allocation->height - 2 * view->renderer->border_width);
309
310 if (view->renderer->width != width ||
311 view->renderer->height != height)
312 {
313 gint border_width = view->renderer->border_width;
314
315 if (view->renderer->size != -1 && view->renderer->viewable)
316 {
317 gint view_width;
318 gint view_height;
319 gint scaled_width;
320 gint scaled_height;
321
322 gimp_viewable_get_preview_size (view->renderer->viewable,
323 GIMP_VIEWABLE_MAX_PREVIEW_SIZE,
324 view->renderer->is_popup,
325 view->renderer->dot_for_dot,
326 &view_width,
327 &view_height);
328
329 gimp_viewable_calc_preview_size (view_width, view_height,
330 width, height,
331 TRUE, 1.0, 1.0,
332 &scaled_width, &scaled_height,
333 NULL);
334
335 if (scaled_width > width)
336 {
337 scaled_height = scaled_height * width / scaled_width;
338 scaled_width = scaled_width * width / scaled_width;
339 }
340 else if (scaled_height > height)
341 {
342 scaled_width = scaled_width * height / scaled_height;
343 scaled_height = scaled_height * height / scaled_height;
344 }
345
346 gimp_view_renderer_set_size (view->renderer,
347 MAX (scaled_width, scaled_height),
348 border_width);
349 }
350 else
351 {
352 gimp_view_renderer_set_size_full (view->renderer,
353 width, height,
354 border_width);
355 }
356
357 gimp_view_renderer_remove_idle (view->renderer);
358 }
359 }
360
361 width = (view->renderer->width +
362 2 * view->renderer->border_width);
363 height = (view->renderer->height +
364 2 * view->renderer->border_width);
365
366 if (allocation->width > width)
367 allocation->x += (allocation->width - width) / 2;
368
369 if (allocation->height > height)
370 allocation->y += (allocation->height - height) / 2;
371
372 allocation->width = width;
373 allocation->height = height;
374
375 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
376
377 if (gtk_widget_get_realized (widget))
378 gdk_window_move_resize (view->event_window,
379 allocation->x,
380 allocation->y,
381 allocation->width,
382 allocation->height);
383 }
384
385 static void
gimp_view_style_set(GtkWidget * widget,GtkStyle * prev_style)386 gimp_view_style_set (GtkWidget *widget,
387 GtkStyle *prev_style)
388 {
389 GimpView *view = GIMP_VIEW (widget);
390
391 GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
392
393 gimp_view_renderer_invalidate (view->renderer);
394 }
395
396 static gboolean
gimp_view_expose_event(GtkWidget * widget,GdkEventExpose * event)397 gimp_view_expose_event (GtkWidget *widget,
398 GdkEventExpose *event)
399 {
400 if (gtk_widget_is_drawable (widget))
401 {
402 GtkAllocation allocation;
403 cairo_t *cr;
404
405 gtk_widget_get_allocation (widget, &allocation);
406
407 cr = gdk_cairo_create (event->window);
408 gdk_cairo_region (cr, event->region);
409 cairo_clip (cr);
410
411 cairo_translate (cr, allocation.x, allocation.y);
412
413 gimp_view_renderer_draw (GIMP_VIEW (widget)->renderer,
414 widget, cr,
415 allocation.width,
416 allocation.height);
417
418 cairo_destroy (cr);
419 }
420
421 return FALSE;
422 }
423
424
425 #define DEBUG_MEMSIZE 1
426
427 #ifdef DEBUG_MEMSIZE
428 extern gboolean gimp_debug_memsize;
429 #endif
430
431 static gboolean
gimp_view_button_press_event(GtkWidget * widget,GdkEventButton * bevent)432 gimp_view_button_press_event (GtkWidget *widget,
433 GdkEventButton *bevent)
434 {
435 GimpView *view = GIMP_VIEW (widget);
436
437 #ifdef DEBUG_MEMSIZE
438 if (bevent->type == GDK_BUTTON_PRESS && bevent->button == 2)
439 {
440 gimp_debug_memsize = TRUE;
441
442 gimp_object_get_memsize (GIMP_OBJECT (view->viewable), NULL);
443
444 gimp_debug_memsize = FALSE;
445 }
446 #endif /* DEBUG_MEMSIZE */
447
448 if (! view->clickable &&
449 ! view->show_popup)
450 return FALSE;
451
452 if (! gtk_widget_get_realized (widget))
453 return FALSE;
454
455 if (bevent->type == GDK_BUTTON_PRESS)
456 {
457 if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
458 {
459 view->press_state = 0;
460
461 g_signal_emit (widget, view_signals[CONTEXT], 0);
462 }
463 else if (bevent->button == 1)
464 {
465 gtk_grab_add (widget);
466
467 view->has_grab = TRUE;
468 view->press_state = bevent->state;
469
470 if (view->show_popup && view->viewable)
471 {
472 gimp_view_popup_show (widget, bevent,
473 view->renderer->context,
474 view->viewable,
475 view->renderer->width,
476 view->renderer->height,
477 view->renderer->dot_for_dot);
478 }
479 }
480 else
481 {
482 view->press_state = 0;
483
484 if (bevent->button == 2)
485 gimp_view_popup_show (widget, bevent,
486 view->renderer->context,
487 view->viewable,
488 view->renderer->width,
489 view->renderer->height,
490 view->renderer->dot_for_dot);
491
492 return FALSE;
493 }
494 }
495 else if (bevent->type == GDK_2BUTTON_PRESS)
496 {
497 if (bevent->button == 1)
498 g_signal_emit (widget, view_signals[DOUBLE_CLICKED], 0);
499 }
500
501 return view->eat_button_events ? TRUE : FALSE;
502 }
503
504 static gboolean
gimp_view_button_release_event(GtkWidget * widget,GdkEventButton * bevent)505 gimp_view_button_release_event (GtkWidget *widget,
506 GdkEventButton *bevent)
507 {
508 GimpView *view = GIMP_VIEW (widget);
509
510 if (! view->clickable &&
511 ! view->show_popup)
512 return FALSE;
513
514 if (bevent->button == 1)
515 {
516 gtk_grab_remove (widget);
517 view->has_grab = FALSE;
518
519 if (view->clickable && view->in_button)
520 {
521 g_signal_emit (widget, view_signals[CLICKED], 0, view->press_state);
522 }
523 }
524 else
525 {
526 return FALSE;
527 }
528
529 return view->eat_button_events ? TRUE : FALSE;
530 }
531
532 static gboolean
gimp_view_enter_notify_event(GtkWidget * widget,GdkEventCrossing * event)533 gimp_view_enter_notify_event (GtkWidget *widget,
534 GdkEventCrossing *event)
535 {
536 GimpView *view = GIMP_VIEW (widget);
537 GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent *) event);
538
539 if ((event_widget == widget) &&
540 (event->detail != GDK_NOTIFY_INFERIOR))
541 {
542 view->in_button = TRUE;
543 }
544
545 return FALSE;
546 }
547
548 static gboolean
gimp_view_leave_notify_event(GtkWidget * widget,GdkEventCrossing * event)549 gimp_view_leave_notify_event (GtkWidget *widget,
550 GdkEventCrossing *event)
551 {
552 GimpView *view = GIMP_VIEW (widget);
553 GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent *) event);
554
555 if ((event_widget == widget) &&
556 (event->detail != GDK_NOTIFY_INFERIOR))
557 {
558 view->in_button = FALSE;
559 }
560
561 return FALSE;
562 }
563
564 static void
gimp_view_real_set_viewable(GimpView * view,GimpViewable * old,GimpViewable * viewable)565 gimp_view_real_set_viewable (GimpView *view,
566 GimpViewable *old,
567 GimpViewable *viewable)
568 {
569 GType viewable_type = G_TYPE_NONE;
570
571 if (viewable == view->viewable)
572 return;
573
574 if (viewable)
575 {
576 viewable_type = G_TYPE_FROM_INSTANCE (viewable);
577
578 g_return_if_fail (g_type_is_a (viewable_type,
579 view->renderer->viewable_type));
580 }
581
582 if (view->viewable)
583 {
584 g_object_remove_weak_pointer (G_OBJECT (view->viewable),
585 (gpointer) &view->viewable);
586
587 if (! viewable && ! view->renderer->is_popup)
588 {
589 if (gimp_dnd_viewable_source_remove (GTK_WIDGET (view),
590 G_TYPE_FROM_INSTANCE (view->viewable)))
591 {
592 if (gimp_viewable_get_size (view->viewable, NULL, NULL))
593 gimp_dnd_pixbuf_source_remove (GTK_WIDGET (view));
594
595 gtk_drag_source_unset (GTK_WIDGET (view));
596 }
597 }
598 }
599 else if (viewable && ! view->renderer->is_popup)
600 {
601 if (gimp_dnd_drag_source_set_by_type (GTK_WIDGET (view),
602 GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
603 viewable_type,
604 GDK_ACTION_COPY))
605 {
606 gimp_dnd_viewable_source_add (GTK_WIDGET (view),
607 viewable_type,
608 gimp_view_drag_viewable,
609 NULL);
610
611 if (gimp_viewable_get_size (viewable, NULL, NULL))
612 gimp_dnd_pixbuf_source_add (GTK_WIDGET (view),
613 gimp_view_drag_pixbuf,
614 NULL);
615 }
616 }
617
618 gimp_view_renderer_set_viewable (view->renderer, viewable);
619 view->viewable = viewable;
620
621 if (view->viewable)
622 {
623 g_object_add_weak_pointer (G_OBJECT (view->viewable),
624 (gpointer) &view->viewable);
625 }
626 }
627
628 /* public functions */
629
630 GtkWidget *
gimp_view_new(GimpContext * context,GimpViewable * viewable,gint size,gint border_width,gboolean is_popup)631 gimp_view_new (GimpContext *context,
632 GimpViewable *viewable,
633 gint size,
634 gint border_width,
635 gboolean is_popup)
636 {
637 GtkWidget *view;
638
639 g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
640 g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
641
642 view = gimp_view_new_by_types (context,
643 GIMP_TYPE_VIEW,
644 G_TYPE_FROM_INSTANCE (viewable),
645 size, border_width, is_popup);
646
647 if (view)
648 gimp_view_set_viewable (GIMP_VIEW (view), viewable);
649
650 gimp_view_renderer_remove_idle (GIMP_VIEW (view)->renderer);
651
652 return view;
653 }
654
655 GtkWidget *
gimp_view_new_full(GimpContext * context,GimpViewable * viewable,gint width,gint height,gint border_width,gboolean is_popup,gboolean clickable,gboolean show_popup)656 gimp_view_new_full (GimpContext *context,
657 GimpViewable *viewable,
658 gint width,
659 gint height,
660 gint border_width,
661 gboolean is_popup,
662 gboolean clickable,
663 gboolean show_popup)
664 {
665 GtkWidget *view;
666
667 g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
668 g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
669
670 view = gimp_view_new_full_by_types (context,
671 GIMP_TYPE_VIEW,
672 G_TYPE_FROM_INSTANCE (viewable),
673 width, height, border_width,
674 is_popup, clickable, show_popup);
675
676 if (view)
677 gimp_view_set_viewable (GIMP_VIEW (view), viewable);
678
679 gimp_view_renderer_remove_idle (GIMP_VIEW (view)->renderer);
680
681 return view;
682 }
683
684 GtkWidget *
gimp_view_new_by_types(GimpContext * context,GType view_type,GType viewable_type,gint size,gint border_width,gboolean is_popup)685 gimp_view_new_by_types (GimpContext *context,
686 GType view_type,
687 GType viewable_type,
688 gint size,
689 gint border_width,
690 gboolean is_popup)
691 {
692 GimpViewRenderer *renderer;
693 GimpView *view;
694
695 g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
696 g_return_val_if_fail (g_type_is_a (view_type, GIMP_TYPE_VIEW), NULL);
697 g_return_val_if_fail (g_type_is_a (viewable_type, GIMP_TYPE_VIEWABLE), NULL);
698 g_return_val_if_fail (size > 0 &&
699 size <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
700 g_return_val_if_fail (border_width >= 0 &&
701 border_width <= GIMP_VIEW_MAX_BORDER_WIDTH, NULL);
702
703 renderer = gimp_view_renderer_new (context, viewable_type, size,
704 border_width, is_popup);
705
706 g_return_val_if_fail (renderer != NULL, NULL);
707
708 view = g_object_new (view_type, NULL);
709
710 g_signal_connect (renderer, "update",
711 G_CALLBACK (gimp_view_update_callback),
712 view);
713
714 view->renderer = renderer;
715
716 return GTK_WIDGET (view);
717 }
718
719 GtkWidget *
gimp_view_new_full_by_types(GimpContext * context,GType view_type,GType viewable_type,gint width,gint height,gint border_width,gboolean is_popup,gboolean clickable,gboolean show_popup)720 gimp_view_new_full_by_types (GimpContext *context,
721 GType view_type,
722 GType viewable_type,
723 gint width,
724 gint height,
725 gint border_width,
726 gboolean is_popup,
727 gboolean clickable,
728 gboolean show_popup)
729 {
730 GimpViewRenderer *renderer;
731 GimpView *view;
732
733 g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
734 g_return_val_if_fail (g_type_is_a (view_type, GIMP_TYPE_VIEW), NULL);
735 g_return_val_if_fail (g_type_is_a (viewable_type, GIMP_TYPE_VIEWABLE), NULL);
736 g_return_val_if_fail (width > 0 &&
737 width <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
738 g_return_val_if_fail (height > 0 &&
739 height <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
740 g_return_val_if_fail (border_width >= 0 &&
741 border_width <= GIMP_VIEW_MAX_BORDER_WIDTH, NULL);
742
743 renderer = gimp_view_renderer_new_full (context, viewable_type,
744 width, height, border_width,
745 is_popup);
746
747 g_return_val_if_fail (renderer != NULL, NULL);
748
749 view = g_object_new (view_type, NULL);
750
751 g_signal_connect (renderer, "update",
752 G_CALLBACK (gimp_view_update_callback),
753 view);
754
755 view->renderer = renderer;
756 view->clickable = clickable ? TRUE : FALSE;
757 view->show_popup = show_popup ? TRUE : FALSE;
758
759 return GTK_WIDGET (view);
760 }
761
762 GimpViewable *
gimp_view_get_viewable(GimpView * view)763 gimp_view_get_viewable (GimpView *view)
764 {
765 g_return_val_if_fail (GIMP_IS_VIEW (view), NULL);
766
767 return view->viewable;
768 }
769
770 void
gimp_view_set_viewable(GimpView * view,GimpViewable * viewable)771 gimp_view_set_viewable (GimpView *view,
772 GimpViewable *viewable)
773 {
774 g_return_if_fail (GIMP_IS_VIEW (view));
775 g_return_if_fail (viewable == NULL || GIMP_IS_VIEWABLE (viewable));
776
777 if (viewable == view->viewable)
778 return;
779
780 g_signal_emit (view, view_signals[SET_VIEWABLE], 0, view->viewable, viewable);
781 }
782
783 void
gimp_view_set_expand(GimpView * view,gboolean expand)784 gimp_view_set_expand (GimpView *view,
785 gboolean expand)
786 {
787 g_return_if_fail (GIMP_IS_VIEW (view));
788
789 if (view->expand != expand)
790 {
791 view->expand = expand ? TRUE : FALSE;
792 gtk_widget_queue_resize (GTK_WIDGET (view));
793 }
794 }
795
796
797 /* private functions */
798
799 static void
gimp_view_update_callback(GimpViewRenderer * renderer,GimpView * view)800 gimp_view_update_callback (GimpViewRenderer *renderer,
801 GimpView *view)
802 {
803 GtkWidget *widget = GTK_WIDGET (view);
804 GtkRequisition requisition;
805 gint width;
806 gint height;
807
808 gtk_widget_get_requisition (widget, &requisition);
809
810 width = renderer->width + 2 * renderer->border_width;
811 height = renderer->height + 2 * renderer->border_width;
812
813 if (width != requisition.width ||
814 height != requisition.height)
815 {
816 gtk_widget_queue_resize (widget);
817 }
818 else
819 {
820 gtk_widget_queue_draw (widget);
821 }
822 }
823
824 static void
gimp_view_monitor_changed(GimpView * view)825 gimp_view_monitor_changed (GimpView *view)
826 {
827 if (view->renderer)
828 gimp_view_renderer_free_color_transform (view->renderer);
829 }
830
831 static GimpViewable *
gimp_view_drag_viewable(GtkWidget * widget,GimpContext ** context,gpointer data)832 gimp_view_drag_viewable (GtkWidget *widget,
833 GimpContext **context,
834 gpointer data)
835 {
836 if (context)
837 *context = GIMP_VIEW (widget)->renderer->context;
838
839 return GIMP_VIEW (widget)->viewable;
840 }
841
842 static GdkPixbuf *
gimp_view_drag_pixbuf(GtkWidget * widget,gpointer data)843 gimp_view_drag_pixbuf (GtkWidget *widget,
844 gpointer data)
845 {
846 GimpView *view = GIMP_VIEW (widget);
847 GimpViewable *viewable = view->viewable;
848 gint width;
849 gint height;
850
851 if (viewable && gimp_viewable_get_size (viewable, &width, &height))
852 return gimp_viewable_get_new_pixbuf (viewable, view->renderer->context,
853 width, height);
854
855 return NULL;
856 }
857