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