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