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