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