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