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