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