New scrolling code.
[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
192 GtkWidget*
193 gtk_myfixed_new ()
194 {
195 GtkMyFixed *myfixed;
196
197 myfixed = gtk_type_new (gtk_myfixed_get_type ());
198
199 return GTK_WIDGET (myfixed);
200 }
201
202 void
203 gtk_myfixed_scroll_set_adjustments (GtkMyFixed *myfixed,
204 GtkAdjustment *hadj,
205 GtkAdjustment *vadj)
206 {
207 /* We handle scrolling in the wxScrolledWindow, not here. */
208 }
209
210 void
211 gtk_myfixed_set_shadow_type (GtkMyFixed *myfixed,
212 GtkMyShadowType type)
213 {
214 g_return_if_fail (myfixed != NULL);
215 g_return_if_fail (GTK_IS_MYFIXED (myfixed));
216
217 if ((GtkMyShadowType) myfixed->shadow_type != type)
218 {
219 myfixed->shadow_type = type;
220
221 if (GTK_WIDGET_VISIBLE (myfixed))
222 {
223 gtk_widget_size_allocate (GTK_WIDGET (myfixed), &(GTK_WIDGET (myfixed)->allocation));
224 gtk_widget_queue_draw (GTK_WIDGET (myfixed));
225 }
226 }
227 }
228
229 void
230 gtk_myfixed_put (GtkMyFixed *myfixed,
231 GtkWidget *widget,
232 gint x,
233 gint y,
234 gint width,
235 gint height)
236 {
237 GtkMyFixedChild *child_info;
238
239 g_return_if_fail (myfixed != NULL);
240 g_return_if_fail (GTK_IS_MYFIXED (myfixed));
241 g_return_if_fail (widget != NULL);
242
243 child_info = g_new (GtkMyFixedChild, 1);
244
245 child_info->widget = widget;
246 child_info->x = x;
247 child_info->y = y;
248 child_info->width = width;
249 child_info->height = height;
250
251 myfixed->children = g_list_append (myfixed->children, child_info);
252
253 gtk_widget_set_parent (widget, GTK_WIDGET (myfixed));
254
255 if (GTK_WIDGET_REALIZED (myfixed))
256 gtk_widget_set_parent_window (widget, myfixed->bin_window);
257
258 if (!IS_ONSCREEN (x, y))
259 GTK_PRIVATE_SET_FLAG (widget, GTK_IS_OFFSCREEN);
260
261 if (GTK_WIDGET_REALIZED (myfixed))
262 gtk_widget_realize (widget);
263
264 gtk_widget_set_usize (widget, width, height);
265
266 if (GTK_WIDGET_VISIBLE (myfixed) && GTK_WIDGET_VISIBLE (widget))
267 {
268 if (GTK_WIDGET_MAPPED (myfixed))
269 gtk_widget_map (widget);
270
271 gtk_widget_queue_resize (widget);
272 }
273 }
274
275 void
276 gtk_myfixed_move (GtkMyFixed *myfixed,
277 GtkWidget *widget,
278 gint x,
279 gint y)
280 {
281 GtkMyFixedChild *child;
282 GList *children;
283
284 g_return_if_fail (myfixed != NULL);
285 g_return_if_fail (GTK_IS_MYFIXED (myfixed));
286 g_return_if_fail (widget != NULL);
287
288 children = myfixed->children;
289 while (children)
290 {
291 child = children->data;
292 children = children->next;
293
294 if (child->widget == widget)
295 {
296 if ((child->x == x) && (child->y == y))
297 break;
298
299 child->x = x;
300 child->y = y;
301
302 if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_VISIBLE (myfixed))
303 gtk_widget_queue_resize (widget);
304 break;
305 }
306 }
307 }
308
309 void
310 gtk_myfixed_resize (GtkMyFixed *myfixed,
311 GtkWidget *widget,
312 gint width,
313 gint height)
314 {
315 GtkMyFixedChild *child;
316 GList *children;
317
318 g_return_if_fail (myfixed != NULL);
319 g_return_if_fail (GTK_IS_MYFIXED (myfixed));
320 g_return_if_fail (widget != NULL);
321
322 children = myfixed->children;
323 while (children)
324 {
325 child = children->data;
326 children = children->next;
327
328 if (child->widget == widget)
329 {
330 if ((child->width == width) && (child->height == height))
331 break;
332
333 child->width = width;
334 child->height = height;
335
336 gtk_widget_set_usize (widget, width, height);
337
338 if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_VISIBLE (myfixed))
339 gtk_widget_queue_resize (widget);
340 break;
341 }
342 }
343 }
344
345 void
346 gtk_myfixed_set_size (GtkMyFixed *myfixed,
347 GtkWidget *widget,
348 gint x,
349 gint y,
350 gint width,
351 gint height)
352 {
353 GtkMyFixedChild *child;
354 GList *children;
355 GtkAllocation child_allocation;
356
357 g_return_if_fail (myfixed != NULL);
358 g_return_if_fail (GTK_IS_MYFIXED (myfixed));
359 g_return_if_fail (widget != NULL);
360
361 children = myfixed->children;
362 while (children)
363 {
364 child = children->data;
365 children = children->next;
366
367 if (child->widget == widget)
368 {
369 if ((child->x == x) &&
370 (child->y == y) &&
371 (child->width == width) &&
372 (child->height == height)) return;
373
374 child->x = x;
375 child->y = y;
376 child->width = width;
377 child->height = height;
378
379 gtk_widget_set_usize (widget, width, height);
380
381 if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_VISIBLE (myfixed))
382 gtk_widget_queue_resize (widget);
383
384 break;
385 }
386 }
387 }
388
389 static void
390 gtk_myfixed_map (GtkWidget *widget)
391 {
392 GtkMyFixed *myfixed;
393 GtkMyFixedChild *child;
394 GList *children;
395
396 g_return_if_fail (widget != NULL);
397 g_return_if_fail (GTK_IS_MYFIXED (widget));
398
399 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
400 myfixed = GTK_MYFIXED (widget);
401
402 children = myfixed->children;
403 while (children)
404 {
405 child = children->data;
406 children = children->next;
407
408 if ( GTK_WIDGET_VISIBLE (child->widget) &&
409 !GTK_WIDGET_MAPPED (child->widget) &&
410 !GTK_WIDGET_IS_OFFSCREEN (child->widget))
411 {
412 gtk_widget_map (child->widget);
413 }
414 }
415
416 gdk_window_show (widget->window);
417 gdk_window_show (myfixed->bin_window);
418 }
419
420 static void
421 gtk_myfixed_realize (GtkWidget *widget)
422 {
423 GtkMyFixed *myfixed;
424 GdkWindowAttr attributes;
425 gint attributes_mask;
426 GtkMyFixedChild *child;
427 GList *children;
428
429 g_return_if_fail (widget != NULL);
430 g_return_if_fail (GTK_IS_MYFIXED (widget));
431
432 myfixed = GTK_MYFIXED (widget);
433
434 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
435
436 attributes.window_type = GDK_WINDOW_CHILD;
437
438 attributes.x = widget->allocation.x;
439 attributes.y = widget->allocation.y;
440 attributes.width = widget->allocation.width;
441 attributes.height = widget->allocation.height;
442
443 if (myfixed->shadow_type == GTK_MYSHADOW_NONE)
444 {
445 /* no border, no changes to sizes */
446 } else
447 if (myfixed->shadow_type == GTK_MYSHADOW_THIN)
448 {
449 /* GTK_MYSHADOW_THIN == wxSIMPLE_BORDER */
450 attributes.x += 1;
451 attributes.y += 1;
452 attributes.width -= 2;
453 attributes.height -= 2;
454 } else
455 {
456 /* GTK_MYSHADOW_IN == wxSUNKEN_BORDER */
457 /* GTK_MYSHADOW_OUT == wxRAISED_BORDER */
458 attributes.x += 2;
459 attributes.y += 2;
460 attributes.width -= 4;
461 attributes.height -= 4;
462 }
463
464 /* minimal size */
465 if (attributes.width < 2) attributes.width = 2;
466 if (attributes.height < 2) attributes.height = 2;
467
468 attributes.wclass = GDK_INPUT_OUTPUT;
469 attributes.visual = gtk_widget_get_visual (widget);
470 attributes.colormap = gtk_widget_get_colormap (widget);
471 attributes.event_mask =
472 GDK_VISIBILITY_NOTIFY_MASK;
473 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
474
475 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
476 &attributes, attributes_mask);
477 gdk_window_set_user_data (widget->window, widget);
478
479 attributes.x = 0;
480 attributes.y = 0;
481
482 attributes.event_mask = gtk_widget_get_events (widget);
483 attributes.event_mask |=
484 GDK_EXPOSURE_MASK |
485 GDK_POINTER_MOTION_MASK |
486 GDK_POINTER_MOTION_HINT_MASK |
487 GDK_BUTTON_MOTION_MASK |
488 GDK_BUTTON1_MOTION_MASK |
489 GDK_BUTTON2_MOTION_MASK |
490 GDK_BUTTON3_MOTION_MASK |
491 GDK_BUTTON_PRESS_MASK |
492 GDK_BUTTON_RELEASE_MASK |
493 GDK_KEY_PRESS_MASK |
494 GDK_KEY_RELEASE_MASK |
495 GDK_ENTER_NOTIFY_MASK |
496 GDK_LEAVE_NOTIFY_MASK |
497 GDK_FOCUS_CHANGE_MASK;
498
499 myfixed->bin_window = gdk_window_new (widget->window,
500 &attributes, attributes_mask);
501 gdk_window_set_user_data (myfixed->bin_window, widget);
502
503 widget->style = gtk_style_attach (widget->style, widget->window);
504 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
505 gtk_style_set_background (widget->style, myfixed->bin_window, GTK_STATE_NORMAL);
506
507 /* add filters for intercepting visibility and expose events */
508 gdk_window_add_filter (widget->window, gtk_myfixed_main_filter, myfixed);
509 gdk_window_add_filter (myfixed->bin_window, gtk_myfixed_filter, myfixed);
510
511 /* we NEED gravity or we'll give up */
512 gravity_works = gdk_window_set_static_gravities (myfixed->bin_window, TRUE);
513
514 /* cannot be done before realisation */
515 children = myfixed->children;
516 while (children)
517 {
518 child = children->data;
519 children = children->next;
520
521 gtk_widget_set_parent_window (child->widget, myfixed->bin_window);
522 }
523 }
524
525 static void
526 gtk_myfixed_unrealize (GtkWidget *widget)
527 {
528 GtkMyFixed *myfixed;
529
530 g_return_if_fail (widget != NULL);
531 g_return_if_fail (GTK_IS_MYFIXED (widget));
532
533 myfixed = GTK_MYFIXED (widget);
534
535 gdk_window_set_user_data (myfixed->bin_window, NULL);
536 gdk_window_destroy (myfixed->bin_window);
537 myfixed->bin_window = NULL;
538
539 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
540 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
541 }
542
543 static void
544 gtk_myfixed_size_request (GtkWidget *widget,
545 GtkRequisition *requisition)
546 {
547 GtkMyFixed *myfixed;
548 GtkMyFixedChild *child;
549 GList *children;
550 GtkRequisition child_requisition;
551
552 g_return_if_fail (widget != NULL);
553 g_return_if_fail (GTK_IS_MYFIXED (widget));
554 g_return_if_fail (requisition != NULL);
555
556 myfixed = GTK_MYFIXED (widget);
557
558 children = myfixed->children;
559 while (children)
560 {
561 child = children->data;
562 children = children->next;
563
564 if (GTK_WIDGET_VISIBLE (child->widget))
565 {
566 gtk_widget_size_request (child->widget, &child_requisition);
567 }
568 }
569
570 /* request very little, I'm not sure if requesting nothing
571 will always have positive effects on stability... */
572 requisition->width = 2;
573 requisition->height = 2;
574 }
575
576 static void
577 gtk_myfixed_size_allocate (GtkWidget *widget,
578 GtkAllocation *allocation)
579 {
580 GtkMyFixed *myfixed;
581 gint border;
582 gint x,y,w,h;
583 GtkMyFixedChild *child;
584 GtkAllocation child_allocation;
585 GList *children;
586
587 g_return_if_fail (widget != NULL);
588 g_return_if_fail (GTK_IS_MYFIXED(widget));
589 g_return_if_fail (allocation != NULL);
590
591 myfixed = GTK_MYFIXED (widget);
592
593 children = myfixed->children;
594 while (children)
595 {
596 child = children->data;
597 children = children->next;
598
599 gtk_myfixed_position_child (myfixed, child);
600 gtk_myfixed_allocate_child (myfixed, child);
601 }
602
603 widget->allocation = *allocation;
604
605 if (myfixed->shadow_type == GTK_MYSHADOW_NONE)
606 border = 0;
607 else
608 if (myfixed->shadow_type == GTK_MYSHADOW_THIN)
609 border = 1;
610 else
611 border = 2;
612
613 x = allocation->x + border;
614 y = allocation->y + border;
615 w = allocation->width - border*2;
616 h = allocation->height - border*2;
617
618 if (GTK_WIDGET_REALIZED (widget))
619 {
620 gdk_window_move_resize( widget->window, x, y, w, h );
621 gdk_window_move_resize( myfixed->bin_window, 0, 0, w, h );
622 }
623 }
624
625 static void
626 gtk_myfixed_draw (GtkWidget *widget,
627 GdkRectangle *area)
628 {
629 GtkMyFixed *myfixed;
630 GtkMyFixedChild *child;
631 GdkRectangle child_area;
632 GList *children;
633
634 g_return_if_fail (widget != NULL);
635 g_return_if_fail (GTK_IS_MYFIXED (widget));
636
637 myfixed = GTK_MYFIXED (widget);
638
639 children = myfixed->children;
640 if ( !(GTK_WIDGET_APP_PAINTABLE (widget)) )
641 {
642 gdk_window_clear_area( myfixed->bin_window,
643 area->x, area->y, area->width, area->height);
644 }
645
646 while (children)
647 {
648 child = children->data;
649 children = children->next;
650
651 if (gtk_widget_intersect (child->widget, area, &child_area))
652 gtk_widget_draw (child->widget, &child_area);
653 }
654 }
655
656 /*
657 static void
658 gtk_myfixed_draw_border (GtkMyFixed *myfixed)
659 {
660 GtkWidget *widget;
661
662 widget = GTK_WIDGET(myfixed);
663
664 if (myfixed->shadow_type == GTK_MYSHADOW_NONE)
665 return;
666
667 if (myfixed->shadow_type == GTK_MYSHADOW_OUT)
668 {
669 gtk_draw_shadow( widget->style,
670 widget->window,
671 GTK_STATE_NORMAL,
672 GTK_SHADOW_OUT,
673 0, 0,
674 widget->allocation.width,
675 widget->allocation.height );
676 return;
677 }
678
679 if (myfixed->shadow_type == GTK_MYSHADOW_IN)
680 {
681 gtk_draw_shadow( widget->style,
682 widget->window,
683 GTK_STATE_NORMAL,
684 GTK_SHADOW_IN,
685 0, 0,
686 widget->allocation.width,
687 widget->allocation.height );
688 return;
689 }
690
691 if (myfixed->shadow_type == GTK_MYSHADOW_THIN)
692 {
693 GdkGC *gc;
694 gc = gdk_gc_new( widget->window );
695 gdk_gc_set_foreground( gc, &widget->style->black );
696 gdk_draw_rectangle( widget->window, gc, FALSE,
697 0, 0,
698 widget->allocation.width-1,
699 widget->allocation.height-1 );
700 gdk_gc_unref( gc );
701 return;
702 }
703 }
704 */
705
706 static gint
707 gtk_myfixed_expose (GtkWidget *widget,
708 GdkEventExpose *event)
709 {
710 GtkMyFixed *myfixed;
711 GtkMyFixedChild *child;
712 GdkEventExpose child_event;
713 GList *children;
714
715 g_return_val_if_fail (widget != NULL, FALSE);
716 g_return_val_if_fail (GTK_IS_MYFIXED (widget), FALSE);
717 g_return_val_if_fail (event != NULL, FALSE);
718
719 myfixed = GTK_MYFIXED (widget);
720
721 /*
722 if (event->window == widget->window)
723 {
724 gtk_myfixed_draw_border( myfixed );
725 return FALSE;
726 }
727 */
728
729 if (event->window != myfixed->bin_window)
730 return FALSE;
731
732 children = myfixed->children;
733 while (children)
734 {
735 child = children->data;
736 children = children->next;
737
738 child_event = *event;
739
740 if (GTK_WIDGET_NO_WINDOW (child->widget) &&
741 GTK_WIDGET_DRAWABLE (child->widget) &&
742 gtk_widget_intersect (child->widget, &event->area, &child_event.area))
743 {
744 gtk_widget_event (child->widget, (GdkEvent*) &child_event);
745 }
746 }
747
748 return FALSE;
749 }
750
751 static void
752 gtk_myfixed_add (GtkContainer *container,
753 GtkWidget *widget)
754 {
755 g_return_if_fail (container != NULL);
756 g_return_if_fail (GTK_IS_MYFIXED (container));
757 g_return_if_fail (widget != NULL);
758
759 gtk_myfixed_put (GTK_MYFIXED (container), widget, 0, 0, 20, 20 );
760 }
761
762 static void
763 gtk_myfixed_remove (GtkContainer *container,
764 GtkWidget *widget)
765 {
766 GtkMyFixed *myfixed;
767 GtkMyFixedChild *child;
768 GList *children;
769
770 g_return_if_fail (container != NULL);
771 g_return_if_fail (GTK_IS_MYFIXED (container));
772 g_return_if_fail (widget != NULL);
773
774 myfixed = GTK_MYFIXED (container);
775
776 children = myfixed->children;
777 while (children)
778 {
779 child = children->data;
780
781 if (child->widget == widget)
782 {
783 gtk_widget_unparent (widget);
784
785 /* security checks */
786 g_return_if_fail (GTK_IS_WIDGET (widget));
787
788 myfixed->children = g_list_remove_link (myfixed->children, children);
789 g_list_free (children);
790 g_free (child);
791
792 /* security checks */
793 g_return_if_fail (GTK_IS_WIDGET (widget));
794
795 GTK_PRIVATE_UNSET_FLAG (widget, GTK_IS_OFFSCREEN);
796
797 break;
798 }
799
800 children = children->next;
801 }
802 }
803
804 static void
805 gtk_myfixed_forall (GtkContainer *container,
806 gboolean include_internals,
807 GtkCallback callback,
808 gpointer callback_data)
809 {
810 GtkMyFixed *myfixed;
811 GtkMyFixedChild *child;
812 GList *children;
813
814 g_return_if_fail (container != NULL);
815 g_return_if_fail (GTK_IS_MYFIXED (container));
816 g_return_if_fail (callback != NULL);
817
818 myfixed = GTK_MYFIXED (container);
819
820 children = myfixed->children;
821 while (children)
822 {
823 child = children->data;
824 children = children->next;
825
826 (* callback) (child->widget, callback_data);
827 }
828 }
829
830
831 /* Operations on children
832 */
833
834 static void
835 gtk_myfixed_position_child (GtkMyFixed *myfixed,
836 GtkMyFixedChild *child)
837 {
838 gint x;
839 gint y;
840
841 x = child->x - myfixed->xoffset;
842 y = child->y - myfixed->yoffset;
843
844 if (IS_ONSCREEN (x,y))
845 {
846 if (GTK_WIDGET_MAPPED (myfixed) &&
847 GTK_WIDGET_VISIBLE (child->widget))
848 {
849 if (!GTK_WIDGET_MAPPED (child->widget))
850 gtk_widget_map (child->widget);
851 }
852
853 if (GTK_WIDGET_IS_OFFSCREEN (child->widget))
854 GTK_PRIVATE_UNSET_FLAG (child->widget, GTK_IS_OFFSCREEN);
855 }
856 else
857 {
858 if (!GTK_WIDGET_IS_OFFSCREEN (child->widget))
859 GTK_PRIVATE_SET_FLAG (child->widget, GTK_IS_OFFSCREEN);
860
861 if (GTK_WIDGET_MAPPED (child->widget))
862 gtk_widget_unmap (child->widget);
863 }
864 }
865
866 static void
867 gtk_myfixed_allocate_child (GtkMyFixed *myfixed,
868 GtkMyFixedChild *child)
869 {
870 GtkAllocation allocation;
871 GtkRequisition requisition;
872
873 allocation.x = child->x - myfixed->xoffset;
874 allocation.y = child->y - myfixed->yoffset;
875 gtk_widget_get_child_requisition (child->widget, &requisition);
876 allocation.width = requisition.width;
877 allocation.height = requisition.height;
878
879 gtk_widget_size_allocate (child->widget, &allocation);
880 }
881
882 static void
883 gtk_myfixed_position_children (GtkMyFixed *myfixed)
884 {
885 GList *tmp_list;
886
887 tmp_list = myfixed->children;
888 while (tmp_list)
889 {
890 GtkMyFixedChild *child = tmp_list->data;
891 tmp_list = tmp_list->next;
892
893 gtk_myfixed_position_child (myfixed, child);
894 }
895 }
896
897 static void
898 gtk_myfixed_adjust_allocations_recurse (GtkWidget *widget,
899 gpointer cb_data)
900 {
901 GtkMyFixedAdjData *data = cb_data;
902
903 widget->allocation.x += data->dx;
904 widget->allocation.y += data->dy;
905
906 if (GTK_WIDGET_NO_WINDOW (widget) &&
907 GTK_IS_CONTAINER (widget))
908 gtk_container_forall (GTK_CONTAINER (widget),
909 gtk_myfixed_adjust_allocations_recurse,
910 cb_data);
911 }
912
913 static void
914 gtk_myfixed_adjust_allocations (GtkMyFixed *myfixed,
915 gint dx,
916 gint dy)
917 {
918 GList *tmp_list;
919 GtkMyFixedAdjData data;
920
921 data.dx = dx;
922 data.dy = dy;
923
924 tmp_list = myfixed->children;
925 while (tmp_list)
926 {
927 GtkMyFixedChild *child = tmp_list->data;
928 tmp_list = tmp_list->next;
929
930 child->widget->allocation.x += dx;
931 child->widget->allocation.y += dy;
932
933 if (GTK_WIDGET_NO_WINDOW (child->widget) &&
934 GTK_IS_CONTAINER (child->widget))
935 gtk_container_forall (GTK_CONTAINER (child->widget),
936 gtk_myfixed_adjust_allocations_recurse,
937 &data);
938 }
939 }
940
941 /* Callbacks */
942
943 /* Send a synthetic expose event to the widget
944 */
945 static void
946 gtk_myfixed_expose_area (GtkMyFixed *myfixed,
947 gint x, gint y, gint width, gint height)
948 {
949 if (myfixed->visibility == GDK_VISIBILITY_UNOBSCURED)
950 {
951 GdkEventExpose event;
952
953 event.type = GDK_EXPOSE;
954 event.send_event = TRUE;
955 event.window = myfixed->bin_window;
956 event.count = 0;
957
958 event.area.x = x;
959 event.area.y = y;
960 event.area.width = width;
961 event.area.height = height;
962
963 gdk_window_ref (event.window);
964 gtk_widget_event (GTK_WIDGET (myfixed), (GdkEvent *)&event);
965 gdk_window_unref (event.window);
966 }
967 }
968
969 /* This function is used to find events to process while scrolling
970 */
971
972 static Bool
973 gtk_myfixed_expose_predicate (Display *display,
974 XEvent *xevent,
975 XPointer arg)
976 {
977 if ((xevent->type == Expose) ||
978 ((xevent->xany.window == *(Window *)arg) &&
979 (xevent->type == ConfigureNotify)))
980 return True;
981 else
982 return False;
983 }
984
985 /* This is the main routine to do the scrolling. Scrolling is
986 * done by "Guffaw" scrolling, as in the Mozilla XFE, with
987 * a few modifications.
988 *
989 * The main improvement is that we keep track of whether we
990 * are obscured or not. If not, we ignore the generated expose
991 * events and instead do the exposes ourself, without having
992 * to wait for a roundtrip to the server. This also provides
993 * a limited form of expose-event compression, since we do
994 * the affected area as one big chunk.
995 */
996
997 void
998 gtk_myfixed_scroll (GtkMyFixed *myfixed, gint dx, gint dy)
999 {
1000 GtkWidget *widget;
1001 XEvent xevent;
1002
1003 gint x,y,w,h,border;
1004
1005 widget = GTK_WIDGET (myfixed);
1006
1007 myfixed->xoffset += dx;
1008 myfixed->yoffset += dy;
1009
1010 if (!GTK_WIDGET_MAPPED (myfixed))
1011 {
1012 gtk_myfixed_position_children (myfixed);
1013 return;
1014 }
1015
1016 gtk_myfixed_adjust_allocations (myfixed, -dx, -dy);
1017
1018 if (myfixed->shadow_type == GTK_MYSHADOW_NONE)
1019 border = 0;
1020 else
1021 if (myfixed->shadow_type == GTK_MYSHADOW_THIN)
1022 border = 1;
1023 else
1024 border = 2;
1025
1026 x = 0;
1027 y = 0;
1028 w = widget->allocation.width - 2*border;
1029 h = widget->allocation.height - 2*border;
1030
1031 if (dx > 0)
1032 {
1033 if (gravity_works)
1034 {
1035 gdk_window_resize (myfixed->bin_window,
1036 w + dx,
1037 h);
1038 gdk_window_move (myfixed->bin_window, x-dx, y);
1039 gdk_window_move_resize (myfixed->bin_window, x, y, w, h );
1040 }
1041 else
1042 {
1043 /* FIXME */
1044 }
1045
1046 gtk_myfixed_expose_area (myfixed,
1047 MAX ((gint)w - dx, 0),
1048 0,
1049 MIN (dx, w),
1050 h);
1051 }
1052 else if (dx < 0)
1053 {
1054 if (gravity_works)
1055 {
1056 gdk_window_move_resize (myfixed->bin_window,
1057 x + dx,
1058 y,
1059 w - dx,
1060 h);
1061 gdk_window_move (myfixed->bin_window, x, y);
1062 gdk_window_resize (myfixed->bin_window, w, h );
1063 }
1064 else
1065 {
1066 /* FIXME */
1067 }
1068
1069 gtk_myfixed_expose_area (myfixed,
1070 0,
1071 0,
1072 MIN (-dx, w),
1073 h);
1074 }
1075
1076 if (dy > 0)
1077 {
1078 if (gravity_works)
1079 {
1080 gdk_window_resize (myfixed->bin_window, w, h + dy);
1081 gdk_window_move (myfixed->bin_window, x, y-dy);
1082 gdk_window_move_resize (myfixed->bin_window,
1083 x, y, w, h );
1084 }
1085 else
1086 {
1087 /* FIXME */
1088 }
1089
1090 gtk_myfixed_expose_area (myfixed,
1091 0,
1092 MAX ((gint)h - dy, 0),
1093 w,
1094 MIN (dy, h));
1095 }
1096 else if (dy < 0)
1097 {
1098 if (gravity_works)
1099 {
1100 gdk_window_move_resize (myfixed->bin_window,
1101 x, y+dy, w, h - dy );
1102 gdk_window_move (myfixed->bin_window, x, y);
1103 gdk_window_resize (myfixed->bin_window, w, h );
1104 }
1105 else
1106 {
1107 /* FIXME */
1108 }
1109 gtk_myfixed_expose_area (myfixed,
1110 0,
1111 0,
1112 w,
1113 MIN (-dy, (gint)h));
1114 }
1115
1116 gtk_myfixed_position_children (myfixed);
1117
1118 /* We have to make sure that all exposes from this scroll get
1119 * processed before we scroll again, or the expose events will
1120 * have invalid coordinates.
1121 *
1122 * We also do expose events for other windows, since otherwise
1123 * their updating will fall behind the scrolling
1124 *
1125 * This also avoids a problem in pre-1.0 GTK where filters don't
1126 * have access to configure events that were compressed.
1127 */
1128
1129 gdk_flush();
1130 while (XCheckIfEvent(GDK_WINDOW_XDISPLAY (myfixed->bin_window),
1131 &xevent,
1132 gtk_myfixed_expose_predicate,
1133 (XPointer)&GDK_WINDOW_XWINDOW (myfixed->bin_window)))
1134 {
1135 GdkEvent event;
1136 GtkWidget *event_widget;
1137
1138 if ((xevent.xany.window == GDK_WINDOW_XWINDOW (myfixed->bin_window)) &&
1139 (gtk_myfixed_filter (&xevent, &event, myfixed) == GDK_FILTER_REMOVE))
1140 continue;
1141
1142 if (xevent.type == Expose)
1143 {
1144 event.expose.window = gdk_window_lookup (xevent.xany.window);
1145 gdk_window_get_user_data (event.expose.window,
1146 (gpointer *)&event_widget);
1147
1148 if (event_widget)
1149 {
1150 event.expose.type = GDK_EXPOSE;
1151 event.expose.area.x = xevent.xexpose.x;
1152 event.expose.area.y = xevent.xexpose.y;
1153 event.expose.area.width = xevent.xexpose.width;
1154 event.expose.area.height = xevent.xexpose.height;
1155 event.expose.count = xevent.xexpose.count;
1156
1157 gdk_window_ref (event.expose.window);
1158 gtk_widget_event (event_widget, &event);
1159 gdk_window_unref (event.expose.window);
1160 }
1161 }
1162 }
1163 }
1164
1165 /* The main event filter. Actually, we probably don't really need
1166 * to install this as a filter at all, since we are calling it
1167 * directly above in the expose-handling hack. But in case scrollbars
1168 * are fixed up in some manner...
1169 *
1170 * This routine identifies expose events that are generated when
1171 * we've temporarily moved the bin_window_origin, and translates
1172 * them or discards them, depending on whether we are obscured
1173 * or not.
1174 */
1175 static GdkFilterReturn
1176 gtk_myfixed_filter (GdkXEvent *gdk_xevent,
1177 GdkEvent *event,
1178 gpointer data)
1179 {
1180 XEvent *xevent;
1181 GtkMyFixed *myfixed;
1182
1183 xevent = (XEvent *)gdk_xevent;
1184 myfixed = GTK_MYFIXED (data);
1185
1186 switch (xevent->type)
1187 {
1188 case Expose:
1189 if (xevent->xexpose.serial == myfixed->configure_serial)
1190 {
1191 if (myfixed->visibility == GDK_VISIBILITY_UNOBSCURED)
1192 return GDK_FILTER_REMOVE;
1193 else
1194 {
1195 xevent->xexpose.x += myfixed->scroll_x;
1196 xevent->xexpose.y += myfixed->scroll_y;
1197
1198 break;
1199 }
1200 }
1201 break;
1202
1203 case ConfigureNotify:
1204 if ((xevent->xconfigure.x != 0) || (xevent->xconfigure.y != 0))
1205 {
1206 myfixed->configure_serial = xevent->xconfigure.serial;
1207 myfixed->scroll_x = xevent->xconfigure.x;
1208 myfixed->scroll_y = xevent->xconfigure.y;
1209 }
1210 break;
1211 }
1212
1213 return GDK_FILTER_CONTINUE;
1214 }
1215
1216 /* Although GDK does have a GDK_VISIBILITY_NOTIFY event,
1217 * there is no corresponding event in GTK, so we have
1218 * to get the events from a filter
1219 */
1220 static GdkFilterReturn
1221 gtk_myfixed_main_filter (GdkXEvent *gdk_xevent,
1222 GdkEvent *event,
1223 gpointer data)
1224 {
1225 XEvent *xevent;
1226 GtkMyFixed *myfixed;
1227
1228 xevent = (XEvent *)gdk_xevent;
1229 myfixed = GTK_MYFIXED (data);
1230
1231 if (xevent->type == VisibilityNotify)
1232 {
1233 switch (xevent->xvisibility.state)
1234 {
1235 case VisibilityFullyObscured:
1236 myfixed->visibility = GDK_VISIBILITY_FULLY_OBSCURED;
1237 break;
1238
1239 case VisibilityPartiallyObscured:
1240 myfixed->visibility = GDK_VISIBILITY_PARTIAL;
1241 break;
1242
1243 case VisibilityUnobscured:
1244 myfixed->visibility = GDK_VISIBILITY_UNOBSCURED;
1245 break;
1246 }
1247
1248 return GDK_FILTER_REMOVE;
1249 }
1250
1251
1252 return GDK_FILTER_CONTINUE;
1253 }
1254
1255
1256
1257
1258 #ifdef __cplusplus
1259 }
1260 #endif /* __cplusplus */
1261