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