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