diff options
author | Owen Taylor <otaylor@redhat.com> | 1998-11-24 04:45:29 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 1998-11-24 04:45:29 +0000 |
commit | ee1d43929c3395fabd43e6b1d6a718363cbe198e (patch) | |
tree | f605372598f5668f17924ac8fbc8aa5ae8d38bbd /gtk | |
parent | 42faec1736c32cfbe1c44ac0ca0d81445c32fd0d (diff) | |
download | gtk+-ee1d43929c3395fabd43e6b1d6a718363cbe198e.tar.gz |
Added layout widget for scrolling arbitrarily big areas. Added plug/socket
Mon Nov 23 22:10:09 1998 Owen Taylor <otaylor@redhat.com>
* gtk/Makefile.am gtk/gtk.h gtk/gtklayout.[ch]
gtk/gtkplug.[ch] gtk/gtksocket.[ch] gtk/gtk.h:
Added layout widget for scrolling arbitrarily big areas.
Added plug/socket widgets for interprocess embedding.
These widgets still, at some point, need to be
made more pure in their use of GDK, as opposed
to raw X.
* gtk/testgtk.c: Added test for layout widget.
Diffstat (limited to 'gtk')
-rw-r--r-- | gtk/Makefile.am | 6 | ||||
-rw-r--r-- | gtk/gtk.h | 3 | ||||
-rw-r--r-- | gtk/gtklayout.c | 1207 | ||||
-rw-r--r-- | gtk/gtklayout.h | 104 | ||||
-rw-r--r-- | gtk/gtkplug.c | 409 | ||||
-rw-r--r-- | gtk/gtkplug.h | 65 | ||||
-rw-r--r-- | gtk/gtksocket.c | 688 | ||||
-rw-r--r-- | gtk/gtksocket.h | 68 | ||||
-rw-r--r-- | gtk/gtkwindow.c | 52 | ||||
-rw-r--r-- | gtk/gtkwindow.h | 3 | ||||
-rw-r--r-- | gtk/testgtk.c | 89 |
11 files changed, 2694 insertions, 0 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am index fd06f46168..6e64f95b29 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -55,6 +55,7 @@ static_sources = \ gtkitem.c \ gtkitemfactory.c \ gtklabel.c \ + gtklayout.c \ gtklist.c \ gtklistitem.c \ gtkmain.c \ @@ -70,6 +71,7 @@ static_sources = \ gtkpacker.c \ gtkpaned.c \ gtkpixmap.c \ + gtkplug.c \ gtkpreview.c \ gtkprogress.c \ gtkprogressbar.c \ @@ -84,6 +86,7 @@ static_sources = \ gtkselection.c \ gtkseparator.c \ gtksignal.c \ + gtksocket.c \ gtkspinbutton.c \ gtkstyle.c \ gtkstatusbar.c \ @@ -169,6 +172,7 @@ source_headers = \ gtkitem.h \ gtkitemfactory.h \ gtklabel.h \ + gtklayout.h \ gtklist.h \ gtklistitem.h \ gtkmain.h \ @@ -184,6 +188,7 @@ source_headers = \ gtkpacker.h \ gtkpaned.h \ gtkpixmap.h \ + gtkplug.h \ gtkpreview.h \ gtkprivate.h \ gtkprogress.h \ @@ -199,6 +204,7 @@ source_headers = \ gtkselection.h \ gtkseparator.h \ gtksignal.h \ + gtksocket.h \ gtkspinbutton.h \ gtkstyle.h \ gtkstatusbar.h \ @@ -71,6 +71,7 @@ #include <gtk/gtkitem.h> #include <gtk/gtkitemfactory.h> #include <gtk/gtklabel.h> +#include <gtk/gtklayout.h> #include <gtk/gtklist.h> #include <gtk/gtklistitem.h> #include <gtk/gtkmain.h> @@ -86,6 +87,7 @@ #include <gtk/gtkpacker.h> #include <gtk/gtkpaned.h> #include <gtk/gtkpixmap.h> +#include <gtk/gtkplug.h> #include <gtk/gtkpreview.h> #include <gtk/gtkprogress.h> #include <gtk/gtkprogressbar.h> @@ -100,6 +102,7 @@ #include <gtk/gtkselection.h> #include <gtk/gtkseparator.h> #include <gtk/gtksignal.h> +#include <gtk/gtksocket.h> #include <gtk/gtkspinbutton.h> #include <gtk/gtkstyle.h> #include <gtk/gtkstatusbar.h> diff --git a/gtk/gtklayout.c b/gtk/gtklayout.c new file mode 100644 index 0000000000..0c9c2be789 --- /dev/null +++ b/gtk/gtklayout.c @@ -0,0 +1,1207 @@ +/* Copyright Owen Taylor, 1998 + * + * This file may be distributed under either the terms of the + * Netscape Public License, or the GNU Library General Public License + * + * Note: No GTK+ or Mozilla code should be added to this file. + * The coding style should be that of the the GTK core. + */ + +#include "gtklayout.h" +#include "gtksignal.h" +#include "gdk/gdkx.h" + + +static void gtk_layout_class_init (GtkLayoutClass *class); +static void gtk_layout_init (GtkLayout *layout); + +static void gtk_layout_realize (GtkWidget *widget); +static void gtk_layout_unrealize (GtkWidget *widget); +static void gtk_layout_map (GtkWidget *widget); +static void gtk_layout_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_layout_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static void gtk_layout_draw (GtkWidget *widget, + GdkRectangle *area); +static gint gtk_layout_expose (GtkWidget *widget, + GdkEventExpose *event); + +static void gtk_layout_remove (GtkContainer *container, + GtkWidget *widget); +static void gtk_layout_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); +static void gtk_layout_set_adjustments (GtkLayout *layout, + GtkAdjustment *hadj, + GtkAdjustment *vadj); + +static void gtk_layout_realize_child (GtkLayout *layout, + GtkLayoutChild *child); +static void gtk_layout_position_child (GtkLayout *layout, + GtkLayoutChild *child, + gboolean force_allocate); +static void gtk_layout_position_children (GtkLayout *layout); + +static void gtk_layout_expose_area (GtkLayout *layout, + gint x, + gint y, + gint width, + gint height); +static void gtk_layout_adjustment_changed (GtkAdjustment *adjustment, + GtkLayout *layout); +static GdkFilterReturn gtk_layout_filter (GdkXEvent *gdk_xevent, + GdkEvent *event, + gpointer data); +static GdkFilterReturn gtk_layout_main_filter (GdkXEvent *gdk_xevent, + GdkEvent *event, + gpointer data); + +static gboolean gtk_layout_gravity_works (void); +static void gtk_layout_set_static_gravity (GdkWindow *win, + gboolean op); + +static GtkWidgetClass *parent_class = NULL; +static gboolean gravity_works; + +/* Public interface + */ + +GtkWidget* +gtk_layout_new (GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment) +{ + GtkLayout *layout; + + layout = gtk_type_new (gtk_layout_get_type()); + + gtk_layout_set_hadjustment (layout, hadjustment); + gtk_layout_set_vadjustment (layout, vadjustment); + + return GTK_WIDGET (layout); +} + +GtkAdjustment* +gtk_layout_get_hadjustment (GtkLayout *layout) +{ + g_return_val_if_fail (layout != NULL, NULL); + g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL); + + return layout->hadjustment; +} +GtkAdjustment* +gtk_layout_get_vadjustment (GtkLayout *layout) +{ + g_return_val_if_fail (layout != NULL, NULL); + g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL); + + return layout->vadjustment; +} + +static void +gtk_layout_set_adjustments (GtkLayout *layout, + GtkAdjustment *hadj, + GtkAdjustment *vadj) +{ + g_return_if_fail (layout != NULL); + g_return_if_fail (GTK_IS_LAYOUT (layout)); + + if (hadj) + g_return_if_fail (GTK_IS_ADJUSTMENT (hadj)); + else + hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); + if (vadj) + g_return_if_fail (GTK_IS_ADJUSTMENT (vadj)); + else + vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); + + if (layout->hadjustment && (layout->hadjustment != hadj)) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (layout->hadjustment), layout); + gtk_object_unref (GTK_OBJECT (layout->hadjustment)); + } + + if (layout->vadjustment && (layout->vadjustment != vadj)) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (layout->vadjustment), layout); + gtk_object_unref (GTK_OBJECT (layout->vadjustment)); + } + + if (layout->hadjustment != hadj) + { + layout->hadjustment = hadj; + gtk_object_ref (GTK_OBJECT (layout->hadjustment)); + gtk_object_sink (GTK_OBJECT (layout->hadjustment)); + + gtk_signal_connect (GTK_OBJECT (layout->hadjustment), "value_changed", + (GtkSignalFunc) gtk_layout_adjustment_changed, + layout); + gtk_layout_adjustment_changed (hadj, layout); + } + + if (layout->vadjustment != vadj) + { + layout->vadjustment = vadj; + gtk_object_ref (GTK_OBJECT (layout->vadjustment)); + gtk_object_sink (GTK_OBJECT (layout->vadjustment)); + + gtk_signal_connect (GTK_OBJECT (layout->vadjustment), "value_changed", + (GtkSignalFunc) gtk_layout_adjustment_changed, + layout); + gtk_layout_adjustment_changed (vadj, layout); + } +} + +void +gtk_layout_set_hadjustment (GtkLayout *layout, + GtkAdjustment *adjustment) +{ + g_return_if_fail (layout != NULL); + g_return_if_fail (GTK_IS_LAYOUT (layout)); + + if (layout->hadjustment) + gtk_object_unref (GTK_OBJECT (layout->hadjustment)); + + if (!adjustment) + adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 10.0, 0.0, 0.0)); + else + gtk_object_ref (GTK_OBJECT (adjustment)); + + gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", + GTK_SIGNAL_FUNC (gtk_layout_adjustment_changed), + layout); + + layout->hadjustment = adjustment; +} + + +void +gtk_layout_set_vadjustment (GtkLayout *layout, + GtkAdjustment *adjustment) +{ + g_return_if_fail (layout != NULL); + g_return_if_fail (GTK_IS_LAYOUT (layout)); + + if (layout->vadjustment) + gtk_object_unref (GTK_OBJECT (layout->hadjustment)); + + if (!adjustment) + adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 10.0, 0.0, 0.0)); + else + gtk_object_ref (GTK_OBJECT (adjustment)); + + gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", + GTK_SIGNAL_FUNC (gtk_layout_adjustment_changed), + layout); + + layout->vadjustment = adjustment; +} + + +void +gtk_layout_put (GtkLayout *layout, + GtkWidget *child_widget, + gint x, + gint y) +{ + GtkLayoutChild *child; + + g_return_if_fail (layout != NULL); + g_return_if_fail (GTK_IS_LAYOUT (layout)); + + child = g_new (GtkLayoutChild, 1); + + child->widget = child_widget; + child->window = NULL; + child->x = x; + child->y = y; + child->widget->requisition.width = 0; + child->widget->requisition.height = 0; + + layout->children = g_list_append (layout->children, child); + + gtk_widget_set_parent (child_widget, GTK_WIDGET (layout)); + + gtk_widget_size_request (child->widget, &child->widget->requisition); + + if (GTK_WIDGET_REALIZED (layout) && + !GTK_WIDGET_REALIZED (child_widget)) + gtk_layout_realize_child (layout, child); + + gtk_layout_position_child (layout, child, TRUE); +} + +void +gtk_layout_move (GtkLayout *layout, + GtkWidget *child_widget, + gint x, + gint y) +{ + GList *tmp_list; + GtkLayoutChild *child; + + g_return_if_fail (layout != NULL); + g_return_if_fail (GTK_IS_LAYOUT (layout)); + + tmp_list = layout->children; + while (tmp_list) + { + child = tmp_list->data; + if (child->widget == child_widget) + { + child->x = x; + child->y = y; + + gtk_layout_position_child (layout, child, TRUE); + return; + } + tmp_list = tmp_list->next; + } +} + +void +gtk_layout_set_size (GtkLayout *layout, + guint width, + guint height) +{ + g_return_if_fail (layout != NULL); + g_return_if_fail (GTK_IS_LAYOUT (layout)); + + layout->width = width; + layout->height = height; + + layout->hadjustment->upper = layout->width; + gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed"); + + layout->vadjustment->upper = layout->height; + gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed"); +} + +void +gtk_layout_freeze (GtkLayout *layout) +{ + g_return_if_fail (layout != NULL); + g_return_if_fail (GTK_IS_LAYOUT (layout)); + + layout->frozen = TRUE; +} + +void +gtk_layout_thaw (GtkLayout *layout) +{ + g_return_if_fail (layout != NULL); + g_return_if_fail (GTK_IS_LAYOUT (layout)); + + if (!layout->frozen) + return; + + layout->frozen = FALSE; + gtk_layout_position_children (layout); + gtk_widget_draw (GTK_WIDGET (layout), NULL); +} + +/* Basic Object handling procedures + */ +guint +gtk_layout_get_type (void) +{ + static guint layout_type = 0; + + if (!layout_type) + { + GtkTypeInfo layout_info = + { + "GtkLayout", + sizeof (GtkLayout), + sizeof (GtkLayoutClass), + (GtkClassInitFunc) gtk_layout_class_init, + (GtkObjectInitFunc) gtk_layout_init, + (GtkArgSetFunc) NULL, + (GtkArgGetFunc) NULL, + }; + + layout_type = gtk_type_unique (gtk_container_get_type (), &layout_info); + } + + return layout_type; +} + +static void +gtk_layout_class_init (GtkLayoutClass *class) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + + object_class = (GtkObjectClass*) class; + widget_class = (GtkWidgetClass*) class; + container_class = (GtkContainerClass*) class; + + parent_class = gtk_type_class (gtk_container_get_type ()); + + widget_class->realize = gtk_layout_realize; + widget_class->unrealize = gtk_layout_unrealize; + widget_class->map = gtk_layout_map; + widget_class->size_request = gtk_layout_size_request; + widget_class->size_allocate = gtk_layout_size_allocate; + widget_class->draw = gtk_layout_draw; + widget_class->expose_event = gtk_layout_expose; + + widget_class->scroll_adjustments_signal = + gtk_signal_new ("scroll_adjustments", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkLayoutClass, scroll_adjustments), + gtk_marshal_NONE__POINTER_POINTER, + GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); + + gravity_works = gtk_layout_gravity_works (); + + container_class->remove = gtk_layout_remove; + container_class->forall = gtk_layout_forall; + + class->scroll_adjustments = gtk_layout_set_adjustments; +} + +static void +gtk_layout_init (GtkLayout *layout) +{ + layout->children = NULL; + + layout->width = 100; + layout->height = 100; + + layout->hadjustment = NULL; + layout->vadjustment = NULL; + + layout->bin_window = NULL; + + layout->configure_serial = 0; + layout->scroll_x = 0; + layout->scroll_y = 0; + layout->visibility = GDK_VISIBILITY_PARTIAL; +} + +/* Widget methods + */ + +static void +gtk_layout_realize (GtkWidget *widget) +{ + GList *tmp_list; + GtkLayout *layout; + GdkWindowAttr attributes; + gint attributes_mask; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_LAYOUT (widget)); + + layout = GTK_LAYOUT (widget); + GTK_WIDGET_SET_FLAGS (layout, GTK_REALIZED); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK; + + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + + widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, attributes_mask); + gdk_window_set_user_data (widget->window, widget); + + attributes.x = 0; + attributes.y = 0; + attributes.event_mask = gtk_widget_get_events (widget); + + layout->bin_window = gdk_window_new (widget->window, + &attributes, attributes_mask); + gdk_window_set_user_data (layout->bin_window, widget); + + if (gravity_works) + gtk_layout_set_static_gravity (layout->bin_window, TRUE); + + widget->style = gtk_style_attach (widget->style, widget->window); + gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); + gtk_style_set_background (widget->style, layout->bin_window, GTK_STATE_NORMAL); + + gdk_window_add_filter (widget->window, gtk_layout_main_filter, layout); + gdk_window_add_filter (layout->bin_window, gtk_layout_filter, layout); + + tmp_list = layout->children; + while (tmp_list) + { + GtkLayoutChild *child = tmp_list->data; + + if (GTK_WIDGET_VISIBLE (child->widget)) + gtk_layout_realize_child (layout, child); + + tmp_list = tmp_list->next; + } +} + +static void +gtk_layout_map (GtkWidget *widget) +{ + GList *tmp_list; + GtkLayout *layout; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_LAYOUT (widget)); + + layout = GTK_LAYOUT (widget); + + GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); + + gdk_window_show (widget->window); + gdk_window_show (layout->bin_window); + + tmp_list = layout->children; + while (tmp_list) + { + GtkLayoutChild *child = tmp_list->data; + + if (GTK_WIDGET_VISIBLE (child->widget) && + !GTK_WIDGET_MAPPED (child->widget)) + gtk_widget_map (child->widget); + + if (child->window) + gdk_window_show (child->window); + + tmp_list = tmp_list->next; + } + +} + +static void +gtk_layout_unrealize (GtkWidget *widget) +{ + GList *tmp_list; + GtkLayout *layout; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_LAYOUT (widget)); + + layout = GTK_LAYOUT (widget); + + tmp_list = layout->children; + + gdk_window_set_user_data (layout->bin_window, NULL); + gdk_window_destroy (layout->bin_window); + layout->bin_window = NULL; + + while (tmp_list) + { + GtkLayoutChild *child = tmp_list->data; + + if (child->window) + { + gdk_window_set_user_data (child->window, NULL); + gdk_window_destroy (child->window); + } + + tmp_list = tmp_list->next; + } +} + +static void +gtk_layout_draw (GtkWidget *widget, GdkRectangle *area) +{ + GtkLayout *layout; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_LAYOUT (widget)); + + layout = GTK_LAYOUT (widget); +} + +static void +gtk_layout_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GList *tmp_list; + GtkLayout *layout; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_LAYOUT (widget)); + + layout = GTK_LAYOUT (widget); + + tmp_list = layout->children; + + while (tmp_list) + { + GtkLayoutChild *child = tmp_list->data; + gtk_widget_size_request (child->widget, &child->widget->requisition); + + tmp_list = tmp_list->next; + } +} + +static void +gtk_layout_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GList *tmp_list; + GtkLayout *layout; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_LAYOUT (widget)); + + widget->allocation = *allocation; + + layout = GTK_LAYOUT (widget); + + tmp_list = layout->children; + + while (tmp_list) + { + GtkLayoutChild *child = tmp_list->data; + gtk_layout_position_child (layout, child, TRUE); + + tmp_list = tmp_list->next; + } + + if (GTK_WIDGET_REALIZED (widget)) + { + gdk_window_move_resize (widget->window, + allocation->x, allocation->y, + allocation->width, allocation->height); + gdk_window_move_resize (GTK_LAYOUT(widget)->bin_window, + 0, 0, + allocation->width, allocation->height); + } + + layout->hadjustment->page_size = allocation->width; + layout->hadjustment->page_increment = allocation->width / 2; + gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed"); + + layout->vadjustment->page_size = allocation->height; + layout->vadjustment->page_increment = allocation->height / 2; + gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed"); +} + +static gint +gtk_layout_expose (GtkWidget *widget, GdkEventExpose *event) +{ + GList *tmp_list; + GtkLayout *layout; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_LAYOUT (widget), FALSE); + + layout = GTK_LAYOUT (widget); + + if (event->window == layout->bin_window) + return FALSE; + + tmp_list = layout->children; + while (tmp_list) + { + GtkLayoutChild *child = tmp_list->data; + + if (event->window == child->window) + return gtk_widget_event (child->widget, (GdkEvent *)event); + + tmp_list = tmp_list->next; + } + + return FALSE; +} + +/* Container method + */ +static void +gtk_layout_remove (GtkContainer *container, + GtkWidget *widget) +{ + GList *tmp_list; + GtkLayout *layout; + GtkLayoutChild *child; + + g_return_if_fail (container != NULL); + g_return_if_fail (GTK_IS_LAYOUT (container)); + + layout = GTK_LAYOUT (container); + + tmp_list = layout->children; + while (tmp_list) + { + child = tmp_list->data; + if (child->widget == widget) + break; + tmp_list = tmp_list->next; + } + + if (tmp_list) + { + if (child->window) + { + /* FIXME: This will cause problems for reparenting NO_WINDOW + * widgets out of a GtkLayout + */ + gdk_window_set_user_data (child->window, NULL); + gdk_window_destroy (child->window); + } + + gtk_widget_unparent (widget); + + layout->children = g_list_remove_link (layout->children, tmp_list); + g_list_free_1 (tmp_list); + g_free (child); + } +} + +static void +gtk_layout_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GtkLayout *layout; + GtkLayoutChild *child; + GList *tmp_list; + + g_return_if_fail (container != NULL); + g_return_if_fail (GTK_IS_LAYOUT (container)); + g_return_if_fail (callback != NULL); + + layout = GTK_LAYOUT (container); + + tmp_list = layout->children; + while (tmp_list) + { + child = tmp_list->data; + tmp_list = tmp_list->next; + + (* callback) (child->widget, callback_data); + } +} + +/* Operations on children + */ + +static void +gtk_layout_realize_child (GtkLayout *layout, + GtkLayoutChild *child) +{ + GtkWidget *widget; + gint attributes_mask; + + widget = GTK_WIDGET (layout); + + if (GTK_WIDGET_NO_WINDOW (child->widget)) + { + GdkWindowAttr attributes; + + gint x = child->x - layout->xoffset; + gint y = child->y - layout->xoffset; + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = x; + attributes.y = y; + attributes.width = child->widget->requisition.width; + attributes.height = child->widget->requisition.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = GDK_EXPOSURE_MASK; + + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + child->window = gdk_window_new (layout->bin_window, + &attributes, attributes_mask); + gdk_window_set_user_data (child->window, widget); + + if (child->window) + gtk_style_set_background (widget->style, + child->window, + GTK_STATE_NORMAL); + } + + gtk_widget_set_parent_window (child->widget, + child->window ? child->window : layout->bin_window); + + gtk_widget_realize (child->widget); + + if (gravity_works) + gtk_layout_set_static_gravity (child->window ? child->window : child->widget->window, TRUE); +} + +static void +gtk_layout_position_child (GtkLayout *layout, + GtkLayoutChild *child, + gboolean force_allocate) +{ + gint x; + gint y; + + x = child->x - layout->xoffset; + y = child->y - layout->yoffset; + + if ((x >= G_MINSHORT) && (x <= G_MAXSHORT) && + (y >= G_MINSHORT) && (y <= G_MAXSHORT)) + { + if (GTK_WIDGET_VISIBLE (child->widget) && + GTK_WIDGET_MAPPED (layout) && + !GTK_WIDGET_MAPPED (child->widget)) + { + gtk_widget_map (child->widget); + force_allocate = TRUE; + } + + if (force_allocate) + { + GtkAllocation allocation; + + if (GTK_WIDGET_NO_WINDOW (child->widget)) + { + if (child->window) + { + gdk_window_move_resize (child->window, + x, y, + child->widget->requisition.width, + child->widget->requisition.height); + } + + allocation.x = 0; + allocation.y = 0; + } + else + { + allocation.x = x; + allocation.y = y; + } + + allocation.width = child->widget->requisition.width; + allocation.height = child->widget->requisition.height; + + gtk_widget_size_allocate (child->widget, &allocation); + } + } + else + { + if (child->window) + gdk_window_hide (child->window); + else if (GTK_WIDGET_MAPPED (child->widget)) + gtk_widget_unmap (child->widget); + } +} + +static void +gtk_layout_position_children (GtkLayout *layout) +{ + GList *tmp_list; + + tmp_list = layout->children; + while (tmp_list) + { + gtk_layout_position_child (layout, tmp_list->data, FALSE); + + tmp_list = tmp_list->next; + } +} + +/* Callbacks */ + +/* Send a synthetic expose event to the widget + */ +static void +gtk_layout_expose_area (GtkLayout *layout, + gint x, gint y, gint width, gint height) +{ + if (layout->visibility == GDK_VISIBILITY_UNOBSCURED) + { + GdkEventExpose event; + + event.type = GDK_EXPOSE; + event.send_event = TRUE; + event.window = layout->bin_window; + event.count = 0; + + event.area.x = x; + event.area.y = y; + event.area.width = width; + event.area.height = height; + + gdk_window_ref (event.window); + gtk_widget_event (GTK_WIDGET (layout), (GdkEvent *)&event); + gdk_window_unref (event.window); + } +} + +/* This function is used to find events to process while scrolling + * Removing the GravityNotify events is a bit of a hack - currently + * GTK uses a lot of time processing them as GtkEventOther - a + * feature that is obsolete and will be removed. Until then... + */ + +static Bool +gtk_layout_expose_predicate (Display *display, + XEvent *xevent, + XPointer arg) +{ + if ((xevent->type == Expose) || (xevent->type == GravityNotify) || + ((xevent->xany.window == *(Window *)arg) && + (xevent->type == ConfigureNotify))) + return True; + else + return False; +} + +/* This is the main routine to do the scrolling. Scrolling is + * done by "Guffaw" scrolling, as in the Mozilla XFE, with + * a few modifications. + * + * The main improvement is that we keep track of whether we + * are obscured or not. If not, we ignore the generated expose + * events and instead do the exposes ourself, without having + * to wait for a roundtrip to the server. This also provides + * a limited form of expose-event compression, since we do + * the affected area as one big chunk. + * + * Real expose event compression, as in the XFE, could be added + * here. It would help opaque drags over the region, and the + * obscured case. + * + * Code needs to be added here to do the scrolling on machines + * that don't have working WindowGravity. That could be done + * + * - XCopyArea and move the windows, and accept trailing the + * background color. (Since it is only a fallback method) + * - XmHTML style. As above, but turn off expose events when + * not obscured and do the exposures ourself. + * - gzilla-style. Move the window continuously, and reset + * every 32768 pixels + */ + +static void +gtk_layout_adjustment_changed (GtkAdjustment *adjustment, + GtkLayout *layout) +{ + GtkWidget *widget; + XEvent xevent; + + gint dx, dy; + + widget = GTK_WIDGET (layout); + + dx = (gint)layout->hadjustment->value - layout->xoffset; + dy = (gint)layout->vadjustment->value - layout->yoffset; + + layout->xoffset = (gint)layout->hadjustment->value; + layout->yoffset = (gint)layout->vadjustment->value; + + if (layout->frozen) + return; + + gtk_layout_position_children (layout); + + if (!GTK_WIDGET_MAPPED (layout)) + return; + + if (dx > 0) + { + if (gravity_works) + { + gdk_window_resize (layout->bin_window, + widget->allocation.width + dx, + widget->allocation.height); + gdk_window_move (layout->bin_window, -dx, 0); + gdk_window_move_resize (layout->bin_window, + 0, 0, + widget->allocation.width, + widget->allocation.height); + } + else + { + /* FIXME */ + } + + gtk_layout_expose_area (layout, + widget->allocation.width - dx, + 0, + dx, + widget->allocation.height); + } + else if (dx < 0) + { + if (gravity_works) + { + gdk_window_move_resize (layout->bin_window, + dx, 0, + widget->allocation.width - dx, + widget->allocation.height); + gdk_window_move (layout->bin_window, 0, 0); + gdk_window_resize (layout->bin_window, + widget->allocation.width, + widget->allocation.height); + } + else + { + /* FIXME */ + } + + gtk_layout_expose_area (layout, + 0, + 0, + -dx, + widget->allocation.height); + } + + if (dy > 0) + { + if (gravity_works) + { + gdk_window_resize (layout->bin_window, + widget->allocation.width, + widget->allocation.height + dy); + gdk_window_move (layout->bin_window, 0, -dy); + gdk_window_move_resize (layout->bin_window, + 0, 0, + widget->allocation.width, + widget->allocation.height); + } + else + { + /* FIXME */ + } + + gtk_layout_expose_area (layout, + 0, + widget->allocation.height - dy, + widget->allocation.width, + dy); + } + else if (dy < 0) + { + if (gravity_works) + { + gdk_window_move_resize (layout->bin_window, + 0, dy, + widget->allocation.width, + widget->allocation.height - dy); + gdk_window_move (layout->bin_window, 0, 0); + gdk_window_resize (layout->bin_window, + widget->allocation.width, + widget->allocation.height); + } + else + { + /* FIXME */ + } + gtk_layout_expose_area (layout, + 0, + 0, + widget->allocation.height, + -dy); + } + + /* We have to make sure that all exposes from this scroll get + * processed before we scroll again, or the expose events will + * have invalid coordinates. + * + * We also do expose events for other windows, since otherwise + * their updating will fall behind the scrolling + * + * This also avoids a problem in pre-1.0 GTK where filters don't + * have access to configure events that were compressed. + */ + + gdk_flush(); + while (XCheckIfEvent(GDK_WINDOW_XDISPLAY (layout->bin_window), + &xevent, + gtk_layout_expose_predicate, + (XPointer)&GDK_WINDOW_XWINDOW (layout->bin_window))) + { + GdkEvent event; + GtkWidget *event_widget; + + if ((xevent.xany.window == GDK_WINDOW_XWINDOW (layout->bin_window)) && + (gtk_layout_filter (&xevent, &event, layout) == GDK_FILTER_REMOVE)) + continue; + + if (xevent.type == Expose) + { + event.expose.window = gdk_window_lookup (xevent.xany.window); + gdk_window_get_user_data (event.expose.window, + (gpointer *)&event_widget); + + if (event_widget) + { + event.expose.type = GDK_EXPOSE; + event.expose.area.x = xevent.xexpose.x; + event.expose.area.y = xevent.xexpose.y; + event.expose.area.width = xevent.xexpose.width; + event.expose.area.height = xevent.xexpose.height; + event.expose.count = xevent.xexpose.count; + + gdk_window_ref (event.expose.window); + gtk_widget_event (event_widget, &event); + gdk_window_unref (event.expose.window); + } + } + } +} + +/* The main event filter. Actually, we probably don't really need + * to install this as a filter at all, since we are calling it + * directly above in the expose-handling hack. But in case scrollbars + * are fixed up in some manner... + * + * This routine identifies expose events that are generated when + * we've temporarily moved the bin_window_origin, and translates + * them or discards them, depending on whether we are obscured + * or not. + */ +static GdkFilterReturn +gtk_layout_filter (GdkXEvent *gdk_xevent, + GdkEvent *event, + gpointer data) +{ + XEvent *xevent; + GtkLayout *layout; + + xevent = (XEvent *)gdk_xevent; + layout = GTK_LAYOUT (data); + + switch (xevent->type) + { + case Expose: + if (xevent->xexpose.serial == layout->configure_serial) + { + if (layout->visibility == GDK_VISIBILITY_UNOBSCURED) + return GDK_FILTER_REMOVE; + else + { + xevent->xexpose.x += layout->scroll_x; + xevent->xexpose.y += layout->scroll_y; + + break; + } + } + break; + + case ConfigureNotify: + if ((xevent->xconfigure.x != 0) || (xevent->xconfigure.y != 0)) + { + layout->configure_serial = xevent->xconfigure.serial; + layout->scroll_x = xevent->xconfigure.x; + layout->scroll_y = xevent->xconfigure.y; + } + break; + } + + return GDK_FILTER_CONTINUE; +} + +/* Although GDK does have a GDK_VISIBILITY_NOTIFY event, + * there is no corresponding event in GTK, so we have + * to get the events from a filter + */ +static GdkFilterReturn +gtk_layout_main_filter (GdkXEvent *gdk_xevent, + GdkEvent *event, + gpointer data) +{ + XEvent *xevent; + GtkLayout *layout; + + xevent = (XEvent *)gdk_xevent; + layout = GTK_LAYOUT (data); + + if (xevent->type == VisibilityNotify) + { + switch (xevent->xvisibility.state) + { + case VisibilityFullyObscured: + layout->visibility = GDK_VISIBILITY_FULLY_OBSCURED; + break; + + case VisibilityPartiallyObscured: + layout->visibility = GDK_VISIBILITY_PARTIAL; + break; + + case VisibilityUnobscured: + layout->visibility = GDK_VISIBILITY_UNOBSCURED; + break; + } + + return GDK_FILTER_REMOVE; + } + + + return GDK_FILTER_CONTINUE; +} + +/* Routines to set the window gravity, and check whether it is + * functional. Extra capabilities need to be added to GDK, so + * we don't have to use Xlib here. + */ +static void +gtk_layout_set_static_gravity (GdkWindow *win, gboolean on) +{ + XSetWindowAttributes xattributes; + + xattributes.win_gravity = on ? StaticGravity : NorthWestGravity; + xattributes.bit_gravity = on ? StaticGravity : NorthWestGravity; + + XChangeWindowAttributes (GDK_WINDOW_XDISPLAY (win), + GDK_WINDOW_XWINDOW (win), + CWBitGravity | CWWinGravity, + &xattributes); +} + +static gboolean +gtk_layout_gravity_works (void) +{ + GdkWindowAttr attr; + + GdkWindow *parent; + GdkWindow *child; + gint y; + + /* This particular server apparently has a bug so that the test + * works but the actual code crashes it + */ + if ((!strcmp (XServerVendor (GDK_DISPLAY()), "Sun Microsystems, Inc.")) && + (VendorRelease (GDK_DISPLAY()) == 3400)) + return FALSE; + + attr.window_type = GDK_WINDOW_TEMP; + attr.wclass = GDK_INPUT_OUTPUT; + attr.x = 0; + attr.y = 0; + attr.width = 100; + attr.height = 100; + attr.event_mask = 0; + + parent = gdk_window_new (NULL, &attr, GDK_WA_X | GDK_WA_Y); + + attr.window_type = GDK_WINDOW_CHILD; + child = gdk_window_new (parent, &attr, GDK_WA_X | GDK_WA_Y); + + gtk_layout_set_static_gravity (parent, TRUE); + gtk_layout_set_static_gravity (child, TRUE); + + gdk_window_resize (parent, 100, 110); + gdk_window_move (parent, 0, -10); + gdk_window_move_resize (parent, 0, 0, 100, 100); + + gdk_window_resize (parent, 100, 110); + gdk_window_move (parent, 0, -10); + gdk_window_move_resize (parent, 0, 0, 100, 100); + + gdk_window_get_geometry (child, NULL, &y, NULL, NULL, NULL); + + gdk_window_destroy (parent); + gdk_window_destroy (child); + + return (y == -20); +} + diff --git a/gtk/gtklayout.h b/gtk/gtklayout.h new file mode 100644 index 0000000000..3f60f0bc6a --- /dev/null +++ b/gtk/gtklayout.h @@ -0,0 +1,104 @@ +/* Copyright Owen Taylor, 1998 + * + * This file may be distributed under either the terms of the + * Netscape Public License, or the GNU Library General Public License + * + * Note: No GTK+ or Mozilla code should be added to this file. + * The coding style should be that of the the GTK core. + */ + +#ifndef __GTK_LAYOUT_H +#define __GTK_LAYOUT_H + +#include <gdk/gdk.h> +#include <gtk/gtkcontainer.h> +#include <gtk/gtkadjustment.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GTK_LAYOUT(obj) GTK_CHECK_CAST (obj, gtk_layout_get_type (), GtkLayout) +#define GTK_LAYOUT_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_layout_get_type (), GtkLayoutClass) +#define GTK_IS_LAYOUT(obj) GTK_CHECK_TYPE (obj, gtk_layout_get_type ()) + +typedef struct _GtkLayout GtkLayout; +typedef struct _GtkLayoutClass GtkLayoutClass; +typedef struct _GtkLayoutChild GtkLayoutChild; + +struct _GtkLayoutChild { + GtkWidget *widget; + GdkWindow *window; /* For NO_WINDOW widgets */ + gint x; + gint y; +}; + +struct _GtkLayout { + GtkContainer container; + + GList *children; + + guint width; + guint height; + + guint xoffset; + guint yoffset; + + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + + GdkWindow *bin_window; + + GdkVisibilityState visibility; + gulong configure_serial; + gint scroll_x; + gint scroll_y; + + guint frozen : 1; +}; + +struct _GtkLayoutClass { + GtkContainerClass parent_class; + + void (*scroll_adjustments) (GtkLayout *text, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); +}; + +GtkWidget* gtk_layout_new (GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); + +guint gtk_layout_get_type (void); +void gtk_layout_put (GtkLayout *layout, + GtkWidget *widget, + gint x, + gint y); + +void gtk_layout_move (GtkLayout *layout, + GtkWidget *widget, + gint x, + gint y); + +void gtk_layout_set_size (GtkLayout *layout, + guint width, + guint height); + +/* These disable and enable moving and repainting the scrolling window of the GtkLayout, + * respectively. If you want to update the layout's offsets but do not want it to + * repaint itself, you should use these functions. + */ +void gtk_layout_freeze (GtkLayout *layout); +void gtk_layout_thaw (GtkLayout *layout); + +GtkAdjustment* gtk_layout_get_hadjustment (GtkLayout *layout); +GtkAdjustment* gtk_layout_get_vadjustment (GtkLayout *layout); +void gtk_layout_set_hadjustment (GtkLayout *layout, + GtkAdjustment *adjustment); +void gtk_layout_set_vadjustment (GtkLayout *layout, + GtkAdjustment *adjustment); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __GTK_LAYOUT_H */ diff --git a/gtk/gtkplug.c b/gtk/gtkplug.c new file mode 100644 index 0000000000..4e95bb1e96 --- /dev/null +++ b/gtk/gtkplug.c @@ -0,0 +1,409 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* By Owen Taylor <otaylor@gtk.org> 98/4/4 */ + +#include "gdk/gdkx.h" +#include "gdk/gdkkeysyms.h" +#include "gtkplug.h" + +static void gtk_plug_class_init (GtkPlugClass *klass); +static void gtk_plug_init (GtkPlug *plug); + +static void gtk_plug_realize (GtkWidget *widget); +static gint gtk_plug_key_press_event (GtkWidget *widget, + GdkEventKey *event); +static void gtk_plug_forward_key_press (GtkPlug *plug, GdkEventKey *event); +static gint gtk_plug_focus_in_event (GtkWidget *widget, GdkEventFocus *event); +static gint gtk_plug_focus_out_event (GtkWidget *widget, GdkEventFocus *event); +static void gtk_plug_set_focus (GtkWindow *window, + GtkWidget *focus); + +/* From Tk */ +#define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20 + +guint +gtk_plug_get_type () +{ + static guint plug_type = 0; + + if (!plug_type) + { + GtkTypeInfo plug_info = + { + "GtkPlug", + sizeof (GtkPlug), + sizeof (GtkPlugClass), + (GtkClassInitFunc) gtk_plug_class_init, + (GtkObjectInitFunc) gtk_plug_init, + (GtkArgSetFunc) NULL, + (GtkArgGetFunc) NULL + }; + + plug_type = gtk_type_unique (gtk_window_get_type (), &plug_info); + } + + return plug_type; +} + +static void +gtk_plug_class_init (GtkPlugClass *class) +{ + GtkWidgetClass *widget_class; + GtkWindowClass *window_class; + + widget_class = (GtkWidgetClass *)class; + window_class = (GtkWindowClass *)class; + + widget_class->realize = gtk_plug_realize; + widget_class->key_press_event = gtk_plug_key_press_event; + widget_class->focus_in_event = gtk_plug_focus_in_event; + widget_class->focus_out_event = gtk_plug_focus_out_event; + + window_class->set_focus = gtk_plug_set_focus; +} + +static void +gtk_plug_init (GtkPlug *plug) +{ + GtkWindow *window; + + window = GTK_WINDOW (plug); + + window->type = GTK_WINDOW_TOPLEVEL; + window->auto_shrink = TRUE; +} +void +gtk_plug_construct (GtkPlug *plug, guint32 socket_id) +{ + plug->socket_window = gdk_window_lookup (socket_id); + plug->same_app = TRUE; + + if (plug->socket_window == NULL) + { + plug->socket_window = gdk_window_foreign_new (socket_id); + plug->same_app = FALSE; + } +} +GtkWidget* +gtk_plug_new (guint32 socket_id) +{ + GtkPlug *plug; + + plug = GTK_PLUG (gtk_type_new (gtk_plug_get_type ())); + gtk_plug_construct (plug, socket_id); + return GTK_WIDGET (plug); +} + +static void +gtk_plug_realize (GtkWidget *widget) +{ + GtkWindow *window; + GtkPlug *plug; + GdkWindowAttr attributes; + gint attributes_mask; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_PLUG (widget)); + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + window = GTK_WINDOW (widget); + plug = GTK_PLUG (widget); + + attributes.window_type = GDK_WINDOW_CHILD; /* XXX GDK_WINDOW_PLUG ? */ + attributes.title = window->title; + attributes.wmclass_name = window->wmclass_name; + attributes.wmclass_class = window->wmclass_class; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + + /* this isn't right - we should match our parent's visual/colormap. + * though that will require handling "foreign" colormaps */ + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = gtk_widget_get_events (widget); + attributes.event_mask |= (GDK_EXPOSURE_MASK | + GDK_KEY_PRESS_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_FOCUS_CHANGE_MASK | + GDK_STRUCTURE_MASK); + + attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP; + attributes_mask |= (window->title ? GDK_WA_TITLE : 0); + attributes_mask |= (window->wmclass_name ? GDK_WA_WMCLASS : 0); + + widget->window = gdk_window_new (plug->socket_window, + &attributes, attributes_mask); + ((GdkWindowPrivate *)widget->window)->window_type = GDK_WINDOW_TOPLEVEL; + gdk_window_set_user_data (widget->window, window); + + widget->style = gtk_style_attach (widget->style, widget->window); + gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); +} + +static gint +gtk_plug_key_press_event (GtkWidget *widget, + GdkEventKey *event) +{ + GtkWindow *window; + GtkPlug *plug; + GtkDirectionType direction = 0; + gint return_val; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_PLUG (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + window = GTK_WINDOW (widget); + plug = GTK_PLUG (widget); + + if (!GTK_WIDGET_HAS_FOCUS(widget)) + { + gtk_plug_forward_key_press (plug, event); + return TRUE; + } + + return_val = FALSE; + if (window->focus_widget) + return_val = gtk_widget_event (window->focus_widget, (GdkEvent*) event); + +#if 0 + if (!return_val && gtk_window_check_accelerator (window, event->keyval, event->state)) + return_val = TRUE; +#endif + + if (!return_val) + { + switch (event->keyval) + { + case GDK_space: + if (window->focus_widget) + { + gtk_widget_activate (window->focus_widget); + return_val = TRUE; + } + break; + case GDK_Return: + case GDK_KP_Enter: + if (window->default_widget) + { + gtk_widget_activate (window->default_widget); + return_val = TRUE; + } + else if (window->focus_widget) + { + gtk_widget_activate (window->focus_widget); + return_val = TRUE; + } + break; + case GDK_Up: + case GDK_Down: + case GDK_Left: + case GDK_Right: + case GDK_Tab: + switch (event->keyval) + { + case GDK_Up: + direction = GTK_DIR_UP; + break; + case GDK_Down: + direction = GTK_DIR_DOWN; + break; + case GDK_Left: + direction = GTK_DIR_LEFT; + break; + case GDK_Right: + direction = GTK_DIR_RIGHT; + break; + case GDK_Tab: + if (event->state & GDK_SHIFT_MASK) + direction = GTK_DIR_TAB_BACKWARD; + else + direction = GTK_DIR_TAB_FORWARD; + break; + default : + direction = GTK_DIR_UP; /* never reached, but makes compiler happy */ + } + + gtk_container_focus (GTK_CONTAINER (widget), direction); + + if (!GTK_CONTAINER (window)->focus_child) + { + gtk_window_set_focus (GTK_WINDOW (widget), NULL); + + XSetInputFocus (GDK_DISPLAY (), + GDK_WINDOW_XWINDOW (plug->socket_window), + RevertToParent, event->time); + + gtk_plug_forward_key_press (plug, event); + } + + return_val = TRUE; + + break; + } + } + + return return_val; +} + +static void +gtk_plug_forward_key_press (GtkPlug *plug, GdkEventKey *event) +{ + XEvent xevent; + + xevent.xkey.type = KeyPress; + xevent.xkey.display = GDK_WINDOW_XDISPLAY (GTK_WIDGET(plug)->window); + xevent.xkey.window = GDK_WINDOW_XWINDOW (plug->socket_window); + xevent.xkey.root = GDK_ROOT_WINDOW (); /* FIXME */ + xevent.xkey.time = event->time; + /* FIXME, the following might cause big problems for + * non-GTK apps */ + xevent.xkey.x = 0; + xevent.xkey.y = 0; + xevent.xkey.x_root = 0; + xevent.xkey.y_root = 0; + xevent.xkey.state = event->state; + xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), + event->keyval); + xevent.xkey.same_screen = TRUE; /* FIXME ? */ + + XSendEvent (gdk_display, + GDK_WINDOW_XWINDOW (plug->socket_window), + False, NoEventMask, &xevent); +} + +/* Copied from Window, Ughh */ + +static gint +gtk_plug_focus_in_event (GtkWidget *widget, + GdkEventFocus *event) +{ + GtkWindow *window; + GdkEventFocus fevent; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_PLUG (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + /* It appears spurious focus in events can occur when + * the window is hidden. So we'll just check to see if + * the window is visible before actually handling the + * event + */ + if (GTK_WIDGET_VISIBLE (widget)) + { + GTK_OBJECT_SET_FLAGS (widget, GTK_HAS_FOCUS); + window = GTK_WINDOW (widget); + if (window->focus_widget && !GTK_WIDGET_HAS_FOCUS (window->focus_widget)) + { + fevent.type = GDK_FOCUS_CHANGE; + fevent.window = window->focus_widget->window; + fevent.in = TRUE; + + gtk_widget_event (window->focus_widget, (GdkEvent*) &fevent); + } + } + + return FALSE; +} + +static gint +gtk_plug_focus_out_event (GtkWidget *widget, + GdkEventFocus *event) +{ + GtkWindow *window; + GdkEventFocus fevent; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_PLUG (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + GTK_OBJECT_UNSET_FLAGS (widget, GTK_HAS_FOCUS); + + window = GTK_WINDOW (widget); + + if (window->focus_widget && GTK_WIDGET_HAS_FOCUS (window->focus_widget)) + { + fevent.type = GDK_FOCUS_CHANGE; + fevent.window = window->focus_widget->window; + fevent.in = FALSE; + + gtk_widget_event (window->focus_widget, (GdkEvent*) &fevent); + } + + return FALSE; +} + +static void +gtk_plug_set_focus (GtkWindow *window, + GtkWidget *focus) +{ + GtkPlug *plug; + GdkEventFocus event; + + g_return_if_fail (window != NULL); + g_return_if_fail (GTK_IS_PLUG (window)); + + plug = GTK_PLUG (window); + + if (focus && !GTK_WIDGET_CAN_FOCUS (focus)) + return; + + if (window->focus_widget != focus) + { + if (window->focus_widget) + { + event.type = GDK_FOCUS_CHANGE; + event.window = window->focus_widget->window; + event.in = FALSE; + + gtk_widget_event (window->focus_widget, (GdkEvent*) &event); + } + + window->focus_widget = focus; + + if (window->focus_widget) + { + event.type = GDK_FOCUS_CHANGE; + event.window = window->focus_widget->window; + event.in = TRUE; + + gtk_widget_event (window->focus_widget, (GdkEvent*) &event); + } + } + + /* Ask for focus from parent */ + + if (focus && !GTK_WIDGET_HAS_FOCUS(window)) + { + XEvent xevent; + + xevent.xfocus.type = FocusIn; + xevent.xfocus.display = GDK_WINDOW_XDISPLAY (GTK_WIDGET(plug)->window); + xevent.xfocus.window = GDK_WINDOW_XWINDOW (plug->socket_window); + xevent.xfocus.mode = EMBEDDED_APP_WANTS_FOCUS; + xevent.xfocus.detail = FALSE; /* Don't force */ + + XSendEvent (gdk_display, + GDK_WINDOW_XWINDOW (plug->socket_window), + False, NoEventMask, &xevent); + } +} diff --git a/gtk/gtkplug.h b/gtk/gtkplug.h new file mode 100644 index 0000000000..32d9836119 --- /dev/null +++ b/gtk/gtkplug.h @@ -0,0 +1,65 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __GTK_PLUG_H__ +#define __GTK_PLUG_H__ + + +#include <gdk/gdk.h> +#include <gtk/gtkwindow.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GTK_PLUG(obj) GTK_CHECK_CAST (obj, gtk_plug_get_type (), GtkPlug) +#define GTK_PLUG_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_plug_get_type (), GtkPlugClass) +#define GTK_IS_PLUG(obj) GTK_CHECK_TYPE (obj, gtk_plug_get_type ()) + + +typedef struct _GtkPlug GtkPlug; +typedef struct _GtkPlugClass GtkPlugClass; +typedef struct _GtkPlugButton GtkPlugButton; + + +struct _GtkPlug +{ + GtkWindow window; + + GdkWindow *socket_window; + gint same_app; +}; + +struct _GtkPlugClass +{ + GtkWindowClass parent_class; +}; + + +guint gtk_plug_get_type (void); +void gtk_plug_construct (GtkPlug *plug, guint32 socket_id); +GtkWidget* gtk_plug_new (guint32 socket_id); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_PLUG_H__ */ diff --git a/gtk/gtksocket.c b/gtk/gtksocket.c new file mode 100644 index 0000000000..dcf905c59f --- /dev/null +++ b/gtk/gtksocket.c @@ -0,0 +1,688 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* By Owen Taylor <otaylor@gtk.org> 98/4/4 */ + +#include "gdk/gdkx.h" +#include "gdk/gdkkeysyms.h" +#include "gtkwindow.h" +#include "gtksignal.h" +#include "gtksocket.h" +#include "gtkdnd.h" + +/* Forward declararations */ + +static void gtk_socket_class_init (GtkSocketClass *klass); +static void gtk_socket_init (GtkSocket *socket); +static void gtk_socket_realize (GtkWidget *widget); +static void gtk_socket_unrealize (GtkWidget *widget); +static void gtk_socket_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_socket_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static gint gtk_socket_focus_in_event (GtkWidget *widget, + GdkEventFocus *event); +static void gtk_socket_claim_focus (GtkSocket *socket); +static gint gtk_socket_focus_out_event (GtkWidget *widget, + GdkEventFocus *event); +static void gtk_socket_send_configure_event (GtkSocket *socket); +static gint gtk_socket_focus (GtkContainer *container, + GtkDirectionType direction); +static GdkFilterReturn gtk_socket_filter_func (GdkXEvent *gdk_xevent, + GdkEvent *event, + gpointer data); + +#ifdef DEBUG_PLUGSOCKET +#define DPRINTF(arg) g_print arg +#else +#define DPRINTF(arg) +#endif + +/* From Tk */ +#define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20 + +/* Local data */ + +static GtkWidgetClass *parent_class = NULL; + +guint +gtk_socket_get_type () +{ + static guint socket_type = 0; + + if (!socket_type) + { + GtkTypeInfo socket_info = + { + "GtkSocket", + sizeof (GtkSocket), + sizeof (GtkSocketClass), + (GtkClassInitFunc) gtk_socket_class_init, + (GtkObjectInitFunc) gtk_socket_init, + (GtkArgSetFunc) NULL, + (GtkArgGetFunc) NULL + }; + + socket_type = gtk_type_unique (gtk_container_get_type (), &socket_info); + } + + return socket_type; +} + +static void +gtk_socket_class_init (GtkSocketClass *class) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + + object_class = (GtkObjectClass*) class; + widget_class = (GtkWidgetClass*) class; + container_class = (GtkContainerClass*) class; + + parent_class = gtk_type_class (gtk_widget_get_type ()); + + widget_class->realize = gtk_socket_realize; + widget_class->unrealize = gtk_socket_unrealize; + widget_class->size_request = gtk_socket_size_request; + widget_class->size_allocate = gtk_socket_size_allocate; + widget_class->focus_in_event = gtk_socket_focus_in_event; + widget_class->focus_out_event = gtk_socket_focus_out_event; + + container_class->focus = gtk_socket_focus; +} + +static void +gtk_socket_init (GtkSocket *socket) +{ + socket->request_width = 0; + socket->request_height = 0; + socket->current_width = 0; + socket->current_height = 0; + + socket->plug_window = NULL; + socket->same_app = FALSE; + socket->focus_in = FALSE; + socket->have_size = FALSE; + socket->need_map = FALSE; +} + +GtkWidget* +gtk_socket_new () +{ + GtkSocket *socket; + + socket = gtk_type_new (gtk_socket_get_type ()); + + return GTK_WIDGET (socket); +} + +void +gtk_socket_steal (GtkSocket *socket, guint32 id) +{ + GtkWidget *widget; + + widget = GTK_WIDGET (socket); + + socket->plug_window = gdk_window_lookup (id); + + if (socket->plug_window && socket->plug_window->user_data) + { + GtkWidget *child_widget = GTK_WIDGET (socket->plug_window->user_data); + + g_warning("Stealing from same app not yet implemented"); + + socket->same_app = TRUE; + } + else + { + socket->plug_window = gdk_window_foreign_new (id); + socket->same_app = FALSE; + socket->have_size = FALSE; + + XSelectInput (GDK_DISPLAY (), + GDK_WINDOW_XWINDOW(socket->plug_window), + StructureNotifyMask | PropertyChangeMask); + + gtk_widget_queue_resize (widget); + } + + gdk_window_hide (socket->plug_window); + gdk_window_reparent (socket->plug_window, widget->window, 0, 0); + socket->need_map = TRUE; +} + +static void +gtk_socket_realize (GtkWidget *widget) +{ + GtkSocket *socket; + GdkWindowAttr attributes; + gint attributes_mask; + XWindowAttributes xattrs; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SOCKET (widget)); + + socket = GTK_SOCKET (widget); + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = GDK_FOCUS_CHANGE_MASK; + + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + + widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, attributes_mask); + gdk_window_set_user_data (widget->window, socket); + + widget->style = gtk_style_attach (widget->style, widget->window); + gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); + + XGetWindowAttributes (GDK_DISPLAY (), + GDK_WINDOW_XWINDOW (widget->window), + &xattrs); + + XSelectInput (GDK_DISPLAY (), + GDK_WINDOW_XWINDOW(widget->window), + xattrs.your_event_mask | + SubstructureNotifyMask | SubstructureRedirectMask); + + gdk_window_add_filter (widget->window, gtk_socket_filter_func, widget); + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + + /* We sync here so that we make sure that if the XID for + * our window is passed to another application, SubstructureRedirectMask + * will be set by the time the other app creates its window. + */ + gdk_flush(); +} + +static void +gtk_socket_unrealize (GtkWidget *widget) +{ + GtkSocket *socket; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SOCKET (widget)); + + socket = GTK_SOCKET (widget); + + if (socket->plug_window) + { + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); + if (toplevel && GTK_IS_WINDOW (toplevel)) + gtk_window_remove_embedded_xid (GTK_WINDOW (toplevel), + GDK_WINDOW_XWINDOW (socket->plug_window)); + } + + if (GTK_WIDGET_CLASS (parent_class)->unrealize) + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); +} + +static void +gtk_socket_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GtkSocket *socket; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SOCKET (widget)); + g_return_if_fail (requisition != NULL); + + socket = GTK_SOCKET (widget); + + if (!socket->have_size && socket->plug_window) + { + XSizeHints hints; + long supplied; + + if (XGetWMNormalHints (GDK_DISPLAY(), + GDK_WINDOW_XWINDOW (socket->plug_window), + &hints, &supplied)) + { + /* This is obsolete, according the X docs, but many programs + * still use it */ + if (hints.flags & (PSize | USSize)) + { + socket->request_width = hints.width; + socket->request_height = hints.height; + } + else if (hints.flags & PMinSize) + { + socket->request_width = hints.min_width; + socket->request_height = hints.min_height; + } + else if (hints.flags & PBaseSize) + { + socket->request_width = hints.base_width; + socket->request_height = hints.base_height; + } + } + socket->have_size = TRUE; /* don't check again? */ + } + + requisition->width = socket->request_width; + requisition->height = socket->request_height; +} + +static void +gtk_socket_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkSocket *socket; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SOCKET (widget)); + g_return_if_fail (allocation != NULL); + + socket = GTK_SOCKET (widget); + + widget->allocation = *allocation; + if (GTK_WIDGET_REALIZED (widget)) + { + gdk_window_move_resize (widget->window, + allocation->x, allocation->y, + allocation->width, allocation->height); + + if (socket->plug_window) + { + if (!socket->need_map && + (allocation->width == socket->current_width) && + (allocation->height == socket->current_height)) + { + gtk_socket_send_configure_event (socket); + DPRINTF(( "No change: %d %d\n", + allocation->width, allocation->height)); + } + else + { + gdk_window_move_resize (socket->plug_window, + 0, 0, + allocation->width, allocation->height); + DPRINTF(("configuring: %d %d\n", + allocation->width, allocation->height)); + socket->current_width = allocation->width; + socket->current_height = allocation->height; + } + + if (socket->need_map) + { + gdk_window_show (socket->plug_window); + socket->need_map = FALSE; + } + + } + } +} + +static gint +gtk_socket_focus_in_event (GtkWidget *widget, GdkEventFocus *event) +{ + GtkSocket *socket; + g_return_val_if_fail (GTK_IS_SOCKET (widget), FALSE); + socket = GTK_SOCKET (widget); + + DPRINTF (( "Got focus\n")); + + if (socket->focus_in && socket->plug_window) + XSetInputFocus (GDK_DISPLAY (), + GDK_WINDOW_XWINDOW (socket->plug_window), + RevertToParent, GDK_CURRENT_TIME); + + return TRUE; +} + +static gint +gtk_socket_focus_out_event (GtkWidget *widget, GdkEventFocus *event) +{ + GtkWidget *toplevel; + GtkSocket *socket; + + g_return_val_if_fail (GTK_IS_SOCKET (widget), FALSE); + socket = GTK_SOCKET (widget); + + toplevel = gtk_widget_get_ancestor (widget, gtk_window_get_type()); + + if (toplevel) + { + XSetInputFocus (GDK_DISPLAY (), + GDK_WINDOW_XWINDOW (toplevel->window), + RevertToParent, CurrentTime); /* FIXME? */ + } + + socket->focus_in = FALSE; + + return TRUE; +} + +static void +gtk_socket_claim_focus (GtkSocket *socket) +{ + + socket->focus_in = TRUE; + + /* Oh, the trickery... */ + + GTK_WIDGET_SET_FLAGS (socket, GTK_CAN_FOCUS); + gtk_widget_grab_focus (GTK_WIDGET (socket)); + GTK_WIDGET_UNSET_FLAGS (socket, GTK_CAN_FOCUS); + + /* FIXME: we might grab the focus even if we don't have + * it as an app... (and see _focus_in ()) */ + if (socket->plug_window) + XSetInputFocus (GDK_DISPLAY (), + GDK_WINDOW_XWINDOW (socket->plug_window), + RevertToParent, GDK_CURRENT_TIME); +} + +static gint +gtk_socket_focus (GtkContainer *container, GtkDirectionType direction) +{ + GtkSocket *socket; + + g_return_val_if_fail (GTK_IS_SOCKET (container), FALSE); + + socket = GTK_SOCKET (container); + + if (!socket->focus_in && socket->plug_window) + { + XEvent xevent; + + gtk_socket_claim_focus (socket); + + xevent.xkey.type = KeyPress; + xevent.xkey.display = GDK_DISPLAY (); + xevent.xkey.window = GDK_WINDOW_XWINDOW (socket->plug_window); + xevent.xkey.root = GDK_ROOT_WINDOW (); /* FIXME */ + xevent.xkey.time = GDK_CURRENT_TIME; /* FIXME */ + /* FIXME, the following might cause big problems for + * non-GTK apps */ + xevent.xkey.x = 0; + xevent.xkey.y = 0; + xevent.xkey.x_root = 0; + xevent.xkey.y_root = 0; + xevent.xkey.state = 0; + xevent.xkey.same_screen = TRUE; /* FIXME ? */ + + switch (direction) + { + case GTK_DIR_UP: + xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Up); + break; + case GTK_DIR_DOWN: + xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Down); + break; + case GTK_DIR_LEFT: + xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Left); + break; + case GTK_DIR_RIGHT: + xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Right); + break; + case GTK_DIR_TAB_FORWARD: + xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Tab); + break; + case GTK_DIR_TAB_BACKWARD: + xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), GDK_Tab); + xevent.xkey.state = ShiftMask; + break; + } + + + XSendEvent (gdk_display, + GDK_WINDOW_XWINDOW (socket->plug_window), + False, NoEventMask, &xevent); + return TRUE; + } + else + { + return FALSE; + } +} + +static void +gtk_socket_send_configure_event (GtkSocket *socket) +{ + XEvent event; + + g_return_if_fail (socket->plug_window != NULL); + + event.xconfigure.type = ConfigureNotify; + event.xconfigure.display = gdk_display; + + event.xconfigure.event = GDK_WINDOW_XWINDOW (socket->plug_window); + event.xconfigure.window = GDK_WINDOW_XWINDOW (socket->plug_window); + + event.xconfigure.x = 0; + event.xconfigure.y = 0; + event.xconfigure.width = GTK_WIDGET(socket)->allocation.width; + event.xconfigure.height = GTK_WIDGET(socket)->allocation.height; + + event.xconfigure.border_width = 0; + event.xconfigure.above = None; + event.xconfigure.override_redirect = False; + + XSendEvent (gdk_display, + GDK_WINDOW_XWINDOW (socket->plug_window), + False, NoEventMask, &event); +} + +static void +gtk_socket_add_window (GtkSocket *socket, guint32 xid) +{ + socket->plug_window = gdk_window_lookup (xid); + socket->same_app = TRUE; + + if (!socket->plug_window) + { + GtkWidget *toplevel; + GdkDragProtocol protocol; + + socket->plug_window = gdk_window_foreign_new (xid); + socket->same_app = FALSE; + + XSelectInput (GDK_DISPLAY (), + GDK_WINDOW_XWINDOW(socket->plug_window), + StructureNotifyMask | PropertyChangeMask); + + if (gdk_drag_get_protocol (xid, &protocol)) + gtk_drag_dest_set_proxy (GTK_WIDGET (socket), socket->plug_window, + protocol, TRUE); + + gdk_window_add_filter (socket->plug_window, + gtk_socket_filter_func, socket); + + /* Add a pointer to the socket on our toplevel window */ + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); + if (toplevel && GTK_IS_WINDOW (toplevel)) + { + gtk_window_add_embedded_xid (GTK_WINDOW (toplevel), xid); + } + } +} + +static GdkFilterReturn +gtk_socket_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) +{ + GtkSocket *socket; + GtkWidget *widget; + XEvent *xevent; + + GdkFilterReturn return_val; + + socket = GTK_SOCKET (data); + widget = GTK_WIDGET (socket); + xevent = (XEvent *)gdk_xevent; + + return_val = GDK_FILTER_CONTINUE; + + switch (xevent->type) + { + case CreateNotify: + { + XCreateWindowEvent *xcwe = &xevent->xcreatewindow; + + if (!socket->plug_window) + { + g_print("Here!\n"); + + gtk_socket_add_window (socket, xcwe->window); + + gdk_window_move_resize(socket->plug_window, + 0, 0, + widget->allocation.width, + widget->allocation.height); + + socket->request_width = xcwe->width; + socket->request_height = xcwe->height; + socket->have_size = TRUE; + + DPRINTF(("Window created with size: %d %d\n", + socket->request_width, + socket->request_height)); + + gtk_widget_queue_resize (widget); + } + + return_val = GDK_FILTER_REMOVE; + + break; + } + + case ConfigureRequest: + { + XConfigureRequestEvent *xcre = &xevent->xconfigurerequest; + + if (!socket->plug_window) + gtk_socket_add_window (socket, xcre->window); + + if (xcre->window == GDK_WINDOW_XWINDOW (socket->plug_window)) + { + if (xcre->value_mask & (CWWidth | CWHeight)) + { + socket->request_width = xcre->width; + socket->request_height = xcre->height; + socket->have_size = TRUE; + + DPRINTF(("Configure request: %d %d\n", + socket->request_width, + socket->request_height)); + + gtk_widget_queue_resize (widget); + } + else if (xcre->value_mask & (CWX | CWY)) + { + gtk_socket_send_configure_event (socket); + } + /* Ignore stacking requests. */ + + return_val = GDK_FILTER_REMOVE; + } + break; + } + + case DestroyNotify: + { + XDestroyWindowEvent *xdwe = &xevent->xdestroywindow; + + if (socket->plug_window && + (xdwe->window == GDK_WINDOW_XWINDOW (socket->plug_window))) + { + GtkWidget *toplevel; + + DPRINTF(("Destroy Notify\n")); + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); + if (toplevel && GTK_IS_WINDOW (toplevel)) + gtk_window_remove_embedded_xid (GTK_WINDOW (toplevel), xdwe->window); + gtk_widget_destroy (widget); + gdk_window_destroy_notify (socket->plug_window); + + socket->plug_window = NULL; + + return_val = GDK_FILTER_REMOVE; + } + break; + } + + case FocusIn: + if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS) + { + gtk_socket_claim_focus (socket); + } + else if (xevent->xfocus.detail == NotifyInferior) + { +#if 0 + GtkWidget *toplevel; + toplevel = gtk_widget_get_ancestor (widget, gtk_window_get_type()); + + if (toplevel) + { + XSetInputFocus (GDK_DISPLAY (), + GDK_WINDOW_XWINDOW (toplevel->window), + RevertToParent, CurrentTime); /* FIXME? */ + } +#endif + } + return_val = GDK_FILTER_REMOVE; + break; + case FocusOut: + return_val = GDK_FILTER_REMOVE; + break; + case MapRequest: + if (!socket->plug_window) + gtk_socket_add_window (socket, xevent->xmaprequest.window); + + if (xevent->xmaprequest.window == + GDK_WINDOW_XWINDOW (socket->plug_window)) + { + DPRINTF(("Map Request\n")); + + gdk_window_show (socket->plug_window); + + return_val = GDK_FILTER_REMOVE; + } + break; + case PropertyNotify: + if (xevent->xproperty.window == + GDK_WINDOW_XWINDOW (socket->plug_window)) + { + GdkDragProtocol protocol; + + if ((xevent->xproperty.atom == gdk_atom_intern ("XdndAware", FALSE)) || + (xevent->xproperty.atom == gdk_atom_intern ("_MOTIF_DRAG_RECEIVER_INFO", FALSE))) + { + if (gdk_drag_get_protocol (xevent->xproperty.window, &protocol)) + gtk_drag_dest_set_proxy (GTK_WIDGET (socket), + socket->plug_window, + protocol, TRUE); + } + return_val = GDK_FILTER_REMOVE; + } + } + + return return_val; +} diff --git a/gtk/gtksocket.h b/gtk/gtksocket.h new file mode 100644 index 0000000000..c1c9d3a325 --- /dev/null +++ b/gtk/gtksocket.h @@ -0,0 +1,68 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __GTK_SOCKET_H__ +#define __GTK_SOCKET_H__ + +#include <gtk/gtkcontainer.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GTK_SOCKET(obj) GTK_CHECK_CAST (obj, gtk_socket_get_type (), GtkSocket) +#define GTK_SOCKET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_socket_get_type (), GtkSocketClass) +#define GTK_IS_SOCKET(obj) GTK_CHECK_TYPE (obj, gtk_socket_get_type ()) + + +typedef struct _GtkSocket GtkSocket; +typedef struct _GtkSocketClass GtkSocketClass; + +struct _GtkSocket +{ + GtkContainer container; + + guint16 request_width; + guint16 request_height; + guint16 current_width; + guint16 current_height; + + GdkWindow *plug_window; + guint same_app : 1; + guint focus_in : 1; + guint have_size : 1; + guint need_map : 1; +}; + +struct _GtkSocketClass +{ + GtkContainerClass parent_class; +}; + + +GtkWidget* gtk_socket_new (void); +guint gtk_socket_get_type (void ); +void gtk_socket_steal (GtkSocket *socket, + guint32 wid); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_SOCKET_H__ */ diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 9f7c65b5b2..c8accc44e9 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -464,6 +464,58 @@ gtk_window_set_modal (GtkWindow *window, gboolean modal) window->modal = modal; } +void +gtk_window_add_embedded_xid (GtkWindow *window, guint xid) +{ + GList *embedded_windows; + + g_return_if_fail (window != NULL); + g_return_if_fail (GTK_IS_WINDOW (window)); + + g_print ("add %#x\n", xid); + + embedded_windows = gtk_object_get_data (GTK_OBJECT (window), "gtk-embedded"); + if (embedded_windows) + gtk_object_remove_no_notify_by_id (GTK_OBJECT (window), + g_quark_from_static_string ("gtk-embedded")); + embedded_windows = g_list_prepend (embedded_windows, + GUINT_TO_POINTER (xid)); + + gtk_object_set_data_full (GTK_OBJECT (window), "gtk-embedded", + embedded_windows, + embedded_windows ? + (GtkDestroyNotify) g_list_free : NULL); +} + +void +gtk_window_remove_embedded_xid (GtkWindow *window, guint xid) +{ + GList *embedded_windows; + GList *node; + + g_return_if_fail (window != NULL); + g_return_if_fail (GTK_IS_WINDOW (window)); + + g_print ("remove %#x\n", xid); + + embedded_windows = gtk_object_get_data (GTK_OBJECT (window), "gtk-embedded"); + if (embedded_windows) + gtk_object_remove_no_notify_by_id (GTK_OBJECT (window), + g_quark_from_static_string ("gtk-embedded")); + + node = g_list_find (embedded_windows, GUINT_TO_POINTER (xid)); + if (node) + { + embedded_windows = g_list_remove_link (embedded_windows, node); + g_list_free_1 (node); + } + + gtk_object_set_data_full (GTK_OBJECT (window), + "gtk-embedded", embedded_windows, + embedded_windows ? + (GtkDestroyNotify) g_list_free : NULL); +} + static void gtk_window_shutdown (GtkObject *object) { diff --git a/gtk/gtkwindow.h b/gtk/gtkwindow.h index 33dea2e753..7f0b616d39 100644 --- a/gtk/gtkwindow.h +++ b/gtk/gtkwindow.h @@ -100,6 +100,9 @@ gint gtk_window_activate_default (GtkWindow *window); void gtk_window_set_modal (GtkWindow *window, gboolean modal); +void gtk_window_remove_embedded_xid (GtkWindow *window, guint xid); +void gtk_window_add_embedded_xid (GtkWindow *window, guint xid); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/gtk/testgtk.c b/gtk/testgtk.c index 4b550881c1..ec5cfdf5d5 100644 --- a/gtk/testgtk.c +++ b/gtk/testgtk.c @@ -7940,6 +7940,94 @@ create_mainloop (void) gtk_widget_destroy (window); } +gint +layout_expose_handler (GtkWidget *widget, GdkEventExpose *event) +{ + GtkLayout *layout; + + gint i,j; + gint imin, imax, jmin, jmax; + + layout = GTK_LAYOUT (widget); + + imin = (layout->xoffset + event->area.x) / 10; + imax = (layout->xoffset + event->area.x + event->area.width + 9) / 10; + + jmin = (layout->yoffset + event->area.y) / 10; + jmax = (layout->yoffset + event->area.y + event->area.height + 9) / 10; + + gdk_window_clear_area (widget->window, + event->area.x, event->area.y, + event->area.width, event->area.height); + + for (i=imin; i<imax; i++) + for (j=jmin; j<jmax; j++) + if ((i+j) % 2) + gdk_draw_rectangle (layout->bin_window, + widget->style->black_gc, + TRUE, + 10*i - layout->xoffset, 10*j - layout->yoffset, + 1+i%10, 1+j%10); + + return TRUE; +} + +void create_layout (void) +{ + static GtkWidget *window = NULL; + GtkWidget *layout; + GtkWidget *scrolledwindow; + GtkWidget *button; + + if (!window) + { + gchar buf[16]; + + gint i, j; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), + &window); + + gtk_window_set_title (GTK_WINDOW (window), "Layout"); + gtk_widget_set_usize (window, 200, 200); + + scrolledwindow = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow); + + gtk_container_add (GTK_CONTAINER (window), scrolledwindow); + + layout = gtk_layout_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER (scrolledwindow), layout); + + gtk_widget_set_events (layout, GDK_EXPOSURE_MASK); + gtk_signal_connect (GTK_OBJECT (layout), "expose_event", + GTK_SIGNAL_FUNC (layout_expose_handler), NULL); + + gtk_layout_set_size (GTK_LAYOUT (layout), 1600, 1600); + gtk_widget_show (layout); + + for (i=0 ; i < 16 ; i++) + for (j=0 ; j < 16 ; j++) + { + sprintf(buf, "Button %d, %d", i, j); + if ((i + j) % 2) + button = gtk_button_new_with_label (buf); + else + button = gtk_label_new (buf); + gtk_layout_put (GTK_LAYOUT (layout), button, + j*100, i*100); + gtk_widget_show (button); + } + } + + if (!GTK_WIDGET_VISIBLE (window)) + gtk_widget_show (window); + else + gtk_widget_destroy (window); +} + /* * Main Window and Exit */ @@ -7974,6 +8062,7 @@ create_main_window (void) { "gamma curve", create_gamma_curve }, { "handle box", create_handle_box }, { "item factory", create_item_factory }, + { "layout", create_layout }, { "list", create_list }, { "menus", create_menus }, { "modal window", create_modal_window }, |