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