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