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