1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23  */
24 
25 #include "config.h"
26 
27 #include "gtkviewport.h"
28 
29 #include "gtkadjustment.h"
30 #include "gtkcsscustomgadgetprivate.h"
31 #include "gtkintl.h"
32 #include "gtkmarshalers.h"
33 #include "gtkpixelcacheprivate.h"
34 #include "gtkprivate.h"
35 #include "gtkscrollable.h"
36 #include "gtkrenderbackgroundprivate.h"
37 #include "gtkstylecontextprivate.h"
38 #include "gtktypebuiltins.h"
39 #include "gtkwidgetprivate.h"
40 
41 
42 /**
43  * SECTION:gtkviewport
44  * @Short_description: An adapter which makes widgets scrollable
45  * @Title: GtkViewport
46  * @See_also:#GtkScrolledWindow, #GtkAdjustment
47  *
48  * The #GtkViewport widget acts as an adaptor class, implementing
49  * scrollability for child widgets that lack their own scrolling
50  * capabilities. Use GtkViewport to scroll child widgets such as
51  * #GtkGrid, #GtkBox, and so on.
52  *
53  * If a widget has native scrolling abilities, such as #GtkTextView,
54  * #GtkTreeView or #GtkIconView, it can be added to a #GtkScrolledWindow
55  * with gtk_container_add(). If a widget does not, you must first add the
56  * widget to a #GtkViewport, then add the viewport to the scrolled window.
57  * gtk_container_add() does this automatically if a child that does not
58  * implement #GtkScrollable is added to a #GtkScrolledWindow, so you can
59  * ignore the presence of the viewport.
60  *
61  * The GtkViewport will start scrolling content only if allocated less
62  * than the child widget’s minimum size in a given orientation.
63  *
64  * # CSS nodes
65  *
66  * GtkViewport has a single CSS node with name viewport.
67  */
68 
69 struct _GtkViewportPrivate
70 {
71   GtkAdjustment  *hadjustment;
72   GtkAdjustment  *vadjustment;
73   GtkShadowType   shadow_type;
74 
75   GdkWindow      *bin_window;
76   GdkWindow      *view_window;
77 
78   GtkCssGadget *gadget;
79 
80   GtkPixelCache *pixel_cache;
81 
82   /* GtkScrollablePolicy needs to be checked when
83    * driving the scrollable adjustment values */
84   guint hscroll_policy : 1;
85   guint vscroll_policy : 1;
86 };
87 
88 enum {
89   PROP_0,
90   PROP_HADJUSTMENT,
91   PROP_VADJUSTMENT,
92   PROP_HSCROLL_POLICY,
93   PROP_VSCROLL_POLICY,
94   PROP_SHADOW_TYPE
95 };
96 
97 
98 static void gtk_viewport_set_property             (GObject         *object,
99 						   guint            prop_id,
100 						   const GValue    *value,
101 						   GParamSpec      *pspec);
102 static void gtk_viewport_get_property             (GObject         *object,
103 						   guint            prop_id,
104 						   GValue          *value,
105 						   GParamSpec      *pspec);
106 static void gtk_viewport_finalize                 (GObject         *object);
107 static void gtk_viewport_destroy                  (GtkWidget        *widget);
108 static void gtk_viewport_realize                  (GtkWidget        *widget);
109 static void gtk_viewport_unrealize                (GtkWidget        *widget);
110 static void gtk_viewport_map                      (GtkWidget        *widget);
111 static void gtk_viewport_unmap                    (GtkWidget        *widget);
112 static gint gtk_viewport_draw                     (GtkWidget        *widget,
113 						   cairo_t          *cr);
114 static void gtk_viewport_remove                   (GtkContainer     *container,
115 						   GtkWidget        *widget);
116 static void gtk_viewport_add                      (GtkContainer     *container,
117 						   GtkWidget        *widget);
118 static void gtk_viewport_size_allocate            (GtkWidget        *widget,
119 						   GtkAllocation    *allocation);
120 static void gtk_viewport_adjustment_value_changed (GtkAdjustment    *adjustment,
121 						   gpointer          data);
122 
123 static void gtk_viewport_get_preferred_width      (GtkWidget        *widget,
124 						   gint             *minimum_size,
125 						   gint             *natural_size);
126 static void gtk_viewport_get_preferred_height     (GtkWidget        *widget,
127 						   gint             *minimum_size,
128 						   gint             *natural_size);
129 static void gtk_viewport_get_preferred_width_for_height (GtkWidget  *widget,
130                                                    gint              height,
131 						   gint             *minimum_size,
132 						   gint             *natural_size);
133 static void gtk_viewport_get_preferred_height_for_width (GtkWidget  *widget,
134                                                    gint              width,
135 						   gint             *minimum_size,
136 						   gint             *natural_size);
137 
138 static void viewport_set_adjustment               (GtkViewport      *viewport,
139                                                    GtkOrientation    orientation,
140                                                    GtkAdjustment    *adjustment);
141 static void gtk_viewport_queue_draw_region        (GtkWidget        *widget,
142 						   const cairo_region_t *region);
143 
G_DEFINE_TYPE_WITH_CODE(GtkViewport,gtk_viewport,GTK_TYPE_BIN,G_ADD_PRIVATE (GtkViewport)G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,NULL))144 G_DEFINE_TYPE_WITH_CODE (GtkViewport, gtk_viewport, GTK_TYPE_BIN,
145                          G_ADD_PRIVATE (GtkViewport)
146                          G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
147 
148 static void
149 gtk_viewport_measure (GtkCssGadget   *gadget,
150                       GtkOrientation  orientation,
151                       int             for_size,
152                       int            *minimum,
153                       int            *natural,
154                       int            *minimum_baseline,
155                       int            *natural_baseline,
156                       gpointer        data)
157 {
158   GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
159   GtkWidget *child;
160 
161   *minimum = *natural = 0;
162 
163   child = gtk_bin_get_child (GTK_BIN (widget));
164   if (child && gtk_widget_get_visible (child))
165     _gtk_widget_get_preferred_size_for_size (child,
166                                              orientation,
167                                              for_size,
168                                              minimum, natural,
169                                              NULL, NULL);
170 }
171 
172 static void
viewport_set_hadjustment_values(GtkViewport * viewport)173 viewport_set_hadjustment_values (GtkViewport *viewport)
174 {
175   GtkBin *bin = GTK_BIN (viewport);
176   GtkAllocation view_allocation;
177   GtkAdjustment *hadjustment = viewport->priv->hadjustment;
178   GtkWidget *child;
179   gdouble upper, value;
180 
181   gtk_css_gadget_get_content_allocation (viewport->priv->gadget,
182                                          &view_allocation, NULL);
183 
184   child = gtk_bin_get_child (bin);
185   if (child && gtk_widget_get_visible (child))
186     {
187       gint minimum_width, natural_width;
188       gint scroll_height;
189 
190       if (viewport->priv->vscroll_policy == GTK_SCROLL_MINIMUM)
191 	gtk_widget_get_preferred_height (child, &scroll_height, NULL);
192       else
193 	gtk_widget_get_preferred_height (child, NULL, &scroll_height);
194 
195       gtk_widget_get_preferred_width_for_height (child,
196                                                  MAX (view_allocation.height, scroll_height),
197                                                  &minimum_width,
198                                                  &natural_width);
199 
200       if (viewport->priv->hscroll_policy == GTK_SCROLL_MINIMUM)
201 	upper = MAX (minimum_width, view_allocation.width);
202       else
203 	upper = MAX (natural_width, view_allocation.width);
204     }
205   else
206     upper = view_allocation.width;
207 
208   value = gtk_adjustment_get_value (hadjustment);
209   /* We clamp to the left in RTL mode */
210   if (gtk_widget_get_direction (GTK_WIDGET (viewport)) == GTK_TEXT_DIR_RTL)
211     {
212       gdouble dist = gtk_adjustment_get_upper (hadjustment)
213                      - value
214                      - gtk_adjustment_get_page_size (hadjustment);
215       value = upper - dist - view_allocation.width;
216     }
217 
218   gtk_adjustment_configure (hadjustment,
219                             value,
220                             0,
221                             upper,
222                             view_allocation.width * 0.1,
223                             view_allocation.width * 0.9,
224                             view_allocation.width);
225 }
226 
227 static void
viewport_set_vadjustment_values(GtkViewport * viewport)228 viewport_set_vadjustment_values (GtkViewport *viewport)
229 {
230   GtkBin *bin = GTK_BIN (viewport);
231   GtkAllocation view_allocation;
232   GtkAdjustment *vadjustment = viewport->priv->vadjustment;
233   GtkWidget *child;
234   gdouble upper;
235 
236   gtk_css_gadget_get_content_allocation (viewport->priv->gadget,
237                                          &view_allocation, NULL);
238 
239   child = gtk_bin_get_child (bin);
240   if (child && gtk_widget_get_visible (child))
241     {
242       gint minimum_height, natural_height;
243       gint scroll_width;
244 
245       if (viewport->priv->hscroll_policy == GTK_SCROLL_MINIMUM)
246 	gtk_widget_get_preferred_width (child, &scroll_width, NULL);
247       else
248 	gtk_widget_get_preferred_width (child, NULL, &scroll_width);
249 
250       gtk_widget_get_preferred_height_for_width (child,
251                                                  MAX (view_allocation.width, scroll_width),
252                                                  &minimum_height,
253                                                  &natural_height);
254 
255       if (viewport->priv->vscroll_policy == GTK_SCROLL_MINIMUM)
256 	upper = MAX (minimum_height, view_allocation.height);
257       else
258 	upper = MAX (natural_height, view_allocation.height);
259     }
260   else
261     upper = view_allocation.height;
262 
263   gtk_adjustment_configure (vadjustment,
264                             gtk_adjustment_get_value (vadjustment),
265                             0,
266                             upper,
267                             view_allocation.height * 0.1,
268                             view_allocation.height * 0.9,
269                             view_allocation.height);
270 }
271 
272 static void
gtk_viewport_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer data)273 gtk_viewport_allocate (GtkCssGadget        *gadget,
274                        const GtkAllocation *allocation,
275                        int                  baseline,
276                        GtkAllocation       *out_clip,
277                        gpointer             data)
278 {
279   GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
280   GtkViewport *viewport = GTK_VIEWPORT (widget);
281   GtkViewportPrivate *priv = viewport->priv;
282   GtkAdjustment *hadjustment = priv->hadjustment;
283   GtkAdjustment *vadjustment = priv->vadjustment;
284   GtkWidget *child;
285 
286   g_object_freeze_notify (G_OBJECT (hadjustment));
287   g_object_freeze_notify (G_OBJECT (vadjustment));
288 
289   viewport_set_hadjustment_values (viewport);
290   viewport_set_vadjustment_values (viewport);
291 
292   if (gtk_widget_get_realized (widget))
293     {
294       gdk_window_move_resize (priv->view_window,
295 			      allocation->x,
296 			      allocation->y,
297 			      allocation->width,
298 			      allocation->height);
299       gdk_window_move_resize (priv->bin_window,
300                               - gtk_adjustment_get_value (hadjustment),
301                               - gtk_adjustment_get_value (vadjustment),
302                               gtk_adjustment_get_upper (hadjustment),
303                               gtk_adjustment_get_upper (vadjustment));
304     }
305 
306   child = gtk_bin_get_child (GTK_BIN (widget));
307   if (child && gtk_widget_get_visible (child))
308     {
309       GtkAllocation child_allocation;
310 
311       child_allocation.x = 0;
312       child_allocation.y = 0;
313       child_allocation.width = gtk_adjustment_get_upper (hadjustment);
314       child_allocation.height = gtk_adjustment_get_upper (vadjustment);
315 
316       gtk_widget_size_allocate (child, &child_allocation);
317     }
318 
319   g_object_thaw_notify (G_OBJECT (hadjustment));
320   g_object_thaw_notify (G_OBJECT (vadjustment));
321 }
322 
323 static void
draw_bin(cairo_t * cr,gpointer user_data)324 draw_bin (cairo_t *cr,
325 	  gpointer user_data)
326 {
327   GtkWidget *widget = GTK_WIDGET (user_data);
328   GTK_WIDGET_CLASS (gtk_viewport_parent_class)->draw (widget, cr);
329 }
330 
331 static gboolean
gtk_viewport_render(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer data)332 gtk_viewport_render (GtkCssGadget *gadget,
333                      cairo_t      *cr,
334                      int           x,
335                      int           y,
336                      int           width,
337                      int           height,
338                      gpointer      data)
339 {
340   GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
341   GtkViewport *viewport = GTK_VIEWPORT (widget);
342   GtkViewportPrivate *priv = viewport->priv;
343 
344   if (gtk_cairo_should_draw_window (cr, priv->bin_window))
345     {
346       cairo_rectangle_int_t view_rect;
347       cairo_rectangle_int_t canvas_rect;
348 
349       gdk_window_get_position (priv->view_window, &view_rect.x, &view_rect.y);
350       view_rect.width = gdk_window_get_width (priv->view_window);
351       view_rect.height = gdk_window_get_height (priv->view_window);
352 
353       gdk_window_get_position (priv->bin_window, &canvas_rect.x, &canvas_rect.y);
354       canvas_rect.width = gdk_window_get_width (priv->bin_window);
355       canvas_rect.height = gdk_window_get_height (priv->bin_window);
356 
357       _gtk_pixel_cache_draw (priv->pixel_cache, cr, priv->bin_window,
358 			     &view_rect, &canvas_rect,
359 			     draw_bin, widget);
360     }
361 
362   return FALSE;
363 }
364 
365 static void
gtk_viewport_class_init(GtkViewportClass * class)366 gtk_viewport_class_init (GtkViewportClass *class)
367 {
368   GObjectClass   *gobject_class;
369   GtkWidgetClass *widget_class;
370   GtkContainerClass *container_class;
371 
372   gobject_class = G_OBJECT_CLASS (class);
373   widget_class = (GtkWidgetClass*) class;
374   container_class = (GtkContainerClass*) class;
375 
376   gobject_class->set_property = gtk_viewport_set_property;
377   gobject_class->get_property = gtk_viewport_get_property;
378   gobject_class->finalize = gtk_viewport_finalize;
379 
380   widget_class->destroy = gtk_viewport_destroy;
381   widget_class->realize = gtk_viewport_realize;
382   widget_class->unrealize = gtk_viewport_unrealize;
383   widget_class->map = gtk_viewport_map;
384   widget_class->unmap = gtk_viewport_unmap;
385   widget_class->draw = gtk_viewport_draw;
386   widget_class->size_allocate = gtk_viewport_size_allocate;
387   widget_class->get_preferred_width = gtk_viewport_get_preferred_width;
388   widget_class->get_preferred_height = gtk_viewport_get_preferred_height;
389   widget_class->get_preferred_width_for_height = gtk_viewport_get_preferred_width_for_height;
390   widget_class->get_preferred_height_for_width = gtk_viewport_get_preferred_height_for_width;
391   widget_class->queue_draw_region = gtk_viewport_queue_draw_region;
392 
393   gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_VIEWPORT);
394 
395   container_class->remove = gtk_viewport_remove;
396   container_class->add = gtk_viewport_add;
397   gtk_container_class_handle_border_width (container_class);
398 
399   /* GtkScrollable implementation */
400   g_object_class_override_property (gobject_class, PROP_HADJUSTMENT,    "hadjustment");
401   g_object_class_override_property (gobject_class, PROP_VADJUSTMENT,    "vadjustment");
402   g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
403   g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
404 
405   g_object_class_install_property (gobject_class,
406                                    PROP_SHADOW_TYPE,
407                                    g_param_spec_enum ("shadow-type",
408 						      P_("Shadow type"),
409 						      P_("Determines how the shadowed box around the viewport is drawn"),
410 						      GTK_TYPE_SHADOW_TYPE,
411 						      GTK_SHADOW_IN,
412 						      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
413 
414   gtk_widget_class_set_css_name (widget_class, "viewport");
415 }
416 
417 static void
gtk_viewport_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)418 gtk_viewport_set_property (GObject         *object,
419 			   guint            prop_id,
420 			   const GValue    *value,
421 			   GParamSpec      *pspec)
422 {
423   GtkViewport *viewport;
424 
425   viewport = GTK_VIEWPORT (object);
426 
427   switch (prop_id)
428     {
429     case PROP_HADJUSTMENT:
430       viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, g_value_get_object (value));
431       break;
432     case PROP_VADJUSTMENT:
433       viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, g_value_get_object (value));
434       break;
435     case PROP_HSCROLL_POLICY:
436       if (viewport->priv->hscroll_policy != g_value_get_enum (value))
437         {
438           viewport->priv->hscroll_policy = g_value_get_enum (value);
439           gtk_widget_queue_resize (GTK_WIDGET (viewport));
440           g_object_notify_by_pspec (object, pspec);
441         }
442       break;
443     case PROP_VSCROLL_POLICY:
444       if (viewport->priv->vscroll_policy != g_value_get_enum (value))
445         {
446           viewport->priv->vscroll_policy = g_value_get_enum (value);
447           gtk_widget_queue_resize (GTK_WIDGET (viewport));
448           g_object_notify_by_pspec (object, pspec);
449         }
450       break;
451     case PROP_SHADOW_TYPE:
452       gtk_viewport_set_shadow_type (viewport, g_value_get_enum (value));
453       break;
454     default:
455       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
456       break;
457     }
458 }
459 
460 static void
gtk_viewport_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)461 gtk_viewport_get_property (GObject         *object,
462 			   guint            prop_id,
463 			   GValue          *value,
464 			   GParamSpec      *pspec)
465 {
466   GtkViewport *viewport = GTK_VIEWPORT (object);
467   GtkViewportPrivate *priv = viewport->priv;
468 
469   switch (prop_id)
470     {
471     case PROP_HADJUSTMENT:
472       g_value_set_object (value, priv->hadjustment);
473       break;
474     case PROP_VADJUSTMENT:
475       g_value_set_object (value, priv->vadjustment);
476       break;
477     case PROP_HSCROLL_POLICY:
478       g_value_set_enum (value, priv->hscroll_policy);
479       break;
480     case PROP_VSCROLL_POLICY:
481       g_value_set_enum (value, priv->vscroll_policy);
482       break;
483     case PROP_SHADOW_TYPE:
484       g_value_set_enum (value, priv->shadow_type);
485       break;
486     default:
487       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
488       break;
489     }
490 }
491 
492 static void
gtk_viewport_init(GtkViewport * viewport)493 gtk_viewport_init (GtkViewport *viewport)
494 {
495   GtkWidget *widget;
496   GtkViewportPrivate *priv;
497   GtkCssNode *widget_node;
498 
499   viewport->priv = gtk_viewport_get_instance_private (viewport);
500   priv = viewport->priv;
501   widget = GTK_WIDGET (viewport);
502 
503   gtk_widget_set_has_window (widget, TRUE);
504 
505   priv->shadow_type = GTK_SHADOW_IN;
506   priv->view_window = NULL;
507   priv->bin_window = NULL;
508   priv->hadjustment = NULL;
509   priv->vadjustment = NULL;
510 
511   priv->pixel_cache = _gtk_pixel_cache_new ();
512 
513   widget_node = gtk_widget_get_css_node (widget);
514   priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node,
515                                                      widget,
516                                                      gtk_viewport_measure,
517                                                      gtk_viewport_allocate,
518                                                      gtk_viewport_render,
519                                                      NULL, NULL);
520 
521   gtk_css_gadget_add_class (priv->gadget, GTK_STYLE_CLASS_FRAME);
522   viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, NULL);
523   viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, NULL);
524 }
525 
526 /**
527  * gtk_viewport_new:
528  * @hadjustment: (allow-none): horizontal adjustment
529  * @vadjustment: (allow-none): vertical adjustment
530  *
531  * Creates a new #GtkViewport with the given adjustments, or with default
532  * adjustments if none are given.
533  *
534  * Returns: a new #GtkViewport
535  */
536 GtkWidget*
gtk_viewport_new(GtkAdjustment * hadjustment,GtkAdjustment * vadjustment)537 gtk_viewport_new (GtkAdjustment *hadjustment,
538 		  GtkAdjustment *vadjustment)
539 {
540   GtkWidget *viewport;
541 
542   viewport = g_object_new (GTK_TYPE_VIEWPORT,
543                            "hadjustment", hadjustment,
544                            "vadjustment", vadjustment,
545                            NULL);
546 
547   return viewport;
548 }
549 
550 #define ADJUSTMENT_POINTER(viewport, orientation)         \
551   (((orientation) == GTK_ORIENTATION_HORIZONTAL) ?        \
552      &(viewport)->priv->hadjustment : &(viewport)->priv->vadjustment)
553 
554 static void
viewport_disconnect_adjustment(GtkViewport * viewport,GtkOrientation orientation)555 viewport_disconnect_adjustment (GtkViewport    *viewport,
556 				GtkOrientation  orientation)
557 {
558   GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (viewport, orientation);
559 
560   if (*adjustmentp)
561     {
562       g_signal_handlers_disconnect_by_func (*adjustmentp,
563 					    gtk_viewport_adjustment_value_changed,
564 					    viewport);
565       g_object_unref (*adjustmentp);
566       *adjustmentp = NULL;
567     }
568 }
569 
570 static void
gtk_viewport_destroy(GtkWidget * widget)571 gtk_viewport_destroy (GtkWidget *widget)
572 {
573   GtkViewport *viewport = GTK_VIEWPORT (widget);
574   GtkViewportPrivate *priv = viewport->priv;
575 
576   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL);
577   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_VERTICAL);
578 
579   GTK_WIDGET_CLASS (gtk_viewport_parent_class)->destroy (widget);
580 
581   g_clear_pointer (&priv->pixel_cache, _gtk_pixel_cache_free);
582 }
583 
584 static void
gtk_viewport_finalize(GObject * object)585 gtk_viewport_finalize (GObject *object)
586 {
587   GtkViewport *viewport = GTK_VIEWPORT (object);
588   GtkViewportPrivate *priv = viewport->priv;
589 
590   g_clear_object (&priv->gadget);
591 
592   G_OBJECT_CLASS (gtk_viewport_parent_class)->finalize (object);
593 }
594 
595 /**
596  * gtk_viewport_get_hadjustment:
597  * @viewport: a #GtkViewport.
598  *
599  * Returns the horizontal adjustment of the viewport.
600  *
601  * Returns: (transfer none): the horizontal adjustment of @viewport.
602  *
603  * Deprecated: 3.0: Use gtk_scrollable_get_hadjustment()
604  **/
605 GtkAdjustment*
gtk_viewport_get_hadjustment(GtkViewport * viewport)606 gtk_viewport_get_hadjustment (GtkViewport *viewport)
607 {
608   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
609 
610   return viewport->priv->hadjustment;
611 }
612 
613 /**
614  * gtk_viewport_get_vadjustment:
615  * @viewport: a #GtkViewport.
616  *
617  * Returns the vertical adjustment of the viewport.
618  *
619  * Returns: (transfer none): the vertical adjustment of @viewport.
620  *
621  * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
622  **/
623 GtkAdjustment*
gtk_viewport_get_vadjustment(GtkViewport * viewport)624 gtk_viewport_get_vadjustment (GtkViewport *viewport)
625 {
626   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
627 
628   return viewport->priv->vadjustment;
629 }
630 
631 static void
viewport_set_adjustment(GtkViewport * viewport,GtkOrientation orientation,GtkAdjustment * adjustment)632 viewport_set_adjustment (GtkViewport    *viewport,
633 			 GtkOrientation  orientation,
634 			 GtkAdjustment  *adjustment)
635 {
636   GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (viewport, orientation);
637 
638   if (adjustment && adjustment == *adjustmentp)
639     return;
640 
641   if (!adjustment)
642     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
643   viewport_disconnect_adjustment (viewport, orientation);
644   *adjustmentp = adjustment;
645   g_object_ref_sink (adjustment);
646 
647   if (orientation == GTK_ORIENTATION_HORIZONTAL)
648     viewport_set_hadjustment_values (viewport);
649   else
650     viewport_set_vadjustment_values (viewport);
651 
652   g_signal_connect (adjustment, "value-changed",
653 		    G_CALLBACK (gtk_viewport_adjustment_value_changed),
654 		    viewport);
655 
656   gtk_viewport_adjustment_value_changed (adjustment, viewport);
657 }
658 
659 /**
660  * gtk_viewport_set_hadjustment:
661  * @viewport: a #GtkViewport.
662  * @adjustment: (allow-none): a #GtkAdjustment.
663  *
664  * Sets the horizontal adjustment of the viewport.
665  *
666  * Deprecated: 3.0: Use gtk_scrollable_set_hadjustment()
667  **/
668 void
gtk_viewport_set_hadjustment(GtkViewport * viewport,GtkAdjustment * adjustment)669 gtk_viewport_set_hadjustment (GtkViewport   *viewport,
670 			      GtkAdjustment *adjustment)
671 {
672   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
673   if (adjustment)
674     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
675 
676   viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, adjustment);
677 
678   g_object_notify (G_OBJECT (viewport), "hadjustment");
679 }
680 
681 /**
682  * gtk_viewport_set_vadjustment:
683  * @viewport: a #GtkViewport.
684  * @adjustment: (allow-none): a #GtkAdjustment.
685  *
686  * Sets the vertical adjustment of the viewport.
687  *
688  * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
689  **/
690 void
gtk_viewport_set_vadjustment(GtkViewport * viewport,GtkAdjustment * adjustment)691 gtk_viewport_set_vadjustment (GtkViewport   *viewport,
692 			      GtkAdjustment *adjustment)
693 {
694   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
695   if (adjustment)
696     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
697 
698   viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, adjustment);
699 
700   g_object_notify (G_OBJECT (viewport), "vadjustment");
701 }
702 
703 /**
704  * gtk_viewport_set_shadow_type:
705  * @viewport: a #GtkViewport.
706  * @type: the new shadow type.
707  *
708  * Sets the shadow type of the viewport.
709  **/
710 void
gtk_viewport_set_shadow_type(GtkViewport * viewport,GtkShadowType type)711 gtk_viewport_set_shadow_type (GtkViewport   *viewport,
712 			      GtkShadowType  type)
713 {
714   GtkViewportPrivate *priv;
715   GtkWidget *widget;
716   GtkStyleContext *context;
717 
718   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
719 
720   widget = GTK_WIDGET (viewport);
721   priv = viewport->priv;
722 
723   if ((GtkShadowType) priv->shadow_type != type)
724     {
725       priv->shadow_type = type;
726 
727       context = gtk_widget_get_style_context (widget);
728       if (type != GTK_SHADOW_NONE)
729         gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
730       else
731         gtk_style_context_remove_class (context, GTK_STYLE_CLASS_FRAME);
732 
733       gtk_widget_queue_resize (widget);
734 
735       g_object_notify (G_OBJECT (viewport), "shadow-type");
736     }
737 }
738 
739 /**
740  * gtk_viewport_get_shadow_type:
741  * @viewport: a #GtkViewport
742  *
743  * Gets the shadow type of the #GtkViewport. See
744  * gtk_viewport_set_shadow_type().
745  *
746  * Returns: the shadow type
747  **/
748 GtkShadowType
gtk_viewport_get_shadow_type(GtkViewport * viewport)749 gtk_viewport_get_shadow_type (GtkViewport *viewport)
750 {
751   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), GTK_SHADOW_NONE);
752 
753   return viewport->priv->shadow_type;
754 }
755 
756 /**
757  * gtk_viewport_get_bin_window:
758  * @viewport: a #GtkViewport
759  *
760  * Gets the bin window of the #GtkViewport.
761  *
762  * Returns: (transfer none): a #GdkWindow
763  *
764  * Since: 2.20
765  **/
766 GdkWindow*
gtk_viewport_get_bin_window(GtkViewport * viewport)767 gtk_viewport_get_bin_window (GtkViewport *viewport)
768 {
769   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
770 
771   return viewport->priv->bin_window;
772 }
773 
774 /**
775  * gtk_viewport_get_view_window:
776  * @viewport: a #GtkViewport
777  *
778  * Gets the view window of the #GtkViewport.
779  *
780  * Returns: (transfer none): a #GdkWindow
781  *
782  * Since: 2.22
783  **/
784 GdkWindow*
gtk_viewport_get_view_window(GtkViewport * viewport)785 gtk_viewport_get_view_window (GtkViewport *viewport)
786 {
787   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
788 
789   return viewport->priv->view_window;
790 }
791 
792 static void
gtk_viewport_bin_window_invalidate_handler(GdkWindow * window,cairo_region_t * region)793 gtk_viewport_bin_window_invalidate_handler (GdkWindow *window,
794 					    cairo_region_t *region)
795 {
796   gpointer widget;
797   GtkViewport *viewport;
798   GtkViewportPrivate *priv;
799 
800   gdk_window_get_user_data (window, &widget);
801   viewport = GTK_VIEWPORT (widget);
802   priv = viewport->priv;
803 
804   _gtk_pixel_cache_invalidate (priv->pixel_cache, region);
805 }
806 
807 static void
gtk_viewport_queue_draw_region(GtkWidget * widget,const cairo_region_t * region)808 gtk_viewport_queue_draw_region (GtkWidget *widget,
809 				const cairo_region_t *region)
810 {
811   GtkViewport *viewport = GTK_VIEWPORT (widget);
812   GtkViewportPrivate *priv = viewport->priv;
813 
814   /* There is no way we can know if a region targets the
815      not-currently-visible but in pixel cache region, so we
816      always just invalidate the whole thing whenever the
817      tree view gets a queue draw. This doesn't normally happen
818      in normal scrolling cases anyway. */
819   _gtk_pixel_cache_invalidate (priv->pixel_cache, NULL);
820 
821   GTK_WIDGET_CLASS (gtk_viewport_parent_class)->queue_draw_region (widget,
822 								   region);
823 }
824 
825 
826 static void
gtk_viewport_realize(GtkWidget * widget)827 gtk_viewport_realize (GtkWidget *widget)
828 {
829   GtkViewport *viewport = GTK_VIEWPORT (widget);
830   GtkViewportPrivate *priv = viewport->priv;
831   GtkBin *bin = GTK_BIN (widget);
832   GtkAdjustment *hadjustment = priv->hadjustment;
833   GtkAdjustment *vadjustment = priv->vadjustment;
834   GtkAllocation allocation;
835   GtkAllocation view_allocation;
836   GtkWidget *child;
837   GdkWindow *window;
838   GdkWindowAttr attributes;
839   gint attributes_mask;
840   gint event_mask;
841 
842   gtk_widget_set_realized (widget, TRUE);
843 
844   gtk_widget_get_allocation (widget, &allocation);
845 
846   attributes.x = allocation.x;
847   attributes.y = allocation.y;
848   attributes.width = allocation.width;
849   attributes.height = allocation.height;
850   attributes.window_type = GDK_WINDOW_CHILD;
851   attributes.wclass = GDK_INPUT_OUTPUT;
852   attributes.visual = gtk_widget_get_visual (widget);
853 
854   event_mask = gtk_widget_get_events (widget);
855 
856   attributes.event_mask = event_mask | GDK_SCROLL_MASK | GDK_TOUCH_MASK | GDK_SMOOTH_SCROLL_MASK;
857 
858   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
859 
860   window = gdk_window_new (gtk_widget_get_parent_window (widget),
861                            &attributes, attributes_mask);
862   gtk_widget_set_window (widget, window);
863   gtk_widget_register_window (widget, window);
864 
865   gtk_css_gadget_get_content_allocation (priv->gadget,
866                                          &view_allocation, NULL);
867 
868   attributes.x = view_allocation.x;
869   attributes.y = view_allocation.y;
870   attributes.width = view_allocation.width;
871   attributes.height = view_allocation.height;
872   attributes.event_mask = 0;
873 
874   priv->view_window = gdk_window_new (window,
875                                       &attributes, attributes_mask);
876   gtk_widget_register_window (widget, priv->view_window);
877 
878   attributes.x = - gtk_adjustment_get_value (hadjustment);
879   attributes.y = - gtk_adjustment_get_value (vadjustment);
880   attributes.width = gtk_adjustment_get_upper (hadjustment);
881   attributes.height = gtk_adjustment_get_upper (vadjustment);
882 
883   attributes.event_mask = event_mask;
884 
885   priv->bin_window = gdk_window_new (priv->view_window, &attributes, attributes_mask);
886   gtk_widget_register_window (widget, priv->bin_window);
887   gdk_window_set_invalidate_handler (priv->bin_window,
888 				     gtk_viewport_bin_window_invalidate_handler);
889 
890   child = gtk_bin_get_child (bin);
891   if (child)
892     gtk_widget_set_parent_window (child, priv->bin_window);
893 
894   gdk_window_show (priv->bin_window);
895   gdk_window_show (priv->view_window);
896 }
897 
898 static void
gtk_viewport_unrealize(GtkWidget * widget)899 gtk_viewport_unrealize (GtkWidget *widget)
900 {
901   GtkViewport *viewport = GTK_VIEWPORT (widget);
902   GtkViewportPrivate *priv = viewport->priv;
903 
904   gtk_widget_unregister_window (widget, priv->view_window);
905   gdk_window_destroy (priv->view_window);
906   priv->view_window = NULL;
907 
908   gtk_widget_unregister_window (widget, priv->bin_window);
909   gdk_window_destroy (priv->bin_window);
910   priv->bin_window = NULL;
911 
912   GTK_WIDGET_CLASS (gtk_viewport_parent_class)->unrealize (widget);
913 }
914 
915 static void
gtk_viewport_map(GtkWidget * widget)916 gtk_viewport_map (GtkWidget *widget)
917 {
918   GtkViewport *viewport = GTK_VIEWPORT (widget);
919   GtkViewportPrivate *priv = viewport->priv;
920 
921   _gtk_pixel_cache_map (priv->pixel_cache);
922 
923   GTK_WIDGET_CLASS (gtk_viewport_parent_class)->map (widget);
924 }
925 
926 static void
gtk_viewport_unmap(GtkWidget * widget)927 gtk_viewport_unmap (GtkWidget *widget)
928 {
929   GtkViewport *viewport = GTK_VIEWPORT (widget);
930   GtkViewportPrivate *priv = viewport->priv;
931 
932   GTK_WIDGET_CLASS (gtk_viewport_parent_class)->unmap (widget);
933 
934   _gtk_pixel_cache_unmap (priv->pixel_cache);
935 }
936 
937 static gint
gtk_viewport_draw(GtkWidget * widget,cairo_t * cr)938 gtk_viewport_draw (GtkWidget *widget,
939                    cairo_t   *cr)
940 {
941   GtkViewport *viewport = GTK_VIEWPORT (widget);
942   GtkViewportPrivate *priv = viewport->priv;
943 
944   if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)) ||
945       gtk_cairo_should_draw_window (cr, priv->bin_window))
946     gtk_css_gadget_draw (priv->gadget, cr);
947 
948   return FALSE;
949 }
950 
951 static void
gtk_viewport_update_pixelcache_opacity(GtkWidget * child,GtkViewport * viewport)952 gtk_viewport_update_pixelcache_opacity (GtkWidget   *child,
953                                         GtkViewport *viewport)
954 {
955   GtkViewportPrivate *priv = viewport->priv;
956 
957   gtk_pixel_cache_set_is_opaque (priv->pixel_cache,
958                                  gtk_css_style_render_background_is_opaque (
959                                    gtk_style_context_lookup_style (
960                                      gtk_widget_get_style_context (child))));
961 }
962 
963 static void
gtk_viewport_remove(GtkContainer * container,GtkWidget * child)964 gtk_viewport_remove (GtkContainer *container,
965 		     GtkWidget    *child)
966 {
967   GtkViewport *viewport = GTK_VIEWPORT (container);
968   GtkViewportPrivate *priv = viewport->priv;
969 
970   if (g_signal_handlers_disconnect_by_func (child, gtk_viewport_update_pixelcache_opacity, viewport) != 1)
971     {
972       g_assert_not_reached ();
973     }
974 
975   GTK_CONTAINER_CLASS (gtk_viewport_parent_class)->remove (container, child);
976 
977   gtk_pixel_cache_set_is_opaque (priv->pixel_cache, FALSE);
978 }
979 
980 static void
gtk_viewport_add(GtkContainer * container,GtkWidget * child)981 gtk_viewport_add (GtkContainer *container,
982 		  GtkWidget    *child)
983 {
984   GtkBin *bin = GTK_BIN (container);
985   GtkViewport *viewport = GTK_VIEWPORT (bin);
986   GtkViewportPrivate *priv = viewport->priv;
987 
988   g_return_if_fail (gtk_bin_get_child (bin) == NULL);
989 
990   gtk_widget_set_parent_window (child, priv->bin_window);
991 
992   GTK_CONTAINER_CLASS (gtk_viewport_parent_class)->add (container, child);
993 
994   g_signal_connect (child, "style-updated", G_CALLBACK (gtk_viewport_update_pixelcache_opacity), viewport);
995   gtk_viewport_update_pixelcache_opacity (child, viewport);
996 }
997 
998 static void
gtk_viewport_size_allocate(GtkWidget * widget,GtkAllocation * allocation)999 gtk_viewport_size_allocate (GtkWidget     *widget,
1000 			    GtkAllocation *allocation)
1001 {
1002   GtkViewport *viewport = GTK_VIEWPORT (widget);
1003   GtkViewportPrivate *priv = viewport->priv;
1004   GtkAllocation clip, content_allocation, widget_allocation;
1005 
1006   /* If our size changed, and we have a shadow, queue a redraw on widget->window to
1007    * redraw the shadow correctly.
1008    */
1009   gtk_widget_get_allocation (widget, &widget_allocation);
1010   if (gtk_widget_get_mapped (widget) &&
1011       priv->shadow_type != GTK_SHADOW_NONE &&
1012       (widget_allocation.width != allocation->width ||
1013        widget_allocation.height != allocation->height))
1014     gdk_window_invalidate_rect (gtk_widget_get_window (widget), NULL, FALSE);
1015 
1016   gtk_widget_set_allocation (widget, allocation);
1017 
1018   if (gtk_widget_get_realized (widget))
1019     gdk_window_move_resize (gtk_widget_get_window (widget),
1020                             allocation->x,
1021                             allocation->y,
1022                             allocation->width,
1023                             allocation->height);
1024 
1025   content_allocation = *allocation;
1026   content_allocation.x = content_allocation.y = 0;
1027   gtk_css_gadget_allocate (priv->gadget,
1028                            &content_allocation,
1029                            gtk_widget_get_allocated_baseline (widget),
1030                            &clip);
1031 
1032   clip.x += allocation->x;
1033   clip.y += allocation->y;
1034   gtk_widget_set_clip (widget, &clip);
1035 }
1036 
1037 static void
gtk_viewport_adjustment_value_changed(GtkAdjustment * adjustment,gpointer data)1038 gtk_viewport_adjustment_value_changed (GtkAdjustment *adjustment,
1039 				       gpointer       data)
1040 {
1041   GtkViewport *viewport = GTK_VIEWPORT (data);
1042   GtkViewportPrivate *priv = viewport->priv;
1043   GtkBin *bin = GTK_BIN (data);
1044   GtkWidget *child;
1045 
1046   child = gtk_bin_get_child (bin);
1047   if (child && gtk_widget_get_visible (child) &&
1048       gtk_widget_get_realized (GTK_WIDGET (viewport)))
1049     {
1050       GtkAdjustment *hadjustment = priv->hadjustment;
1051       GtkAdjustment *vadjustment = priv->vadjustment;
1052       gint old_x, old_y;
1053       gint new_x, new_y;
1054 
1055       gdk_window_get_position (priv->bin_window, &old_x, &old_y);
1056       new_x = - gtk_adjustment_get_value (hadjustment);
1057       new_y = - gtk_adjustment_get_value (vadjustment);
1058 
1059       if (new_x != old_x || new_y != old_y)
1060 	gdk_window_move (priv->bin_window, new_x, new_y);
1061     }
1062 }
1063 
1064 static void
gtk_viewport_get_preferred_width(GtkWidget * widget,gint * minimum_size,gint * natural_size)1065 gtk_viewport_get_preferred_width (GtkWidget *widget,
1066                                   gint      *minimum_size,
1067                                   gint      *natural_size)
1068 {
1069   gtk_css_gadget_get_preferred_size (GTK_VIEWPORT (widget)->priv->gadget,
1070                                      GTK_ORIENTATION_HORIZONTAL,
1071                                      -1,
1072                                      minimum_size, natural_size,
1073                                      NULL, NULL);
1074 }
1075 
1076 static void
gtk_viewport_get_preferred_height(GtkWidget * widget,gint * minimum_size,gint * natural_size)1077 gtk_viewport_get_preferred_height (GtkWidget *widget,
1078                                    gint      *minimum_size,
1079                                    gint      *natural_size)
1080 {
1081   gtk_css_gadget_get_preferred_size (GTK_VIEWPORT (widget)->priv->gadget,
1082                                      GTK_ORIENTATION_VERTICAL,
1083                                      -1,
1084                                      minimum_size, natural_size,
1085                                      NULL, NULL);
1086 }
1087 
1088 static void
gtk_viewport_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum_size,gint * natural_size)1089 gtk_viewport_get_preferred_width_for_height (GtkWidget *widget,
1090                                              gint       height,
1091                                              gint      *minimum_size,
1092                                              gint      *natural_size)
1093 {
1094   gtk_css_gadget_get_preferred_size (GTK_VIEWPORT (widget)->priv->gadget,
1095                                      GTK_ORIENTATION_HORIZONTAL,
1096                                      height,
1097                                      minimum_size, natural_size,
1098                                      NULL, NULL);
1099 }
1100 
1101 static void
gtk_viewport_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum_size,gint * natural_size)1102 gtk_viewport_get_preferred_height_for_width (GtkWidget *widget,
1103                                              gint       width,
1104                                              gint      *minimum_size,
1105                                              gint      *natural_size)
1106 {
1107   gtk_css_gadget_get_preferred_size (GTK_VIEWPORT (widget)->priv->gadget,
1108                                      GTK_ORIENTATION_VERTICAL,
1109                                      width,
1110                                      minimum_size, natural_size,
1111                                      NULL, NULL);
1112 }
1113