1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
19 * GtkLayout: Widget for scrolling of arbitrary-sized areas.
21 * Copyright Owen Taylor, 1998
24 #include "gtklayout.h"
25 #include "gtksignal.h"
29 static void gtk_layout_class_init (GtkLayoutClass *class);
30 static void gtk_layout_init (GtkLayout *layout);
32 static void gtk_layout_realize (GtkWidget *widget);
33 static void gtk_layout_unrealize (GtkWidget *widget);
34 static void gtk_layout_map (GtkWidget *widget);
35 static void gtk_layout_size_request (GtkWidget *widget,
36 GtkRequisition *requisition);
37 static void gtk_layout_size_allocate (GtkWidget *widget,
38 GtkAllocation *allocation);
39 static void gtk_layout_draw (GtkWidget *widget,
41 static gint gtk_layout_expose (GtkWidget *widget,
42 GdkEventExpose *event);
44 static void gtk_layout_remove (GtkContainer *container,
46 static void gtk_layout_forall (GtkContainer *container,
47 gboolean include_internals,
49 gpointer callback_data);
50 static void gtk_layout_set_adjustments (GtkLayout *layout,
54 static void gtk_layout_realize_child (GtkLayout *layout,
55 GtkLayoutChild *child);
56 static void gtk_layout_position_child (GtkLayout *layout,
57 GtkLayoutChild *child,
58 gboolean force_allocate);
59 static void gtk_layout_position_children (GtkLayout *layout);
61 static void gtk_layout_expose_area (GtkLayout *layout,
66 static void gtk_layout_adjustment_changed (GtkAdjustment *adjustment,
68 static GdkFilterReturn gtk_layout_filter (GdkXEvent *gdk_xevent,
71 static GdkFilterReturn gtk_layout_main_filter (GdkXEvent *gdk_xevent,
75 static gboolean gtk_layout_gravity_works (void);
76 static void gtk_layout_set_static_gravity (GdkWindow *win,
79 static GtkWidgetClass *parent_class = NULL;
80 static gboolean gravity_works;
86 gtk_layout_new (GtkAdjustment *hadjustment,
87 GtkAdjustment *vadjustment)
91 layout = gtk_type_new (GTK_TYPE_LAYOUT);
93 gtk_layout_set_adjustments (layout, hadjustment, vadjustment);
95 return GTK_WIDGET (layout);
99 gtk_layout_get_hadjustment (GtkLayout *layout)
101 g_return_val_if_fail (layout != NULL, NULL);
102 g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL);
104 return layout->hadjustment;
107 gtk_layout_get_vadjustment (GtkLayout *layout)
109 g_return_val_if_fail (layout != NULL, NULL);
110 g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL);
112 return layout->vadjustment;
116 gtk_layout_set_adjustments (GtkLayout *layout,
120 gboolean need_adjust = FALSE;
122 g_return_if_fail (layout != NULL);
123 g_return_if_fail (GTK_IS_LAYOUT (layout));
126 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
128 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
130 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
132 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
134 if (layout->hadjustment && (layout->hadjustment != hadj))
136 gtk_signal_disconnect_by_data (GTK_OBJECT (layout->hadjustment), layout);
137 gtk_object_unref (GTK_OBJECT (layout->hadjustment));
140 if (layout->vadjustment && (layout->vadjustment != vadj))
142 gtk_signal_disconnect_by_data (GTK_OBJECT (layout->vadjustment), layout);
143 gtk_object_unref (GTK_OBJECT (layout->vadjustment));
146 if (layout->hadjustment != hadj)
148 layout->hadjustment = hadj;
149 gtk_object_ref (GTK_OBJECT (layout->hadjustment));
150 gtk_object_sink (GTK_OBJECT (layout->hadjustment));
152 gtk_signal_connect (GTK_OBJECT (layout->hadjustment), "value_changed",
153 (GtkSignalFunc) gtk_layout_adjustment_changed,
158 if (layout->vadjustment != vadj)
160 layout->vadjustment = vadj;
161 gtk_object_ref (GTK_OBJECT (layout->vadjustment));
162 gtk_object_sink (GTK_OBJECT (layout->vadjustment));
164 gtk_signal_connect (GTK_OBJECT (layout->vadjustment), "value_changed",
165 (GtkSignalFunc) gtk_layout_adjustment_changed,
171 gtk_layout_adjustment_changed (NULL, layout);
175 gtk_layout_set_hadjustment (GtkLayout *layout,
176 GtkAdjustment *adjustment)
178 g_return_if_fail (layout != NULL);
179 g_return_if_fail (GTK_IS_LAYOUT (layout));
181 gtk_layout_set_adjustments (layout, adjustment, layout->vadjustment);
186 gtk_layout_set_vadjustment (GtkLayout *layout,
187 GtkAdjustment *adjustment)
189 g_return_if_fail (layout != NULL);
190 g_return_if_fail (GTK_IS_LAYOUT (layout));
192 gtk_layout_set_adjustments (layout, layout->hadjustment, adjustment);
197 gtk_layout_put (GtkLayout *layout,
198 GtkWidget *child_widget,
202 GtkLayoutChild *child;
204 g_return_if_fail (layout != NULL);
205 g_return_if_fail (GTK_IS_LAYOUT (layout));
207 child = g_new (GtkLayoutChild, 1);
209 child->widget = child_widget;
210 child->window = NULL;
213 child->widget->requisition.width = 0;
214 child->widget->requisition.height = 0;
215 child->mapped = FALSE;
217 layout->children = g_list_append (layout->children, child);
219 gtk_widget_set_parent (child_widget, GTK_WIDGET (layout));
221 gtk_widget_size_request (child->widget, &child->widget->requisition);
223 if (GTK_WIDGET_REALIZED (layout) &&
224 !GTK_WIDGET_REALIZED (child_widget))
225 gtk_layout_realize_child (layout, child);
227 gtk_layout_position_child (layout, child, TRUE);
231 gtk_layout_move (GtkLayout *layout,
232 GtkWidget *child_widget,
237 GtkLayoutChild *child;
239 g_return_if_fail (layout != NULL);
240 g_return_if_fail (GTK_IS_LAYOUT (layout));
242 tmp_list = layout->children;
245 child = tmp_list->data;
246 if (child->widget == child_widget)
251 gtk_layout_position_child (layout, child, TRUE);
254 tmp_list = tmp_list->next;
259 gtk_layout_set_size (GtkLayout *layout,
263 g_return_if_fail (layout != NULL);
264 g_return_if_fail (GTK_IS_LAYOUT (layout));
266 layout->width = width;
267 layout->height = height;
269 layout->hadjustment->upper = layout->width;
270 gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
272 layout->vadjustment->upper = layout->height;
273 gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
277 gtk_layout_freeze (GtkLayout *layout)
279 g_return_if_fail (layout != NULL);
280 g_return_if_fail (GTK_IS_LAYOUT (layout));
282 layout->freeze_count++;
286 gtk_layout_thaw (GtkLayout *layout)
288 g_return_if_fail (layout != NULL);
289 g_return_if_fail (GTK_IS_LAYOUT (layout));
291 if (layout->freeze_count)
292 if (!(--layout->freeze_count))
294 gtk_layout_position_children (layout);
295 gtk_widget_draw (GTK_WIDGET (layout), NULL);
299 /* Basic Object handling procedures
302 gtk_layout_get_type (void)
304 static GtkType layout_type = 0;
308 static const GtkTypeInfo layout_info =
312 sizeof (GtkLayoutClass),
313 (GtkClassInitFunc) gtk_layout_class_init,
314 (GtkObjectInitFunc) gtk_layout_init,
315 (GtkArgSetFunc) NULL,
316 (GtkArgGetFunc) NULL,
319 layout_type = gtk_type_unique (GTK_TYPE_CONTAINER, &layout_info);
326 gtk_layout_class_init (GtkLayoutClass *class)
328 GtkObjectClass *object_class;
329 GtkWidgetClass *widget_class;
330 GtkContainerClass *container_class;
332 object_class = (GtkObjectClass*) class;
333 widget_class = (GtkWidgetClass*) class;
334 container_class = (GtkContainerClass*) class;
336 parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
338 widget_class->realize = gtk_layout_realize;
339 widget_class->unrealize = gtk_layout_unrealize;
340 widget_class->map = gtk_layout_map;
341 widget_class->size_request = gtk_layout_size_request;
342 widget_class->size_allocate = gtk_layout_size_allocate;
343 widget_class->draw = gtk_layout_draw;
344 widget_class->expose_event = gtk_layout_expose;
346 widget_class->set_scroll_adjustments_signal =
347 gtk_signal_new ("set_scroll_adjustments",
350 GTK_SIGNAL_OFFSET (GtkLayoutClass, set_scroll_adjustments),
351 gtk_marshal_NONE__POINTER_POINTER,
352 GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
354 gravity_works = gtk_layout_gravity_works ();
356 container_class->remove = gtk_layout_remove;
357 container_class->forall = gtk_layout_forall;
359 class->set_scroll_adjustments = gtk_layout_set_adjustments;
363 gtk_layout_init (GtkLayout *layout)
365 layout->children = NULL;
368 layout->height = 100;
370 layout->hadjustment = NULL;
371 layout->vadjustment = NULL;
373 layout->bin_window = NULL;
375 layout->configure_serial = 0;
376 layout->scroll_x = 0;
377 layout->scroll_y = 0;
378 layout->visibility = GDK_VISIBILITY_PARTIAL;
380 layout->freeze_count = 0;
387 gtk_layout_realize (GtkWidget *widget)
391 GdkWindowAttr attributes;
392 gint attributes_mask;
394 g_return_if_fail (widget != NULL);
395 g_return_if_fail (GTK_IS_LAYOUT (widget));
397 layout = GTK_LAYOUT (widget);
398 GTK_WIDGET_SET_FLAGS (layout, GTK_REALIZED);
400 attributes.window_type = GDK_WINDOW_CHILD;
401 attributes.x = widget->allocation.x;
402 attributes.y = widget->allocation.y;
403 attributes.width = widget->allocation.width;
404 attributes.height = widget->allocation.height;
405 attributes.wclass = GDK_INPUT_OUTPUT;
406 attributes.visual = gtk_widget_get_visual (widget);
407 attributes.colormap = gtk_widget_get_colormap (widget);
408 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
410 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
412 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
413 &attributes, attributes_mask);
414 gdk_window_set_user_data (widget->window, widget);
418 attributes.event_mask = gtk_widget_get_events (widget);
420 layout->bin_window = gdk_window_new (widget->window,
421 &attributes, attributes_mask);
422 gdk_window_set_user_data (layout->bin_window, widget);
425 gtk_layout_set_static_gravity (layout->bin_window, TRUE);
427 widget->style = gtk_style_attach (widget->style, widget->window);
428 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
429 gtk_style_set_background (widget->style, layout->bin_window, GTK_STATE_NORMAL);
431 gdk_window_add_filter (widget->window, gtk_layout_main_filter, layout);
432 gdk_window_add_filter (layout->bin_window, gtk_layout_filter, layout);
434 tmp_list = layout->children;
437 GtkLayoutChild *child = tmp_list->data;
439 if (GTK_WIDGET_VISIBLE (child->widget))
440 gtk_layout_realize_child (layout, child);
442 tmp_list = tmp_list->next;
447 gtk_layout_map (GtkWidget *widget)
452 g_return_if_fail (widget != NULL);
453 g_return_if_fail (GTK_IS_LAYOUT (widget));
455 layout = GTK_LAYOUT (widget);
457 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
459 gdk_window_show (widget->window);
460 gdk_window_show (layout->bin_window);
462 tmp_list = layout->children;
465 GtkLayoutChild *child = tmp_list->data;
467 if (child->mapped && GTK_WIDGET_VISIBLE (child->widget))
469 if (!GTK_WIDGET_MAPPED (child->widget))
470 gtk_widget_map (child->widget);
473 gdk_window_show (child->window);
476 tmp_list = tmp_list->next;
482 gtk_layout_unrealize (GtkWidget *widget)
487 g_return_if_fail (widget != NULL);
488 g_return_if_fail (GTK_IS_LAYOUT (widget));
490 layout = GTK_LAYOUT (widget);
492 tmp_list = layout->children;
494 gdk_window_set_user_data (layout->bin_window, NULL);
495 gdk_window_destroy (layout->bin_window);
496 layout->bin_window = NULL;
500 GtkLayoutChild *child = tmp_list->data;
504 gdk_window_set_user_data (child->window, NULL);
505 gdk_window_destroy (child->window);
506 child->window = NULL;
509 tmp_list = tmp_list->next;
512 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
513 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
517 gtk_layout_draw (GtkWidget *widget, GdkRectangle *area)
521 g_return_if_fail (widget != NULL);
522 g_return_if_fail (GTK_IS_LAYOUT (widget));
524 layout = GTK_LAYOUT (widget);
528 gtk_layout_size_request (GtkWidget *widget,
529 GtkRequisition *requisition)
534 g_return_if_fail (widget != NULL);
535 g_return_if_fail (GTK_IS_LAYOUT (widget));
537 layout = GTK_LAYOUT (widget);
539 requisition->width = 0;
540 requisition->height = 0;
542 tmp_list = layout->children;
546 GtkLayoutChild *child = tmp_list->data;
547 gtk_widget_size_request (child->widget, &child->widget->requisition);
549 tmp_list = tmp_list->next;
554 gtk_layout_size_allocate (GtkWidget *widget,
555 GtkAllocation *allocation)
560 g_return_if_fail (widget != NULL);
561 g_return_if_fail (GTK_IS_LAYOUT (widget));
563 widget->allocation = *allocation;
565 layout = GTK_LAYOUT (widget);
567 tmp_list = layout->children;
571 GtkLayoutChild *child = tmp_list->data;
572 gtk_layout_position_child (layout, child, TRUE);
574 tmp_list = tmp_list->next;
577 if (GTK_WIDGET_REALIZED (widget))
579 gdk_window_move_resize (widget->window,
580 allocation->x, allocation->y,
581 allocation->width, allocation->height);
582 gdk_window_move_resize (GTK_LAYOUT(widget)->bin_window,
584 allocation->width, allocation->height);
587 layout->hadjustment->page_size = allocation->width;
588 layout->hadjustment->page_increment = allocation->width / 2;
589 gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
591 layout->vadjustment->page_size = allocation->height;
592 layout->vadjustment->page_increment = allocation->height / 2;
593 gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
597 gtk_layout_expose (GtkWidget *widget, GdkEventExpose *event)
602 g_return_val_if_fail (widget != NULL, FALSE);
603 g_return_val_if_fail (GTK_IS_LAYOUT (widget), FALSE);
605 layout = GTK_LAYOUT (widget);
607 if (event->window == layout->bin_window)
610 tmp_list = layout->children;
613 GtkLayoutChild *child = tmp_list->data;
615 if (event->window == child->window)
616 return gtk_widget_event (child->widget, (GdkEvent *)event);
618 tmp_list = tmp_list->next;
627 gtk_layout_remove (GtkContainer *container,
632 GtkLayoutChild *child;
634 g_return_if_fail (container != NULL);
635 g_return_if_fail (GTK_IS_LAYOUT (container));
637 layout = GTK_LAYOUT (container);
639 tmp_list = layout->children;
642 child = tmp_list->data;
643 if (child->widget == widget)
645 tmp_list = tmp_list->next;
652 /* FIXME: This will cause problems for reparenting NO_WINDOW
653 * widgets out of a GtkLayout
655 gdk_window_set_user_data (child->window, NULL);
656 gdk_window_destroy (child->window);
659 gtk_widget_unparent (widget);
661 layout->children = g_list_remove_link (layout->children, tmp_list);
662 g_list_free_1 (tmp_list);
668 gtk_layout_forall (GtkContainer *container,
669 gboolean include_internals,
670 GtkCallback callback,
671 gpointer callback_data)
674 GtkLayoutChild *child;
677 g_return_if_fail (container != NULL);
678 g_return_if_fail (GTK_IS_LAYOUT (container));
679 g_return_if_fail (callback != NULL);
681 layout = GTK_LAYOUT (container);
683 tmp_list = layout->children;
686 child = tmp_list->data;
687 tmp_list = tmp_list->next;
689 (* callback) (child->widget, callback_data);
693 /* Operations on children
697 gtk_layout_realize_child (GtkLayout *layout,
698 GtkLayoutChild *child)
701 gint attributes_mask;
703 widget = GTK_WIDGET (layout);
705 if (GTK_WIDGET_NO_WINDOW (child->widget))
707 GdkWindowAttr attributes;
709 gint x = child->x - layout->xoffset;
710 gint y = child->y - layout->xoffset;
712 attributes.window_type = GDK_WINDOW_CHILD;
715 attributes.width = child->widget->requisition.width;
716 attributes.height = child->widget->requisition.height;
717 attributes.wclass = GDK_INPUT_OUTPUT;
718 attributes.visual = gtk_widget_get_visual (widget);
719 attributes.colormap = gtk_widget_get_colormap (widget);
720 attributes.event_mask = GDK_EXPOSURE_MASK;
722 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
723 child->window = gdk_window_new (layout->bin_window,
724 &attributes, attributes_mask);
725 gdk_window_set_user_data (child->window, widget);
728 gtk_style_set_background (widget->style,
733 gtk_widget_set_parent_window (child->widget,
734 child->window ? child->window : layout->bin_window);
736 gtk_widget_realize (child->widget);
739 gtk_layout_set_static_gravity (child->window ? child->window : child->widget->window, TRUE);
743 gtk_layout_position_child (GtkLayout *layout,
744 GtkLayoutChild *child,
745 gboolean force_allocate)
750 x = child->x - layout->xoffset;
751 y = child->y - layout->yoffset;
753 if ((x >= G_MINSHORT) && (x <= G_MAXSHORT) &&
754 (y >= G_MINSHORT) && (y <= G_MAXSHORT))
758 child->mapped = TRUE;
760 if (GTK_WIDGET_MAPPED (layout) &&
761 GTK_WIDGET_VISIBLE (child->widget))
764 gdk_window_show (child->window);
765 if (!GTK_WIDGET_MAPPED (child->widget))
766 gtk_widget_map (child->widget);
768 child->mapped = TRUE;
769 force_allocate = TRUE;
775 GtkAllocation allocation;
777 if (GTK_WIDGET_NO_WINDOW (child->widget))
781 gdk_window_move_resize (child->window,
783 child->widget->requisition.width,
784 child->widget->requisition.height);
796 allocation.width = child->widget->requisition.width;
797 allocation.height = child->widget->requisition.height;
799 gtk_widget_size_allocate (child->widget, &allocation);
806 child->mapped = FALSE;
808 gdk_window_hide (child->window);
809 else if (GTK_WIDGET_MAPPED (child->widget))
810 gtk_widget_unmap (child->widget);
816 gtk_layout_position_children (GtkLayout *layout)
820 tmp_list = layout->children;
823 gtk_layout_position_child (layout, tmp_list->data, FALSE);
825 tmp_list = tmp_list->next;
831 /* Send a synthetic expose event to the widget
834 gtk_layout_expose_area (GtkLayout *layout,
835 gint x, gint y, gint width, gint height)
837 if (layout->visibility == GDK_VISIBILITY_UNOBSCURED)
839 GdkEventExpose event;
841 event.type = GDK_EXPOSE;
842 event.send_event = TRUE;
843 event.window = layout->bin_window;
848 event.area.width = width;
849 event.area.height = height;
851 gdk_window_ref (event.window);
852 gtk_widget_event (GTK_WIDGET (layout), (GdkEvent *)&event);
853 gdk_window_unref (event.window);
857 /* This function is used to find events to process while scrolling
858 * Removing the GravityNotify events is a bit of a hack - currently
859 * GTK uses a lot of time processing them as GtkEventOther - a
860 * feature that is obsolete and will be removed. Until then...
864 gtk_layout_expose_predicate (Display *display,
868 if ((xevent->type == Expose) || (xevent->type == GravityNotify) ||
869 ((xevent->xany.window == *(Window *)arg) &&
870 (xevent->type == ConfigureNotify)))
876 /* This is the main routine to do the scrolling. Scrolling is
877 * done by "Guffaw" scrolling, as in the Mozilla XFE, with
878 * a few modifications.
880 * The main improvement is that we keep track of whether we
881 * are obscured or not. If not, we ignore the generated expose
882 * events and instead do the exposes ourself, without having
883 * to wait for a roundtrip to the server. This also provides
884 * a limited form of expose-event compression, since we do
885 * the affected area as one big chunk.
887 * Real expose event compression, as in the XFE, could be added
888 * here. It would help opaque drags over the region, and the
891 * Code needs to be added here to do the scrolling on machines
892 * that don't have working WindowGravity. That could be done
894 * - XCopyArea and move the windows, and accept trailing the
895 * background color. (Since it is only a fallback method)
896 * - XmHTML style. As above, but turn off expose events when
897 * not obscured and do the exposures ourself.
898 * - gzilla-style. Move the window continuously, and reset
903 gtk_layout_adjustment_changed (GtkAdjustment *adjustment,
911 widget = GTK_WIDGET (layout);
913 dx = (gint)layout->hadjustment->value - layout->xoffset;
914 dy = (gint)layout->vadjustment->value - layout->yoffset;
916 layout->xoffset = (gint)layout->hadjustment->value;
917 layout->yoffset = (gint)layout->vadjustment->value;
919 if (layout->freeze_count)
922 if (!GTK_WIDGET_MAPPED (layout))
924 gtk_layout_position_children (layout);
932 gdk_window_resize (layout->bin_window,
933 widget->allocation.width + dx,
934 widget->allocation.height);
935 gdk_window_move (layout->bin_window, -dx, 0);
936 gdk_window_move_resize (layout->bin_window,
938 widget->allocation.width,
939 widget->allocation.height);
946 gtk_layout_expose_area (layout,
947 widget->allocation.width - dx,
950 widget->allocation.height);
956 gdk_window_move_resize (layout->bin_window,
958 widget->allocation.width - dx,
959 widget->allocation.height);
960 gdk_window_move (layout->bin_window, 0, 0);
961 gdk_window_resize (layout->bin_window,
962 widget->allocation.width,
963 widget->allocation.height);
970 gtk_layout_expose_area (layout,
974 widget->allocation.height);
981 gdk_window_resize (layout->bin_window,
982 widget->allocation.width,
983 widget->allocation.height + dy);
984 gdk_window_move (layout->bin_window, 0, -dy);
985 gdk_window_move_resize (layout->bin_window,
987 widget->allocation.width,
988 widget->allocation.height);
995 gtk_layout_expose_area (layout,
997 widget->allocation.height - dy,
998 widget->allocation.width,
1005 gdk_window_move_resize (layout->bin_window,
1007 widget->allocation.width,
1008 widget->allocation.height - dy);
1009 gdk_window_move (layout->bin_window, 0, 0);
1010 gdk_window_resize (layout->bin_window,
1011 widget->allocation.width,
1012 widget->allocation.height);
1018 gtk_layout_expose_area (layout,
1021 widget->allocation.height,
1025 gtk_layout_position_children (layout);
1027 /* We have to make sure that all exposes from this scroll get
1028 * processed before we scroll again, or the expose events will
1029 * have invalid coordinates.
1031 * We also do expose events for other windows, since otherwise
1032 * their updating will fall behind the scrolling
1034 * This also avoids a problem in pre-1.0 GTK where filters don't
1035 * have access to configure events that were compressed.
1039 while (XCheckIfEvent(GDK_WINDOW_XDISPLAY (layout->bin_window),
1041 gtk_layout_expose_predicate,
1042 (XPointer)&GDK_WINDOW_XWINDOW (layout->bin_window)))
1045 GtkWidget *event_widget;
1047 if ((xevent.xany.window == GDK_WINDOW_XWINDOW (layout->bin_window)) &&
1048 (gtk_layout_filter (&xevent, &event, layout) == GDK_FILTER_REMOVE))
1051 if (xevent.type == Expose)
1053 event.expose.window = gdk_window_lookup (xevent.xany.window);
1054 gdk_window_get_user_data (event.expose.window,
1055 (gpointer *)&event_widget);
1059 event.expose.type = GDK_EXPOSE;
1060 event.expose.area.x = xevent.xexpose.x;
1061 event.expose.area.y = xevent.xexpose.y;
1062 event.expose.area.width = xevent.xexpose.width;
1063 event.expose.area.height = xevent.xexpose.height;
1064 event.expose.count = xevent.xexpose.count;
1066 gdk_window_ref (event.expose.window);
1067 gtk_widget_event (event_widget, &event);
1068 gdk_window_unref (event.expose.window);
1074 /* The main event filter. Actually, we probably don't really need
1075 * to install this as a filter at all, since we are calling it
1076 * directly above in the expose-handling hack. But in case scrollbars
1077 * are fixed up in some manner...
1079 * This routine identifies expose events that are generated when
1080 * we've temporarily moved the bin_window_origin, and translates
1081 * them or discards them, depending on whether we are obscured
1084 static GdkFilterReturn
1085 gtk_layout_filter (GdkXEvent *gdk_xevent,
1092 xevent = (XEvent *)gdk_xevent;
1093 layout = GTK_LAYOUT (data);
1095 switch (xevent->type)
1098 if (xevent->xexpose.serial == layout->configure_serial)
1100 if (layout->visibility == GDK_VISIBILITY_UNOBSCURED)
1101 return GDK_FILTER_REMOVE;
1104 xevent->xexpose.x += layout->scroll_x;
1105 xevent->xexpose.y += layout->scroll_y;
1112 case ConfigureNotify:
1113 if ((xevent->xconfigure.x != 0) || (xevent->xconfigure.y != 0))
1115 layout->configure_serial = xevent->xconfigure.serial;
1116 layout->scroll_x = xevent->xconfigure.x;
1117 layout->scroll_y = xevent->xconfigure.y;
1122 return GDK_FILTER_CONTINUE;
1125 /* Although GDK does have a GDK_VISIBILITY_NOTIFY event,
1126 * there is no corresponding event in GTK, so we have
1127 * to get the events from a filter
1129 static GdkFilterReturn
1130 gtk_layout_main_filter (GdkXEvent *gdk_xevent,
1137 xevent = (XEvent *)gdk_xevent;
1138 layout = GTK_LAYOUT (data);
1140 if (xevent->type == VisibilityNotify)
1142 switch (xevent->xvisibility.state)
1144 case VisibilityFullyObscured:
1145 layout->visibility = GDK_VISIBILITY_FULLY_OBSCURED;
1148 case VisibilityPartiallyObscured:
1149 layout->visibility = GDK_VISIBILITY_PARTIAL;
1152 case VisibilityUnobscured:
1153 layout->visibility = GDK_VISIBILITY_UNOBSCURED;
1157 return GDK_FILTER_REMOVE;
1161 return GDK_FILTER_CONTINUE;
1164 /* Routines to set the window gravity, and check whether it is
1165 * functional. Extra capabilities need to be added to GDK, so
1166 * we don't have to use Xlib here.
1169 gtk_layout_set_static_gravity (GdkWindow *win, gboolean on)
1171 XSetWindowAttributes xattributes;
1173 xattributes.win_gravity = on ? StaticGravity : NorthWestGravity;
1174 xattributes.bit_gravity = on ? StaticGravity : NorthWestGravity;
1176 XChangeWindowAttributes (GDK_WINDOW_XDISPLAY (win),
1177 GDK_WINDOW_XWINDOW (win),
1178 CWBitGravity | CWWinGravity,
1183 gtk_layout_gravity_works (void)
1191 /* This particular server apparently has a bug so that the test
1192 * works but the actual code crashes it
1194 if ((!strcmp (XServerVendor (GDK_DISPLAY()), "Sun Microsystems, Inc.")) &&
1195 (VendorRelease (GDK_DISPLAY()) == 3400))
1198 attr.window_type = GDK_WINDOW_TEMP;
1199 attr.wclass = GDK_INPUT_OUTPUT;
1204 attr.event_mask = 0;
1206 parent = gdk_window_new (NULL, &attr, GDK_WA_X | GDK_WA_Y);
1208 attr.window_type = GDK_WINDOW_CHILD;
1209 child = gdk_window_new (parent, &attr, GDK_WA_X | GDK_WA_Y);
1211 gtk_layout_set_static_gravity (parent, TRUE);
1212 gtk_layout_set_static_gravity (child, TRUE);
1214 gdk_window_resize (parent, 100, 110);
1215 gdk_window_move (parent, 0, -10);
1216 gdk_window_move_resize (parent, 0, 0, 100, 100);
1218 gdk_window_resize (parent, 100, 110);
1219 gdk_window_move (parent, 0, -10);
1220 gdk_window_move_resize (parent, 0, 0, 100, 100);
1222 gdk_window_get_geometry (child, NULL, &y, NULL, NULL, NULL);
1224 gdk_window_destroy (parent);
1225 gdk_window_destroy (child);