]> Pileus Git - ~andy/gtk/blob - gtk/gtklayout.c
Change FSF Address
[~andy/gtk] / gtk / gtklayout.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, see <http://www.gnu.org/licenses/>.
16  *
17  * GtkLayout: Widget for scrolling of arbitrary-sized areas.
18  *
19  * Copyright Owen Taylor, 1998
20  */
21
22 /*
23  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
24  * file for a list of people on the GTK+ Team.  See the ChangeLog
25  * files for a list of changes.  These files are distributed with
26  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
27  */
28
29 #include "config.h"
30
31 #include "gtklayout.h"
32
33 #include "gdk/gdk.h"
34
35 #include "gtkprivate.h"
36 #include "gtkintl.h"
37 #include "gtkmarshalers.h"
38 #include "gtkscrollable.h"
39
40
41 /**
42  * SECTION:gtklayout
43  * @Short_description: Infinite scrollable area containing child widgets
44  *   and/or custom drawing
45  * @Title: GtkLayout
46  * @See_also: #GtkDrawingArea, #GtkScrolledWindow
47  *
48  * #GtkLayout is similar to #GtkDrawingArea in that it's a "blank slate"
49  * and doesn't do anything but paint a blank background by default. It's
50  * different in that it supports scrolling natively (you can add it to a
51  * #GtkScrolledWindow), and it can contain child widgets, since it's a
52  * #GtkContainer. However if you're just going to draw, a #GtkDrawingArea
53  * is a better choice since it has lower overhead.
54  *
55  * When handling expose events on a #GtkLayout, you must draw to
56  * GTK_LAYOUT (layout)->bin_window, rather than to
57  * GTK_WIDGET (layout)->window, as you would for a drawing
58  * area.
59  */
60
61
62 typedef struct _GtkLayoutChild   GtkLayoutChild;
63
64 struct _GtkLayoutPrivate
65 {
66   /* Properties */
67   guint width;
68   guint height;
69
70   GtkAdjustment *hadjustment;
71   GtkAdjustment *vadjustment;
72
73   /* GtkScrollablePolicy needs to be checked when
74    * driving the scrollable adjustment values */
75   guint hscroll_policy : 1;
76   guint vscroll_policy : 1;
77
78   /* Properties */
79
80   GdkVisibilityState visibility;
81   GdkWindow *bin_window;
82
83   GList *children;
84
85   gint scroll_x;
86   gint scroll_y;
87
88   guint freeze_count;
89 };
90
91 struct _GtkLayoutChild {
92   GtkWidget *widget;
93   gint x;
94   gint y;
95 };
96
97 enum {
98    PROP_0,
99    PROP_HADJUSTMENT,
100    PROP_VADJUSTMENT,
101    PROP_HSCROLL_POLICY,
102    PROP_VSCROLL_POLICY,
103    PROP_WIDTH,
104    PROP_HEIGHT
105 };
106
107 enum {
108   CHILD_PROP_0,
109   CHILD_PROP_X,
110   CHILD_PROP_Y
111 };
112
113 static void gtk_layout_get_property       (GObject        *object,
114                                            guint           prop_id,
115                                            GValue         *value,
116                                            GParamSpec     *pspec);
117 static void gtk_layout_set_property       (GObject        *object,
118                                            guint           prop_id,
119                                            const GValue   *value,
120                                            GParamSpec     *pspec);
121 static void gtk_layout_finalize           (GObject        *object);
122 static void gtk_layout_realize            (GtkWidget      *widget);
123 static void gtk_layout_unrealize          (GtkWidget      *widget);
124 static void gtk_layout_map                (GtkWidget      *widget);
125 static void gtk_layout_get_preferred_width  (GtkWidget     *widget,
126                                              gint          *minimum,
127                                              gint          *natural);
128 static void gtk_layout_get_preferred_height (GtkWidget     *widget,
129                                              gint          *minimum,
130                                              gint          *natural);
131 static void gtk_layout_size_allocate      (GtkWidget      *widget,
132                                            GtkAllocation  *allocation);
133 static gint gtk_layout_draw               (GtkWidget      *widget,
134                                            cairo_t        *cr);
135 static void gtk_layout_add                (GtkContainer   *container,
136                                            GtkWidget      *widget);
137 static void gtk_layout_remove             (GtkContainer   *container,
138                                            GtkWidget      *widget);
139 static void gtk_layout_forall             (GtkContainer   *container,
140                                            gboolean        include_internals,
141                                            GtkCallback     callback,
142                                            gpointer        callback_data);
143 static void gtk_layout_set_child_property (GtkContainer   *container,
144                                            GtkWidget      *child,
145                                            guint           property_id,
146                                            const GValue   *value,
147                                            GParamSpec     *pspec);
148 static void gtk_layout_get_child_property (GtkContainer   *container,
149                                            GtkWidget      *child,
150                                            guint           property_id,
151                                            GValue         *value,
152                                            GParamSpec     *pspec);
153 static void gtk_layout_allocate_child     (GtkLayout      *layout,
154                                            GtkLayoutChild *child);
155 static void gtk_layout_adjustment_changed (GtkAdjustment  *adjustment,
156                                            GtkLayout      *layout);
157 static void gtk_layout_style_updated      (GtkWidget      *widget);
158
159 static void gtk_layout_set_hadjustment_values (GtkLayout      *layout);
160 static void gtk_layout_set_vadjustment_values (GtkLayout      *layout);
161
162 G_DEFINE_TYPE_WITH_CODE (GtkLayout, gtk_layout, GTK_TYPE_CONTAINER,
163                          G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
164
165 /* Public interface
166  */
167 /**
168  * gtk_layout_new:
169  * @hadjustment: (allow-none): horizontal scroll adjustment, or %NULL
170  * @vadjustment: (allow-none): vertical scroll adjustment, or %NULL
171  * 
172  * Creates a new #GtkLayout. Unless you have a specific adjustment
173  * you'd like the layout to use for scrolling, pass %NULL for
174  * @hadjustment and @vadjustment.
175  * 
176  * Return value: a new #GtkLayout
177  **/
178   
179 GtkWidget*    
180 gtk_layout_new (GtkAdjustment *hadjustment,
181                 GtkAdjustment *vadjustment)
182 {
183   GtkLayout *layout;
184
185   layout = g_object_new (GTK_TYPE_LAYOUT,
186                          "hadjustment", hadjustment,
187                          "vadjustment", vadjustment,
188                          NULL);
189
190   return GTK_WIDGET (layout);
191 }
192
193 /**
194  * gtk_layout_get_bin_window:
195  * @layout: a #GtkLayout
196  *
197  * Retrieve the bin window of the layout used for drawing operations.
198  *
199  * Return value: (transfer none): a #GdkWindow
200  *
201  * Since: 2.14
202  **/
203 GdkWindow*
204 gtk_layout_get_bin_window (GtkLayout *layout)
205 {
206   g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL);
207
208   return layout->priv->bin_window;
209 }
210
211 /**
212  * gtk_layout_get_hadjustment:
213  * @layout: a #GtkLayout
214  *
215  * This function should only be called after the layout has been
216  * placed in a #GtkScrolledWindow or otherwise configured for
217  * scrolling. It returns the #GtkAdjustment used for communication
218  * between the horizontal scrollbar and @layout.
219  *
220  * See #GtkScrolledWindow, #GtkScrollbar, #GtkAdjustment for details.
221  *
222  * Return value: (transfer none): horizontal scroll adjustment
223  *
224  * Deprecated: 3.0: Use gtk_scrollable_get_hadjustment()
225  **/
226 GtkAdjustment*
227 gtk_layout_get_hadjustment (GtkLayout *layout)
228 {
229   g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL);
230
231   return layout->priv->hadjustment;
232 }
233 /**
234  * gtk_layout_get_vadjustment:
235  * @layout: a #GtkLayout
236  *
237  * This function should only be called after the layout has been
238  * placed in a #GtkScrolledWindow or otherwise configured for
239  * scrolling. It returns the #GtkAdjustment used for communication
240  * between the vertical scrollbar and @layout.
241  *
242  * See #GtkScrolledWindow, #GtkScrollbar, #GtkAdjustment for details.
243  *
244  * Return value: (transfer none): vertical scroll adjustment
245  *
246  * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
247  **/
248 GtkAdjustment*
249 gtk_layout_get_vadjustment (GtkLayout *layout)
250 {
251   g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL);
252
253   return layout->priv->vadjustment;
254 }
255
256 static void
257 gtk_layout_set_hadjustment_values (GtkLayout *layout)
258 {
259   GtkLayoutPrivate *priv = layout->priv;
260   GtkAllocation  allocation;
261   GtkAdjustment *adj = priv->hadjustment;
262   gdouble old_value;
263   gdouble new_value;
264   gdouble new_upper;
265
266   gtk_widget_get_allocation (GTK_WIDGET (layout), &allocation);
267
268   old_value = gtk_adjustment_get_value (adj);
269   new_upper = MAX (allocation.width, priv->width);
270
271   g_object_set (adj,
272                 "lower", 0.0,
273                 "upper", new_upper,
274                 "page-size", (gdouble)allocation.width,
275                 "step-increment", allocation.width * 0.1,
276                 "page-increment", allocation.width * 0.9,
277                 NULL);
278
279   new_value = CLAMP (old_value, 0, new_upper - allocation.width);
280   if (new_value != old_value)
281     gtk_adjustment_set_value (adj, new_value);
282 }
283
284 static void
285 gtk_layout_set_vadjustment_values (GtkLayout *layout)
286 {
287   GtkAllocation  allocation;
288   GtkAdjustment *adj = layout->priv->vadjustment;
289   gdouble old_value;
290   gdouble new_value;
291   gdouble new_upper;
292
293   gtk_widget_get_allocation (GTK_WIDGET (layout), &allocation);
294
295   old_value = gtk_adjustment_get_value (adj);
296   new_upper = MAX (allocation.height, layout->priv->height);
297
298   g_object_set (adj,
299                 "lower", 0.0,
300                 "upper", new_upper,
301                 "page-size", (gdouble)allocation.height,
302                 "step-increment", allocation.height * 0.1,
303                 "page-increment", allocation.height * 0.9,
304                 NULL);
305
306   new_value = CLAMP (old_value, 0, new_upper - allocation.height);
307   if (new_value != old_value)
308     gtk_adjustment_set_value (adj, new_value);
309 }
310
311 static void
312 gtk_layout_finalize (GObject *object)
313 {
314   GtkLayout *layout = GTK_LAYOUT (object);
315   GtkLayoutPrivate *priv = layout->priv;
316
317   g_object_unref (priv->hadjustment);
318   g_object_unref (priv->vadjustment);
319
320   G_OBJECT_CLASS (gtk_layout_parent_class)->finalize (object);
321 }
322
323 static void
324 gtk_layout_do_set_hadjustment (GtkLayout     *layout,
325                                GtkAdjustment *adjustment)
326 {
327   GtkLayoutPrivate *priv;
328
329   priv = layout->priv;
330
331   if (adjustment && priv->hadjustment == adjustment)
332         return;
333
334   if (priv->hadjustment != NULL)
335     {
336       g_signal_handlers_disconnect_by_func (priv->hadjustment,
337                                             gtk_layout_adjustment_changed,
338                                             layout);
339       g_object_unref (priv->hadjustment);
340     }
341
342   if (adjustment == NULL)
343     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
344
345   g_signal_connect (adjustment, "value-changed",
346                     G_CALLBACK (gtk_layout_adjustment_changed), layout);
347   priv->hadjustment = g_object_ref_sink (adjustment);
348   gtk_layout_set_hadjustment_values (layout);
349
350   g_object_notify (G_OBJECT (layout), "hadjustment");
351 }
352
353 /**
354  * gtk_layout_set_hadjustment:
355  * @layout: a #GtkLayout
356  * @adjustment: (allow-none): new scroll adjustment
357  *
358  * Sets the horizontal scroll adjustment for the layout.
359  *
360  * See #GtkScrolledWindow, #GtkScrollbar, #GtkAdjustment for details.
361  *
362  * Deprecated: 3.0: Use gtk_scrollable_set_hadjustment()
363  **/
364 void
365 gtk_layout_set_hadjustment (GtkLayout     *layout,
366                             GtkAdjustment *adjustment)
367 {
368   g_return_if_fail (GTK_IS_LAYOUT (layout));
369   g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
370
371   gtk_layout_do_set_hadjustment (layout, adjustment);
372 }
373
374 static void
375 gtk_layout_do_set_vadjustment (GtkLayout     *layout,
376                                GtkAdjustment *adjustment)
377 {
378   GtkLayoutPrivate *priv;
379
380   priv = layout->priv;
381
382   if (adjustment && priv->vadjustment == adjustment)
383         return;
384
385   if (priv->vadjustment != NULL)
386     {
387       g_signal_handlers_disconnect_by_func (priv->vadjustment,
388                                             gtk_layout_adjustment_changed,
389                                             layout);
390       g_object_unref (priv->vadjustment);
391     }
392
393   if (adjustment == NULL)
394     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
395
396   g_signal_connect (adjustment, "value-changed",
397                     G_CALLBACK (gtk_layout_adjustment_changed), layout);
398   priv->vadjustment = g_object_ref_sink (adjustment);
399   gtk_layout_set_vadjustment_values (layout);
400
401   g_object_notify (G_OBJECT (layout), "vadjustment");
402 }
403
404 /**
405  * gtk_layout_set_vadjustment:
406  * @layout: a #GtkLayout
407  * @adjustment: (allow-none): new scroll adjustment
408  *
409  * Sets the vertical scroll adjustment for the layout.
410  *
411  * See #GtkScrolledWindow, #GtkScrollbar, #GtkAdjustment for details.
412  *
413  * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
414  **/
415 void
416 gtk_layout_set_vadjustment (GtkLayout     *layout,
417                             GtkAdjustment *adjustment)
418 {
419   g_return_if_fail (GTK_IS_LAYOUT (layout));
420   g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
421
422   gtk_layout_do_set_vadjustment (layout, adjustment);
423 }
424
425 static GtkLayoutChild*
426 get_child (GtkLayout  *layout,
427            GtkWidget  *widget)
428 {
429   GtkLayoutPrivate *priv = layout->priv;
430   GList *children;
431
432   children = priv->children;
433   while (children)
434     {
435       GtkLayoutChild *child;
436       
437       child = children->data;
438       children = children->next;
439
440       if (child->widget == widget)
441         return child;
442     }
443
444   return NULL;
445 }
446
447 /**
448  * gtk_layout_put:
449  * @layout: a #GtkLayout
450  * @child_widget: child widget
451  * @x: X position of child widget
452  * @y: Y position of child widget
453  *
454  * Adds @child_widget to @layout, at position (@x,@y).
455  * @layout becomes the new parent container of @child_widget.
456  * 
457  **/
458 void           
459 gtk_layout_put (GtkLayout     *layout, 
460                 GtkWidget     *child_widget, 
461                 gint           x, 
462                 gint           y)
463 {
464   GtkLayoutPrivate *priv;
465   GtkLayoutChild *child;
466
467   g_return_if_fail (GTK_IS_LAYOUT (layout));
468   g_return_if_fail (GTK_IS_WIDGET (child_widget));
469
470   priv = layout->priv;
471
472   child = g_new (GtkLayoutChild, 1);
473
474   child->widget = child_widget;
475   child->x = x;
476   child->y = y;
477
478   priv->children = g_list_append (priv->children, child);
479
480   if (gtk_widget_get_realized (GTK_WIDGET (layout)))
481     gtk_widget_set_parent_window (child->widget, priv->bin_window);
482
483   gtk_widget_set_parent (child_widget, GTK_WIDGET (layout));
484 }
485
486 static void
487 gtk_layout_move_internal (GtkLayout       *layout,
488                           GtkWidget       *widget,
489                           gboolean         change_x,
490                           gint             x,
491                           gboolean         change_y,
492                           gint             y)
493 {
494   GtkLayoutChild *child;
495
496   child = get_child (layout, widget);
497
498   g_assert (child);
499
500   gtk_widget_freeze_child_notify (widget);
501   
502   if (change_x)
503     {
504       child->x = x;
505       gtk_widget_child_notify (widget, "x");
506     }
507
508   if (change_y)
509     {
510       child->y = y;
511       gtk_widget_child_notify (widget, "y");
512     }
513
514   gtk_widget_thaw_child_notify (widget);
515   
516   if (gtk_widget_get_visible (widget) &&
517       gtk_widget_get_visible (GTK_WIDGET (layout)))
518     gtk_widget_queue_resize (widget);
519 }
520
521 /**
522  * gtk_layout_move:
523  * @layout: a #GtkLayout
524  * @child_widget: a current child of @layout
525  * @x: X position to move to
526  * @y: Y position to move to
527  *
528  * Moves a current child of @layout to a new position.
529  * 
530  **/
531 void           
532 gtk_layout_move (GtkLayout     *layout, 
533                  GtkWidget     *child_widget, 
534                  gint           x, 
535                  gint           y)
536 {
537   g_return_if_fail (GTK_IS_LAYOUT (layout));
538   g_return_if_fail (GTK_IS_WIDGET (child_widget));
539   g_return_if_fail (gtk_widget_get_parent (child_widget) == GTK_WIDGET (layout));
540
541   gtk_layout_move_internal (layout, child_widget, TRUE, x, TRUE, y);
542 }
543
544 /**
545  * gtk_layout_set_size:
546  * @layout: a #GtkLayout
547  * @width: width of entire scrollable area
548  * @height: height of entire scrollable area
549  *
550  * Sets the size of the scrollable area of the layout.
551  * 
552  **/
553 void
554 gtk_layout_set_size (GtkLayout     *layout, 
555                      guint          width,
556                      guint          height)
557 {
558   GtkLayoutPrivate *priv;
559   GtkWidget *widget;
560
561   g_return_if_fail (GTK_IS_LAYOUT (layout));
562
563   priv = layout->priv;
564   widget = GTK_WIDGET (layout);
565
566   g_object_freeze_notify (G_OBJECT (layout));
567   if (width != priv->width)
568      {
569         priv->width = width;
570         g_object_notify (G_OBJECT (layout), "width");
571      }
572   if (height != priv->height)
573      {
574         priv->height = height;
575         g_object_notify (G_OBJECT (layout), "height");
576      }
577   g_object_thaw_notify (G_OBJECT (layout));
578
579   if (gtk_widget_get_realized (widget))
580     {
581       GtkAllocation allocation;
582
583       gtk_widget_get_allocation (widget, &allocation);
584       width = MAX (width, allocation.width);
585       height = MAX (height, allocation.height);
586       gdk_window_resize (priv->bin_window, width, height);
587     }
588
589   gtk_layout_set_hadjustment_values (layout);
590   gtk_layout_set_vadjustment_values (layout);
591 }
592
593 /**
594  * gtk_layout_get_size:
595  * @layout: a #GtkLayout
596  * @width: (out) (allow-none): location to store the width set on
597  *     @layout, or %NULL
598  * @height: (out) (allow-none): location to store the height set on
599  *     @layout, or %NULL
600  *
601  * Gets the size that has been set on the layout, and that determines
602  * the total extents of the layout's scrollbar area. See
603  * gtk_layout_set_size ().
604  **/
605 void
606 gtk_layout_get_size (GtkLayout *layout,
607                      guint     *width,
608                      guint     *height)
609 {
610   GtkLayoutPrivate *priv;
611
612   g_return_if_fail (GTK_IS_LAYOUT (layout));
613
614   priv = layout->priv;
615
616   if (width)
617     *width = priv->width;
618   if (height)
619     *height = priv->height;
620 }
621
622 /* Basic Object handling procedures
623  */
624 static void
625 gtk_layout_class_init (GtkLayoutClass *class)
626 {
627   GObjectClass *gobject_class;
628   GtkWidgetClass *widget_class;
629   GtkContainerClass *container_class;
630
631   gobject_class = (GObjectClass*) class;
632   widget_class = (GtkWidgetClass*) class;
633   container_class = (GtkContainerClass*) class;
634
635   gobject_class->set_property = gtk_layout_set_property;
636   gobject_class->get_property = gtk_layout_get_property;
637   gobject_class->finalize = gtk_layout_finalize;
638
639   container_class->set_child_property = gtk_layout_set_child_property;
640   container_class->get_child_property = gtk_layout_get_child_property;
641
642   gtk_container_class_install_child_property (container_class,
643                                               CHILD_PROP_X,
644                                               g_param_spec_int ("x",
645                                                                 P_("X position"),
646                                                                 P_("X position of child widget"),
647                                                                 G_MININT,
648                                                                 G_MAXINT,
649                                                                 0,
650                                                                 GTK_PARAM_READWRITE));
651
652   gtk_container_class_install_child_property (container_class,
653                                               CHILD_PROP_Y,
654                                               g_param_spec_int ("y",
655                                                                 P_("Y position"),
656                                                                 P_("Y position of child widget"),
657                                                                 G_MININT,
658                                                                 G_MAXINT,
659                                                                 0,
660                                                                 GTK_PARAM_READWRITE));
661   
662   /* Scrollable interface */
663   g_object_class_override_property (gobject_class, PROP_HADJUSTMENT,    "hadjustment");
664   g_object_class_override_property (gobject_class, PROP_VADJUSTMENT,    "vadjustment");
665   g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
666   g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
667
668   g_object_class_install_property (gobject_class,
669                                    PROP_WIDTH,
670                                    g_param_spec_uint ("width",
671                                                      P_("Width"),
672                                                      P_("The width of the layout"),
673                                                      0,
674                                                      G_MAXINT,
675                                                      100,
676                                                      GTK_PARAM_READWRITE));
677   g_object_class_install_property (gobject_class,
678                                    PROP_HEIGHT,
679                                    g_param_spec_uint ("height",
680                                                      P_("Height"),
681                                                      P_("The height of the layout"),
682                                                      0,
683                                                      G_MAXINT,
684                                                      100,
685                                                      GTK_PARAM_READWRITE));
686   widget_class->realize = gtk_layout_realize;
687   widget_class->unrealize = gtk_layout_unrealize;
688   widget_class->map = gtk_layout_map;
689   widget_class->get_preferred_width = gtk_layout_get_preferred_width;
690   widget_class->get_preferred_height = gtk_layout_get_preferred_height;
691   widget_class->size_allocate = gtk_layout_size_allocate;
692   widget_class->draw = gtk_layout_draw;
693   widget_class->style_updated = gtk_layout_style_updated;
694
695   container_class->add = gtk_layout_add;
696   container_class->remove = gtk_layout_remove;
697   container_class->forall = gtk_layout_forall;
698
699   g_type_class_add_private (class, sizeof (GtkLayoutPrivate));
700 }
701
702 static void
703 gtk_layout_get_property (GObject     *object,
704                          guint        prop_id,
705                          GValue      *value,
706                          GParamSpec  *pspec)
707 {
708   GtkLayout *layout = GTK_LAYOUT (object);
709   GtkLayoutPrivate *priv = layout->priv;
710
711   switch (prop_id)
712     {
713     case PROP_HADJUSTMENT:
714       g_value_set_object (value, priv->hadjustment);
715       break;
716     case PROP_VADJUSTMENT:
717       g_value_set_object (value, priv->vadjustment);
718       break;
719     case PROP_HSCROLL_POLICY:
720       g_value_set_enum (value, priv->hscroll_policy);
721       break;
722     case PROP_VSCROLL_POLICY:
723       g_value_set_enum (value, priv->vscroll_policy);
724       break;
725     case PROP_WIDTH:
726       g_value_set_uint (value, priv->width);
727       break;
728     case PROP_HEIGHT:
729       g_value_set_uint (value, priv->height);
730       break;
731     default:
732       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
733       break;
734     }
735 }
736
737 static void
738 gtk_layout_set_property (GObject      *object,
739                          guint         prop_id,
740                          const GValue *value,
741                          GParamSpec   *pspec)
742 {
743   GtkLayout *layout = GTK_LAYOUT (object);
744   GtkLayoutPrivate *priv = layout->priv;
745
746   switch (prop_id)
747     {
748     case PROP_HADJUSTMENT:
749       gtk_layout_do_set_hadjustment (layout, g_value_get_object (value));
750       break;
751     case PROP_VADJUSTMENT:
752       gtk_layout_do_set_vadjustment (layout, g_value_get_object (value));
753       break;
754     case PROP_HSCROLL_POLICY:
755       priv->hscroll_policy = g_value_get_enum (value);
756       gtk_widget_queue_resize (GTK_WIDGET (layout));
757       break;
758     case PROP_VSCROLL_POLICY:
759       priv->vscroll_policy = g_value_get_enum (value);
760       gtk_widget_queue_resize (GTK_WIDGET (layout));
761       break;
762     case PROP_WIDTH:
763       gtk_layout_set_size (layout, g_value_get_uint (value),
764                            priv->height);
765       break;
766     case PROP_HEIGHT:
767       gtk_layout_set_size (layout, priv->width,
768                            g_value_get_uint (value));
769       break;
770     default:
771       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
772       break;
773     }
774 }
775
776 static void
777 gtk_layout_set_child_property (GtkContainer    *container,
778                                GtkWidget       *child,
779                                guint            property_id,
780                                const GValue    *value,
781                                GParamSpec      *pspec)
782 {
783   switch (property_id)
784     {
785     case CHILD_PROP_X:
786       gtk_layout_move_internal (GTK_LAYOUT (container),
787                                 child,
788                                 TRUE, g_value_get_int (value),
789                                 FALSE, 0);
790       break;
791     case CHILD_PROP_Y:
792       gtk_layout_move_internal (GTK_LAYOUT (container),
793                                 child,
794                                 FALSE, 0,
795                                 TRUE, g_value_get_int (value));
796       break;
797     default:
798       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
799       break;
800     }
801 }
802
803 static void
804 gtk_layout_get_child_property (GtkContainer *container,
805                                GtkWidget    *child,
806                                guint         property_id,
807                                GValue       *value,
808                                GParamSpec   *pspec)
809 {
810   GtkLayoutChild *layout_child;
811
812   layout_child = get_child (GTK_LAYOUT (container), child);
813   
814   switch (property_id)
815     {
816     case CHILD_PROP_X:
817       g_value_set_int (value, layout_child->x);
818       break;
819     case CHILD_PROP_Y:
820       g_value_set_int (value, layout_child->y);
821       break;
822     default:
823       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
824       break;
825     }
826 }
827
828 static void
829 gtk_layout_init (GtkLayout *layout)
830 {
831   GtkLayoutPrivate *priv;
832
833   layout->priv = G_TYPE_INSTANCE_GET_PRIVATE (layout,
834                                               GTK_TYPE_LAYOUT,
835                                               GtkLayoutPrivate);
836   priv = layout->priv;
837
838   priv->children = NULL;
839
840   priv->width = 100;
841   priv->height = 100;
842
843   priv->hadjustment = NULL;
844   priv->vadjustment = NULL;
845
846   priv->bin_window = NULL;
847
848   priv->scroll_x = 0;
849   priv->scroll_y = 0;
850   priv->visibility = GDK_VISIBILITY_PARTIAL;
851
852   priv->freeze_count = 0;
853 }
854
855 /* Widget methods
856  */
857
858 static void 
859 gtk_layout_realize (GtkWidget *widget)
860 {
861   GtkLayout *layout = GTK_LAYOUT (widget);
862   GtkLayoutPrivate *priv = layout->priv;
863   GtkAllocation allocation;
864   GdkWindow *window;
865   GdkWindowAttr attributes;
866   GList *tmp_list;
867   gint attributes_mask;
868
869   gtk_widget_set_realized (widget, TRUE);
870
871   gtk_widget_get_allocation (widget, &allocation);
872
873   attributes.window_type = GDK_WINDOW_CHILD;
874   attributes.x = allocation.x;
875   attributes.y = allocation.y;
876   attributes.width = allocation.width;
877   attributes.height = allocation.height;
878   attributes.wclass = GDK_INPUT_OUTPUT;
879   attributes.visual = gtk_widget_get_visual (widget);
880   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
881
882   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
883
884   window = gdk_window_new (gtk_widget_get_parent_window (widget),
885                            &attributes, attributes_mask);
886   gtk_widget_set_window (widget, window);
887   gdk_window_set_user_data (window, widget);
888
889   gtk_widget_get_allocation (widget, &allocation);
890
891   attributes.x = - gtk_adjustment_get_value (priv->hadjustment),
892   attributes.y = - gtk_adjustment_get_value (priv->vadjustment);
893   attributes.width = MAX (priv->width, allocation.width);
894   attributes.height = MAX (priv->height, allocation.height);
895   attributes.event_mask = GDK_EXPOSURE_MASK | GDK_SCROLL_MASK | 
896                           gtk_widget_get_events (widget);
897
898   priv->bin_window = gdk_window_new (window,
899                                      &attributes, attributes_mask);
900   gdk_window_set_user_data (priv->bin_window, widget);
901   gtk_style_context_set_background (gtk_widget_get_style_context (widget), priv->bin_window);
902
903   tmp_list = priv->children;
904   while (tmp_list)
905     {
906       GtkLayoutChild *child = tmp_list->data;
907       tmp_list = tmp_list->next;
908
909       gtk_widget_set_parent_window (child->widget, priv->bin_window);
910     }
911 }
912
913 static void
914 gtk_layout_style_updated (GtkWidget *widget)
915 {
916   GtkLayoutPrivate *priv;
917
918   GTK_WIDGET_CLASS (gtk_layout_parent_class)->style_updated (widget);
919
920   if (gtk_widget_get_realized (widget))
921     {
922       priv = GTK_LAYOUT (widget)->priv;
923       gtk_style_context_set_background (gtk_widget_get_style_context (widget), priv->bin_window);
924     }
925 }
926
927 static void
928 gtk_layout_map (GtkWidget *widget)
929 {
930   GtkLayout *layout = GTK_LAYOUT (widget);
931   GtkLayoutPrivate *priv = layout->priv;
932   GList *tmp_list;
933
934   gtk_widget_set_mapped (widget, TRUE);
935
936   tmp_list = priv->children;
937   while (tmp_list)
938     {
939       GtkLayoutChild *child = tmp_list->data;
940       tmp_list = tmp_list->next;
941
942       if (gtk_widget_get_visible (child->widget))
943         {
944           if (!gtk_widget_get_mapped (child->widget))
945             gtk_widget_map (child->widget);
946         }
947     }
948
949   gdk_window_show (priv->bin_window);
950   gdk_window_show (gtk_widget_get_window (widget));
951 }
952
953 static void 
954 gtk_layout_unrealize (GtkWidget *widget)
955 {
956   GtkLayout *layout = GTK_LAYOUT (widget);
957   GtkLayoutPrivate *priv = layout->priv;
958
959   gdk_window_set_user_data (priv->bin_window, NULL);
960   gdk_window_destroy (priv->bin_window);
961   priv->bin_window = NULL;
962
963   GTK_WIDGET_CLASS (gtk_layout_parent_class)->unrealize (widget);
964 }
965
966 static void
967 gtk_layout_get_preferred_width (GtkWidget *widget,
968                                 gint      *minimum,
969                                 gint      *natural)
970 {
971   *minimum = *natural = 0;
972 }
973
974 static void
975 gtk_layout_get_preferred_height (GtkWidget *widget,
976                                  gint      *minimum,
977                                  gint      *natural)
978 {
979   *minimum = *natural = 0;
980 }
981
982 static void
983 gtk_layout_size_allocate (GtkWidget     *widget,
984                           GtkAllocation *allocation)
985 {
986   GtkLayout *layout = GTK_LAYOUT (widget);
987   GtkLayoutPrivate *priv = layout->priv;
988   GList *tmp_list;
989
990   gtk_widget_set_allocation (widget, allocation);
991
992   tmp_list = priv->children;
993
994   while (tmp_list)
995     {
996       GtkLayoutChild *child = tmp_list->data;
997       tmp_list = tmp_list->next;
998
999       gtk_layout_allocate_child (layout, child);
1000     }
1001
1002   if (gtk_widget_get_realized (widget))
1003     {
1004       gdk_window_move_resize (gtk_widget_get_window (widget),
1005                               allocation->x, allocation->y,
1006                               allocation->width, allocation->height);
1007
1008       gdk_window_resize (priv->bin_window,
1009                          MAX (priv->width, allocation->width),
1010                          MAX (priv->height, allocation->height));
1011     }
1012
1013   gtk_layout_set_hadjustment_values (layout);
1014   gtk_layout_set_vadjustment_values (layout);
1015 }
1016
1017 static gboolean
1018 gtk_layout_draw (GtkWidget *widget,
1019                  cairo_t   *cr)
1020 {
1021   GtkLayout *layout = GTK_LAYOUT (widget);
1022   GtkLayoutPrivate *priv = layout->priv;
1023
1024   if (gtk_cairo_should_draw_window (cr, priv->bin_window))
1025     GTK_WIDGET_CLASS (gtk_layout_parent_class)->draw (widget, cr);
1026
1027   return FALSE;
1028 }
1029
1030 /* Container methods
1031  */
1032 static void
1033 gtk_layout_add (GtkContainer *container,
1034                 GtkWidget    *widget)
1035 {
1036   gtk_layout_put (GTK_LAYOUT (container), widget, 0, 0);
1037 }
1038
1039 static void
1040 gtk_layout_remove (GtkContainer *container, 
1041                    GtkWidget    *widget)
1042 {
1043   GtkLayout *layout = GTK_LAYOUT (container);
1044   GtkLayoutPrivate *priv = layout->priv;
1045   GList *tmp_list;
1046   GtkLayoutChild *child = NULL;
1047
1048   tmp_list = priv->children;
1049   while (tmp_list)
1050     {
1051       child = tmp_list->data;
1052       if (child->widget == widget)
1053         break;
1054       tmp_list = tmp_list->next;
1055     }
1056
1057   if (tmp_list)
1058     {
1059       gtk_widget_unparent (widget);
1060
1061       priv->children = g_list_remove_link (priv->children, tmp_list);
1062       g_list_free_1 (tmp_list);
1063       g_free (child);
1064     }
1065 }
1066
1067 static void
1068 gtk_layout_forall (GtkContainer *container,
1069                    gboolean      include_internals,
1070                    GtkCallback   callback,
1071                    gpointer      callback_data)
1072 {
1073   GtkLayout *layout = GTK_LAYOUT (container);
1074   GtkLayoutPrivate *priv = layout->priv;
1075   GtkLayoutChild *child;
1076   GList *tmp_list;
1077
1078   tmp_list = priv->children;
1079   while (tmp_list)
1080     {
1081       child = tmp_list->data;
1082       tmp_list = tmp_list->next;
1083
1084       (* callback) (child->widget, callback_data);
1085     }
1086 }
1087
1088 /* Operations on children
1089  */
1090
1091 static void
1092 gtk_layout_allocate_child (GtkLayout      *layout,
1093                            GtkLayoutChild *child)
1094 {
1095   GtkAllocation allocation;
1096   GtkRequisition requisition;
1097
1098   allocation.x = child->x;
1099   allocation.y = child->y;
1100
1101   gtk_widget_get_preferred_size (child->widget, &requisition, NULL);
1102   allocation.width = requisition.width;
1103   allocation.height = requisition.height;
1104   
1105   gtk_widget_size_allocate (child->widget, &allocation);
1106 }
1107
1108 /* Callbacks */
1109
1110 static void
1111 gtk_layout_adjustment_changed (GtkAdjustment *adjustment,
1112                                GtkLayout     *layout)
1113 {
1114   GtkLayoutPrivate *priv = layout->priv;
1115
1116   if (priv->freeze_count)
1117     return;
1118
1119   if (gtk_widget_get_realized (GTK_WIDGET (layout)))
1120     {
1121       gdk_window_move (priv->bin_window,
1122                        - gtk_adjustment_get_value (priv->hadjustment),
1123                        - gtk_adjustment_get_value (priv->vadjustment));
1124
1125       gdk_window_process_updates (priv->bin_window, TRUE);
1126     }
1127 }