]> Pileus Git - ~andy/gtk/blob - gtk/gtkviewport.c
Fixes #136082 and #135265, patch by Morten Welinder.
[~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 #include "gtkviewport.h"
29 #include "gtkintl.h"
30 #include "gtkmarshalers.h"
31
32 enum {
33   PROP_0,
34   PROP_HADJUSTMENT,
35   PROP_VADJUSTMENT,
36   PROP_SHADOW_TYPE
37 };
38
39
40 static void gtk_viewport_class_init               (GtkViewportClass *klass);
41 static void gtk_viewport_init                     (GtkViewport      *viewport);
42 static void gtk_viewport_finalize                 (GObject          *object);
43 static void gtk_viewport_destroy                  (GtkObject        *object);
44 static void gtk_viewport_set_property             (GObject         *object,
45                                                    guint            prop_id,
46                                                    const GValue    *value,
47                                                    GParamSpec      *pspec);
48 static void gtk_viewport_get_property             (GObject         *object,
49                                                    guint            prop_id,
50                                                    GValue          *value,
51                                                    GParamSpec      *pspec);
52 static void gtk_viewport_set_scroll_adjustments   (GtkViewport      *viewport,
53                                                    GtkAdjustment    *hadjustment,
54                                                    GtkAdjustment    *vadjustment);
55 static void gtk_viewport_realize                  (GtkWidget        *widget);
56 static void gtk_viewport_unrealize                (GtkWidget        *widget);
57 static void gtk_viewport_paint                    (GtkWidget        *widget,
58                                                    GdkRectangle     *area);
59 static gint gtk_viewport_expose                   (GtkWidget        *widget,
60                                                    GdkEventExpose   *event);
61 static void gtk_viewport_add                      (GtkContainer     *container,
62                                                    GtkWidget        *widget);
63 static void gtk_viewport_size_request             (GtkWidget        *widget,
64                                                    GtkRequisition   *requisition);
65 static void gtk_viewport_size_allocate            (GtkWidget        *widget,
66                                                    GtkAllocation    *allocation);
67 static void gtk_viewport_adjustment_value_changed (GtkAdjustment    *adjustment,
68                                                    gpointer          data);
69 static void gtk_viewport_style_set                (GtkWidget *widget,
70                                                    GtkStyle  *previous_style);
71
72 static GtkBinClass *parent_class;
73
74 GType
75 gtk_viewport_get_type (void)
76 {
77   static GType viewport_type = 0;
78
79   if (!viewport_type)
80     {
81       static const GTypeInfo viewport_info =
82       {
83         sizeof (GtkViewportClass),
84         NULL,           /* base_init */
85         NULL,           /* base_finalize */
86         (GClassInitFunc) gtk_viewport_class_init,
87         NULL,           /* class_finalize */
88         NULL,           /* class_data */
89         sizeof (GtkViewport),
90         0,              /* n_preallocs */
91         (GInstanceInitFunc) gtk_viewport_init,
92       };
93
94       viewport_type = g_type_register_static (GTK_TYPE_BIN, "GtkViewport",
95                                               &viewport_info, 0);
96     }
97
98   return viewport_type;
99 }
100
101 static void
102 gtk_viewport_class_init (GtkViewportClass *class)
103 {
104   GtkObjectClass *object_class;
105   GObjectClass   *gobject_class;
106   GtkWidgetClass *widget_class;
107   GtkContainerClass *container_class;
108
109   object_class = (GtkObjectClass*) class;
110   gobject_class = G_OBJECT_CLASS (class);
111   widget_class = (GtkWidgetClass*) class;
112   container_class = (GtkContainerClass*) class;
113
114   parent_class = g_type_class_peek_parent (class);
115
116   gobject_class->finalize = gtk_viewport_finalize;
117   gobject_class->set_property = gtk_viewport_set_property;
118   gobject_class->get_property = gtk_viewport_get_property;
119   object_class->destroy = gtk_viewport_destroy;
120   
121   widget_class->realize = gtk_viewport_realize;
122   widget_class->unrealize = gtk_viewport_unrealize;
123   widget_class->expose_event = gtk_viewport_expose;
124   widget_class->size_request = gtk_viewport_size_request;
125   widget_class->size_allocate = gtk_viewport_size_allocate;
126   widget_class->style_set = gtk_viewport_style_set;
127   
128   container_class->add = gtk_viewport_add;
129
130   class->set_scroll_adjustments = gtk_viewport_set_scroll_adjustments;
131
132   g_object_class_install_property (gobject_class,
133                                    PROP_HADJUSTMENT,
134                                    g_param_spec_object ("hadjustment",
135                                                         P_("Horizontal adjustment"),
136                                                         P_("The GtkAdjustment that determines the values of the horizontal position for this viewport"),
137                                                         GTK_TYPE_ADJUSTMENT,
138                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
139
140   g_object_class_install_property (gobject_class,
141                                    PROP_VADJUSTMENT,
142                                    g_param_spec_object ("vadjustment",
143                                                         P_("Vertical adjustment"),
144                                                         P_("The GtkAdjustment that determines the values of the vertical position for this viewport"),
145                                                         GTK_TYPE_ADJUSTMENT,
146                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
147
148   g_object_class_install_property (gobject_class,
149                                    PROP_SHADOW_TYPE,
150                                    g_param_spec_enum ("shadow_type",
151                                                       P_("Shadow type"),
152                                                       P_("Determines how the shadowed box around the viewport is drawn"),
153                                                       GTK_TYPE_SHADOW_TYPE,
154                                                       GTK_SHADOW_IN,
155                                                       G_PARAM_READWRITE));
156
157   widget_class->set_scroll_adjustments_signal =
158     g_signal_new ("set_scroll_adjustments",
159                   G_OBJECT_CLASS_TYPE (gobject_class),
160                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
161                   G_STRUCT_OFFSET (GtkViewportClass, set_scroll_adjustments),
162                   NULL, NULL,
163                   _gtk_marshal_VOID__OBJECT_OBJECT,
164                   G_TYPE_NONE, 2,
165                   GTK_TYPE_ADJUSTMENT,
166                   GTK_TYPE_ADJUSTMENT);
167 }
168
169 static void
170 gtk_viewport_set_property (GObject         *object,
171                            guint            prop_id,
172                            const GValue    *value,
173                            GParamSpec      *pspec)
174 {
175   GtkViewport *viewport;
176
177   viewport = GTK_VIEWPORT (object);
178
179   switch (prop_id)
180     {
181     case PROP_HADJUSTMENT:
182       gtk_viewport_set_hadjustment (viewport, g_value_get_object (value));
183       break;
184     case PROP_VADJUSTMENT:
185       gtk_viewport_set_vadjustment (viewport, g_value_get_object (value));
186       break;
187     case PROP_SHADOW_TYPE:
188       gtk_viewport_set_shadow_type (viewport, g_value_get_enum (value));
189       break;
190     default:
191       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
192       break;
193     }
194 }
195
196 static void
197 gtk_viewport_get_property (GObject         *object,
198                            guint            prop_id,
199                            GValue          *value,
200                            GParamSpec      *pspec)
201 {
202   GtkViewport *viewport;
203
204   viewport = GTK_VIEWPORT (object);
205
206   switch (prop_id)
207     {
208     case PROP_HADJUSTMENT:
209       g_value_set_object (value, viewport->hadjustment);
210       break;
211     case PROP_VADJUSTMENT:
212       g_value_set_object (value, viewport->vadjustment);
213       break;
214     case PROP_SHADOW_TYPE:
215       g_value_set_enum (value, viewport->shadow_type);
216       break;
217     default:
218       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
219       break;
220     }
221 }
222
223 static void
224 gtk_viewport_init (GtkViewport *viewport)
225 {
226   GTK_WIDGET_UNSET_FLAGS (viewport, GTK_NO_WINDOW);
227
228   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (viewport), FALSE);
229   gtk_container_set_resize_mode (GTK_CONTAINER (viewport), GTK_RESIZE_QUEUE);
230   
231   viewport->shadow_type = GTK_SHADOW_IN;
232   viewport->view_window = NULL;
233   viewport->bin_window = NULL;
234   viewport->hadjustment = NULL;
235   viewport->vadjustment = NULL;
236 }
237
238 /**
239  * gtk_viewport_new:
240  * @hadjustment: horizontal adjustment.
241  * @vadjustment: vertical adjustment.
242  * @returns: a new #GtkViewport.
243  *
244  * Creates a new #GtkViewport with the given adjustments.
245  *
246  **/
247 GtkWidget*
248 gtk_viewport_new (GtkAdjustment *hadjustment,
249                   GtkAdjustment *vadjustment)
250 {
251   GtkWidget *viewport;
252
253   viewport = gtk_widget_new (GTK_TYPE_VIEWPORT,
254                              "hadjustment", hadjustment,
255                              "vadjustment", vadjustment,
256                              NULL);
257
258   return viewport;
259 }
260
261 #define ADJUSTMENT_POINTER(viewport, orientation)         \
262   (((orientation) == GTK_ORIENTATION_HORIZONTAL) ?        \
263      &(viewport)->hadjustment : &(viewport)->vadjustment)
264
265 static void
266 viewport_disconnect_adjustment (GtkViewport    *viewport,
267                                 GtkOrientation  orientation)
268 {
269   GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (viewport, orientation);
270
271   if (*adjustmentp)
272     {
273       g_signal_handlers_disconnect_by_func (*adjustmentp,
274                                             gtk_viewport_adjustment_value_changed,
275                                             viewport);
276       g_object_unref (*adjustmentp);
277       *adjustmentp = NULL;
278     }
279 }
280
281 static void
282 gtk_viewport_finalize (GObject *object)
283 {
284   GtkViewport *viewport = GTK_VIEWPORT (object);
285
286   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL);
287   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_VERTICAL);
288
289   G_OBJECT_CLASS (parent_class)->finalize (object);
290 }
291
292 static void
293 gtk_viewport_destroy (GtkObject *object)
294 {
295   GtkViewport *viewport = GTK_VIEWPORT (object);
296
297   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL);
298   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_VERTICAL);
299
300   GTK_OBJECT_CLASS (parent_class)->destroy (object);
301 }
302
303 /**
304  * gtk_viewport_get_hadjustment:
305  * @viewport: a #GtkViewport.
306  * 
307  * Returns the horizontal adjustment of the viewport.
308  *
309  * Return value: the horizontal adjustment of @viewport.
310  **/
311 GtkAdjustment*
312 gtk_viewport_get_hadjustment (GtkViewport *viewport)
313 {
314   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
315
316   if (!viewport->hadjustment)
317     gtk_viewport_set_hadjustment (viewport, NULL);
318
319   return viewport->hadjustment;
320 }
321
322 /**
323  * gtk_viewport_get_vadjustment:
324  * @viewport: a #GtkViewport.
325  * 
326  * Returns the vertical adjustment of the viewport.
327  *
328  * Return value: the vertical adjustment of @viewport.
329  **/
330 GtkAdjustment*
331 gtk_viewport_get_vadjustment (GtkViewport *viewport)
332 {
333   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
334
335   if (!viewport->vadjustment)
336     gtk_viewport_set_vadjustment (viewport, NULL);
337
338   return viewport->vadjustment;
339 }
340
341 static void
342 viewport_get_view_allocation (GtkViewport   *viewport,
343                               GtkAllocation *view_allocation)
344 {
345   GtkWidget *widget = GTK_WIDGET (viewport);
346   GtkAllocation *allocation = &widget->allocation;
347   gint border_width = GTK_CONTAINER (viewport)->border_width;
348   
349   view_allocation->x = 0;
350   view_allocation->y = 0;
351
352   if (viewport->shadow_type != GTK_SHADOW_NONE)
353     {
354       view_allocation->x = widget->style->xthickness;
355       view_allocation->y = widget->style->ythickness;
356     }
357
358   view_allocation->width = MAX (1, allocation->width - view_allocation->x * 2 - border_width * 2);
359   view_allocation->height = MAX (1, allocation->height - view_allocation->y * 2 - border_width * 2);
360 }
361
362 static void
363 viewport_reclamp_adjustment (GtkAdjustment *adjustment,
364                              gboolean      *value_changed)
365 {
366   gdouble value = adjustment->value;
367   
368   value = CLAMP (value, 0, adjustment->upper - adjustment->page_size);
369   if (value != adjustment->value)
370     {
371       adjustment->value = value;
372       if (value_changed)
373         *value_changed = TRUE;
374     }
375   else if (value_changed)
376     *value_changed = FALSE;
377 }
378
379 static void
380 viewport_set_hadjustment_values (GtkViewport *viewport,
381                                  gboolean    *value_changed)
382 {
383   GtkBin *bin = GTK_BIN (viewport);
384   GtkAllocation view_allocation;
385   GtkAdjustment *hadjustment = gtk_viewport_get_hadjustment (viewport);
386   gdouble old_page_size;
387   gdouble old_upper;
388   gdouble old_value;
389   
390   viewport_get_view_allocation (viewport, &view_allocation);  
391
392   old_page_size = hadjustment->page_size;
393   old_upper = hadjustment->upper;
394   old_value = hadjustment->value;
395   hadjustment->page_size = view_allocation.width;
396   hadjustment->step_increment = view_allocation.width * 0.1;
397   hadjustment->page_increment = view_allocation.width * 0.9;
398   
399   hadjustment->lower = 0;
400
401   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
402     {
403       GtkRequisition child_requisition;
404       
405       gtk_widget_get_child_requisition (bin->child, &child_requisition);
406       hadjustment->upper = MAX (child_requisition.width, view_allocation.width);
407     }
408   else
409     hadjustment->upper = view_allocation.width;
410
411   if (gtk_widget_get_direction (GTK_WIDGET (viewport)) == GTK_TEXT_DIR_RTL) 
412     {
413       gdouble dist = old_upper - (old_value + old_page_size);
414       hadjustment->value = hadjustment->upper - dist - hadjustment->page_size;
415       viewport_reclamp_adjustment (hadjustment, value_changed);
416       *value_changed = (old_value != hadjustment->value);
417     }
418   else
419     viewport_reclamp_adjustment (hadjustment, value_changed);
420 }
421
422 static void
423 viewport_set_vadjustment_values (GtkViewport *viewport,
424                                  gboolean    *value_changed)
425 {
426   GtkBin *bin = GTK_BIN (viewport);
427   GtkAllocation view_allocation;
428   GtkAdjustment *vadjustment = gtk_viewport_get_vadjustment (viewport);
429
430   viewport_get_view_allocation (viewport, &view_allocation);  
431
432   vadjustment->page_size = view_allocation.height;
433   vadjustment->step_increment = view_allocation.height * 0.1;
434   vadjustment->page_increment = view_allocation.height * 0.9;
435   
436   vadjustment->lower = 0;
437
438   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
439     {
440       GtkRequisition child_requisition;
441       
442       gtk_widget_get_child_requisition (bin->child, &child_requisition);
443       vadjustment->upper = MAX (child_requisition.height, view_allocation.height);
444     }
445   else
446     vadjustment->upper = view_allocation.height;
447
448   viewport_reclamp_adjustment (vadjustment, value_changed);
449 }
450
451 static void
452 viewport_set_adjustment (GtkViewport    *viewport,
453                          GtkOrientation  orientation,
454                          GtkAdjustment  *adjustment)
455 {
456   GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (viewport, orientation);
457   gboolean value_changed;
458
459   if (adjustment && adjustment == *adjustmentp)
460     return;
461
462   if (!adjustment)
463     adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0,
464                                                      0.0, 0.0, 0.0));
465
466   *adjustmentp = adjustment;
467   g_object_ref (adjustment);
468   gtk_object_sink (GTK_OBJECT (adjustment));
469
470   if (orientation == GTK_ORIENTATION_HORIZONTAL)
471     viewport_set_hadjustment_values (viewport, &value_changed);
472   else
473     viewport_set_vadjustment_values (viewport, &value_changed);
474
475   g_signal_connect (adjustment, "value_changed",
476                     G_CALLBACK (gtk_viewport_adjustment_value_changed),
477                     viewport);
478
479   gtk_adjustment_changed (adjustment);
480   
481   if (value_changed)
482     gtk_adjustment_value_changed (adjustment);
483   else
484     gtk_viewport_adjustment_value_changed (adjustment, viewport);
485 }
486
487 /**
488  * gtk_viewport_set_hadjustment:
489  * @viewport: a #GtkViewport.
490  * @adjustment: a #GtkAdjustment.
491  * 
492  * Sets the horizontal adjustment of the viewport.
493  **/
494 void
495 gtk_viewport_set_hadjustment (GtkViewport   *viewport,
496                               GtkAdjustment *adjustment)
497 {
498   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
499   if (adjustment)
500     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
501
502   viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, adjustment);
503
504   g_object_notify (G_OBJECT (viewport), "hadjustment");
505 }
506
507 /**
508  * gtk_viewport_set_vadjustment:
509  * @viewport: a #GtkViewport.
510  * @adjustment: a #GtkAdjustment.
511  * 
512  * Sets the vertical adjustment of the viewport.
513  **/
514 void
515 gtk_viewport_set_vadjustment (GtkViewport   *viewport,
516                               GtkAdjustment *adjustment)
517 {
518   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
519   if (adjustment)
520     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
521
522   viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, adjustment);
523
524   g_object_notify (G_OBJECT (viewport), "vadjustment");
525 }
526
527 static void
528 gtk_viewport_set_scroll_adjustments (GtkViewport      *viewport,
529                                      GtkAdjustment    *hadjustment,
530                                      GtkAdjustment    *vadjustment)
531 {
532   gtk_viewport_set_hadjustment (viewport, hadjustment);
533   gtk_viewport_set_vadjustment (viewport, vadjustment);
534 }
535
536 /** 
537  * gtk_viewport_set_shadow_type:
538  * @viewport: a #GtkViewport.
539  * @type: the new shadow type.
540  *
541  * Sets the shadow type of the viewport.
542  **/ 
543 void
544 gtk_viewport_set_shadow_type (GtkViewport   *viewport,
545                               GtkShadowType  type)
546 {
547   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
548
549   if ((GtkShadowType) viewport->shadow_type != type)
550     {
551       viewport->shadow_type = type;
552
553       if (GTK_WIDGET_VISIBLE (viewport))
554         {
555           gtk_widget_size_allocate (GTK_WIDGET (viewport), &(GTK_WIDGET (viewport)->allocation));
556           gtk_widget_queue_draw (GTK_WIDGET (viewport));
557         }
558
559       g_object_notify (G_OBJECT (viewport), "shadow_type");
560     }
561 }
562
563 /**
564  * gtk_viewport_get_shadow_type:
565  * @viewport: a #GtkViewport
566  *
567  * Gets the shadow type of the #GtkViewport. See
568  * gtk_viewport_set_shadow_type().
569  
570  * Return value: the shadow type 
571  **/
572 GtkShadowType
573 gtk_viewport_get_shadow_type (GtkViewport *viewport)
574 {
575   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), GTK_SHADOW_NONE);
576
577   return viewport->shadow_type;
578 }
579
580 static void
581 gtk_viewport_realize (GtkWidget *widget)
582 {
583   GtkViewport *viewport = GTK_VIEWPORT (widget);
584   GtkBin *bin = GTK_BIN (widget);
585   GtkAdjustment *hadjustment = gtk_viewport_get_hadjustment (viewport);
586   GtkAdjustment *vadjustment = gtk_viewport_get_vadjustment (viewport);
587   gint border_width = GTK_CONTAINER (widget)->border_width;
588   
589   GtkAllocation view_allocation;
590   GdkWindowAttr attributes;
591   gint attributes_mask;
592   gint event_mask;
593
594   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
595
596   attributes.x = widget->allocation.x + border_width;
597   attributes.y = widget->allocation.y + border_width;
598   attributes.width = widget->allocation.width - border_width * 2;
599   attributes.height = widget->allocation.height - border_width * 2;
600   attributes.window_type = GDK_WINDOW_CHILD;
601   attributes.wclass = GDK_INPUT_OUTPUT;
602   attributes.visual = gtk_widget_get_visual (widget);
603   attributes.colormap = gtk_widget_get_colormap (widget);
604
605   event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
606   /* We select on button_press_mask so that button 4-5 scrolls are trapped.
607    */
608   attributes.event_mask = event_mask | GDK_BUTTON_PRESS_MASK;
609
610   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
611
612   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
613                                    &attributes, attributes_mask);
614   gdk_window_set_user_data (widget->window, viewport);
615
616   viewport_get_view_allocation (viewport, &view_allocation);
617   
618   attributes.x = view_allocation.x;
619   attributes.y = view_allocation.y;
620   attributes.width = view_allocation.width;
621   attributes.height = view_allocation.height;
622   attributes.event_mask = 0;
623
624   viewport->view_window = gdk_window_new (widget->window, &attributes, attributes_mask);
625   gdk_window_set_user_data (viewport->view_window, viewport);
626
627   attributes.x = - hadjustment->value;
628   attributes.y = - vadjustment->value;
629   attributes.width = hadjustment->upper;
630   attributes.height = vadjustment->upper;
631   
632   attributes.event_mask = event_mask;
633
634   viewport->bin_window = gdk_window_new (viewport->view_window, &attributes, attributes_mask);
635   gdk_window_set_user_data (viewport->bin_window, viewport);
636
637   if (bin->child)
638     gtk_widget_set_parent_window (bin->child, viewport->bin_window);
639
640   widget->style = gtk_style_attach (widget->style, widget->window);
641   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
642   gtk_style_set_background (widget->style, viewport->bin_window, GTK_STATE_NORMAL);
643
644   /* Call paint here to allow a theme to set the background without flashing
645    */
646   gtk_paint_flat_box(widget->style, viewport->bin_window, GTK_STATE_NORMAL,
647                      GTK_SHADOW_NONE,
648                      NULL, widget, "viewportbin",
649                      0, 0, -1, -1);
650    
651   gdk_window_show (viewport->bin_window);
652   gdk_window_show (viewport->view_window);
653 }
654
655 static void
656 gtk_viewport_unrealize (GtkWidget *widget)
657 {
658   GtkViewport *viewport = GTK_VIEWPORT (widget);
659
660   gdk_window_set_user_data (viewport->view_window, NULL);
661   gdk_window_destroy (viewport->view_window);
662   viewport->view_window = NULL;
663
664   gdk_window_set_user_data (viewport->bin_window, NULL);
665   gdk_window_destroy (viewport->bin_window);
666   viewport->bin_window = NULL;
667
668   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
669     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
670 }
671
672 static void
673 gtk_viewport_paint (GtkWidget    *widget,
674                     GdkRectangle *area)
675 {
676   GtkViewport *viewport;
677
678   g_return_if_fail (GTK_IS_VIEWPORT (widget));
679   g_return_if_fail (area != NULL);
680
681   if (GTK_WIDGET_DRAWABLE (widget))
682     {
683       viewport = GTK_VIEWPORT (widget);
684
685       gtk_paint_shadow (widget->style, widget->window,
686                         GTK_STATE_NORMAL, viewport->shadow_type,
687                         NULL, widget, "viewport",
688                         0, 0, -1, -1);
689     }
690 }
691
692 static gint
693 gtk_viewport_expose (GtkWidget      *widget,
694                      GdkEventExpose *event)
695 {
696   GtkViewport *viewport;
697   GtkBin *bin;
698
699   if (GTK_WIDGET_DRAWABLE (widget))
700     {
701       viewport = GTK_VIEWPORT (widget);
702       bin = GTK_BIN (widget);
703
704       if (event->window == widget->window)
705         gtk_viewport_paint (widget, &event->area);
706       else if (event->window == viewport->bin_window)
707         {
708           gtk_paint_flat_box(widget->style, viewport->bin_window, 
709                              GTK_STATE_NORMAL, GTK_SHADOW_NONE,
710                              &event->area, widget, "viewportbin",
711                              0, 0, -1, -1);
712           
713           (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
714         }
715     }
716
717   return FALSE;
718 }
719
720 static void
721 gtk_viewport_add (GtkContainer *container,
722                   GtkWidget    *child)
723 {
724   GtkBin *bin;
725
726   g_return_if_fail (GTK_IS_WIDGET (child));
727
728   bin = GTK_BIN (container);
729   g_return_if_fail (bin->child == NULL);
730
731   gtk_widget_set_parent_window (child, GTK_VIEWPORT (bin)->bin_window);
732
733   GTK_CONTAINER_CLASS (parent_class)->add (container, child);
734 }
735
736 static void
737 gtk_viewport_size_request (GtkWidget      *widget,
738                            GtkRequisition *requisition)
739 {
740   GtkViewport *viewport;
741   GtkBin *bin;
742   GtkRequisition child_requisition;
743
744   viewport = GTK_VIEWPORT (widget);
745   bin = GTK_BIN (widget);
746
747   requisition->width = (GTK_CONTAINER (widget)->border_width +
748                         GTK_WIDGET (widget)->style->xthickness) * 2;
749
750   requisition->height = (GTK_CONTAINER (widget)->border_width * 2 +
751                          GTK_WIDGET (widget)->style->ythickness) * 2;
752
753   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
754     {
755       gtk_widget_size_request (bin->child, &child_requisition);
756       requisition->width += child_requisition.width;
757       requisition->height += child_requisition.height;
758     }
759 }
760
761 static void
762 gtk_viewport_size_allocate (GtkWidget     *widget,
763                             GtkAllocation *allocation)
764 {
765   GtkViewport *viewport = GTK_VIEWPORT (widget);
766   GtkBin *bin = GTK_BIN (widget);
767   gint border_width = GTK_CONTAINER (widget)->border_width;
768   gboolean hadjustment_value_changed, vadjustment_value_changed;
769
770   GtkAdjustment *hadjustment = gtk_viewport_get_hadjustment (viewport);
771   GtkAdjustment *vadjustment = gtk_viewport_get_vadjustment (viewport);
772
773   /* If our size changed, and we have a shadow, queue a redraw on widget->window to
774    * redraw the shadow correctly.
775    */
776   if (GTK_WIDGET_MAPPED (widget) &&
777       viewport->shadow_type != GTK_SHADOW_NONE &&
778       (widget->allocation.width != allocation->width ||
779        widget->allocation.height != allocation->height))
780     gdk_window_invalidate_rect (widget->window, NULL, FALSE);
781   
782   widget->allocation = *allocation;
783
784   viewport_set_hadjustment_values (viewport, &hadjustment_value_changed);
785   viewport_set_vadjustment_values (viewport, &vadjustment_value_changed);
786
787   if (GTK_WIDGET_REALIZED (widget))
788     {
789       GtkAllocation view_allocation;
790       
791       viewport_get_view_allocation (viewport, &view_allocation);
792   
793       gdk_window_move_resize (widget->window,
794                               allocation->x + border_width,
795                               allocation->y + border_width,
796                               allocation->width - border_width * 2,
797                               allocation->height - border_width * 2);
798
799       gdk_window_move_resize (viewport->view_window,
800                               view_allocation.x,
801                               view_allocation.y,
802                               view_allocation.width,
803                               view_allocation.height);
804     }
805
806   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
807     {
808       GtkAllocation child_allocation;
809       
810       child_allocation.x = 0;
811       child_allocation.y = 0;
812       child_allocation.width = hadjustment->upper;
813       child_allocation.height = vadjustment->upper;
814
815       if (GTK_WIDGET_REALIZED (widget))
816         gdk_window_move_resize (viewport->bin_window,
817                                 - hadjustment->value,
818                                 - vadjustment->value,
819                                 child_allocation.width,
820                                 child_allocation.height);
821
822       gtk_widget_size_allocate (bin->child, &child_allocation);
823     }
824
825   gtk_adjustment_changed (hadjustment);
826   gtk_adjustment_changed (vadjustment);
827   if (hadjustment_value_changed)
828     gtk_adjustment_value_changed (hadjustment);
829   if (vadjustment_value_changed)
830     gtk_adjustment_value_changed (vadjustment);
831 }
832
833 static void
834 gtk_viewport_adjustment_value_changed (GtkAdjustment *adjustment,
835                                        gpointer       data)
836 {
837   GtkViewport *viewport;
838   GtkBin *bin;
839
840   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
841   g_return_if_fail (GTK_IS_VIEWPORT (data));
842
843   viewport = GTK_VIEWPORT (data);
844   bin = GTK_BIN (data);
845
846   if (bin->child && GTK_WIDGET_VISIBLE (bin->child)  &&
847       GTK_WIDGET_REALIZED (viewport))
848     {
849       GtkAdjustment *hadjustment = gtk_viewport_get_hadjustment (viewport);
850       GtkAdjustment *vadjustment = gtk_viewport_get_vadjustment (viewport);
851       gint old_x, old_y;
852       gint new_x, new_y;
853
854       gdk_window_get_position (viewport->bin_window, &old_x, &old_y);
855       new_x = - hadjustment->value;
856       new_y = - vadjustment->value;
857
858       if (new_x != old_x || new_y != old_y)
859         {
860           gdk_window_move (viewport->bin_window, new_x, new_y);
861           gdk_window_process_updates (viewport->bin_window, TRUE);
862         }
863     }
864 }
865
866 static void
867 gtk_viewport_style_set (GtkWidget *widget,
868                         GtkStyle  *previous_style)
869 {
870    GtkViewport *viewport;
871    
872    if (GTK_WIDGET_REALIZED (widget) &&
873        !GTK_WIDGET_NO_WINDOW (widget))
874      {
875         viewport = GTK_VIEWPORT (widget);
876         
877         gtk_style_set_background (widget->style, viewport->bin_window, GTK_STATE_NORMAL);
878         gtk_style_set_background (widget->style, widget->window, widget->state);
879      }
880 }