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
25 * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
26 * file for a list of people on the GTK+ Team. See the ChangeLog
27 * files for a list of changes. These files are distributed with
28 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
31 #include "gdkconfig.h"
33 #if defined (GDK_WINDOWING_X11)
35 #elif defined (GDK_WINDOWING_WIN32)
36 #include "win32/gdkwin32.h"
39 #include "gtklayout.h"
40 #include "gtksignal.h"
41 #include "gtkprivate.h"
43 typedef struct _GtkLayoutAdjData GtkLayoutAdjData;
44 typedef struct _GtkLayoutChild GtkLayoutChild;
46 struct _GtkLayoutAdjData {
51 struct _GtkLayoutChild {
57 #define IS_ONSCREEN(x,y) ((x >= G_MINSHORT) && (x <= G_MAXSHORT) && \
58 (y >= G_MINSHORT) && (y <= G_MAXSHORT))
60 static void gtk_layout_class_init (GtkLayoutClass *class);
61 static void gtk_layout_init (GtkLayout *layout);
63 static void gtk_layout_finalize (GtkObject *object);
64 static void gtk_layout_realize (GtkWidget *widget);
65 static void gtk_layout_unrealize (GtkWidget *widget);
66 static void gtk_layout_map (GtkWidget *widget);
67 static void gtk_layout_size_request (GtkWidget *widget,
68 GtkRequisition *requisition);
69 static void gtk_layout_size_allocate (GtkWidget *widget,
70 GtkAllocation *allocation);
71 static void gtk_layout_draw (GtkWidget *widget,
73 static gint gtk_layout_expose (GtkWidget *widget,
74 GdkEventExpose *event);
76 static void gtk_layout_remove (GtkContainer *container,
78 static void gtk_layout_forall (GtkContainer *container,
79 gboolean include_internals,
81 gpointer callback_data);
82 static void gtk_layout_set_adjustments (GtkLayout *layout,
86 static void gtk_layout_position_child (GtkLayout *layout,
87 GtkLayoutChild *child);
88 static void gtk_layout_allocate_child (GtkLayout *layout,
89 GtkLayoutChild *child);
90 static void gtk_layout_position_children (GtkLayout *layout);
92 static void gtk_layout_adjust_allocations_recurse (GtkWidget *widget,
94 static void gtk_layout_adjust_allocations (GtkLayout *layout,
100 static void gtk_layout_expose_area (GtkLayout *layout,
105 static void gtk_layout_adjustment_changed (GtkAdjustment *adjustment,
107 static GdkFilterReturn gtk_layout_main_filter (GdkXEvent *gdk_xevent,
111 static GtkWidgetClass *parent_class = NULL;
112 static gboolean gravity_works;
118 gtk_layout_new (GtkAdjustment *hadjustment,
119 GtkAdjustment *vadjustment)
123 layout = gtk_type_new (GTK_TYPE_LAYOUT);
125 gtk_layout_set_adjustments (layout, hadjustment, vadjustment);
127 return GTK_WIDGET (layout);
131 gtk_layout_get_hadjustment (GtkLayout *layout)
133 g_return_val_if_fail (layout != NULL, NULL);
134 g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL);
136 return layout->hadjustment;
139 gtk_layout_get_vadjustment (GtkLayout *layout)
141 g_return_val_if_fail (layout != NULL, NULL);
142 g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL);
144 return layout->vadjustment;
148 gtk_layout_set_adjustments (GtkLayout *layout,
152 gboolean need_adjust = FALSE;
154 g_return_if_fail (layout != NULL);
155 g_return_if_fail (GTK_IS_LAYOUT (layout));
158 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
160 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
162 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
164 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
166 if (layout->hadjustment && (layout->hadjustment != hadj))
168 gtk_signal_disconnect_by_data (GTK_OBJECT (layout->hadjustment), layout);
169 gtk_object_unref (GTK_OBJECT (layout->hadjustment));
172 if (layout->vadjustment && (layout->vadjustment != vadj))
174 gtk_signal_disconnect_by_data (GTK_OBJECT (layout->vadjustment), layout);
175 gtk_object_unref (GTK_OBJECT (layout->vadjustment));
178 if (layout->hadjustment != hadj)
180 layout->hadjustment = hadj;
181 gtk_object_ref (GTK_OBJECT (layout->hadjustment));
182 gtk_object_sink (GTK_OBJECT (layout->hadjustment));
184 gtk_signal_connect (GTK_OBJECT (layout->hadjustment), "value_changed",
185 (GtkSignalFunc) gtk_layout_adjustment_changed,
190 if (layout->vadjustment != vadj)
192 layout->vadjustment = vadj;
193 gtk_object_ref (GTK_OBJECT (layout->vadjustment));
194 gtk_object_sink (GTK_OBJECT (layout->vadjustment));
196 gtk_signal_connect (GTK_OBJECT (layout->vadjustment), "value_changed",
197 (GtkSignalFunc) gtk_layout_adjustment_changed,
203 gtk_layout_adjustment_changed (NULL, layout);
207 gtk_layout_finalize (GtkObject *object)
209 GtkLayout *layout = GTK_LAYOUT (object);
211 gtk_object_unref (GTK_OBJECT (layout->hadjustment));
212 gtk_object_unref (GTK_OBJECT (layout->vadjustment));
214 GTK_OBJECT_CLASS (parent_class)->finalize (object);
218 gtk_layout_set_hadjustment (GtkLayout *layout,
219 GtkAdjustment *adjustment)
221 g_return_if_fail (layout != NULL);
222 g_return_if_fail (GTK_IS_LAYOUT (layout));
224 gtk_layout_set_adjustments (layout, adjustment, layout->vadjustment);
229 gtk_layout_set_vadjustment (GtkLayout *layout,
230 GtkAdjustment *adjustment)
232 g_return_if_fail (layout != NULL);
233 g_return_if_fail (GTK_IS_LAYOUT (layout));
235 gtk_layout_set_adjustments (layout, layout->hadjustment, adjustment);
240 gtk_layout_put (GtkLayout *layout,
241 GtkWidget *child_widget,
245 GtkLayoutChild *child;
247 g_return_if_fail (layout != NULL);
248 g_return_if_fail (GTK_IS_LAYOUT (layout));
249 g_return_if_fail (child_widget != NULL);
250 g_return_if_fail (GTK_IS_WIDGET (child_widget));
252 child = g_new (GtkLayoutChild, 1);
254 child->widget = child_widget;
258 layout->children = g_list_append (layout->children, child);
260 gtk_widget_set_parent (child_widget, GTK_WIDGET (layout));
261 if (GTK_WIDGET_REALIZED (layout))
262 gtk_widget_set_parent_window (child->widget, layout->bin_window);
264 if (!IS_ONSCREEN (x, y))
265 GTK_PRIVATE_SET_FLAG (child_widget, GTK_IS_OFFSCREEN);
267 if (GTK_WIDGET_REALIZED (layout))
268 gtk_widget_realize (child_widget);
270 if (GTK_WIDGET_VISIBLE (layout) && GTK_WIDGET_VISIBLE (child_widget))
272 if (GTK_WIDGET_MAPPED (layout))
273 gtk_widget_map (child_widget);
275 gtk_widget_queue_resize (child_widget);
280 gtk_layout_move (GtkLayout *layout,
281 GtkWidget *child_widget,
286 GtkLayoutChild *child;
288 g_return_if_fail (layout != NULL);
289 g_return_if_fail (GTK_IS_LAYOUT (layout));
291 tmp_list = layout->children;
294 child = tmp_list->data;
295 tmp_list = tmp_list->next;
297 if (child->widget == child_widget)
302 if (GTK_WIDGET_VISIBLE (child_widget) && GTK_WIDGET_VISIBLE (layout))
303 gtk_widget_queue_resize (child_widget);
311 gtk_layout_set_size (GtkLayout *layout,
315 g_return_if_fail (layout != NULL);
316 g_return_if_fail (GTK_IS_LAYOUT (layout));
318 layout->width = width;
319 layout->height = height;
321 layout->hadjustment->upper = layout->width;
322 gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
324 layout->vadjustment->upper = layout->height;
325 gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
329 gtk_layout_freeze (GtkLayout *layout)
331 g_return_if_fail (layout != NULL);
332 g_return_if_fail (GTK_IS_LAYOUT (layout));
334 layout->freeze_count++;
338 gtk_layout_thaw (GtkLayout *layout)
340 g_return_if_fail (layout != NULL);
341 g_return_if_fail (GTK_IS_LAYOUT (layout));
343 if (layout->freeze_count)
344 if (!(--layout->freeze_count))
346 gtk_layout_position_children (layout);
347 gtk_widget_draw (GTK_WIDGET (layout), NULL);
351 /* Basic Object handling procedures
354 gtk_layout_get_type (void)
356 static GtkType layout_type = 0;
360 static const GtkTypeInfo layout_info =
364 sizeof (GtkLayoutClass),
365 (GtkClassInitFunc) gtk_layout_class_init,
366 (GtkObjectInitFunc) gtk_layout_init,
367 (GtkArgSetFunc) NULL,
368 (GtkArgGetFunc) NULL,
371 layout_type = gtk_type_unique (GTK_TYPE_CONTAINER, &layout_info);
378 gtk_layout_class_init (GtkLayoutClass *class)
380 GtkObjectClass *object_class;
381 GtkWidgetClass *widget_class;
382 GtkContainerClass *container_class;
384 object_class = (GtkObjectClass*) class;
385 widget_class = (GtkWidgetClass*) class;
386 container_class = (GtkContainerClass*) class;
388 parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
390 object_class->finalize = gtk_layout_finalize;
392 widget_class->realize = gtk_layout_realize;
393 widget_class->unrealize = gtk_layout_unrealize;
394 widget_class->map = gtk_layout_map;
395 widget_class->size_request = gtk_layout_size_request;
396 widget_class->size_allocate = gtk_layout_size_allocate;
397 widget_class->draw = gtk_layout_draw;
398 widget_class->expose_event = gtk_layout_expose;
400 container_class->remove = gtk_layout_remove;
401 container_class->forall = gtk_layout_forall;
403 class->set_scroll_adjustments = gtk_layout_set_adjustments;
405 widget_class->set_scroll_adjustments_signal =
406 gtk_signal_new ("set_scroll_adjustments",
409 GTK_SIGNAL_OFFSET (GtkLayoutClass, set_scroll_adjustments),
410 gtk_marshal_NONE__POINTER_POINTER,
411 GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
415 gtk_layout_init (GtkLayout *layout)
417 layout->children = NULL;
420 layout->height = 100;
422 layout->hadjustment = NULL;
423 layout->vadjustment = NULL;
425 layout->bin_window = NULL;
427 layout->configure_serial = 0;
428 layout->scroll_x = 0;
429 layout->scroll_y = 0;
430 layout->visibility = GDK_VISIBILITY_PARTIAL;
432 layout->freeze_count = 0;
439 gtk_layout_realize (GtkWidget *widget)
443 GdkWindowAttr attributes;
444 gint attributes_mask;
446 g_return_if_fail (widget != NULL);
447 g_return_if_fail (GTK_IS_LAYOUT (widget));
449 layout = GTK_LAYOUT (widget);
450 GTK_WIDGET_SET_FLAGS (layout, GTK_REALIZED);
452 attributes.window_type = GDK_WINDOW_CHILD;
453 attributes.x = widget->allocation.x;
454 attributes.y = widget->allocation.y;
455 attributes.width = widget->allocation.width;
456 attributes.height = widget->allocation.height;
457 attributes.wclass = GDK_INPUT_OUTPUT;
458 attributes.visual = gtk_widget_get_visual (widget);
459 attributes.colormap = gtk_widget_get_colormap (widget);
460 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
462 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
464 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
465 &attributes, attributes_mask);
466 gdk_window_set_user_data (widget->window, widget);
470 attributes.event_mask = GDK_EXPOSURE_MASK |
471 gtk_widget_get_events (widget);
473 layout->bin_window = gdk_window_new (widget->window,
474 &attributes, attributes_mask);
475 gdk_window_set_user_data (layout->bin_window, widget);
477 widget->style = gtk_style_attach (widget->style, widget->window);
478 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
479 gtk_style_set_background (widget->style, layout->bin_window, GTK_STATE_NORMAL);
481 gdk_window_add_filter (widget->window, gtk_layout_main_filter, layout);
482 /* gdk_window_add_filter (layout->bin_window, gtk_layout_filter, layout);*/
484 /* XXX: If we ever get multiple displays for GTK+, then gravity_works
485 * will have to become a widget member. Right now we just
486 * keep it as a global
488 gravity_works = gdk_window_set_static_gravities (layout->bin_window, TRUE);
490 tmp_list = layout->children;
493 GtkLayoutChild *child = tmp_list->data;
494 tmp_list = tmp_list->next;
496 gtk_widget_set_parent_window (child->widget, layout->bin_window);
501 gtk_layout_map (GtkWidget *widget)
506 g_return_if_fail (widget != NULL);
507 g_return_if_fail (GTK_IS_LAYOUT (widget));
509 layout = GTK_LAYOUT (widget);
511 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
513 tmp_list = layout->children;
516 GtkLayoutChild *child = tmp_list->data;
517 tmp_list = tmp_list->next;
519 if (GTK_WIDGET_VISIBLE (child->widget))
521 if (!GTK_WIDGET_MAPPED (child->widget) &&
522 !GTK_WIDGET_IS_OFFSCREEN (child->widget))
523 gtk_widget_map (child->widget);
527 gdk_window_show (layout->bin_window);
528 gdk_window_show (widget->window);
532 gtk_layout_unrealize (GtkWidget *widget)
536 g_return_if_fail (widget != NULL);
537 g_return_if_fail (GTK_IS_LAYOUT (widget));
539 layout = GTK_LAYOUT (widget);
541 gdk_window_set_user_data (layout->bin_window, NULL);
542 gdk_window_destroy (layout->bin_window);
543 layout->bin_window = NULL;
545 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
546 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
550 gtk_layout_size_request (GtkWidget *widget,
551 GtkRequisition *requisition)
556 g_return_if_fail (widget != NULL);
557 g_return_if_fail (GTK_IS_LAYOUT (widget));
559 layout = GTK_LAYOUT (widget);
561 requisition->width = 0;
562 requisition->height = 0;
564 tmp_list = layout->children;
568 GtkLayoutChild *child = tmp_list->data;
569 GtkRequisition child_requisition;
571 tmp_list = tmp_list->next;
573 gtk_widget_size_request (child->widget, &child_requisition);
578 gtk_layout_size_allocate (GtkWidget *widget,
579 GtkAllocation *allocation)
584 g_return_if_fail (widget != NULL);
585 g_return_if_fail (GTK_IS_LAYOUT (widget));
587 widget->allocation = *allocation;
589 layout = GTK_LAYOUT (widget);
591 tmp_list = layout->children;
595 GtkLayoutChild *child = tmp_list->data;
596 tmp_list = tmp_list->next;
598 gtk_layout_position_child (layout, child);
599 gtk_layout_allocate_child (layout, child);
602 if (GTK_WIDGET_REALIZED (widget))
604 gdk_window_move_resize (widget->window,
605 allocation->x, allocation->y,
606 allocation->width, allocation->height);
607 gdk_window_move_resize (GTK_LAYOUT(widget)->bin_window,
609 allocation->width, allocation->height);
612 layout->hadjustment->page_size = allocation->width;
613 layout->hadjustment->page_increment = allocation->width / 2;
614 layout->hadjustment->lower = 0;
615 layout->hadjustment->upper = layout->width;
616 gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
618 layout->vadjustment->page_size = allocation->height;
619 layout->vadjustment->page_increment = allocation->height / 2;
620 layout->vadjustment->lower = 0;
621 layout->vadjustment->upper = layout->height;
622 gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
626 gtk_layout_draw (GtkWidget *widget, GdkRectangle *area)
630 GdkRectangle child_area;
632 g_return_if_fail (widget != NULL);
633 g_return_if_fail (GTK_IS_LAYOUT (widget));
635 layout = GTK_LAYOUT (widget);
637 /* We don't have any way of telling themes about this properly,
638 * so we just assume a background pixmap
640 if (!GTK_WIDGET_APP_PAINTABLE (widget))
641 gdk_window_clear_area (layout->bin_window,
642 area->x, area->y, area->width, area->height);
644 tmp_list = layout->children;
647 GtkLayoutChild *child = tmp_list->data;
648 tmp_list = tmp_list->next;
650 if (gtk_widget_intersect (child->widget, area, &child_area))
651 gtk_widget_draw (child->widget, &child_area);
656 gtk_layout_expose (GtkWidget *widget, GdkEventExpose *event)
660 GdkEventExpose child_event;
662 g_return_val_if_fail (widget != NULL, FALSE);
663 g_return_val_if_fail (GTK_IS_LAYOUT (widget), FALSE);
665 layout = GTK_LAYOUT (widget);
667 if (event->window != layout->bin_window)
670 tmp_list = layout->children;
673 GtkLayoutChild *child = tmp_list->data;
674 tmp_list = tmp_list->next;
676 child_event = *event;
677 if (GTK_WIDGET_DRAWABLE (child->widget) &&
678 GTK_WIDGET_NO_WINDOW (child->widget) &&
679 gtk_widget_intersect (child->widget, &event->area, &child_event.area))
680 gtk_widget_event (child->widget, (GdkEvent*) &child_event);
689 gtk_layout_remove (GtkContainer *container,
694 GtkLayoutChild *child = NULL;
696 g_return_if_fail (container != NULL);
697 g_return_if_fail (GTK_IS_LAYOUT (container));
699 layout = GTK_LAYOUT (container);
701 tmp_list = layout->children;
704 child = tmp_list->data;
705 if (child->widget == widget)
707 tmp_list = tmp_list->next;
712 GTK_PRIVATE_UNSET_FLAG (widget, GTK_IS_OFFSCREEN);
714 gtk_widget_unparent (widget);
716 layout->children = g_list_remove_link (layout->children, tmp_list);
717 g_list_free_1 (tmp_list);
723 gtk_layout_forall (GtkContainer *container,
724 gboolean include_internals,
725 GtkCallback callback,
726 gpointer callback_data)
729 GtkLayoutChild *child;
732 g_return_if_fail (container != NULL);
733 g_return_if_fail (GTK_IS_LAYOUT (container));
734 g_return_if_fail (callback != NULL);
736 layout = GTK_LAYOUT (container);
738 tmp_list = layout->children;
741 child = tmp_list->data;
742 tmp_list = tmp_list->next;
744 (* callback) (child->widget, callback_data);
748 /* Operations on children
752 gtk_layout_position_child (GtkLayout *layout,
753 GtkLayoutChild *child)
758 x = child->x - layout->xoffset;
759 y = child->y - layout->yoffset;
761 if (IS_ONSCREEN (x,y))
763 if (GTK_WIDGET_MAPPED (layout) &&
764 GTK_WIDGET_VISIBLE (child->widget))
766 if (!GTK_WIDGET_MAPPED (child->widget))
767 gtk_widget_map (child->widget);
770 if (GTK_WIDGET_IS_OFFSCREEN (child->widget))
771 GTK_PRIVATE_UNSET_FLAG (child->widget, GTK_IS_OFFSCREEN);
775 if (!GTK_WIDGET_IS_OFFSCREEN (child->widget))
776 GTK_PRIVATE_SET_FLAG (child->widget, GTK_IS_OFFSCREEN);
778 if (GTK_WIDGET_MAPPED (child->widget))
779 gtk_widget_unmap (child->widget);
784 gtk_layout_allocate_child (GtkLayout *layout,
785 GtkLayoutChild *child)
787 GtkAllocation allocation;
788 GtkRequisition requisition;
790 allocation.x = child->x - layout->xoffset;
791 allocation.y = child->y - layout->yoffset;
792 gtk_widget_get_child_requisition (child->widget, &requisition);
793 allocation.width = requisition.width;
794 allocation.height = requisition.height;
796 gtk_widget_size_allocate (child->widget, &allocation);
800 gtk_layout_position_children (GtkLayout *layout)
804 tmp_list = layout->children;
807 GtkLayoutChild *child = tmp_list->data;
808 tmp_list = tmp_list->next;
810 gtk_layout_position_child (layout, child);
815 gtk_layout_adjust_allocations_recurse (GtkWidget *widget,
818 GtkLayoutAdjData *data = cb_data;
820 widget->allocation.x += data->dx;
821 widget->allocation.y += data->dy;
823 if (GTK_WIDGET_NO_WINDOW (widget) &&
824 GTK_IS_CONTAINER (widget))
825 gtk_container_forall (GTK_CONTAINER (widget),
826 gtk_layout_adjust_allocations_recurse,
831 gtk_layout_adjust_allocations (GtkLayout *layout,
836 GtkLayoutAdjData data;
841 tmp_list = layout->children;
844 GtkLayoutChild *child = tmp_list->data;
845 tmp_list = tmp_list->next;
847 child->widget->allocation.x += dx;
848 child->widget->allocation.y += dy;
850 if (GTK_WIDGET_NO_WINDOW (child->widget) &&
851 GTK_IS_CONTAINER (child->widget))
852 gtk_container_forall (GTK_CONTAINER (child->widget),
853 gtk_layout_adjust_allocations_recurse,
860 /* Send a synthetic expose event to the widget
863 gtk_layout_expose_area (GtkLayout *layout,
864 gint x, gint y, gint width, gint height)
866 if (layout->visibility == GDK_VISIBILITY_UNOBSCURED)
868 GdkEventExpose event;
870 event.type = GDK_EXPOSE;
871 event.send_event = TRUE;
872 event.window = layout->bin_window;
877 event.area.width = width;
878 event.area.height = height;
880 gdk_window_ref (event.window);
881 gtk_widget_event (GTK_WIDGET (layout), (GdkEvent *)&event);
882 gdk_window_unref (event.window);
886 #ifdef GDK_WINDOWING_X11
888 /* This function is used to find events to process while scrolling
892 gtk_layout_expose_predicate (Display *display,
896 if ((xevent->type == Expose) ||
897 ((xevent->xany.window == *(Window *)arg) &&
898 (xevent->type == ConfigureNotify)))
906 /* This is the main routine to do the scrolling. Scrolling is
907 * done by "Guffaw" scrolling, as in the Mozilla XFE, with
908 * a few modifications.
910 * The main improvement is that we keep track of whether we
911 * are obscured or not. If not, we ignore the generated expose
912 * events and instead do the exposes ourself, without having
913 * to wait for a roundtrip to the server. This also provides
914 * a limited form of expose-event compression, since we do
915 * the affected area as one big chunk.
917 * Real expose event compression, as in the XFE, could be added
918 * here. It would help opaque drags over the region, and the
921 * Code needs to be added here to do the scrolling on machines
922 * that don't have working WindowGravity. That could be done
924 * - XCopyArea and move the windows, and accept trailing the
925 * background color. (Since it is only a fallback method)
926 * - XmHTML style. As above, but turn off expose events when
927 * not obscured and do the exposures ourself.
928 * - gzilla-style. Move the window continuously, and reset
933 gtk_layout_adjustment_changed (GtkAdjustment *adjustment,
937 #ifdef GDK_WINDOWING_X11
942 widget = GTK_WIDGET (layout);
944 dx = (gint)layout->hadjustment->value - layout->xoffset;
945 dy = (gint)layout->vadjustment->value - layout->yoffset;
947 layout->xoffset = (gint)layout->hadjustment->value;
948 layout->yoffset = (gint)layout->vadjustment->value;
950 if (layout->freeze_count)
953 if (!GTK_WIDGET_MAPPED (layout))
955 gtk_layout_position_children (layout);
959 gtk_layout_adjust_allocations (layout, -dx, -dy);
965 gdk_window_resize (layout->bin_window,
966 widget->allocation.width + dx,
967 widget->allocation.height);
968 gdk_window_move (layout->bin_window, -dx, 0);
969 gdk_window_move_resize (layout->bin_window,
971 widget->allocation.width,
972 widget->allocation.height);
979 gtk_layout_expose_area (layout,
980 MAX ((gint)widget->allocation.width - dx, 0),
982 MIN (dx, widget->allocation.width),
983 widget->allocation.height);
989 gdk_window_move_resize (layout->bin_window,
991 widget->allocation.width - dx,
992 widget->allocation.height);
993 gdk_window_move (layout->bin_window, 0, 0);
994 gdk_window_resize (layout->bin_window,
995 widget->allocation.width,
996 widget->allocation.height);
1003 gtk_layout_expose_area (layout,
1006 MIN (-dx, widget->allocation.width),
1007 widget->allocation.height);
1014 gdk_window_resize (layout->bin_window,
1015 widget->allocation.width,
1016 widget->allocation.height + dy);
1017 gdk_window_move (layout->bin_window, 0, -dy);
1018 gdk_window_move_resize (layout->bin_window,
1020 widget->allocation.width,
1021 widget->allocation.height);
1028 gtk_layout_expose_area (layout,
1030 MAX ((gint)widget->allocation.height - dy, 0),
1031 widget->allocation.width,
1032 MIN (dy, widget->allocation.height));
1038 gdk_window_move_resize (layout->bin_window,
1040 widget->allocation.width,
1041 widget->allocation.height - dy);
1042 gdk_window_move (layout->bin_window, 0, 0);
1043 gdk_window_resize (layout->bin_window,
1044 widget->allocation.width,
1045 widget->allocation.height);
1051 gtk_layout_expose_area (layout,
1054 widget->allocation.width,
1055 MIN (-dy, (gint)widget->allocation.height));
1058 gtk_layout_position_children (layout);
1060 /* We have to make sure that all exposes from this scroll get
1061 * processed before we scroll again, or the expose events will
1062 * have invalid coordinates.
1064 * We also do expose events for other windows, since otherwise
1065 * their updating will fall behind the scrolling
1067 * This also avoids a problem in pre-1.0 GTK where filters don't
1068 * have access to configure events that were compressed.
1073 #ifdef GDK_WINDOWING_X11
1074 while (XCheckIfEvent(GDK_WINDOW_XDISPLAY (layout->bin_window),
1076 gtk_layout_expose_predicate,
1077 (XPointer)&GDK_WINDOW_XWINDOW (layout->bin_window)))
1080 GtkWidget *event_widget;
1082 switch (xevent.type)
1086 if (xevent.xany.window == GDK_WINDOW_XWINDOW (layout->bin_window))
1088 /* If the window is unobscured, then we've exposed the
1089 * regions with the following serials already, so we
1090 * can throw out the expose events.
1092 if (layout->visibility == GDK_VISIBILITY_UNOBSCURED &&
1093 (((dx > 0 || dy > 0) &&
1094 xevent.xexpose.serial == layout->configure_serial) ||
1095 ((dx < 0 || dy < 0) &&
1096 xevent.xexpose.serial == layout->configure_serial + 1)))
1098 /* The following expose was generated while the origin was
1099 * different from the current origin, so we need to offset it.
1101 else if (xevent.xexpose.serial == layout->configure_serial)
1103 xevent.xexpose.x += layout->scroll_x;
1104 xevent.xexpose.y += layout->scroll_y;
1106 event.expose.window = layout->bin_window;
1107 event_widget = widget;
1111 event.expose.window = gdk_window_lookup (xevent.xany.window);
1112 gdk_window_get_user_data (event.expose.window,
1113 (gpointer *)&event_widget);
1118 event.expose.type = GDK_EXPOSE;
1119 event.expose.area.x = xevent.xexpose.x;
1120 event.expose.area.y = xevent.xexpose.y;
1121 event.expose.area.width = xevent.xexpose.width;
1122 event.expose.area.height = xevent.xexpose.height;
1123 event.expose.count = xevent.xexpose.count;
1125 gdk_window_ref (event.expose.window);
1126 gtk_widget_event (event_widget, &event);
1127 gdk_window_unref (event.expose.window);
1131 case ConfigureNotify:
1132 if (xevent.xany.window == GDK_WINDOW_XWINDOW (layout->bin_window) &&
1133 (xevent.xconfigure.x != 0 || xevent.xconfigure.y != 0))
1135 layout->configure_serial = xevent.xconfigure.serial;
1136 layout->scroll_x = xevent.xconfigure.x;
1137 layout->scroll_y = xevent.xconfigure.y;
1142 #elif defined (GDK_WINDOWING_WIN32)
1143 /* XXX Not implemented */
1148 /* The main event filter. Actually, we probably don't really need
1149 * this filter at all, since we are calling it directly above in the
1150 * expose-handling hack. But in case scrollbars
1151 * are fixed up in some manner...
1153 * This routine identifies expose events that are generated when
1154 * we've temporarily moved the bin_window_origin, and translates
1155 * them or discards them, depending on whether we are obscured
1158 static GdkFilterReturn
1159 gtk_layout_filter (GdkXEvent *gdk_xevent,
1163 #ifdef GDK_WINDOWING_X11
1168 xevent = (XEvent *)gdk_xevent;
1169 layout = GTK_LAYOUT (data);
1171 switch (xevent->type)
1174 if (xevent->xexpose.serial == layout->configure_serial)
1176 if (layout->visibility == GDK_VISIBILITY_UNOBSCURED)
1177 return GDK_FILTER_REMOVE;
1180 xevent->xexpose.x += layout->scroll_x;
1181 xevent->xexpose.y += layout->scroll_y;
1188 case ConfigureNotify:
1189 if ((xevent->xconfigure.x != 0) || (xevent->xconfigure.y != 0))
1191 layout->configure_serial = xevent->xconfigure.serial;
1192 layout->scroll_x = xevent->xconfigure.x;
1193 layout->scroll_y = xevent->xconfigure.y;
1197 #elif defined (GDK_WINDOWING_WIN32)
1198 /* XXX Not implemented */
1201 return GDK_FILTER_CONTINUE;
1205 /* Although GDK does have a GDK_VISIBILITY_NOTIFY event,
1206 * there is no corresponding event in GTK, so we have
1207 * to get the events from a filter
1209 static GdkFilterReturn
1210 gtk_layout_main_filter (GdkXEvent *gdk_xevent,
1214 #ifdef GDK_WINDOWING_X11
1218 xevent = (XEvent *)gdk_xevent;
1219 layout = GTK_LAYOUT (data);
1221 if (xevent->type == VisibilityNotify)
1223 switch (xevent->xvisibility.state)
1225 case VisibilityFullyObscured:
1226 layout->visibility = GDK_VISIBILITY_FULLY_OBSCURED;
1229 case VisibilityPartiallyObscured:
1230 layout->visibility = GDK_VISIBILITY_PARTIAL;
1233 case VisibilityUnobscured:
1234 layout->visibility = GDK_VISIBILITY_UNOBSCURED;
1238 return GDK_FILTER_REMOVE;
1240 #elif defined (GDK_WINDOWING_WIN32)
1241 /* XXX Not implemented */
1244 return GDK_FILTER_CONTINUE;