]> Pileus Git - ~andy/gtk/blob - gtk/gtkviewport.c
Remove GtkObject completely
[~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 "gtktypeutils.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_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_destroy                  (GtkWidget        *widget);
88 static void gtk_viewport_realize                  (GtkWidget        *widget);
89 static void gtk_viewport_unrealize                (GtkWidget        *widget);
90 static gint gtk_viewport_draw                     (GtkWidget        *widget,
91                                                    cairo_t          *cr);
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_get_preferred_width      (GtkWidget        *widget,
102                                                    gint             *minimum_size,
103                                                    gint             *natural_size);
104 static void gtk_viewport_get_preferred_height     (GtkWidget        *widget,
105                                                    gint             *minimum_size,
106                                                    gint             *natural_size);
107
108
109 G_DEFINE_TYPE (GtkViewport, gtk_viewport, GTK_TYPE_BIN)
110
111 static void
112 gtk_viewport_class_init (GtkViewportClass *class)
113 {
114   GObjectClass   *gobject_class;
115   GtkWidgetClass *widget_class;
116   GtkContainerClass *container_class;
117
118   gobject_class = G_OBJECT_CLASS (class);
119   widget_class = (GtkWidgetClass*) class;
120   container_class = (GtkContainerClass*) class;
121
122   gobject_class->finalize = gtk_viewport_finalize;
123   gobject_class->set_property = gtk_viewport_set_property;
124   gobject_class->get_property = gtk_viewport_get_property;
125
126   widget_class->destroy = gtk_viewport_destroy;
127   widget_class->realize = gtk_viewport_realize;
128   widget_class->unrealize = gtk_viewport_unrealize;
129   widget_class->draw = gtk_viewport_draw;
130   widget_class->size_allocate = gtk_viewport_size_allocate;
131   widget_class->style_set = gtk_viewport_style_set;
132   widget_class->get_preferred_width = gtk_viewport_get_preferred_width;
133   widget_class->get_preferred_height = gtk_viewport_get_preferred_height;
134   
135   container_class->add = gtk_viewport_add;
136
137   class->set_scroll_adjustments = gtk_viewport_set_scroll_adjustments;
138
139   g_object_class_install_property (gobject_class,
140                                    PROP_HADJUSTMENT,
141                                    g_param_spec_object ("hadjustment",
142                                                         P_("Horizontal adjustment"),
143                                                         P_("The GtkAdjustment that determines the values of the horizontal position for this viewport"),
144                                                         GTK_TYPE_ADJUSTMENT,
145                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
146
147   g_object_class_install_property (gobject_class,
148                                    PROP_VADJUSTMENT,
149                                    g_param_spec_object ("vadjustment",
150                                                         P_("Vertical adjustment"),
151                                                         P_("The GtkAdjustment that determines the values of the vertical position for this viewport"),
152                                                         GTK_TYPE_ADJUSTMENT,
153                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
154
155   g_object_class_install_property (gobject_class,
156                                    PROP_SHADOW_TYPE,
157                                    g_param_spec_enum ("shadow-type",
158                                                       P_("Shadow type"),
159                                                       P_("Determines how the shadowed box around the viewport is drawn"),
160                                                       GTK_TYPE_SHADOW_TYPE,
161                                                       GTK_SHADOW_IN,
162                                                       GTK_PARAM_READWRITE));
163
164   /**
165    * GtkViewport::set-scroll-adjustments
166    * @horizontal: the horizontal #GtkAdjustment
167    * @vertical: the vertical #GtkAdjustment
168    *
169    * Set the scroll adjustments for the viewport. Usually scrolled containers
170    * like #GtkScrolledWindow will emit this signal to connect two instances
171    * of #GtkScrollbar to the scroll directions of the #GtkViewport.
172    */
173   widget_class->set_scroll_adjustments_signal =
174     g_signal_new (I_("set-scroll-adjustments"),
175                   G_OBJECT_CLASS_TYPE (gobject_class),
176                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
177                   G_STRUCT_OFFSET (GtkViewportClass, set_scroll_adjustments),
178                   NULL, NULL,
179                   _gtk_marshal_VOID__OBJECT_OBJECT,
180                   G_TYPE_NONE, 2,
181                   GTK_TYPE_ADJUSTMENT,
182                   GTK_TYPE_ADJUSTMENT);
183
184   g_type_class_add_private (class, sizeof (GtkViewportPrivate));
185 }
186
187 static void
188 gtk_viewport_set_property (GObject         *object,
189                            guint            prop_id,
190                            const GValue    *value,
191                            GParamSpec      *pspec)
192 {
193   GtkViewport *viewport;
194
195   viewport = GTK_VIEWPORT (object);
196
197   switch (prop_id)
198     {
199     case PROP_HADJUSTMENT:
200       gtk_viewport_set_hadjustment (viewport, g_value_get_object (value));
201       break;
202     case PROP_VADJUSTMENT:
203       gtk_viewport_set_vadjustment (viewport, g_value_get_object (value));
204       break;
205     case PROP_SHADOW_TYPE:
206       gtk_viewport_set_shadow_type (viewport, g_value_get_enum (value));
207       break;
208     default:
209       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
210       break;
211     }
212 }
213
214 static void
215 gtk_viewport_get_property (GObject         *object,
216                            guint            prop_id,
217                            GValue          *value,
218                            GParamSpec      *pspec)
219 {
220   GtkViewport *viewport = GTK_VIEWPORT (object);
221   GtkViewportPrivate *priv = viewport->priv;
222
223   switch (prop_id)
224     {
225     case PROP_HADJUSTMENT:
226       g_value_set_object (value, priv->hadjustment);
227       break;
228     case PROP_VADJUSTMENT:
229       g_value_set_object (value, priv->vadjustment);
230       break;
231     case PROP_SHADOW_TYPE:
232       g_value_set_enum (value, priv->shadow_type);
233       break;
234     default:
235       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
236       break;
237     }
238 }
239
240 static void
241 gtk_viewport_init (GtkViewport *viewport)
242 {
243   GtkViewportPrivate *priv;
244
245   viewport->priv = G_TYPE_INSTANCE_GET_PRIVATE (viewport,
246                                                 GTK_TYPE_VIEWPORT,
247                                                 GtkViewportPrivate);
248   priv = viewport->priv;
249
250   gtk_widget_set_has_window (GTK_WIDGET (viewport), TRUE);
251
252   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (viewport), FALSE);
253   gtk_container_set_resize_mode (GTK_CONTAINER (viewport), GTK_RESIZE_QUEUE);
254
255   priv->shadow_type = GTK_SHADOW_IN;
256   priv->view_window = NULL;
257   priv->bin_window = NULL;
258   priv->hadjustment = NULL;
259   priv->vadjustment = NULL;
260 }
261
262 /**
263  * gtk_viewport_new:
264  * @hadjustment: horizontal adjustment.
265  * @vadjustment: vertical adjustment.
266  * @returns: a new #GtkViewport.
267  *
268  * Creates a new #GtkViewport with the given adjustments.
269  *
270  **/
271 GtkWidget*
272 gtk_viewport_new (GtkAdjustment *hadjustment,
273                   GtkAdjustment *vadjustment)
274 {
275   GtkWidget *viewport;
276
277   viewport = g_object_new (GTK_TYPE_VIEWPORT,
278                              "hadjustment", hadjustment,
279                              "vadjustment", vadjustment,
280                              NULL);
281
282   return viewport;
283 }
284
285 #define ADJUSTMENT_POINTER(viewport, orientation)         \
286   (((orientation) == GTK_ORIENTATION_HORIZONTAL) ?        \
287      &(viewport)->priv->hadjustment : &(viewport)->priv->vadjustment)
288
289 static void
290 viewport_disconnect_adjustment (GtkViewport    *viewport,
291                                 GtkOrientation  orientation)
292 {
293   GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (viewport, orientation);
294
295   if (*adjustmentp)
296     {
297       g_signal_handlers_disconnect_by_func (*adjustmentp,
298                                             gtk_viewport_adjustment_value_changed,
299                                             viewport);
300       g_object_unref (*adjustmentp);
301       *adjustmentp = NULL;
302     }
303 }
304
305 static void
306 gtk_viewport_finalize (GObject *object)
307 {
308   GtkViewport *viewport = GTK_VIEWPORT (object);
309
310   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL);
311   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_VERTICAL);
312
313   G_OBJECT_CLASS (gtk_viewport_parent_class)->finalize (object);
314 }
315
316 static void
317 gtk_viewport_destroy (GtkWidget *widget)
318 {
319   GtkViewport *viewport = GTK_VIEWPORT (widget);
320
321   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL);
322   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_VERTICAL);
323
324   GTK_WIDGET_CLASS (gtk_viewport_parent_class)->destroy (widget);
325 }
326
327 /**
328  * gtk_viewport_get_hadjustment:
329  * @viewport: a #GtkViewport.
330  *
331  * Returns the horizontal adjustment of the viewport.
332  *
333  * Return value: (transfer none): the horizontal adjustment of @viewport.
334  **/
335 GtkAdjustment*
336 gtk_viewport_get_hadjustment (GtkViewport *viewport)
337 {
338   GtkViewportPrivate *priv;
339
340   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
341
342   priv = viewport->priv;
343
344   if (!priv->hadjustment)
345     gtk_viewport_set_hadjustment (viewport, NULL);
346
347   return priv->hadjustment;
348 }
349
350 /**
351  * gtk_viewport_get_vadjustment:
352  * @viewport: a #GtkViewport.
353  * 
354  * Returns the vertical adjustment of the viewport.
355  *
356  * Return value: (transfer none): the vertical adjustment of @viewport.
357  **/
358 GtkAdjustment*
359 gtk_viewport_get_vadjustment (GtkViewport *viewport)
360 {
361   GtkViewportPrivate *priv;
362
363   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
364
365   priv = viewport->priv;
366
367   if (!priv->vadjustment)
368     gtk_viewport_set_vadjustment (viewport, NULL);
369
370   return priv->vadjustment;
371 }
372
373 static void
374 viewport_get_view_allocation (GtkViewport   *viewport,
375                               GtkAllocation *view_allocation)
376 {
377   GtkViewportPrivate *priv = viewport->priv;
378   GtkStyle *style;
379   GtkWidget *widget = GTK_WIDGET (viewport);
380   GtkAllocation allocation;
381   guint border_width;
382
383   gtk_widget_get_allocation (widget, &allocation);
384   border_width = gtk_container_get_border_width (GTK_CONTAINER (viewport));
385
386   view_allocation->x = 0;
387   view_allocation->y = 0;
388
389   if (priv->shadow_type != GTK_SHADOW_NONE)
390     {
391       style = gtk_widget_get_style (widget);
392       view_allocation->x = style->xthickness;
393       view_allocation->y = style->ythickness;
394     }
395
396   view_allocation->width = MAX (1, allocation.width - view_allocation->x * 2 - border_width * 2);
397   view_allocation->height = MAX (1, allocation.height - view_allocation->y * 2 - border_width * 2);
398 }
399
400 static void
401 viewport_reclamp_adjustment (GtkAdjustment *adjustment,
402                              gboolean      *value_changed)
403 {
404   gdouble value = adjustment->value;
405   
406   value = CLAMP (value, 0, adjustment->upper - adjustment->page_size);
407   if (value != adjustment->value)
408     {
409       adjustment->value = value;
410       if (value_changed)
411         *value_changed = TRUE;
412     }
413   else if (value_changed)
414     *value_changed = FALSE;
415 }
416
417 static void
418 viewport_set_hadjustment_values (GtkViewport *viewport,
419                                  gboolean    *value_changed)
420 {
421   GtkBin *bin = GTK_BIN (viewport);
422   GtkAllocation view_allocation;
423   GtkAdjustment *hadjustment = gtk_viewport_get_hadjustment (viewport);
424   GtkWidget *child;
425   gdouble old_page_size;
426   gdouble old_upper;
427   gdouble old_value;
428   
429   viewport_get_view_allocation (viewport, &view_allocation);  
430
431   old_page_size = hadjustment->page_size;
432   old_upper = hadjustment->upper;
433   old_value = hadjustment->value;
434   hadjustment->page_size = view_allocation.width;
435   hadjustment->step_increment = view_allocation.width * 0.1;
436   hadjustment->page_increment = view_allocation.width * 0.9;
437   
438   hadjustment->lower = 0;
439
440   child = gtk_bin_get_child (bin);
441   if (child && gtk_widget_get_visible (child))
442     {
443       gint minimum_width;
444
445       gtk_widget_get_preferred_width_for_height (child,
446                                                  view_allocation.height,
447                                                  &minimum_width,
448                                                  NULL);
449       hadjustment->upper = MAX (minimum_width, view_allocation.width);
450     }
451   else
452     hadjustment->upper = view_allocation.width;
453
454   if (gtk_widget_get_direction (GTK_WIDGET (viewport)) == GTK_TEXT_DIR_RTL) 
455     {
456       gdouble dist = old_upper - (old_value + old_page_size);
457       hadjustment->value = hadjustment->upper - dist - hadjustment->page_size;
458       viewport_reclamp_adjustment (hadjustment, value_changed);
459       *value_changed = (old_value != hadjustment->value);
460     }
461   else
462     viewport_reclamp_adjustment (hadjustment, value_changed);
463 }
464
465 static void
466 viewport_set_vadjustment_values (GtkViewport *viewport,
467                                  gboolean    *value_changed)
468 {
469   GtkBin *bin = GTK_BIN (viewport);
470   GtkAllocation view_allocation;
471   GtkAdjustment *vadjustment = gtk_viewport_get_vadjustment (viewport);
472   GtkWidget *child;
473
474   viewport_get_view_allocation (viewport, &view_allocation);  
475
476   vadjustment->page_size = view_allocation.height;
477   vadjustment->step_increment = view_allocation.height * 0.1;
478   vadjustment->page_increment = view_allocation.height * 0.9;
479   
480   vadjustment->lower = 0;
481
482   child = gtk_bin_get_child (bin);
483   if (child && gtk_widget_get_visible (child))
484     {
485       gint minimum_height;
486
487       gtk_widget_get_preferred_height_for_width (child,
488                                                  view_allocation.width,
489                                                  &minimum_height,
490                                                  NULL);
491
492       vadjustment->upper = MAX (minimum_height, view_allocation.height);
493     }
494   else
495     vadjustment->upper = view_allocation.height;
496
497   viewport_reclamp_adjustment (vadjustment, value_changed);
498 }
499
500 static void
501 viewport_set_adjustment (GtkViewport    *viewport,
502                          GtkOrientation  orientation,
503                          GtkAdjustment  *adjustment)
504 {
505   GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (viewport, orientation);
506   gboolean value_changed;
507
508   if (adjustment && adjustment == *adjustmentp)
509     return;
510
511   if (!adjustment)
512     adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0,
513                                                      0.0, 0.0, 0.0));
514   viewport_disconnect_adjustment (viewport, orientation);
515   *adjustmentp = adjustment;
516   g_object_ref_sink (adjustment);
517
518   if (orientation == GTK_ORIENTATION_HORIZONTAL)
519     viewport_set_hadjustment_values (viewport, &value_changed);
520   else
521     viewport_set_vadjustment_values (viewport, &value_changed);
522
523   g_signal_connect (adjustment, "value-changed",
524                     G_CALLBACK (gtk_viewport_adjustment_value_changed),
525                     viewport);
526
527   gtk_adjustment_changed (adjustment);
528   
529   if (value_changed)
530     gtk_adjustment_value_changed (adjustment);
531   else
532     gtk_viewport_adjustment_value_changed (adjustment, viewport);
533 }
534
535 /**
536  * gtk_viewport_set_hadjustment:
537  * @viewport: a #GtkViewport.
538  * @adjustment: (allow-none): a #GtkAdjustment.
539  *
540  * Sets the horizontal adjustment of the viewport.
541  **/
542 void
543 gtk_viewport_set_hadjustment (GtkViewport   *viewport,
544                               GtkAdjustment *adjustment)
545 {
546   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
547   if (adjustment)
548     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
549
550   viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, adjustment);
551
552   g_object_notify (G_OBJECT (viewport), "hadjustment");
553 }
554
555 /**
556  * gtk_viewport_set_vadjustment:
557  * @viewport: a #GtkViewport.
558  * @adjustment: (allow-none): a #GtkAdjustment.
559  *
560  * Sets the vertical adjustment of the viewport.
561  **/
562 void
563 gtk_viewport_set_vadjustment (GtkViewport   *viewport,
564                               GtkAdjustment *adjustment)
565 {
566   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
567   if (adjustment)
568     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
569
570   viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, adjustment);
571
572   g_object_notify (G_OBJECT (viewport), "vadjustment");
573 }
574
575 static void
576 gtk_viewport_set_scroll_adjustments (GtkViewport      *viewport,
577                                      GtkAdjustment    *hadjustment,
578                                      GtkAdjustment    *vadjustment)
579 {
580   gtk_viewport_set_hadjustment (viewport, hadjustment);
581   gtk_viewport_set_vadjustment (viewport, vadjustment);
582 }
583
584 /** 
585  * gtk_viewport_set_shadow_type:
586  * @viewport: a #GtkViewport.
587  * @type: the new shadow type.
588  *
589  * Sets the shadow type of the viewport.
590  **/ 
591 void
592 gtk_viewport_set_shadow_type (GtkViewport   *viewport,
593                               GtkShadowType  type)
594 {
595   GtkViewportPrivate *priv;
596   GtkAllocation allocation;
597   GtkWidget *widget;
598
599   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
600
601   widget = GTK_WIDGET (viewport);
602   priv = viewport->priv;
603
604   if ((GtkShadowType) priv->shadow_type != type)
605     {
606       priv->shadow_type = type;
607
608       if (gtk_widget_get_visible (widget))
609         {
610           gtk_widget_get_allocation (widget, &allocation);
611           gtk_widget_size_allocate (widget, &allocation);
612           gtk_widget_set_allocation (widget, &allocation);
613           gtk_widget_queue_draw (widget);
614         }
615
616       g_object_notify (G_OBJECT (viewport), "shadow-type");
617     }
618 }
619
620 /**
621  * gtk_viewport_get_shadow_type:
622  * @viewport: a #GtkViewport
623  *
624  * Gets the shadow type of the #GtkViewport. See
625  * gtk_viewport_set_shadow_type().
626  *
627  * Return value: the shadow type 
628  **/
629 GtkShadowType
630 gtk_viewport_get_shadow_type (GtkViewport *viewport)
631 {
632   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), GTK_SHADOW_NONE);
633
634   return viewport->priv->shadow_type;
635 }
636
637 /**
638  * gtk_viewport_get_bin_window:
639  * @viewport: a #GtkViewport
640  *
641  * Gets the bin window of the #GtkViewport.
642  *
643  * Return value: (transfer none): a #GdkWindow
644  *
645  * Since: 2.20
646  **/
647 GdkWindow*
648 gtk_viewport_get_bin_window (GtkViewport *viewport)
649 {
650   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
651
652   return viewport->priv->bin_window;
653 }
654
655 /**
656  * gtk_viewport_get_view_window:
657  * @viewport: a #GtkViewport
658  *
659  * Gets the view window of the #GtkViewport.
660  *
661  * Return value: (transfer none): a #GdkWindow
662  *
663  * Since: 2.22
664  **/
665 GdkWindow*
666 gtk_viewport_get_view_window (GtkViewport *viewport)
667 {
668   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
669
670   return viewport->priv->view_window;
671 }
672
673 static void
674 gtk_viewport_realize (GtkWidget *widget)
675 {
676   GtkViewport *viewport = GTK_VIEWPORT (widget);
677   GtkViewportPrivate *priv = viewport->priv;
678   GtkBin *bin = GTK_BIN (widget);
679   GtkAdjustment *hadjustment = gtk_viewport_get_hadjustment (viewport);
680   GtkAdjustment *vadjustment = gtk_viewport_get_vadjustment (viewport);
681   GtkAllocation allocation;
682   GtkAllocation view_allocation;
683   GtkStyle *style;
684   GtkWidget *child;
685   GdkWindow *window;
686   GdkWindowAttr attributes;
687   gint attributes_mask;
688   gint event_mask;
689   guint border_width;
690
691   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
692
693   gtk_widget_set_realized (widget, TRUE);
694
695   gtk_widget_get_allocation (widget, &allocation);
696
697   attributes.x = allocation.x + border_width;
698   attributes.y = allocation.y + border_width;
699   attributes.width = allocation.width - border_width * 2;
700   attributes.height = allocation.height - border_width * 2;
701   attributes.window_type = GDK_WINDOW_CHILD;
702   attributes.wclass = GDK_INPUT_OUTPUT;
703   attributes.visual = gtk_widget_get_visual (widget);
704
705   event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
706   /* We select on button_press_mask so that button 4-5 scrolls are trapped.
707    */
708   attributes.event_mask = event_mask | GDK_BUTTON_PRESS_MASK;
709
710   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
711
712   window = gdk_window_new (gtk_widget_get_parent_window (widget),
713                            &attributes, attributes_mask);
714   gtk_widget_set_window (widget, window);
715   gdk_window_set_user_data (window, viewport);
716
717   viewport_get_view_allocation (viewport, &view_allocation);
718   
719   attributes.x = view_allocation.x;
720   attributes.y = view_allocation.y;
721   attributes.width = view_allocation.width;
722   attributes.height = view_allocation.height;
723   attributes.event_mask = 0;
724
725   priv->view_window = gdk_window_new (window,
726                                       &attributes, attributes_mask);
727   gdk_window_set_user_data (priv->view_window, viewport);
728
729   attributes.x = - hadjustment->value;
730   attributes.y = - vadjustment->value;
731   attributes.width = hadjustment->upper;
732   attributes.height = vadjustment->upper;
733   
734   attributes.event_mask = event_mask;
735
736   priv->bin_window = gdk_window_new (priv->view_window, &attributes, attributes_mask);
737   gdk_window_set_user_data (priv->bin_window, viewport);
738
739   child = gtk_bin_get_child (bin);
740   if (child)
741     gtk_widget_set_parent_window (child, priv->bin_window);
742
743   gtk_widget_style_attach (widget);
744   style = gtk_widget_get_style (widget);
745   gtk_style_set_background (style, window, GTK_STATE_NORMAL);
746   gtk_style_set_background (style, priv->bin_window, GTK_STATE_NORMAL);
747
748   gdk_window_show (priv->bin_window);
749   gdk_window_show (priv->view_window);
750 }
751
752 static void
753 gtk_viewport_unrealize (GtkWidget *widget)
754 {
755   GtkViewport *viewport = GTK_VIEWPORT (widget);
756   GtkViewportPrivate *priv = viewport->priv;
757
758   gdk_window_set_user_data (priv->view_window, NULL);
759   gdk_window_destroy (priv->view_window);
760   priv->view_window = NULL;
761
762   gdk_window_set_user_data (priv->bin_window, NULL);
763   gdk_window_destroy (priv->bin_window);
764   priv->bin_window = NULL;
765
766   GTK_WIDGET_CLASS (gtk_viewport_parent_class)->unrealize (widget);
767 }
768
769 static gint
770 gtk_viewport_draw (GtkWidget *widget,
771                    cairo_t   *cr)
772 {
773   GtkViewport *viewport = GTK_VIEWPORT (widget);
774   GtkViewportPrivate *priv = viewport->priv;
775   int x, y;
776
777   if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
778     {
779       gtk_paint_shadow (gtk_widget_get_style (widget),
780                         cr,
781                         GTK_STATE_NORMAL, priv->shadow_type,
782                         widget, "viewport",
783                         0, 0,
784                         gdk_window_get_width (gtk_widget_get_window (widget)),
785                         gdk_window_get_height (gtk_widget_get_window (widget)));
786     }
787   
788   if (gtk_cairo_should_draw_window (cr, priv->bin_window))
789     {
790       gdk_window_get_position (priv->bin_window, &x, &y);
791       gtk_paint_flat_box (gtk_widget_get_style (widget), cr,
792                           GTK_STATE_NORMAL, GTK_SHADOW_NONE,
793                           widget, "viewportbin",
794                           x, y,
795                           gdk_window_get_width (priv->bin_window),
796                           gdk_window_get_height (priv->bin_window));
797
798       GTK_WIDGET_CLASS (gtk_viewport_parent_class)->draw (widget, cr);
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_get_preferred_size (GtkWidget      *widget,
942                                  GtkOrientation  orientation,
943                                  gint           *minimum_size,
944                                  gint           *natural_size)
945 {
946   GtkViewport *viewport = GTK_VIEWPORT (widget);
947   GtkViewportPrivate *priv = viewport->priv;
948   GtkStyle  *style;
949   GtkWidget *child;
950   gint       child_min, child_nat;
951   gint       minimum, natural;
952
953   child = gtk_bin_get_child (GTK_BIN (widget));
954
955   /* XXX This should probably be (border_width * 2); but GTK+ has
956    * been doing this with a single border for a while now...
957    */
958   minimum = gtk_container_get_border_width (GTK_CONTAINER (widget));
959
960   if (priv->shadow_type != GTK_SHADOW_NONE)
961     {
962       style = gtk_widget_get_style (GTK_WIDGET (widget));
963       if (orientation == GTK_ORIENTATION_HORIZONTAL)
964           minimum += 2 * style->xthickness;
965       else
966           minimum += 2 * style->ythickness;
967     }
968
969   natural = minimum;
970
971   if (child && gtk_widget_get_visible (child))
972     {
973       if (orientation == GTK_ORIENTATION_HORIZONTAL)
974         gtk_widget_get_preferred_width (child, &child_min, &child_nat);
975       else
976         gtk_widget_get_preferred_height (child, &child_min, &child_nat);
977
978       minimum += child_min;
979       natural += child_nat;
980     }
981
982   if (minimum_size)
983     *minimum_size = minimum;
984
985   if (natural_size)
986     *natural_size = natural;
987 }
988
989 static void
990 gtk_viewport_get_preferred_width (GtkWidget *widget,
991                                   gint      *minimum_size,
992                                   gint      *natural_size)
993 {
994   gtk_viewport_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
995 }
996
997 static void
998 gtk_viewport_get_preferred_height (GtkWidget *widget,
999                                    gint      *minimum_size,
1000                                    gint      *natural_size)
1001 {
1002   gtk_viewport_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
1003 }