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