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