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