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