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