]> Pileus Git - ~andy/gtk/blob - gtk/gtklayout.c
Must mark also gdk_error_warnings and gdk_error_code as GDKVAR, as they
[~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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library 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-1999.  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 "gdkconfig.h"
32
33 #if defined (GDK_WINDOWING_X11)
34 #include "x11/gdkx.h"
35 #elif defined (GDK_WINDOWING_WIN32)
36 #include "win32/gdkwin32.h"
37 #endif
38
39 #include "gtklayout.h"
40 #include "gtksignal.h"
41 #include "gtkprivate.h"
42
43 typedef struct _GtkLayoutAdjData GtkLayoutAdjData;
44 typedef struct _GtkLayoutChild   GtkLayoutChild;
45
46 struct _GtkLayoutAdjData {
47   gint dx;
48   gint dy;
49 };
50
51 struct _GtkLayoutChild {
52   GtkWidget *widget;
53   gint x;
54   gint y;
55 };
56
57 #define IS_ONSCREEN(x,y) ((x >= G_MINSHORT) && (x <= G_MAXSHORT) && \
58                           (y >= G_MINSHORT) && (y <= G_MAXSHORT))
59
60 static void     gtk_layout_class_init         (GtkLayoutClass *class);
61 static void     gtk_layout_init               (GtkLayout      *layout);
62
63 static void     gtk_layout_finalize           (GtkObject      *object);
64 static void     gtk_layout_realize            (GtkWidget      *widget);
65 static void     gtk_layout_unrealize          (GtkWidget      *widget);
66 static void     gtk_layout_map                (GtkWidget      *widget);
67 static void     gtk_layout_size_request       (GtkWidget      *widget,
68                                                GtkRequisition *requisition);
69 static void     gtk_layout_size_allocate      (GtkWidget      *widget,
70                                                GtkAllocation  *allocation);
71 static void     gtk_layout_draw               (GtkWidget      *widget, 
72                                                GdkRectangle   *area);
73 static gint     gtk_layout_expose             (GtkWidget      *widget, 
74                                                GdkEventExpose *event);
75
76 static void     gtk_layout_remove             (GtkContainer *container, 
77                                                GtkWidget    *widget);
78 static void     gtk_layout_forall             (GtkContainer *container,
79                                                gboolean      include_internals,
80                                                GtkCallback   callback,
81                                                gpointer      callback_data);
82 static void     gtk_layout_set_adjustments    (GtkLayout     *layout,
83                                                GtkAdjustment *hadj,
84                                                GtkAdjustment *vadj);
85
86 static void     gtk_layout_position_child     (GtkLayout      *layout,
87                                                GtkLayoutChild *child);
88 static void     gtk_layout_allocate_child     (GtkLayout      *layout,
89                                                GtkLayoutChild *child);
90 static void     gtk_layout_position_children  (GtkLayout      *layout);
91
92 static void     gtk_layout_adjust_allocations_recurse (GtkWidget *widget,
93                                                        gpointer   cb_data);
94 static void     gtk_layout_adjust_allocations         (GtkLayout *layout,
95                                                        gint       dx,
96                                                        gint       dy);
97
98
99
100 static void     gtk_layout_expose_area        (GtkLayout      *layout,
101                                                gint            x, 
102                                                gint            y, 
103                                                gint            width, 
104                                                gint            height);
105 static void     gtk_layout_adjustment_changed (GtkAdjustment  *adjustment,
106                                                GtkLayout      *layout);
107 static GdkFilterReturn gtk_layout_main_filter (GdkXEvent      *gdk_xevent,
108                                                GdkEvent       *event,
109                                                gpointer        data);
110
111 static GtkWidgetClass *parent_class = NULL;
112 static gboolean gravity_works;
113
114 /* Public interface
115  */
116   
117 GtkWidget*    
118 gtk_layout_new (GtkAdjustment *hadjustment,
119                 GtkAdjustment *vadjustment)
120 {
121   GtkLayout *layout;
122
123   layout = gtk_type_new (GTK_TYPE_LAYOUT);
124
125   gtk_layout_set_adjustments (layout, hadjustment, vadjustment);
126
127   return GTK_WIDGET (layout);
128 }
129
130 GtkAdjustment* 
131 gtk_layout_get_hadjustment (GtkLayout     *layout)
132 {
133   g_return_val_if_fail (layout != NULL, NULL);
134   g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL);
135
136   return layout->hadjustment;
137 }
138 GtkAdjustment* 
139 gtk_layout_get_vadjustment (GtkLayout     *layout)
140 {
141   g_return_val_if_fail (layout != NULL, NULL);
142   g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL);
143
144   return layout->vadjustment;
145 }
146
147 static void           
148 gtk_layout_set_adjustments (GtkLayout     *layout,
149                             GtkAdjustment *hadj,
150                             GtkAdjustment *vadj)
151 {
152   gboolean need_adjust = FALSE;
153
154   g_return_if_fail (layout != NULL);
155   g_return_if_fail (GTK_IS_LAYOUT (layout));
156
157   if (hadj)
158     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
159   else
160     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
161   if (vadj)
162     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
163   else
164     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
165   
166   if (layout->hadjustment && (layout->hadjustment != hadj))
167     {
168       gtk_signal_disconnect_by_data (GTK_OBJECT (layout->hadjustment), layout);
169       gtk_object_unref (GTK_OBJECT (layout->hadjustment));
170     }
171   
172   if (layout->vadjustment && (layout->vadjustment != vadj))
173     {
174       gtk_signal_disconnect_by_data (GTK_OBJECT (layout->vadjustment), layout);
175       gtk_object_unref (GTK_OBJECT (layout->vadjustment));
176     }
177   
178   if (layout->hadjustment != hadj)
179     {
180       layout->hadjustment = hadj;
181       gtk_object_ref (GTK_OBJECT (layout->hadjustment));
182       gtk_object_sink (GTK_OBJECT (layout->hadjustment));
183       
184       gtk_signal_connect (GTK_OBJECT (layout->hadjustment), "value_changed",
185                           (GtkSignalFunc) gtk_layout_adjustment_changed,
186                           layout);
187       need_adjust = TRUE;
188     }
189   
190   if (layout->vadjustment != vadj)
191     {
192       layout->vadjustment = vadj;
193       gtk_object_ref (GTK_OBJECT (layout->vadjustment));
194       gtk_object_sink (GTK_OBJECT (layout->vadjustment));
195       
196       gtk_signal_connect (GTK_OBJECT (layout->vadjustment), "value_changed",
197                           (GtkSignalFunc) gtk_layout_adjustment_changed,
198                           layout);
199       need_adjust = TRUE;
200     }
201
202   if (need_adjust)
203     gtk_layout_adjustment_changed (NULL, layout);
204 }
205
206 static void
207 gtk_layout_finalize (GtkObject *object)
208 {
209   GtkLayout *layout = GTK_LAYOUT (object);
210
211   gtk_object_unref (GTK_OBJECT (layout->hadjustment));
212   gtk_object_unref (GTK_OBJECT (layout->vadjustment));
213
214   GTK_OBJECT_CLASS (parent_class)->finalize (object);
215 }
216
217 void           
218 gtk_layout_set_hadjustment (GtkLayout     *layout,
219                             GtkAdjustment *adjustment)
220 {
221   g_return_if_fail (layout != NULL);
222   g_return_if_fail (GTK_IS_LAYOUT (layout));
223
224   gtk_layout_set_adjustments (layout, adjustment, layout->vadjustment);
225 }
226  
227
228 void           
229 gtk_layout_set_vadjustment (GtkLayout     *layout,
230                             GtkAdjustment *adjustment)
231 {
232   g_return_if_fail (layout != NULL);
233   g_return_if_fail (GTK_IS_LAYOUT (layout));
234   
235   gtk_layout_set_adjustments (layout, layout->hadjustment, adjustment);
236 }
237
238
239 void           
240 gtk_layout_put (GtkLayout     *layout, 
241                 GtkWidget     *child_widget, 
242                 gint           x, 
243                 gint           y)
244 {
245   GtkLayoutChild *child;
246
247   g_return_if_fail (layout != NULL);
248   g_return_if_fail (GTK_IS_LAYOUT (layout));
249   g_return_if_fail (child_widget != NULL);
250   g_return_if_fail (GTK_IS_WIDGET (child_widget));
251   
252   child = g_new (GtkLayoutChild, 1);
253
254   child->widget = child_widget;
255   child->x = x;
256   child->y = y;
257
258   layout->children = g_list_append (layout->children, child);
259   
260   gtk_widget_set_parent (child_widget, GTK_WIDGET (layout));
261   if (GTK_WIDGET_REALIZED (layout))
262     gtk_widget_set_parent_window (child->widget, layout->bin_window);
263
264   if (!IS_ONSCREEN (x, y))
265     GTK_PRIVATE_SET_FLAG (child_widget, GTK_IS_OFFSCREEN);
266
267   if (GTK_WIDGET_REALIZED (layout))
268     gtk_widget_realize (child_widget);
269     
270   if (GTK_WIDGET_VISIBLE (layout) && GTK_WIDGET_VISIBLE (child_widget))
271     {
272       if (GTK_WIDGET_MAPPED (layout))
273         gtk_widget_map (child_widget);
274
275       gtk_widget_queue_resize (child_widget);
276     }
277 }
278
279 void           
280 gtk_layout_move (GtkLayout     *layout, 
281                  GtkWidget     *child_widget, 
282                  gint           x, 
283                  gint           y)
284 {
285   GList *tmp_list;
286   GtkLayoutChild *child;
287   
288   g_return_if_fail (layout != NULL);
289   g_return_if_fail (GTK_IS_LAYOUT (layout));
290
291   tmp_list = layout->children;
292   while (tmp_list)
293     {
294       child = tmp_list->data;
295       tmp_list = tmp_list->next;
296
297       if (child->widget == child_widget)
298         {
299           child->x = x;
300           child->y = y;
301
302           if (GTK_WIDGET_VISIBLE (child_widget) && GTK_WIDGET_VISIBLE (layout))
303             gtk_widget_queue_resize (child_widget);
304
305           return;
306         }
307     }
308 }
309
310 void
311 gtk_layout_set_size (GtkLayout     *layout, 
312                      guint          width,
313                      guint          height)
314 {
315   g_return_if_fail (layout != NULL);
316   g_return_if_fail (GTK_IS_LAYOUT (layout));
317
318   layout->width = width;
319   layout->height = height;
320
321   layout->hadjustment->upper = layout->width;
322   gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
323
324   layout->vadjustment->upper = layout->height;
325   gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
326 }
327
328 void
329 gtk_layout_freeze (GtkLayout *layout)
330 {
331   g_return_if_fail (layout != NULL);
332   g_return_if_fail (GTK_IS_LAYOUT (layout));
333
334   layout->freeze_count++;
335 }
336
337 void
338 gtk_layout_thaw (GtkLayout *layout)
339 {
340   g_return_if_fail (layout != NULL);
341   g_return_if_fail (GTK_IS_LAYOUT (layout));
342
343   if (layout->freeze_count)
344     if (!(--layout->freeze_count))
345       {
346         gtk_layout_position_children (layout);
347         gtk_widget_draw (GTK_WIDGET (layout), NULL);
348       }
349 }
350
351 /* Basic Object handling procedures
352  */
353 GtkType
354 gtk_layout_get_type (void)
355 {
356   static GtkType layout_type = 0;
357
358   if (!layout_type)
359     {
360       static const GtkTypeInfo layout_info =
361       {
362         "GtkLayout",
363         sizeof (GtkLayout),
364         sizeof (GtkLayoutClass),
365         (GtkClassInitFunc) gtk_layout_class_init,
366         (GtkObjectInitFunc) gtk_layout_init,
367         (GtkArgSetFunc) NULL,
368         (GtkArgGetFunc) NULL,
369       };
370
371       layout_type = gtk_type_unique (GTK_TYPE_CONTAINER, &layout_info);
372     }
373
374   return layout_type;
375 }
376
377 static void
378 gtk_layout_class_init (GtkLayoutClass *class)
379 {
380   GtkObjectClass *object_class;
381   GtkWidgetClass *widget_class;
382   GtkContainerClass *container_class;
383
384   object_class = (GtkObjectClass*) class;
385   widget_class = (GtkWidgetClass*) class;
386   container_class = (GtkContainerClass*) class;
387
388   parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
389
390   object_class->finalize = gtk_layout_finalize;
391
392   widget_class->realize = gtk_layout_realize;
393   widget_class->unrealize = gtk_layout_unrealize;
394   widget_class->map = gtk_layout_map;
395   widget_class->size_request = gtk_layout_size_request;
396   widget_class->size_allocate = gtk_layout_size_allocate;
397   widget_class->draw = gtk_layout_draw;
398   widget_class->expose_event = gtk_layout_expose;
399
400   container_class->remove = gtk_layout_remove;
401   container_class->forall = gtk_layout_forall;
402
403   class->set_scroll_adjustments = gtk_layout_set_adjustments;
404
405   widget_class->set_scroll_adjustments_signal =
406     gtk_signal_new ("set_scroll_adjustments",
407                     GTK_RUN_LAST,
408                     object_class->type,
409                     GTK_SIGNAL_OFFSET (GtkLayoutClass, set_scroll_adjustments),
410                     gtk_marshal_NONE__POINTER_POINTER,
411                     GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
412 }
413
414 static void
415 gtk_layout_init (GtkLayout *layout)
416 {
417   layout->children = NULL;
418
419   layout->width = 100;
420   layout->height = 100;
421
422   layout->hadjustment = NULL;
423   layout->vadjustment = NULL;
424
425   layout->bin_window = NULL;
426
427   layout->configure_serial = 0;
428   layout->scroll_x = 0;
429   layout->scroll_y = 0;
430   layout->visibility = GDK_VISIBILITY_PARTIAL;
431
432   layout->freeze_count = 0;
433 }
434
435 /* Widget methods
436  */
437
438 static void 
439 gtk_layout_realize (GtkWidget *widget)
440 {
441   GList *tmp_list;
442   GtkLayout *layout;
443   GdkWindowAttr attributes;
444   gint attributes_mask;
445
446   g_return_if_fail (widget != NULL);
447   g_return_if_fail (GTK_IS_LAYOUT (widget));
448
449   layout = GTK_LAYOUT (widget);
450   GTK_WIDGET_SET_FLAGS (layout, GTK_REALIZED);
451
452   attributes.window_type = GDK_WINDOW_CHILD;
453   attributes.x = widget->allocation.x;
454   attributes.y = widget->allocation.y;
455   attributes.width = widget->allocation.width;
456   attributes.height = widget->allocation.height;
457   attributes.wclass = GDK_INPUT_OUTPUT;
458   attributes.visual = gtk_widget_get_visual (widget);
459   attributes.colormap = gtk_widget_get_colormap (widget);
460   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
461
462   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
463
464   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
465                                    &attributes, attributes_mask);
466   gdk_window_set_user_data (widget->window, widget);
467
468   attributes.x = 0;
469   attributes.y = 0;
470   attributes.event_mask = GDK_EXPOSURE_MASK | 
471                           gtk_widget_get_events (widget);
472
473   layout->bin_window = gdk_window_new (widget->window,
474                                         &attributes, attributes_mask);
475   gdk_window_set_user_data (layout->bin_window, widget);
476
477   widget->style = gtk_style_attach (widget->style, widget->window);
478   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
479   gtk_style_set_background (widget->style, layout->bin_window, GTK_STATE_NORMAL);
480
481   gdk_window_add_filter (widget->window, gtk_layout_main_filter, layout);
482   /*   gdk_window_add_filter (layout->bin_window, gtk_layout_filter, layout);*/
483
484   /* XXX: If we ever get multiple displays for GTK+, then gravity_works
485    *      will have to become a widget member. Right now we just
486    *      keep it as a global
487    */
488   gravity_works = gdk_window_set_static_gravities (layout->bin_window, TRUE);
489
490   tmp_list = layout->children;
491   while (tmp_list)
492     {
493       GtkLayoutChild *child = tmp_list->data;
494       tmp_list = tmp_list->next;
495
496       gtk_widget_set_parent_window (child->widget, layout->bin_window);
497     }
498 }
499
500 static void 
501 gtk_layout_map (GtkWidget *widget)
502 {
503   GList *tmp_list;
504   GtkLayout *layout;
505
506   g_return_if_fail (widget != NULL);
507   g_return_if_fail (GTK_IS_LAYOUT (widget));
508
509   layout = GTK_LAYOUT (widget);
510
511   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
512
513   tmp_list = layout->children;
514   while (tmp_list)
515     {
516       GtkLayoutChild *child = tmp_list->data;
517       tmp_list = tmp_list->next;
518
519       if (GTK_WIDGET_VISIBLE (child->widget))
520         {
521           if (!GTK_WIDGET_MAPPED (child->widget) && 
522               !GTK_WIDGET_IS_OFFSCREEN (child->widget))
523             gtk_widget_map (child->widget);
524         }
525     }
526
527   gdk_window_show (layout->bin_window);
528   gdk_window_show (widget->window);
529 }
530
531 static void 
532 gtk_layout_unrealize (GtkWidget *widget)
533 {
534   GtkLayout *layout;
535
536   g_return_if_fail (widget != NULL);
537   g_return_if_fail (GTK_IS_LAYOUT (widget));
538
539   layout = GTK_LAYOUT (widget);
540
541   gdk_window_set_user_data (layout->bin_window, NULL);
542   gdk_window_destroy (layout->bin_window);
543   layout->bin_window = NULL;
544
545   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
546     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
547 }
548
549 static void     
550 gtk_layout_size_request (GtkWidget     *widget,
551                          GtkRequisition *requisition)
552 {
553   GList *tmp_list;
554   GtkLayout *layout;
555
556   g_return_if_fail (widget != NULL);
557   g_return_if_fail (GTK_IS_LAYOUT (widget));
558
559   layout = GTK_LAYOUT (widget);
560
561   requisition->width = 0;
562   requisition->height = 0;
563
564   tmp_list = layout->children;
565
566   while (tmp_list)
567     {
568       GtkLayoutChild *child = tmp_list->data;
569       GtkRequisition child_requisition;
570       
571       tmp_list = tmp_list->next;
572
573       gtk_widget_size_request (child->widget, &child_requisition);
574     }
575 }
576
577 static void     
578 gtk_layout_size_allocate (GtkWidget     *widget,
579                           GtkAllocation *allocation)
580 {
581   GList *tmp_list;
582   GtkLayout *layout;
583
584   g_return_if_fail (widget != NULL);
585   g_return_if_fail (GTK_IS_LAYOUT (widget));
586
587   widget->allocation = *allocation;
588   
589   layout = GTK_LAYOUT (widget);
590
591   tmp_list = layout->children;
592
593   while (tmp_list)
594     {
595       GtkLayoutChild *child = tmp_list->data;
596       tmp_list = tmp_list->next;
597
598       gtk_layout_position_child (layout, child);
599       gtk_layout_allocate_child (layout, child);
600     }
601
602   if (GTK_WIDGET_REALIZED (widget))
603     {
604       gdk_window_move_resize (widget->window,
605                               allocation->x, allocation->y,
606                               allocation->width, allocation->height);
607       gdk_window_move_resize (GTK_LAYOUT(widget)->bin_window,
608                               0, 0,
609                               allocation->width, allocation->height);
610     }
611
612   layout->hadjustment->page_size = allocation->width;
613   layout->hadjustment->page_increment = allocation->width / 2;
614   layout->hadjustment->lower = 0;
615   layout->hadjustment->upper = layout->width;
616   gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
617
618   layout->vadjustment->page_size = allocation->height;
619   layout->vadjustment->page_increment = allocation->height / 2;
620   layout->vadjustment->lower = 0;
621   layout->vadjustment->upper = layout->height;
622   gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
623 }
624
625 static void 
626 gtk_layout_draw (GtkWidget *widget, GdkRectangle *area)
627 {
628   GList *tmp_list;
629   GtkLayout *layout;
630   GdkRectangle child_area;
631
632   g_return_if_fail (widget != NULL);
633   g_return_if_fail (GTK_IS_LAYOUT (widget));
634
635   layout = GTK_LAYOUT (widget);
636
637   /* We don't have any way of telling themes about this properly,
638    * so we just assume a background pixmap
639    */
640   if (!GTK_WIDGET_APP_PAINTABLE (widget))
641     gdk_window_clear_area (layout->bin_window,
642                            area->x, area->y, area->width, area->height);
643   
644   tmp_list = layout->children;
645   while (tmp_list)
646     {
647       GtkLayoutChild *child = tmp_list->data;
648       tmp_list = tmp_list->next;
649
650       if (gtk_widget_intersect (child->widget, area, &child_area))
651         gtk_widget_draw (child->widget, &child_area);
652     }
653 }
654
655 static gint 
656 gtk_layout_expose (GtkWidget *widget, GdkEventExpose *event)
657 {
658   GList *tmp_list;
659   GtkLayout *layout;
660   GdkEventExpose child_event;
661
662   g_return_val_if_fail (widget != NULL, FALSE);
663   g_return_val_if_fail (GTK_IS_LAYOUT (widget), FALSE);
664
665   layout = GTK_LAYOUT (widget);
666
667   if (event->window != layout->bin_window)
668     return FALSE;
669   
670   tmp_list = layout->children;
671   while (tmp_list)
672     {
673       GtkLayoutChild *child = tmp_list->data;
674       tmp_list = tmp_list->next;
675
676       child_event = *event;
677       if (GTK_WIDGET_DRAWABLE (child->widget) &&
678           GTK_WIDGET_NO_WINDOW (child->widget) &&
679           gtk_widget_intersect (child->widget, &event->area, &child_event.area))
680         gtk_widget_event (child->widget, (GdkEvent*) &child_event);
681     }
682
683   return FALSE;
684 }
685
686 /* Container method
687  */
688 static void
689 gtk_layout_remove (GtkContainer *container, 
690                    GtkWidget    *widget)
691 {
692   GList *tmp_list;
693   GtkLayout *layout;
694   GtkLayoutChild *child = NULL;
695   
696   g_return_if_fail (container != NULL);
697   g_return_if_fail (GTK_IS_LAYOUT (container));
698   
699   layout = GTK_LAYOUT (container);
700
701   tmp_list = layout->children;
702   while (tmp_list)
703     {
704       child = tmp_list->data;
705       if (child->widget == widget)
706         break;
707       tmp_list = tmp_list->next;
708     }
709
710   if (tmp_list)
711     {
712       GTK_PRIVATE_UNSET_FLAG (widget, GTK_IS_OFFSCREEN);
713
714       gtk_widget_unparent (widget);
715
716       layout->children = g_list_remove_link (layout->children, tmp_list);
717       g_list_free_1 (tmp_list);
718       g_free (child);
719     }
720 }
721
722 static void
723 gtk_layout_forall (GtkContainer *container,
724                    gboolean      include_internals,
725                    GtkCallback   callback,
726                    gpointer      callback_data)
727 {
728   GtkLayout *layout;
729   GtkLayoutChild *child;
730   GList *tmp_list;
731
732   g_return_if_fail (container != NULL);
733   g_return_if_fail (GTK_IS_LAYOUT (container));
734   g_return_if_fail (callback != NULL);
735
736   layout = GTK_LAYOUT (container);
737
738   tmp_list = layout->children;
739   while (tmp_list)
740     {
741       child = tmp_list->data;
742       tmp_list = tmp_list->next;
743
744       (* callback) (child->widget, callback_data);
745     }
746 }
747
748 /* Operations on children
749  */
750
751 static void
752 gtk_layout_position_child (GtkLayout      *layout,
753                            GtkLayoutChild *child)
754 {
755   gint x;
756   gint y;
757
758   x = child->x - layout->xoffset;
759   y = child->y - layout->yoffset;
760
761   if (IS_ONSCREEN (x,y))
762     {
763       if (GTK_WIDGET_MAPPED (layout) &&
764           GTK_WIDGET_VISIBLE (child->widget))
765         {
766           if (!GTK_WIDGET_MAPPED (child->widget))
767             gtk_widget_map (child->widget);
768         }
769
770       if (GTK_WIDGET_IS_OFFSCREEN (child->widget))
771         GTK_PRIVATE_UNSET_FLAG (child->widget, GTK_IS_OFFSCREEN);
772     }
773   else
774     {
775       if (!GTK_WIDGET_IS_OFFSCREEN (child->widget))
776         GTK_PRIVATE_SET_FLAG (child->widget, GTK_IS_OFFSCREEN);
777
778       if (GTK_WIDGET_MAPPED (child->widget))
779         gtk_widget_unmap (child->widget);
780     }
781 }
782
783 static void
784 gtk_layout_allocate_child (GtkLayout      *layout,
785                            GtkLayoutChild *child)
786 {
787   GtkAllocation allocation;
788   GtkRequisition requisition;
789
790   allocation.x = child->x - layout->xoffset;
791   allocation.y = child->y - layout->yoffset;
792   gtk_widget_get_child_requisition (child->widget, &requisition);
793   allocation.width = requisition.width;
794   allocation.height = requisition.height;
795   
796   gtk_widget_size_allocate (child->widget, &allocation);
797 }
798
799 static void
800 gtk_layout_position_children (GtkLayout *layout)
801 {
802   GList *tmp_list;
803
804   tmp_list = layout->children;
805   while (tmp_list)
806     {
807       GtkLayoutChild *child = tmp_list->data;
808       tmp_list = tmp_list->next;
809       
810       gtk_layout_position_child (layout, child);
811     }
812 }
813
814 static void
815 gtk_layout_adjust_allocations_recurse (GtkWidget *widget,
816                                        gpointer   cb_data)
817 {
818   GtkLayoutAdjData *data = cb_data;
819
820   widget->allocation.x += data->dx;
821   widget->allocation.y += data->dy;
822
823   if (GTK_WIDGET_NO_WINDOW (widget) &&
824       GTK_IS_CONTAINER (widget))
825     gtk_container_forall (GTK_CONTAINER (widget), 
826                           gtk_layout_adjust_allocations_recurse,
827                           cb_data);
828 }
829
830 static void
831 gtk_layout_adjust_allocations (GtkLayout *layout,
832                                gint       dx,
833                                gint       dy)
834 {
835   GList *tmp_list;
836   GtkLayoutAdjData data;
837
838   data.dx = dx;
839   data.dy = dy;
840
841   tmp_list = layout->children;
842   while (tmp_list)
843     {
844       GtkLayoutChild *child = tmp_list->data;
845       tmp_list = tmp_list->next;
846       
847       child->widget->allocation.x += dx;
848       child->widget->allocation.y += dy;
849
850       if (GTK_WIDGET_NO_WINDOW (child->widget) &&
851           GTK_IS_CONTAINER (child->widget))
852         gtk_container_forall (GTK_CONTAINER (child->widget), 
853                               gtk_layout_adjust_allocations_recurse,
854                               &data);
855     }
856 }
857   
858 /* Callbacks */
859
860 /* Send a synthetic expose event to the widget
861  */
862 static void
863 gtk_layout_expose_area (GtkLayout    *layout,
864                         gint x, gint y, gint width, gint height)
865 {
866   if (layout->visibility == GDK_VISIBILITY_UNOBSCURED)
867     {
868       GdkEventExpose event;
869       
870       event.type = GDK_EXPOSE;
871       event.send_event = TRUE;
872       event.window = layout->bin_window;
873       event.count = 0;
874       
875       event.area.x = x;
876       event.area.y = y;
877       event.area.width = width;
878       event.area.height = height;
879       
880       gdk_window_ref (event.window);
881       gtk_widget_event (GTK_WIDGET (layout), (GdkEvent *)&event);
882       gdk_window_unref (event.window);
883     }
884 }
885
886 #ifdef GDK_WINDOWING_X11
887
888 /* This function is used to find events to process while scrolling
889  */
890
891 static Bool 
892 gtk_layout_expose_predicate (Display *display, 
893                   XEvent  *xevent, 
894                   XPointer arg)
895 {
896   if ((xevent->type == Expose) || 
897       ((xevent->xany.window == *(Window *)arg) &&
898        (xevent->type == ConfigureNotify)))
899     return True;
900   else
901     return False;
902 }
903
904 #endif /* X11 */
905
906 /* This is the main routine to do the scrolling. Scrolling is
907  * done by "Guffaw" scrolling, as in the Mozilla XFE, with
908  * a few modifications.
909  * 
910  * The main improvement is that we keep track of whether we
911  * are obscured or not. If not, we ignore the generated expose
912  * events and instead do the exposes ourself, without having
913  * to wait for a roundtrip to the server. This also provides
914  * a limited form of expose-event compression, since we do
915  * the affected area as one big chunk.
916  *
917  * Real expose event compression, as in the XFE, could be added
918  * here. It would help opaque drags over the region, and the
919  * obscured case.
920  *
921  * Code needs to be added here to do the scrolling on machines
922  * that don't have working WindowGravity. That could be done
923  *
924  *  - XCopyArea and move the windows, and accept trailing the
925  *    background color. (Since it is only a fallback method)
926  *  - XmHTML style. As above, but turn off expose events when
927  *    not obscured and do the exposures ourself.
928  *  - gzilla-style. Move the window continuously, and reset
929  *    every 32768 pixels
930  */
931
932 static void
933 gtk_layout_adjustment_changed (GtkAdjustment *adjustment,
934                                GtkLayout     *layout)
935 {
936   GtkWidget *widget;
937 #ifdef GDK_WINDOWING_X11
938   XEvent xevent;
939 #endif
940   gint dx, dy;
941
942   widget = GTK_WIDGET (layout);
943
944   dx = (gint)layout->hadjustment->value - layout->xoffset;
945   dy = (gint)layout->vadjustment->value - layout->yoffset;
946
947   layout->xoffset = (gint)layout->hadjustment->value;
948   layout->yoffset = (gint)layout->vadjustment->value;
949
950   if (layout->freeze_count)
951     return;
952
953   if (!GTK_WIDGET_MAPPED (layout))
954     {
955       gtk_layout_position_children (layout);
956       return;
957     }
958
959   gtk_layout_adjust_allocations (layout, -dx, -dy);
960
961   if (dx > 0)
962     {
963       if (gravity_works)
964         {
965           gdk_window_resize (layout->bin_window,
966                              widget->allocation.width + dx,
967                              widget->allocation.height);
968           gdk_window_move   (layout->bin_window, -dx, 0);
969           gdk_window_move_resize (layout->bin_window,
970                                   0, 0,
971                                   widget->allocation.width,
972                                   widget->allocation.height);
973         }
974       else
975         {
976           /* FIXME */
977         }
978
979       gtk_layout_expose_area (layout, 
980                               MAX ((gint)widget->allocation.width - dx, 0),
981                               0,
982                               MIN (dx, widget->allocation.width),
983                               widget->allocation.height);
984     }
985   else if (dx < 0)
986     {
987       if (gravity_works)
988         {
989           gdk_window_move_resize (layout->bin_window,
990                                   dx, 0,
991                                   widget->allocation.width - dx,
992                                   widget->allocation.height);
993           gdk_window_move   (layout->bin_window, 0, 0);
994           gdk_window_resize (layout->bin_window,
995                              widget->allocation.width,
996                              widget->allocation.height);
997         }
998       else
999         {
1000           /* FIXME */
1001         }
1002
1003       gtk_layout_expose_area (layout,
1004                               0,
1005                               0,
1006                               MIN (-dx, widget->allocation.width),
1007                               widget->allocation.height);
1008     }
1009
1010   if (dy > 0)
1011     {
1012       if (gravity_works)
1013         {
1014           gdk_window_resize (layout->bin_window,
1015                              widget->allocation.width,
1016                              widget->allocation.height + dy);
1017           gdk_window_move   (layout->bin_window, 0, -dy);
1018           gdk_window_move_resize (layout->bin_window,
1019                                   0, 0,
1020                                   widget->allocation.width,
1021                                   widget->allocation.height);
1022         }
1023       else
1024         {
1025           /* FIXME */
1026         }
1027
1028       gtk_layout_expose_area (layout, 
1029                               0,
1030                               MAX ((gint)widget->allocation.height - dy, 0),
1031                               widget->allocation.width,
1032                               MIN (dy, widget->allocation.height));
1033     }
1034   else if (dy < 0)
1035     {
1036       if (gravity_works)
1037         {
1038           gdk_window_move_resize (layout->bin_window,
1039                                   0, dy,
1040                                   widget->allocation.width,
1041                                   widget->allocation.height - dy);
1042           gdk_window_move   (layout->bin_window, 0, 0);
1043           gdk_window_resize (layout->bin_window,
1044                              widget->allocation.width,
1045                              widget->allocation.height);
1046         }
1047       else
1048         {
1049           /* FIXME */
1050         }
1051       gtk_layout_expose_area (layout, 
1052                               0,
1053                               0,
1054                               widget->allocation.width,
1055                               MIN (-dy, (gint)widget->allocation.height));
1056     }
1057
1058   gtk_layout_position_children (layout);
1059
1060   /* We have to make sure that all exposes from this scroll get
1061    * processed before we scroll again, or the expose events will
1062    * have invalid coordinates.
1063    *
1064    * We also do expose events for other windows, since otherwise
1065    * their updating will fall behind the scrolling 
1066    *
1067    * This also avoids a problem in pre-1.0 GTK where filters don't
1068    * have access to configure events that were compressed.
1069    */
1070
1071   gdk_flush();
1072
1073 #ifdef GDK_WINDOWING_X11
1074   while (XCheckIfEvent(GDK_WINDOW_XDISPLAY (layout->bin_window),
1075                        &xevent,
1076                        gtk_layout_expose_predicate,
1077                        (XPointer)&GDK_WINDOW_XWINDOW (layout->bin_window)))
1078     {
1079       GdkEvent event;
1080       GtkWidget *event_widget;
1081
1082       switch (xevent.type)
1083         {
1084         case Expose:
1085
1086           if (xevent.xany.window == GDK_WINDOW_XWINDOW (layout->bin_window))
1087             {
1088               /* If the window is unobscured, then we've exposed the
1089                * regions with the following serials already, so we
1090                * can throw out the expose events.
1091                */
1092               if (layout->visibility == GDK_VISIBILITY_UNOBSCURED &&
1093                   (((dx > 0 || dy > 0) && 
1094                     xevent.xexpose.serial == layout->configure_serial) ||
1095                    ((dx < 0 || dy < 0) && 
1096                     xevent.xexpose.serial == layout->configure_serial + 1)))
1097                 continue;
1098               /* The following expose was generated while the origin was
1099                * different from the current origin, so we need to offset it.
1100                */
1101               else if (xevent.xexpose.serial == layout->configure_serial)
1102                 {
1103                   xevent.xexpose.x += layout->scroll_x;
1104                   xevent.xexpose.y += layout->scroll_y;
1105                 }
1106               event.expose.window = layout->bin_window;
1107               event_widget = widget;
1108             }
1109           else
1110             {
1111               event.expose.window = gdk_window_lookup (xevent.xany.window);
1112               gdk_window_get_user_data (event.expose.window, 
1113                                         (gpointer *)&event_widget);
1114             }
1115
1116           if (event_widget)
1117             {
1118               event.expose.type = GDK_EXPOSE;
1119               event.expose.area.x = xevent.xexpose.x;
1120               event.expose.area.y = xevent.xexpose.y;
1121               event.expose.area.width = xevent.xexpose.width;
1122               event.expose.area.height = xevent.xexpose.height;
1123               event.expose.count = xevent.xexpose.count;
1124               
1125               gdk_window_ref (event.expose.window);
1126               gtk_widget_event (event_widget, &event);
1127               gdk_window_unref (event.expose.window);
1128             }
1129           break;
1130
1131         case ConfigureNotify:
1132           if (xevent.xany.window == GDK_WINDOW_XWINDOW (layout->bin_window) &&
1133               (xevent.xconfigure.x != 0 || xevent.xconfigure.y != 0))
1134             {
1135               layout->configure_serial = xevent.xconfigure.serial;
1136               layout->scroll_x = xevent.xconfigure.x;
1137               layout->scroll_y = xevent.xconfigure.y;
1138             }
1139           break;
1140         }
1141     }
1142 #elif defined (GDK_WINDOWING_WIN32)
1143   /* XXX Not implemented */
1144 #endif
1145 }
1146
1147 #if 0
1148 /* The main event filter. Actually, we probably don't really need
1149  * this filter at all, since we are calling it directly above in the
1150  * expose-handling hack. But in case scrollbars
1151  * are fixed up in some manner...
1152  *
1153  * This routine identifies expose events that are generated when
1154  * we've temporarily moved the bin_window_origin, and translates
1155  * them or discards them, depending on whether we are obscured
1156  * or not.
1157  */
1158 static GdkFilterReturn 
1159 gtk_layout_filter (GdkXEvent *gdk_xevent,
1160                    GdkEvent  *event,
1161                    gpointer   data)
1162 {
1163 #ifdef GDK_WINDOWING_X11
1164
1165   XEvent *xevent;
1166   GtkLayout *layout;
1167
1168   xevent = (XEvent *)gdk_xevent;
1169   layout = GTK_LAYOUT (data);
1170
1171   switch (xevent->type)
1172     {
1173     case Expose:
1174       if (xevent->xexpose.serial == layout->configure_serial)
1175         {
1176           if (layout->visibility == GDK_VISIBILITY_UNOBSCURED)
1177             return GDK_FILTER_REMOVE;
1178           else
1179             {
1180               xevent->xexpose.x += layout->scroll_x;
1181               xevent->xexpose.y += layout->scroll_y;
1182               
1183               break;
1184             }
1185         }
1186       break;
1187       
1188     case ConfigureNotify:
1189        if ((xevent->xconfigure.x != 0) || (xevent->xconfigure.y != 0))
1190         {
1191           layout->configure_serial = xevent->xconfigure.serial;
1192           layout->scroll_x = xevent->xconfigure.x;
1193           layout->scroll_y = xevent->xconfigure.y;
1194         }
1195       break;
1196     }
1197 #elif defined (GDK_WINDOWING_WIN32)
1198   /* XXX Not implemented */
1199 #endif
1200   
1201   return GDK_FILTER_CONTINUE;
1202 }
1203 #endif 0
1204
1205 /* Although GDK does have a GDK_VISIBILITY_NOTIFY event,
1206  * there is no corresponding event in GTK, so we have
1207  * to get the events from a filter
1208  */
1209 static GdkFilterReturn 
1210 gtk_layout_main_filter (GdkXEvent *gdk_xevent,
1211                         GdkEvent  *event,
1212                         gpointer   data)
1213 {
1214 #ifdef GDK_WINDOWING_X11
1215   XEvent *xevent;
1216   GtkLayout *layout;
1217
1218   xevent = (XEvent *)gdk_xevent;
1219   layout = GTK_LAYOUT (data);
1220
1221   if (xevent->type == VisibilityNotify)
1222     {
1223       switch (xevent->xvisibility.state)
1224         {
1225         case VisibilityFullyObscured:
1226           layout->visibility = GDK_VISIBILITY_FULLY_OBSCURED;
1227           break;
1228
1229         case VisibilityPartiallyObscured:
1230           layout->visibility = GDK_VISIBILITY_PARTIAL;
1231           break;
1232
1233         case VisibilityUnobscured:
1234           layout->visibility = GDK_VISIBILITY_UNOBSCURED;
1235           break;
1236         }
1237
1238       return GDK_FILTER_REMOVE;
1239     }
1240 #elif defined (GDK_WINDOWING_WIN32)
1241   /* XXX Not implemented */
1242 #endif
1243   
1244   return GDK_FILTER_CONTINUE;
1245 }
1246