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