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