Tried to make wxGLCanvas work again. WIP.
[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 #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 _GtkMyFixedAdjData GtkMyFixedAdjData;
25 typedef struct _GtkMyFixedChild GtkMyFixedChild;
26
27 struct _GtkMyFixedAdjData
28 {
29 gint dx;
30 gint dy;
31 };
32
33 struct _GtkMyFixedChild
34 {
35 GtkWidget *widget;
36 gint x;
37 gint y;
38 gint width;
39 gint height;
40 };
41
42 static void gtk_myfixed_class_init (GtkMyFixedClass *klass);
43 static void gtk_myfixed_init (GtkMyFixed *myfixed);
44
45 static void gtk_myfixed_realize (GtkWidget *widget);
46 static void gtk_myfixed_unrealize (GtkWidget *widget);
47
48 static void gtk_myfixed_map (GtkWidget *widget);
49
50 static void gtk_myfixed_size_request (GtkWidget *widget,
51 GtkRequisition *requisition);
52 static void gtk_myfixed_size_allocate (GtkWidget *widget,
53 GtkAllocation *allocation);
54 static void gtk_myfixed_draw (GtkWidget *widget,
55 GdkRectangle *area);
56 static gint gtk_myfixed_expose (GtkWidget *widget,
57 GdkEventExpose *event);
58 static void gtk_myfixed_add (GtkContainer *container,
59 GtkWidget *widget);
60 static void gtk_myfixed_remove (GtkContainer *container,
61 GtkWidget *widget);
62 static void gtk_myfixed_forall (GtkContainer *container,
63 gboolean include_internals,
64 GtkCallback callback,
65 gpointer callback_data);
66
67 static void gtk_myfixed_position_child (GtkMyFixed *myfixed,
68 GtkMyFixedChild *child);
69 static void gtk_myfixed_allocate_child (GtkMyFixed *myfixed,
70 GtkMyFixedChild *child);
71 static void gtk_myfixed_position_children (GtkMyFixed *myfixed);
72
73 static void gtk_myfixed_adjust_allocations_recurse (GtkWidget *widget,
74 gpointer cb_data);
75 static void gtk_myfixed_adjust_allocations (GtkMyFixed *myfixed,
76 gint dx,
77 gint dy);
78
79
80 static void gtk_myfixed_expose_area (GtkMyFixed *myfixed,
81 gint x,
82 gint y,
83 gint width,
84 gint height);
85 static void gtk_myfixed_adjustment_changed (GtkAdjustment *adjustment,
86 GtkMyFixed *myfixed);
87 static GdkFilterReturn gtk_myfixed_filter (GdkXEvent *gdk_xevent,
88 GdkEvent *event,
89 gpointer data);
90 static GdkFilterReturn gtk_myfixed_main_filter (GdkXEvent *gdk_xevent,
91 GdkEvent *event,
92 gpointer data);
93
94
95 static GtkType gtk_myfixed_child_type (GtkContainer *container);
96
97 static void gtk_myfixed_scroll_set_adjustments (GtkMyFixed *myfixed,
98 GtkAdjustment *hadj,
99 GtkAdjustment *vadj);
100
101
102 static GtkContainerClass *parent_class = NULL;
103 static gboolean gravity_works;
104
105 guint
106 gtk_myfixed_get_type ()
107 {
108 static guint myfixed_type = 0;
109
110 if (!myfixed_type)
111 {
112 GtkTypeInfo myfixed_info =
113 {
114 "GtkMyFixed",
115 sizeof (GtkMyFixed),
116 sizeof (GtkMyFixedClass),
117 (GtkClassInitFunc) gtk_myfixed_class_init,
118 (GtkObjectInitFunc) gtk_myfixed_init,
119 /* reserved_1 */ NULL,
120 /* reserved_2 */ NULL,
121 (GtkClassInitFunc) NULL,
122 };
123 myfixed_type = gtk_type_unique (gtk_container_get_type (), &myfixed_info);
124 }
125
126 return myfixed_type;
127 }
128
129 static void
130 gtk_myfixed_class_init (GtkMyFixedClass *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_myfixed_map;
142 widget_class->realize = gtk_myfixed_realize;
143 widget_class->unrealize = gtk_myfixed_unrealize;
144 widget_class->size_request = gtk_myfixed_size_request;
145 widget_class->size_allocate = gtk_myfixed_size_allocate;
146 widget_class->draw = gtk_myfixed_draw;
147 widget_class->expose_event = gtk_myfixed_expose;
148
149 container_class->add = gtk_myfixed_add;
150 container_class->remove = gtk_myfixed_remove;
151 container_class->forall = gtk_myfixed_forall;
152
153 container_class->child_type = gtk_myfixed_child_type;
154
155 klass->set_scroll_adjustments = gtk_myfixed_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 (GtkMyFixedClass, 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_myfixed_child_type (GtkContainer *container)
168 {
169 return GTK_TYPE_WIDGET;
170 }
171
172 static void
173 gtk_myfixed_init (GtkMyFixed *myfixed)
174 {
175 GTK_WIDGET_UNSET_FLAGS (myfixed, GTK_NO_WINDOW);
176
177 myfixed->shadow_type = GTK_MYSHADOW_NONE;
178
179 myfixed->children = NULL;
180
181 myfixed->width = 20;
182 myfixed->height = 20;
183
184 myfixed->bin_window = NULL;
185
186 myfixed->configure_serial = 0;
187 myfixed->scroll_x = 0;
188 myfixed->scroll_y = 0;
189 myfixed->visibility = GDK_VISIBILITY_PARTIAL;
190
191 myfixed->clear_on_draw = TRUE;
192 }
193
194 GtkWidget*
195 gtk_myfixed_new ()
196 {
197 GtkMyFixed *myfixed;
198
199 myfixed = gtk_type_new (gtk_myfixed_get_type ());
200
201 return GTK_WIDGET (myfixed);
202 }
203
204 void
205 gtk_myfixed_scroll_set_adjustments (GtkMyFixed *myfixed,
206 GtkAdjustment *hadj,
207 GtkAdjustment *vadj)
208 {
209 /* We handle scrolling in the wxScrolledWindow, not here. */
210 }
211
212 void
213 gtk_myfixed_set_shadow_type (GtkMyFixed *myfixed,
214 GtkMyShadowType type)
215 {
216 g_return_if_fail (myfixed != NULL);
217 g_return_if_fail (GTK_IS_MYFIXED (myfixed));
218
219 if ((GtkMyShadowType) myfixed->shadow_type != type)
220 {
221 myfixed->shadow_type = type;
222
223 if (GTK_WIDGET_VISIBLE (myfixed))
224 {
225 gtk_widget_size_allocate (GTK_WIDGET (myfixed), &(GTK_WIDGET (myfixed)->allocation));
226 gtk_widget_queue_draw (GTK_WIDGET (myfixed));
227 }
228 }
229 }
230
231 void
232 gtk_my_fixed_set_clear (GtkMyFixed *myfixed,
233 gboolean clear)
234 {
235 g_return_if_fail (myfixed != NULL);
236 g_return_if_fail (GTK_IS_MYFIXED (myfixed));
237
238 myfixed->clear_on_draw = clear;
239 }
240
241 void
242 gtk_myfixed_put (GtkMyFixed *myfixed,
243 GtkWidget *widget,
244 gint x,
245 gint y,
246 gint width,
247 gint height)
248 {
249 GtkMyFixedChild *child_info;
250
251 g_return_if_fail (myfixed != NULL);
252 g_return_if_fail (GTK_IS_MYFIXED (myfixed));
253 g_return_if_fail (widget != NULL);
254
255 child_info = g_new (GtkMyFixedChild, 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 myfixed->children = g_list_append (myfixed->children, child_info);
264
265 gtk_widget_set_parent (widget, GTK_WIDGET (myfixed));
266
267 if (GTK_WIDGET_REALIZED (myfixed))
268 gtk_widget_set_parent_window (widget, myfixed->bin_window);
269
270 if (!IS_ONSCREEN (x, y))
271 GTK_PRIVATE_SET_FLAG (widget, GTK_IS_OFFSCREEN);
272
273 if (GTK_WIDGET_REALIZED (myfixed))
274 gtk_widget_realize (widget);
275
276 gtk_widget_set_usize (widget, width, height);
277
278 if (GTK_WIDGET_VISIBLE (myfixed) && GTK_WIDGET_VISIBLE (widget))
279 {
280 if (GTK_WIDGET_MAPPED (myfixed))
281 gtk_widget_map (widget);
282
283 gtk_widget_queue_resize (widget);
284 }
285 }
286
287 void
288 gtk_myfixed_move (GtkMyFixed *myfixed,
289 GtkWidget *widget,
290 gint x,
291 gint y)
292 {
293 GtkMyFixedChild *child;
294 GList *children;
295
296 g_return_if_fail (myfixed != NULL);
297 g_return_if_fail (GTK_IS_MYFIXED (myfixed));
298 g_return_if_fail (widget != NULL);
299
300 children = myfixed->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 (myfixed))
315 gtk_widget_queue_resize (widget);
316 break;
317 }
318 }
319 }
320
321 void
322 gtk_myfixed_resize (GtkMyFixed *myfixed,
323 GtkWidget *widget,
324 gint width,
325 gint height)
326 {
327 GtkMyFixedChild *child;
328 GList *children;
329
330 g_return_if_fail (myfixed != NULL);
331 g_return_if_fail (GTK_IS_MYFIXED (myfixed));
332 g_return_if_fail (widget != NULL);
333
334 children = myfixed->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 (myfixed))
351 gtk_widget_queue_resize (widget);
352 break;
353 }
354 }
355 }
356
357 void
358 gtk_myfixed_set_size (GtkMyFixed *myfixed,
359 GtkWidget *widget,
360 gint x,
361 gint y,
362 gint width,
363 gint height)
364 {
365 GtkMyFixedChild *child;
366 GList *children;
367 GtkAllocation child_allocation;
368
369 g_return_if_fail (myfixed != NULL);
370 g_return_if_fail (GTK_IS_MYFIXED (myfixed));
371 g_return_if_fail (widget != NULL);
372
373 children = myfixed->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 (myfixed))
394 gtk_widget_queue_resize (widget);
395
396 break;
397 }
398 }
399 }
400
401 static void
402 gtk_myfixed_map (GtkWidget *widget)
403 {
404 GtkMyFixed *myfixed;
405 GtkMyFixedChild *child;
406 GList *children;
407
408 g_return_if_fail (widget != NULL);
409 g_return_if_fail (GTK_IS_MYFIXED (widget));
410
411 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
412 myfixed = GTK_MYFIXED (widget);
413
414 children = myfixed->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 (myfixed->bin_window);
430 }
431
432 static void
433 gtk_myfixed_realize (GtkWidget *widget)
434 {
435 GtkMyFixed *myfixed;
436 GdkWindowAttr attributes;
437 gint attributes_mask;
438 GtkMyFixedChild *child;
439 GList *children;
440
441 g_return_if_fail (widget != NULL);
442 g_return_if_fail (GTK_IS_MYFIXED (widget));
443
444 myfixed = GTK_MYFIXED (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 (myfixed->shadow_type == GTK_MYSHADOW_NONE)
456 {
457 /* no border, no changes to sizes */
458 } else
459 if (myfixed->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 myfixed->bin_window = gdk_window_new (widget->window,
512 &attributes, attributes_mask);
513 gdk_window_set_user_data (myfixed->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, myfixed->bin_window, GTK_STATE_NORMAL);
518
519 /* add filters for intercepting visibility and expose events */
520 gdk_window_add_filter (widget->window, gtk_myfixed_main_filter, myfixed);
521 gdk_window_add_filter (myfixed->bin_window, gtk_myfixed_filter, myfixed);
522
523 /* we NEED gravity or we'll give up */
524 gravity_works = gdk_window_set_static_gravities (myfixed->bin_window, TRUE);
525
526 /* cannot be done before realisation */
527 children = myfixed->children;
528 while (children)
529 {
530 child = children->data;
531 children = children->next;
532
533 gtk_widget_set_parent_window (child->widget, myfixed->bin_window);
534 }
535 }
536
537 static void
538 gtk_myfixed_unrealize (GtkWidget *widget)
539 {
540 GtkMyFixed *myfixed;
541
542 g_return_if_fail (widget != NULL);
543 g_return_if_fail (GTK_IS_MYFIXED (widget));
544
545 myfixed = GTK_MYFIXED (widget);
546
547 gdk_window_set_user_data (myfixed->bin_window, NULL);
548 gdk_window_destroy (myfixed->bin_window);
549 myfixed->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_myfixed_size_request (GtkWidget *widget,
557 GtkRequisition *requisition)
558 {
559 GtkMyFixed *myfixed;
560 GtkMyFixedChild *child;
561 GList *children;
562 GtkRequisition child_requisition;
563
564 g_return_if_fail (widget != NULL);
565 g_return_if_fail (GTK_IS_MYFIXED (widget));
566 g_return_if_fail (requisition != NULL);
567
568 myfixed = GTK_MYFIXED (widget);
569
570 children = myfixed->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_myfixed_size_allocate (GtkWidget *widget,
590 GtkAllocation *allocation)
591 {
592 GtkMyFixed *myfixed;
593 gint border;
594 gint x,y,w,h;
595 GtkMyFixedChild *child;
596 GtkAllocation child_allocation;
597 GList *children;
598
599 g_return_if_fail (widget != NULL);
600 g_return_if_fail (GTK_IS_MYFIXED(widget));
601 g_return_if_fail (allocation != NULL);
602
603 myfixed = GTK_MYFIXED (widget);
604
605 children = myfixed->children;
606 while (children)
607 {
608 child = children->data;
609 children = children->next;
610
611 gtk_myfixed_position_child (myfixed, child);
612 gtk_myfixed_allocate_child (myfixed, child);
613 }
614
615 widget->allocation = *allocation;
616
617 if (myfixed->shadow_type == GTK_MYSHADOW_NONE)
618 border = 0;
619 else
620 if (myfixed->shadow_type == GTK_MYSHADOW_THIN)
621 border = 1;
622 else
623 border = 2;
624
625 x = allocation->x + border;
626 y = allocation->y + border;
627 w = allocation->width - border*2;
628 h = allocation->height - border*2;
629
630 if (GTK_WIDGET_REALIZED (widget))
631 {
632 gdk_window_move_resize( widget->window, x, y, w, h );
633 gdk_window_move_resize( myfixed->bin_window, 0, 0, w, h );
634 }
635 }
636
637 static void
638 gtk_myfixed_draw (GtkWidget *widget,
639 GdkRectangle *area)
640 {
641 GtkMyFixed *myfixed;
642 GtkMyFixedChild *child;
643 GdkRectangle child_area;
644 GList *children;
645
646 g_return_if_fail (widget != NULL);
647 g_return_if_fail (GTK_IS_MYFIXED (widget));
648
649 myfixed = GTK_MYFIXED (widget);
650
651 children = myfixed->children;
652 if ( !(GTK_WIDGET_APP_PAINTABLE (widget)) &&
653 (myfixed->clear_on_draw))
654 {
655 gdk_window_clear_area( myfixed->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 /*
670 static void
671 gtk_myfixed_draw_border (GtkMyFixed *myfixed)
672 {
673 GtkWidget *widget;
674
675 widget = GTK_WIDGET(myfixed);
676
677 if (myfixed->shadow_type == GTK_MYSHADOW_NONE)
678 return;
679
680 if (myfixed->shadow_type == GTK_MYSHADOW_OUT)
681 {
682 gtk_draw_shadow( widget->style,
683 widget->window,
684 GTK_STATE_NORMAL,
685 GTK_SHADOW_OUT,
686 0, 0,
687 widget->allocation.width,
688 widget->allocation.height );
689 return;
690 }
691
692 if (myfixed->shadow_type == GTK_MYSHADOW_IN)
693 {
694 gtk_draw_shadow( widget->style,
695 widget->window,
696 GTK_STATE_NORMAL,
697 GTK_SHADOW_IN,
698 0, 0,
699 widget->allocation.width,
700 widget->allocation.height );
701 return;
702 }
703
704 if (myfixed->shadow_type == GTK_MYSHADOW_THIN)
705 {
706 GdkGC *gc;
707 gc = gdk_gc_new( widget->window );
708 gdk_gc_set_foreground( gc, &widget->style->black );
709 gdk_draw_rectangle( widget->window, gc, FALSE,
710 0, 0,
711 widget->allocation.width-1,
712 widget->allocation.height-1 );
713 gdk_gc_unref( gc );
714 return;
715 }
716 }
717 */
718
719 static gint
720 gtk_myfixed_expose (GtkWidget *widget,
721 GdkEventExpose *event)
722 {
723 GtkMyFixed *myfixed;
724 GtkMyFixedChild *child;
725 GdkEventExpose child_event;
726 GList *children;
727
728 g_return_val_if_fail (widget != NULL, FALSE);
729 g_return_val_if_fail (GTK_IS_MYFIXED (widget), FALSE);
730 g_return_val_if_fail (event != NULL, FALSE);
731
732 myfixed = GTK_MYFIXED (widget);
733
734 /*
735 if (event->window == widget->window)
736 {
737 gtk_myfixed_draw_border( myfixed );
738 return FALSE;
739 }
740 */
741
742 if (event->window != myfixed->bin_window)
743 return FALSE;
744
745 children = myfixed->children;
746 while (children)
747 {
748 child = children->data;
749 children = children->next;
750
751 child_event = *event;
752
753 if (GTK_WIDGET_NO_WINDOW (child->widget) &&
754 GTK_WIDGET_DRAWABLE (child->widget) &&
755 gtk_widget_intersect (child->widget, &event->area, &child_event.area))
756 {
757 gtk_widget_event (child->widget, (GdkEvent*) &child_event);
758 }
759 }
760
761 return FALSE;
762 }
763
764 static void
765 gtk_myfixed_add (GtkContainer *container,
766 GtkWidget *widget)
767 {
768 g_return_if_fail (container != NULL);
769 g_return_if_fail (GTK_IS_MYFIXED (container));
770 g_return_if_fail (widget != NULL);
771
772 gtk_myfixed_put (GTK_MYFIXED (container), widget, 0, 0, 20, 20 );
773 }
774
775 static void
776 gtk_myfixed_remove (GtkContainer *container,
777 GtkWidget *widget)
778 {
779 GtkMyFixed *myfixed;
780 GtkMyFixedChild *child;
781 GList *children;
782
783 g_return_if_fail (container != NULL);
784 g_return_if_fail (GTK_IS_MYFIXED (container));
785 g_return_if_fail (widget != NULL);
786
787 myfixed = GTK_MYFIXED (container);
788
789 children = myfixed->children;
790 while (children)
791 {
792 child = children->data;
793
794 if (child->widget == widget)
795 {
796 gtk_widget_unparent (widget);
797
798 /* security checks */
799 g_return_if_fail (GTK_IS_WIDGET (widget));
800
801 myfixed->children = g_list_remove_link (myfixed->children, children);
802 g_list_free (children);
803 g_free (child);
804
805 /* security checks */
806 g_return_if_fail (GTK_IS_WIDGET (widget));
807
808 GTK_PRIVATE_UNSET_FLAG (widget, GTK_IS_OFFSCREEN);
809
810 break;
811 }
812
813 children = children->next;
814 }
815 }
816
817 static void
818 gtk_myfixed_forall (GtkContainer *container,
819 gboolean include_internals,
820 GtkCallback callback,
821 gpointer callback_data)
822 {
823 GtkMyFixed *myfixed;
824 GtkMyFixedChild *child;
825 GList *children;
826
827 g_return_if_fail (container != NULL);
828 g_return_if_fail (GTK_IS_MYFIXED (container));
829 g_return_if_fail (callback != NULL);
830
831 myfixed = GTK_MYFIXED (container);
832
833 children = myfixed->children;
834 while (children)
835 {
836 child = children->data;
837 children = children->next;
838
839 (* callback) (child->widget, callback_data);
840 }
841 }
842
843
844 /* Operations on children
845 */
846
847 static void
848 gtk_myfixed_position_child (GtkMyFixed *myfixed,
849 GtkMyFixedChild *child)
850 {
851 gint x;
852 gint y;
853
854 x = child->x - myfixed->xoffset;
855 y = child->y - myfixed->yoffset;
856
857 if (IS_ONSCREEN (x,y))
858 {
859 if (GTK_WIDGET_MAPPED (myfixed) &&
860 GTK_WIDGET_VISIBLE (child->widget))
861 {
862 if (!GTK_WIDGET_MAPPED (child->widget))
863 gtk_widget_map (child->widget);
864 }
865
866 if (GTK_WIDGET_IS_OFFSCREEN (child->widget))
867 GTK_PRIVATE_UNSET_FLAG (child->widget, GTK_IS_OFFSCREEN);
868 }
869 else
870 {
871 if (!GTK_WIDGET_IS_OFFSCREEN (child->widget))
872 GTK_PRIVATE_SET_FLAG (child->widget, GTK_IS_OFFSCREEN);
873
874 if (GTK_WIDGET_MAPPED (child->widget))
875 gtk_widget_unmap (child->widget);
876 }
877 }
878
879 static void
880 gtk_myfixed_allocate_child (GtkMyFixed *myfixed,
881 GtkMyFixedChild *child)
882 {
883 GtkAllocation allocation;
884 GtkRequisition requisition;
885
886 allocation.x = child->x - myfixed->xoffset;
887 allocation.y = child->y - myfixed->yoffset;
888 gtk_widget_get_child_requisition (child->widget, &requisition);
889 allocation.width = requisition.width;
890 allocation.height = requisition.height;
891
892 gtk_widget_size_allocate (child->widget, &allocation);
893 }
894
895 static void
896 gtk_myfixed_position_children (GtkMyFixed *myfixed)
897 {
898 GList *tmp_list;
899
900 tmp_list = myfixed->children;
901 while (tmp_list)
902 {
903 GtkMyFixedChild *child = tmp_list->data;
904 tmp_list = tmp_list->next;
905
906 gtk_myfixed_position_child (myfixed, child);
907 }
908 }
909
910 static void
911 gtk_myfixed_adjust_allocations_recurse (GtkWidget *widget,
912 gpointer cb_data)
913 {
914 GtkMyFixedAdjData *data = cb_data;
915
916 widget->allocation.x += data->dx;
917 widget->allocation.y += data->dy;
918
919 if (GTK_WIDGET_NO_WINDOW (widget) &&
920 GTK_IS_CONTAINER (widget))
921 gtk_container_forall (GTK_CONTAINER (widget),
922 gtk_myfixed_adjust_allocations_recurse,
923 cb_data);
924 }
925
926 static void
927 gtk_myfixed_adjust_allocations (GtkMyFixed *myfixed,
928 gint dx,
929 gint dy)
930 {
931 GList *tmp_list;
932 GtkMyFixedAdjData data;
933
934 data.dx = dx;
935 data.dy = dy;
936
937 tmp_list = myfixed->children;
938 while (tmp_list)
939 {
940 GtkMyFixedChild *child = tmp_list->data;
941 tmp_list = tmp_list->next;
942
943 child->widget->allocation.x += dx;
944 child->widget->allocation.y += dy;
945
946 if (GTK_WIDGET_NO_WINDOW (child->widget) &&
947 GTK_IS_CONTAINER (child->widget))
948 gtk_container_forall (GTK_CONTAINER (child->widget),
949 gtk_myfixed_adjust_allocations_recurse,
950 &data);
951 }
952 }
953
954 /* Callbacks */
955
956 /* Send a synthetic expose event to the widget
957 */
958 static void
959 gtk_myfixed_expose_area (GtkMyFixed *myfixed,
960 gint x, gint y, gint width, gint height)
961 {
962 if (myfixed->visibility == GDK_VISIBILITY_UNOBSCURED)
963 {
964 GdkEventExpose event;
965
966 event.type = GDK_EXPOSE;
967 event.send_event = TRUE;
968 event.window = myfixed->bin_window;
969 event.count = 0;
970
971 event.area.x = x;
972 event.area.y = y;
973 event.area.width = width;
974 event.area.height = height;
975
976 gdk_window_ref (event.window);
977 gtk_widget_event (GTK_WIDGET (myfixed), (GdkEvent *)&event);
978 gdk_window_unref (event.window);
979 }
980 }
981
982 /* This function is used to find events to process while scrolling
983 */
984
985 static Bool
986 gtk_myfixed_expose_predicate (Display *display,
987 XEvent *xevent,
988 XPointer arg)
989 {
990 if ((xevent->type == Expose) ||
991 ((xevent->xany.window == *(Window *)arg) &&
992 (xevent->type == ConfigureNotify)))
993 return True;
994 else
995 return False;
996 }
997
998 /* This is the main routine to do the scrolling. Scrolling is
999 * done by "Guffaw" scrolling, as in the Mozilla XFE, with
1000 * a few modifications.
1001 *
1002 * The main improvement is that we keep track of whether we
1003 * are obscured or not. If not, we ignore the generated expose
1004 * events and instead do the exposes ourself, without having
1005 * to wait for a roundtrip to the server. This also provides
1006 * a limited form of expose-event compression, since we do
1007 * the affected area as one big chunk.
1008 */
1009
1010 void
1011 gtk_myfixed_scroll (GtkMyFixed *myfixed, gint dx, gint dy)
1012 {
1013 GtkWidget *widget;
1014 XEvent xevent;
1015
1016 gint x,y,w,h,border;
1017
1018 widget = GTK_WIDGET (myfixed);
1019
1020 myfixed->xoffset += dx;
1021 myfixed->yoffset += dy;
1022
1023 if (!GTK_WIDGET_MAPPED (myfixed))
1024 {
1025 gtk_myfixed_position_children (myfixed);
1026 return;
1027 }
1028
1029 gtk_myfixed_adjust_allocations (myfixed, -dx, -dy);
1030
1031 if (myfixed->shadow_type == GTK_MYSHADOW_NONE)
1032 border = 0;
1033 else
1034 if (myfixed->shadow_type == GTK_MYSHADOW_THIN)
1035 border = 1;
1036 else
1037 border = 2;
1038
1039 x = 0;
1040 y = 0;
1041 w = widget->allocation.width - 2*border;
1042 h = widget->allocation.height - 2*border;
1043
1044 if (dx > 0)
1045 {
1046 if (gravity_works)
1047 {
1048 gdk_window_resize (myfixed->bin_window,
1049 w + dx,
1050 h);
1051 gdk_window_move (myfixed->bin_window, x-dx, y);
1052 gdk_window_move_resize (myfixed->bin_window, x, y, w, h );
1053 }
1054 else
1055 {
1056 /* FIXME */
1057 }
1058
1059 gtk_myfixed_expose_area (myfixed,
1060 MAX ((gint)w - dx, 0),
1061 0,
1062 MIN (dx, w),
1063 h);
1064 }
1065 else if (dx < 0)
1066 {
1067 if (gravity_works)
1068 {
1069 gdk_window_move_resize (myfixed->bin_window,
1070 x + dx,
1071 y,
1072 w - dx,
1073 h);
1074 gdk_window_move (myfixed->bin_window, x, y);
1075 gdk_window_resize (myfixed->bin_window, w, h );
1076 }
1077 else
1078 {
1079 /* FIXME */
1080 }
1081
1082 gtk_myfixed_expose_area (myfixed,
1083 0,
1084 0,
1085 MIN (-dx, w),
1086 h);
1087 }
1088
1089 if (dy > 0)
1090 {
1091 if (gravity_works)
1092 {
1093 gdk_window_resize (myfixed->bin_window, w, h + dy);
1094 gdk_window_move (myfixed->bin_window, x, y-dy);
1095 gdk_window_move_resize (myfixed->bin_window,
1096 x, y, w, h );
1097 }
1098 else
1099 {
1100 /* FIXME */
1101 }
1102
1103 gtk_myfixed_expose_area (myfixed,
1104 0,
1105 MAX ((gint)h - dy, 0),
1106 w,
1107 MIN (dy, h));
1108 }
1109 else if (dy < 0)
1110 {
1111 if (gravity_works)
1112 {
1113 gdk_window_move_resize (myfixed->bin_window,
1114 x, y+dy, w, h - dy );
1115 gdk_window_move (myfixed->bin_window, x, y);
1116 gdk_window_resize (myfixed->bin_window, w, h );
1117 }
1118 else
1119 {
1120 /* FIXME */
1121 }
1122 gtk_myfixed_expose_area (myfixed,
1123 0,
1124 0,
1125 w,
1126 MIN (-dy, (gint)h));
1127 }
1128
1129 gtk_myfixed_position_children (myfixed);
1130
1131 /* We have to make sure that all exposes from this scroll get
1132 * processed before we scroll again, or the expose events will
1133 * have invalid coordinates.
1134 *
1135 * We also do expose events for other windows, since otherwise
1136 * their updating will fall behind the scrolling
1137 *
1138 * This also avoids a problem in pre-1.0 GTK where filters don't
1139 * have access to configure events that were compressed.
1140 */
1141
1142 gdk_flush();
1143 while (XCheckIfEvent(GDK_WINDOW_XDISPLAY (myfixed->bin_window),
1144 &xevent,
1145 gtk_myfixed_expose_predicate,
1146 (XPointer)&GDK_WINDOW_XWINDOW (myfixed->bin_window)))
1147 {
1148 GdkEvent event;
1149 GtkWidget *event_widget;
1150
1151 if ((xevent.xany.window == GDK_WINDOW_XWINDOW (myfixed->bin_window)) &&
1152 (gtk_myfixed_filter (&xevent, &event, myfixed) == GDK_FILTER_REMOVE))
1153 continue;
1154
1155 if (xevent.type == Expose)
1156 {
1157 event.expose.window = gdk_window_lookup (xevent.xany.window);
1158 gdk_window_get_user_data (event.expose.window,
1159 (gpointer *)&event_widget);
1160
1161 if (event_widget)
1162 {
1163 event.expose.type = GDK_EXPOSE;
1164 event.expose.area.x = xevent.xexpose.x;
1165 event.expose.area.y = xevent.xexpose.y;
1166 event.expose.area.width = xevent.xexpose.width;
1167 event.expose.area.height = xevent.xexpose.height;
1168 event.expose.count = xevent.xexpose.count;
1169
1170 gdk_window_ref (event.expose.window);
1171 gtk_widget_event (event_widget, &event);
1172 gdk_window_unref (event.expose.window);
1173 }
1174 }
1175 }
1176 }
1177
1178 /* The main event filter. Actually, we probably don't really need
1179 * to install this as a filter at all, since we are calling it
1180 * directly above in the expose-handling hack. But in case scrollbars
1181 * are fixed up in some manner...
1182 *
1183 * This routine identifies expose events that are generated when
1184 * we've temporarily moved the bin_window_origin, and translates
1185 * them or discards them, depending on whether we are obscured
1186 * or not.
1187 */
1188 static GdkFilterReturn
1189 gtk_myfixed_filter (GdkXEvent *gdk_xevent,
1190 GdkEvent *event,
1191 gpointer data)
1192 {
1193 XEvent *xevent;
1194 GtkMyFixed *myfixed;
1195
1196 xevent = (XEvent *)gdk_xevent;
1197 myfixed = GTK_MYFIXED (data);
1198
1199 switch (xevent->type)
1200 {
1201 case Expose:
1202 if (xevent->xexpose.serial == myfixed->configure_serial)
1203 {
1204 if (myfixed->visibility == GDK_VISIBILITY_UNOBSCURED)
1205 return GDK_FILTER_REMOVE;
1206 else
1207 {
1208 xevent->xexpose.x += myfixed->scroll_x;
1209 xevent->xexpose.y += myfixed->scroll_y;
1210
1211 break;
1212 }
1213 }
1214 break;
1215
1216 case ConfigureNotify:
1217 if ((xevent->xconfigure.x != 0) || (xevent->xconfigure.y != 0))
1218 {
1219 myfixed->configure_serial = xevent->xconfigure.serial;
1220 myfixed->scroll_x = xevent->xconfigure.x;
1221 myfixed->scroll_y = xevent->xconfigure.y;
1222 }
1223 break;
1224 }
1225
1226 return GDK_FILTER_CONTINUE;
1227 }
1228
1229 /* Although GDK does have a GDK_VISIBILITY_NOTIFY event,
1230 * there is no corresponding event in GTK, so we have
1231 * to get the events from a filter
1232 */
1233 static GdkFilterReturn
1234 gtk_myfixed_main_filter (GdkXEvent *gdk_xevent,
1235 GdkEvent *event,
1236 gpointer data)
1237 {
1238 XEvent *xevent;
1239 GtkMyFixed *myfixed;
1240
1241 xevent = (XEvent *)gdk_xevent;
1242 myfixed = GTK_MYFIXED (data);
1243
1244 if (xevent->type == VisibilityNotify)
1245 {
1246 switch (xevent->xvisibility.state)
1247 {
1248 case VisibilityFullyObscured:
1249 myfixed->visibility = GDK_VISIBILITY_FULLY_OBSCURED;
1250 break;
1251
1252 case VisibilityPartiallyObscured:
1253 myfixed->visibility = GDK_VISIBILITY_PARTIAL;
1254 break;
1255
1256 case VisibilityUnobscured:
1257 myfixed->visibility = GDK_VISIBILITY_UNOBSCURED;
1258 break;
1259 }
1260
1261 return GDK_FILTER_REMOVE;
1262 }
1263
1264
1265 return GDK_FILTER_CONTINUE;
1266 }
1267
1268
1269
1270
1271 #ifdef __cplusplus
1272 }
1273 #endif /* __cplusplus */
1274