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