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