]> Pileus Git - ~andy/gtk/blob - gtk/gtkviewport.c
Improve AtkAction implementations
[~andy/gtk] / gtk / gtkviewport.c
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 "gtkintl.h"
31 #include "gtkmarshalers.h"
32 #include "gtkprivate.h"
33 #include "gtkscrollable.h"
34 #include "gtktypebuiltins.h"
35
36
37 /**
38  * SECTION:gtkviewport
39  * @Short_description: An adapter which makes widgets scrollable
40  * @Title: GtkViewport
41  * @See_also:#GtkScrolledWindow, #GtkAdjustment
42  *
43  * The #GtkViewport widget acts as an adaptor class, implementing
44  * scrollability for child widgets that lack their own scrolling
45  * capabilities. Use #GtkViewport to scroll child widgets such as
46  * #GtkGrid, #GtkBox, and so on.
47  *
48  * If a widget has native scrolling abilities, such as #GtkTextView,
49  * #GtkTreeView or #GtkIconView, it can be added to a #GtkScrolledWindow
50  * with gtk_container_add(). If a widget does not, you must first add the
51  * widget to a #GtkViewport, then add the viewport to the scrolled window.
52  * gtk_container_add() does this automatically if a child that does not
53  * implement #GtkScrollable is added to a #GtkScrolledWindow, so you can
54  * ignore the presence of the viewport.
55  *
56  * The #GtkViewport will start scrolling content only if allocated less
57  * than the child widget's minimum size in a given orientation.
58  */
59
60 struct _GtkViewportPrivate
61 {
62   GtkAdjustment  *hadjustment;
63   GtkAdjustment  *vadjustment;
64   GtkShadowType   shadow_type;
65
66   GdkWindow      *bin_window;
67   GdkWindow      *view_window;
68
69   /* GtkScrollablePolicy needs to be checked when
70    * driving the scrollable adjustment values */
71   guint hscroll_policy : 1;
72   guint vscroll_policy : 1;
73 };
74
75 enum {
76   PROP_0,
77   PROP_HADJUSTMENT,
78   PROP_VADJUSTMENT,
79   PROP_HSCROLL_POLICY,
80   PROP_VSCROLL_POLICY,
81   PROP_SHADOW_TYPE
82 };
83
84
85 static void gtk_viewport_set_property             (GObject         *object,
86                                                    guint            prop_id,
87                                                    const GValue    *value,
88                                                    GParamSpec      *pspec);
89 static void gtk_viewport_get_property             (GObject         *object,
90                                                    guint            prop_id,
91                                                    GValue          *value,
92                                                    GParamSpec      *pspec);
93 static void gtk_viewport_destroy                  (GtkWidget        *widget);
94 static void gtk_viewport_realize                  (GtkWidget        *widget);
95 static void gtk_viewport_unrealize                (GtkWidget        *widget);
96 static gint gtk_viewport_draw                     (GtkWidget        *widget,
97                                                    cairo_t          *cr);
98 static void gtk_viewport_add                      (GtkContainer     *container,
99                                                    GtkWidget        *widget);
100 static void gtk_viewport_size_allocate            (GtkWidget        *widget,
101                                                    GtkAllocation    *allocation);
102 static void gtk_viewport_adjustment_value_changed (GtkAdjustment    *adjustment,
103                                                    gpointer          data);
104 static void gtk_viewport_style_updated            (GtkWidget        *widget);
105
106 static void gtk_viewport_get_preferred_width      (GtkWidget        *widget,
107                                                    gint             *minimum_size,
108                                                    gint             *natural_size);
109 static void gtk_viewport_get_preferred_height     (GtkWidget        *widget,
110                                                    gint             *minimum_size,
111                                                    gint             *natural_size);
112
113 static void viewport_set_adjustment               (GtkViewport      *viewport,
114                                                    GtkOrientation    orientation,
115                                                    GtkAdjustment    *adjustment);
116
117 G_DEFINE_TYPE_WITH_CODE (GtkViewport, gtk_viewport, GTK_TYPE_BIN,
118                          G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
119
120 static void
121 gtk_viewport_class_init (GtkViewportClass *class)
122 {
123   GObjectClass   *gobject_class;
124   GtkWidgetClass *widget_class;
125   GtkContainerClass *container_class;
126
127   gobject_class = G_OBJECT_CLASS (class);
128   widget_class = (GtkWidgetClass*) class;
129   container_class = (GtkContainerClass*) class;
130
131   gobject_class->set_property = gtk_viewport_set_property;
132   gobject_class->get_property = gtk_viewport_get_property;
133
134   widget_class->destroy = gtk_viewport_destroy;
135   widget_class->realize = gtk_viewport_realize;
136   widget_class->unrealize = gtk_viewport_unrealize;
137   widget_class->draw = gtk_viewport_draw;
138   widget_class->size_allocate = gtk_viewport_size_allocate;
139   widget_class->style_updated = gtk_viewport_style_updated;
140   widget_class->get_preferred_width = gtk_viewport_get_preferred_width;
141   widget_class->get_preferred_height = gtk_viewport_get_preferred_height;
142   
143   gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_VIEWPORT);
144
145   container_class->add = gtk_viewport_add;
146
147   /* GtkScrollable implementation */
148   g_object_class_override_property (gobject_class, PROP_HADJUSTMENT,    "hadjustment");
149   g_object_class_override_property (gobject_class, PROP_VADJUSTMENT,    "vadjustment");
150   g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
151   g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
152
153   g_object_class_install_property (gobject_class,
154                                    PROP_SHADOW_TYPE,
155                                    g_param_spec_enum ("shadow-type",
156                                                       P_("Shadow type"),
157                                                       P_("Determines how the shadowed box around the viewport is drawn"),
158                                                       GTK_TYPE_SHADOW_TYPE,
159                                                       GTK_SHADOW_IN,
160                                                       GTK_PARAM_READWRITE));
161
162   g_type_class_add_private (class, sizeof (GtkViewportPrivate));
163 }
164
165 static void
166 gtk_viewport_set_property (GObject         *object,
167                            guint            prop_id,
168                            const GValue    *value,
169                            GParamSpec      *pspec)
170 {
171   GtkViewport *viewport;
172
173   viewport = GTK_VIEWPORT (object);
174
175   switch (prop_id)
176     {
177     case PROP_HADJUSTMENT:
178       viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, g_value_get_object (value));
179       break;
180     case PROP_VADJUSTMENT:
181       viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, g_value_get_object (value));
182       break;
183     case PROP_HSCROLL_POLICY:
184       viewport->priv->hscroll_policy = g_value_get_enum (value);
185       gtk_widget_queue_resize (GTK_WIDGET (viewport));
186       break;
187     case PROP_VSCROLL_POLICY:
188       viewport->priv->vscroll_policy = g_value_get_enum (value);
189       gtk_widget_queue_resize (GTK_WIDGET (viewport));
190       break;
191     case PROP_SHADOW_TYPE:
192       gtk_viewport_set_shadow_type (viewport, g_value_get_enum (value));
193       break;
194     default:
195       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
196       break;
197     }
198 }
199
200 static void
201 gtk_viewport_get_property (GObject         *object,
202                            guint            prop_id,
203                            GValue          *value,
204                            GParamSpec      *pspec)
205 {
206   GtkViewport *viewport = GTK_VIEWPORT (object);
207   GtkViewportPrivate *priv = viewport->priv;
208
209   switch (prop_id)
210     {
211     case PROP_HADJUSTMENT:
212       g_value_set_object (value, priv->hadjustment);
213       break;
214     case PROP_VADJUSTMENT:
215       g_value_set_object (value, priv->vadjustment);
216       break;
217     case PROP_HSCROLL_POLICY:
218       g_value_set_enum (value, priv->hscroll_policy);
219       break;
220     case PROP_VSCROLL_POLICY:
221       g_value_set_enum (value, priv->vscroll_policy);
222       break;
223     case PROP_SHADOW_TYPE:
224       g_value_set_enum (value, priv->shadow_type);
225       break;
226     default:
227       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
228       break;
229     }
230 }
231
232 static void
233 gtk_viewport_init (GtkViewport *viewport)
234 {
235   GtkViewportPrivate *priv;
236
237   viewport->priv = G_TYPE_INSTANCE_GET_PRIVATE (viewport,
238                                                 GTK_TYPE_VIEWPORT,
239                                                 GtkViewportPrivate);
240   priv = viewport->priv;
241
242   gtk_widget_set_has_window (GTK_WIDGET (viewport), TRUE);
243
244   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (viewport), FALSE);
245
246   priv->shadow_type = GTK_SHADOW_IN;
247   priv->view_window = NULL;
248   priv->bin_window = NULL;
249   priv->hadjustment = NULL;
250   priv->vadjustment = NULL;
251
252   viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, NULL);
253   viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, NULL);
254 }
255
256 /**
257  * gtk_viewport_new:
258  * @hadjustment: horizontal adjustment
259  * @vadjustment: vertical adjustment
260  *
261  * Creates a new #GtkViewport with the given adjustments.
262  *
263  * Returns: a new #GtkViewport
264  */
265 GtkWidget*
266 gtk_viewport_new (GtkAdjustment *hadjustment,
267                   GtkAdjustment *vadjustment)
268 {
269   GtkWidget *viewport;
270
271   viewport = g_object_new (GTK_TYPE_VIEWPORT,
272                              "hadjustment", hadjustment,
273                              "vadjustment", vadjustment,
274                              NULL);
275
276   return viewport;
277 }
278
279 #define ADJUSTMENT_POINTER(viewport, orientation)         \
280   (((orientation) == GTK_ORIENTATION_HORIZONTAL) ?        \
281      &(viewport)->priv->hadjustment : &(viewport)->priv->vadjustment)
282
283 static void
284 viewport_disconnect_adjustment (GtkViewport    *viewport,
285                                 GtkOrientation  orientation)
286 {
287   GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (viewport, orientation);
288
289   if (*adjustmentp)
290     {
291       g_signal_handlers_disconnect_by_func (*adjustmentp,
292                                             gtk_viewport_adjustment_value_changed,
293                                             viewport);
294       g_object_unref (*adjustmentp);
295       *adjustmentp = NULL;
296     }
297 }
298
299 static void
300 gtk_viewport_destroy (GtkWidget *widget)
301 {
302   GtkViewport *viewport = GTK_VIEWPORT (widget);
303
304   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL);
305   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_VERTICAL);
306
307   GTK_WIDGET_CLASS (gtk_viewport_parent_class)->destroy (widget);
308 }
309
310 static void
311 viewport_get_view_allocation (GtkViewport   *viewport,
312                               GtkAllocation *view_allocation)
313 {
314   GtkViewportPrivate *priv = viewport->priv;
315   GtkWidget *widget = GTK_WIDGET (viewport);
316   GtkAllocation allocation;
317   GtkStyleContext *context;
318   GtkStateFlags state;
319   GtkBorder padding, border;
320   guint border_width;
321
322   gtk_widget_get_allocation (widget, &allocation);
323   border_width = gtk_container_get_border_width (GTK_CONTAINER (viewport));
324
325   view_allocation->x = 0;
326   view_allocation->y = 0;
327
328   context = gtk_widget_get_style_context (widget);
329   state = gtk_widget_get_state_flags (widget);
330   gtk_style_context_save (context);
331   gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
332
333   gtk_style_context_get_padding (context, state, &padding);
334   gtk_style_context_get_border (context, state, &border);
335
336   gtk_style_context_restore (context);
337
338   if (priv->shadow_type != GTK_SHADOW_NONE)
339     {
340       view_allocation->x = border.left;
341       view_allocation->y = border.top;
342     }
343
344   view_allocation->x += padding.left;
345   view_allocation->y += padding.top;
346   view_allocation->width = MAX (1, allocation.width - padding.left - padding.right - border_width * 2);
347   view_allocation->height = MAX (1, allocation.height - padding.top - padding.bottom - border_width * 2);
348
349   if (priv->shadow_type != GTK_SHADOW_NONE)
350     {
351       view_allocation->width = MAX (1, view_allocation->width - border.left - border.right);
352       view_allocation->height = MAX (1, view_allocation->height - border.top - border.bottom);
353     }
354 }
355
356 /**
357  * gtk_viewport_get_hadjustment:
358  * @viewport: a #GtkViewport.
359  *
360  * Returns the horizontal adjustment of the viewport.
361  *
362  * Return value: (transfer none): the horizontal adjustment of @viewport.
363  *
364  * Deprecated: 3.0: Use gtk_scrollable_get_hadjustment()
365  **/
366 GtkAdjustment*
367 gtk_viewport_get_hadjustment (GtkViewport *viewport)
368 {
369   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
370
371   return viewport->priv->hadjustment;
372 }
373
374 /**
375  * gtk_viewport_get_vadjustment:
376  * @viewport: a #GtkViewport.
377  * 
378  * Returns the vertical adjustment of the viewport.
379  *
380  * Return value: (transfer none): the vertical adjustment of @viewport.
381  *
382  * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
383  **/
384 GtkAdjustment*
385 gtk_viewport_get_vadjustment (GtkViewport *viewport)
386 {
387   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
388
389   return viewport->priv->vadjustment;
390 }
391
392 static void
393 viewport_set_hadjustment_values (GtkViewport *viewport)
394 {
395   GtkBin *bin = GTK_BIN (viewport);
396   GtkAllocation view_allocation;
397   GtkAdjustment *hadjustment = viewport->priv->hadjustment;
398   GtkWidget *child;
399   gdouble upper, value;
400   
401   viewport_get_view_allocation (viewport, &view_allocation);  
402
403   child = gtk_bin_get_child (bin);
404   if (child && gtk_widget_get_visible (child))
405     {
406       gint minimum_width, natural_width;
407       gint scroll_height;
408       
409       if (viewport->priv->vscroll_policy == GTK_SCROLL_MINIMUM)
410         gtk_widget_get_preferred_height (child, &scroll_height, NULL);
411       else
412         gtk_widget_get_preferred_height (child, NULL, &scroll_height);
413
414       gtk_widget_get_preferred_width_for_height (child,
415                                                  MAX (view_allocation.height, scroll_height),
416                                                  &minimum_width,
417                                                  &natural_width);
418
419       if (viewport->priv->hscroll_policy == GTK_SCROLL_MINIMUM)
420         upper = MAX (minimum_width, view_allocation.width);
421       else
422         upper = MAX (natural_width, view_allocation.width);
423     }
424   else
425     upper = view_allocation.width;
426
427   value = gtk_adjustment_get_value (hadjustment);
428   /* We clamp to the left in RTL mode */
429   if (gtk_widget_get_direction (GTK_WIDGET (viewport)) == GTK_TEXT_DIR_RTL)
430     {
431       gdouble dist = gtk_adjustment_get_upper (hadjustment)
432                      - value
433                      - gtk_adjustment_get_page_size (hadjustment);
434       value = upper - dist - view_allocation.width;
435     }
436
437   gtk_adjustment_configure (hadjustment,
438                             value,
439                             0,
440                             upper,
441                             view_allocation.width * 0.1,
442                             view_allocation.width * 0.9,
443                             view_allocation.width);
444 }
445
446 static void
447 viewport_set_vadjustment_values (GtkViewport *viewport)
448 {
449   GtkBin *bin = GTK_BIN (viewport);
450   GtkAllocation view_allocation;
451   GtkAdjustment *vadjustment = viewport->priv->vadjustment;
452   GtkWidget *child;
453   gdouble upper;
454
455   viewport_get_view_allocation (viewport, &view_allocation);  
456
457   child = gtk_bin_get_child (bin);
458   if (child && gtk_widget_get_visible (child))
459     {
460       gint minimum_height, natural_height;
461       gint scroll_width;
462
463       if (viewport->priv->hscroll_policy == GTK_SCROLL_MINIMUM)
464         gtk_widget_get_preferred_width (child, &scroll_width, NULL);
465       else
466         gtk_widget_get_preferred_width (child, NULL, &scroll_width);
467
468       gtk_widget_get_preferred_height_for_width (child,
469                                                  MAX (view_allocation.width, scroll_width),
470                                                  &minimum_height,
471                                                  &natural_height);
472
473       if (viewport->priv->vscroll_policy == GTK_SCROLL_MINIMUM)
474         upper = MAX (minimum_height, view_allocation.height);
475       else
476         upper = MAX (natural_height, view_allocation.height);
477     }
478   else
479     upper = view_allocation.height;
480
481   gtk_adjustment_configure (vadjustment,
482                             gtk_adjustment_get_value (vadjustment),
483                             0,
484                             upper,
485                             view_allocation.height * 0.1,
486                             view_allocation.height * 0.9,
487                             view_allocation.height);
488 }
489
490 static void
491 viewport_set_adjustment (GtkViewport    *viewport,
492                          GtkOrientation  orientation,
493                          GtkAdjustment  *adjustment)
494 {
495   GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (viewport, orientation);
496
497   if (adjustment && adjustment == *adjustmentp)
498     return;
499
500   if (!adjustment)
501     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
502   viewport_disconnect_adjustment (viewport, orientation);
503   *adjustmentp = adjustment;
504   g_object_ref_sink (adjustment);
505
506   if (orientation == GTK_ORIENTATION_HORIZONTAL)
507     viewport_set_hadjustment_values (viewport);
508   else
509     viewport_set_vadjustment_values (viewport);
510
511   g_signal_connect (adjustment, "value-changed",
512                     G_CALLBACK (gtk_viewport_adjustment_value_changed),
513                     viewport);
514
515   gtk_viewport_adjustment_value_changed (adjustment, viewport);
516 }
517
518 /**
519  * gtk_viewport_set_hadjustment:
520  * @viewport: a #GtkViewport.
521  * @adjustment: (allow-none): a #GtkAdjustment.
522  *
523  * Sets the horizontal adjustment of the viewport.
524  *
525  * Deprecated: 3.0: Use gtk_scrollable_set_hadjustment()
526  **/
527 void
528 gtk_viewport_set_hadjustment (GtkViewport   *viewport,
529                               GtkAdjustment *adjustment)
530 {
531   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
532   if (adjustment)
533     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
534
535   viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, adjustment);
536
537   g_object_notify (G_OBJECT (viewport), "hadjustment");
538 }
539
540 /**
541  * gtk_viewport_set_vadjustment:
542  * @viewport: a #GtkViewport.
543  * @adjustment: (allow-none): a #GtkAdjustment.
544  *
545  * Sets the vertical adjustment of the viewport.
546  *
547  * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
548  **/
549 void
550 gtk_viewport_set_vadjustment (GtkViewport   *viewport,
551                               GtkAdjustment *adjustment)
552 {
553   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
554   if (adjustment)
555     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
556
557   viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, adjustment);
558
559   g_object_notify (G_OBJECT (viewport), "vadjustment");
560 }
561
562 /** 
563  * gtk_viewport_set_shadow_type:
564  * @viewport: a #GtkViewport.
565  * @type: the new shadow type.
566  *
567  * Sets the shadow type of the viewport.
568  **/ 
569 void
570 gtk_viewport_set_shadow_type (GtkViewport   *viewport,
571                               GtkShadowType  type)
572 {
573   GtkViewportPrivate *priv;
574   GtkAllocation allocation;
575   GtkWidget *widget;
576
577   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
578
579   widget = GTK_WIDGET (viewport);
580   priv = viewport->priv;
581
582   if ((GtkShadowType) priv->shadow_type != type)
583     {
584       priv->shadow_type = type;
585
586       if (gtk_widget_get_visible (widget))
587         {
588           gtk_widget_get_allocation (widget, &allocation);
589           gtk_widget_size_allocate (widget, &allocation);
590           gtk_widget_set_allocation (widget, &allocation);
591           gtk_widget_queue_draw (widget);
592         }
593
594       g_object_notify (G_OBJECT (viewport), "shadow-type");
595     }
596 }
597
598 /**
599  * gtk_viewport_get_shadow_type:
600  * @viewport: a #GtkViewport
601  *
602  * Gets the shadow type of the #GtkViewport. See
603  * gtk_viewport_set_shadow_type().
604  *
605  * Return value: the shadow type 
606  **/
607 GtkShadowType
608 gtk_viewport_get_shadow_type (GtkViewport *viewport)
609 {
610   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), GTK_SHADOW_NONE);
611
612   return viewport->priv->shadow_type;
613 }
614
615 /**
616  * gtk_viewport_get_bin_window:
617  * @viewport: a #GtkViewport
618  *
619  * Gets the bin window of the #GtkViewport.
620  *
621  * Return value: (transfer none): a #GdkWindow
622  *
623  * Since: 2.20
624  **/
625 GdkWindow*
626 gtk_viewport_get_bin_window (GtkViewport *viewport)
627 {
628   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
629
630   return viewport->priv->bin_window;
631 }
632
633 /**
634  * gtk_viewport_get_view_window:
635  * @viewport: a #GtkViewport
636  *
637  * Gets the view window of the #GtkViewport.
638  *
639  * Return value: (transfer none): a #GdkWindow
640  *
641  * Since: 2.22
642  **/
643 GdkWindow*
644 gtk_viewport_get_view_window (GtkViewport *viewport)
645 {
646   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
647
648   return viewport->priv->view_window;
649 }
650
651 static void
652 gtk_viewport_realize (GtkWidget *widget)
653 {
654   GtkViewport *viewport = GTK_VIEWPORT (widget);
655   GtkViewportPrivate *priv = viewport->priv;
656   GtkBin *bin = GTK_BIN (widget);
657   GtkAdjustment *hadjustment = priv->hadjustment;
658   GtkAdjustment *vadjustment = priv->vadjustment;
659   GtkAllocation allocation;
660   GtkAllocation view_allocation;
661   GtkStyleContext *context;
662   GtkWidget *child;
663   GdkWindow *window;
664   GdkWindowAttr attributes;
665   gint attributes_mask;
666   gint event_mask;
667   guint border_width;
668
669   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
670
671   gtk_widget_set_realized (widget, TRUE);
672
673   gtk_widget_get_allocation (widget, &allocation);
674
675   attributes.x = allocation.x + border_width;
676   attributes.y = allocation.y + border_width;
677   attributes.width = allocation.width - border_width * 2;
678   attributes.height = allocation.height - border_width * 2;
679   attributes.window_type = GDK_WINDOW_CHILD;
680   attributes.wclass = GDK_INPUT_OUTPUT;
681   attributes.visual = gtk_widget_get_visual (widget);
682
683   event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
684
685   attributes.event_mask = event_mask | GDK_SCROLL_MASK | GDK_TOUCH_MASK | GDK_SMOOTH_SCROLL_MASK;
686
687   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
688
689   window = gdk_window_new (gtk_widget_get_parent_window (widget),
690                            &attributes, attributes_mask);
691   gtk_widget_set_window (widget, window);
692   gdk_window_set_user_data (window, viewport);
693
694   viewport_get_view_allocation (viewport, &view_allocation);
695   
696   attributes.x = view_allocation.x;
697   attributes.y = view_allocation.y;
698   attributes.width = view_allocation.width;
699   attributes.height = view_allocation.height;
700   attributes.event_mask = 0;
701
702   priv->view_window = gdk_window_new (window,
703                                       &attributes, attributes_mask);
704   gdk_window_set_user_data (priv->view_window, viewport);
705
706   attributes.x = - gtk_adjustment_get_value (hadjustment);
707   attributes.y = - gtk_adjustment_get_value (vadjustment);
708   attributes.width = gtk_adjustment_get_upper (hadjustment);
709   attributes.height = gtk_adjustment_get_upper (vadjustment);
710   
711   attributes.event_mask = event_mask;
712
713   priv->bin_window = gdk_window_new (priv->view_window, &attributes, attributes_mask);
714   gdk_window_set_user_data (priv->bin_window, viewport);
715
716   child = gtk_bin_get_child (bin);
717   if (child)
718     gtk_widget_set_parent_window (child, priv->bin_window);
719
720   context = gtk_widget_get_style_context (widget);
721   gtk_style_context_set_background (context, window);
722   gtk_style_context_set_background (context, priv->bin_window);
723
724   gdk_window_show (priv->bin_window);
725   gdk_window_show (priv->view_window);
726 }
727
728 static void
729 gtk_viewport_unrealize (GtkWidget *widget)
730 {
731   GtkViewport *viewport = GTK_VIEWPORT (widget);
732   GtkViewportPrivate *priv = viewport->priv;
733
734   gdk_window_set_user_data (priv->view_window, NULL);
735   gdk_window_destroy (priv->view_window);
736   priv->view_window = NULL;
737
738   gdk_window_set_user_data (priv->bin_window, NULL);
739   gdk_window_destroy (priv->bin_window);
740   priv->bin_window = NULL;
741
742   GTK_WIDGET_CLASS (gtk_viewport_parent_class)->unrealize (widget);
743 }
744
745 static gint
746 gtk_viewport_draw (GtkWidget *widget,
747                    cairo_t   *cr)
748 {
749   GtkViewport *viewport = GTK_VIEWPORT (widget);
750   GtkViewportPrivate *priv = viewport->priv;
751   GtkStyleContext *context;
752   int x, y;
753
754   context = gtk_widget_get_style_context (widget);
755
756   if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)) &&
757       priv->shadow_type != GTK_SHADOW_NONE)
758     {
759       gtk_style_context_save (context);
760       gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
761
762       gtk_render_frame (context, cr, 0, 0,
763                         gdk_window_get_width (gtk_widget_get_window (widget)),
764                         gdk_window_get_height (gtk_widget_get_window (widget)));
765
766       gtk_style_context_restore (context);
767     }
768   
769   if (gtk_cairo_should_draw_window (cr, priv->view_window))
770     {
771       /* This is a cute hack to ensure the contents of bin_window are
772        * restricted to where they are visible. We only need to do this
773        * clipping when called via gtk_widget_draw() and not in expose
774        * events. And when that happens every window (including this one)
775        * should be drawn.
776        */
777       gdk_window_get_position (priv->view_window, &x, &y);
778       cairo_rectangle (cr, x, y, 
779                        gdk_window_get_width (priv->view_window),
780                        gdk_window_get_height (priv->view_window));
781       cairo_clip (cr);
782     }
783
784   if (gtk_cairo_should_draw_window (cr, priv->bin_window))
785     {
786       gdk_window_get_position (priv->bin_window, &x, &y);
787       gtk_render_background (context, cr, x, y,
788                              gdk_window_get_width (priv->bin_window),
789                              gdk_window_get_height (priv->bin_window));
790
791       GTK_WIDGET_CLASS (gtk_viewport_parent_class)->draw (widget, cr);
792     }
793
794   return FALSE;
795 }
796
797 static void
798 gtk_viewport_add (GtkContainer *container,
799                   GtkWidget    *child)
800 {
801   GtkBin *bin = GTK_BIN (container);
802   GtkViewport *viewport = GTK_VIEWPORT (bin);
803   GtkViewportPrivate *priv = viewport->priv;
804
805   g_return_if_fail (gtk_bin_get_child (bin) == NULL);
806
807   gtk_widget_set_parent_window (child, priv->bin_window);
808
809   GTK_CONTAINER_CLASS (gtk_viewport_parent_class)->add (container, child);
810 }
811
812 static void
813 gtk_viewport_size_allocate (GtkWidget     *widget,
814                             GtkAllocation *allocation)
815 {
816   GtkAllocation widget_allocation;
817   GtkViewport *viewport = GTK_VIEWPORT (widget);
818   GtkViewportPrivate *priv = viewport->priv;
819   GtkBin *bin = GTK_BIN (widget);
820   guint border_width;
821   GtkAdjustment *hadjustment = priv->hadjustment;
822   GtkAdjustment *vadjustment = priv->vadjustment;
823   GtkAllocation child_allocation;
824   GtkWidget *child;
825
826   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
827
828   /* If our size changed, and we have a shadow, queue a redraw on widget->window to
829    * redraw the shadow correctly.
830    */
831   gtk_widget_get_allocation (widget, &widget_allocation);
832   if (gtk_widget_get_mapped (widget) &&
833       priv->shadow_type != GTK_SHADOW_NONE &&
834       (widget_allocation.width != allocation->width ||
835        widget_allocation.height != allocation->height))
836     gdk_window_invalidate_rect (gtk_widget_get_window (widget), NULL, FALSE);
837
838   gtk_widget_set_allocation (widget, allocation);
839
840   g_object_freeze_notify (G_OBJECT (hadjustment));
841   g_object_freeze_notify (G_OBJECT (vadjustment));
842
843   viewport_set_hadjustment_values (viewport);
844   viewport_set_vadjustment_values (viewport);
845   
846   child_allocation.x = 0;
847   child_allocation.y = 0;
848   child_allocation.width = gtk_adjustment_get_upper (hadjustment);
849   child_allocation.height = gtk_adjustment_get_upper (vadjustment);
850   if (gtk_widget_get_realized (widget))
851     {
852       GtkAllocation view_allocation;
853
854       gdk_window_move_resize (gtk_widget_get_window (widget),
855                               allocation->x + border_width,
856                               allocation->y + border_width,
857                               allocation->width - border_width * 2,
858                               allocation->height - border_width * 2);
859       
860       viewport_get_view_allocation (viewport, &view_allocation);
861       gdk_window_move_resize (priv->view_window,
862                               view_allocation.x,
863                               view_allocation.y,
864                               view_allocation.width,
865                               view_allocation.height);
866       gdk_window_move_resize (priv->bin_window,
867                               - gtk_adjustment_get_value (hadjustment),
868                               - gtk_adjustment_get_value (vadjustment),
869                               child_allocation.width,
870                               child_allocation.height);
871     }
872
873   child = gtk_bin_get_child (bin);
874   if (child && gtk_widget_get_visible (child))
875     gtk_widget_size_allocate (child, &child_allocation);
876
877   g_object_thaw_notify (G_OBJECT (hadjustment));
878   g_object_thaw_notify (G_OBJECT (vadjustment));
879 }
880
881 static void
882 gtk_viewport_adjustment_value_changed (GtkAdjustment *adjustment,
883                                        gpointer       data)
884 {
885   GtkViewport *viewport = GTK_VIEWPORT (data);
886   GtkViewportPrivate *priv = viewport->priv;
887   GtkBin *bin = GTK_BIN (data);
888   GtkWidget *child;
889
890   child = gtk_bin_get_child (bin);
891   if (child && gtk_widget_get_visible (child) &&
892       gtk_widget_get_realized (GTK_WIDGET (viewport)))
893     {
894       GtkAdjustment *hadjustment = priv->hadjustment;
895       GtkAdjustment *vadjustment = priv->vadjustment;
896       gint old_x, old_y;
897       gint new_x, new_y;
898
899       gdk_window_get_position (priv->bin_window, &old_x, &old_y);
900       new_x = - gtk_adjustment_get_value (hadjustment);
901       new_y = - gtk_adjustment_get_value (vadjustment);
902
903       if (new_x != old_x || new_y != old_y)
904         gdk_window_move (priv->bin_window, new_x, new_y);
905     }
906 }
907
908 static void
909 gtk_viewport_style_updated (GtkWidget *widget)
910 {
911    GTK_WIDGET_CLASS (gtk_viewport_parent_class)->style_updated (widget);
912
913    if (gtk_widget_get_realized (widget) &&
914        gtk_widget_get_has_window (widget))
915      {
916         GtkStyleContext *context;
917         GtkViewport *viewport = GTK_VIEWPORT (widget);
918         GtkViewportPrivate *priv = viewport->priv;
919
920         context = gtk_widget_get_style_context (widget);
921         gtk_style_context_set_background (context, priv->bin_window);
922         gtk_style_context_set_background (context, gtk_widget_get_window (widget));
923      }
924 }
925
926
927 static void
928 gtk_viewport_get_preferred_size (GtkWidget      *widget,
929                                  GtkOrientation  orientation,
930                                  gint           *minimum_size,
931                                  gint           *natural_size)
932 {
933   GtkViewport *viewport = GTK_VIEWPORT (widget);
934   GtkViewportPrivate *priv = viewport->priv;
935   GtkStyleContext *context;
936   GtkStateFlags state;
937   GtkBorder padding, border;
938   GtkWidget *child;
939   gint       child_min, child_nat;
940   gint       minimum, natural;
941
942   child = gtk_bin_get_child (GTK_BIN (widget));
943
944   /* XXX This should probably be (border_width * 2); but GTK+ has
945    * been doing this with a single border for a while now...
946    */
947   minimum = gtk_container_get_border_width (GTK_CONTAINER (widget));
948
949   context = gtk_widget_get_style_context (GTK_WIDGET (widget));
950   state = gtk_widget_get_state_flags (GTK_WIDGET (widget));
951   gtk_style_context_get_padding (context, state, &padding);
952
953   if (priv->shadow_type != GTK_SHADOW_NONE)
954     {
955       gtk_style_context_get_border (context, state, &border);
956
957       if (orientation == GTK_ORIENTATION_HORIZONTAL)
958         minimum += border.left + border.right;
959       else
960         minimum += border.top + border.bottom;
961     }
962
963   if (orientation == GTK_ORIENTATION_HORIZONTAL)
964     minimum += padding.left + padding.right;
965   else
966     minimum += padding.top + padding.bottom;
967
968   natural = minimum;
969
970   if (child && gtk_widget_get_visible (child))
971     {
972       if (orientation == GTK_ORIENTATION_HORIZONTAL)
973         gtk_widget_get_preferred_width (child, &child_min, &child_nat);
974       else
975         gtk_widget_get_preferred_height (child, &child_min, &child_nat);
976
977       minimum += child_min;
978       natural += child_nat;
979     }
980
981   if (minimum_size)
982     *minimum_size = minimum;
983
984   if (natural_size)
985     *natural_size = natural;
986 }
987
988 static void
989 gtk_viewport_get_preferred_width (GtkWidget *widget,
990                                   gint      *minimum_size,
991                                   gint      *natural_size)
992 {
993   gtk_viewport_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
994 }
995
996 static void
997 gtk_viewport_get_preferred_height (GtkWidget *widget,
998                                    gint      *minimum_size,
999                                    gint      *natural_size)
1000 {
1001   gtk_viewport_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
1002 }