| /******************************************************************************* |
| * Copyright (c) 2018 Red Hat and others. All rights reserved. |
| * The contents of this file are made available under the terms |
| * of the GNU Lesser General Public License (LGPL) Version 2.1 that |
| * accompanies this distribution (lgpl-v21.txt). The LGPL is also |
| * available at http://www.gnu.org/licenses/lgpl.html. If the version |
| * of the LGPL at http://www.gnu.org is different to the version of |
| * the LGPL accompanying this distribution and there is any conflict |
| * between the two license versions, the terms of the LGPL accompanying |
| * this distribution shall govern. |
| * |
| * Contributors: |
| * Red Hat - initial API and implementation |
| *******************************************************************************/ |
| #include "os_custom.h" |
| |
| struct _SwtFixedPrivate { |
| GtkAdjustment *hadjustment; |
| GtkAdjustment *vadjustment; |
| guint hscroll_policy : 1; |
| guint vscroll_policy : 1; |
| GList *children; |
| }; |
| |
| struct _SwtFixedChild |
| { |
| GtkWidget *widget; |
| gint x; |
| gint y; |
| gint width; |
| gint height; |
| }; |
| typedef struct _SwtFixedChild SwtFixedChild; |
| |
| enum { |
| PROP_0, |
| PROP_HADJUSTMENT, |
| PROP_VADJUSTMENT, |
| PROP_HSCROLL_POLICY, |
| PROP_VSCROLL_POLICY, |
| }; |
| |
| static void swt_fixed_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); |
| static void swt_fixed_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); |
| static void swt_fixed_finalize (GObject *object); |
| static void swt_fixed_realize (GtkWidget *widget); |
| static void swt_fixed_map (GtkWidget *widget); |
| static AtkObject *swt_fixed_get_accessible (GtkWidget *widget); |
| static void swt_fixed_measure (GtkWidget *widget, GtkOrientation orientation, int for_size, int *minimum, |
| int *natural, int *minimum_baseline, int *natural_baseline); |
| static void swt_fixed_size_allocate (GtkWidget *widget, const GtkAllocation *allocation, int baseline); |
| static void swt_fixed_add (GtkContainer *container, GtkWidget *widget); |
| static void swt_fixed_remove (GtkContainer *container, GtkWidget *widget); |
| static void swt_fixed_forall (GtkContainer *container, GtkCallback callback, gpointer callback_data); |
| |
| G_DEFINE_TYPE_WITH_CODE (SwtFixed, swt_fixed, GTK_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL)) |
| |
| static void swt_fixed_class_init (SwtFixedClass *class) { |
| GObjectClass *gobject_class = (GObjectClass*) class; |
| GtkWidgetClass *widget_class = (GtkWidgetClass*) class; |
| GtkContainerClass *container_class = (GtkContainerClass*) class; |
| |
| /* GOject implementation */ |
| gobject_class->set_property = swt_fixed_set_property; |
| gobject_class->get_property = swt_fixed_get_property; |
| gobject_class->finalize = swt_fixed_finalize; |
| |
| /* Scrollable implemetation */ |
| g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment"); |
| g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment"); |
| g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy"); |
| g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy"); |
| |
| /* Widget implementation */ |
| widget_class->realize = swt_fixed_realize; |
| widget_class->map = swt_fixed_map; |
| widget_class->measure = swt_fixed_measure; |
| widget_class->size_allocate = swt_fixed_size_allocate; |
| |
| /* Container implementation */ |
| container_class->add = swt_fixed_add; |
| container_class->remove = swt_fixed_remove; |
| container_class->forall = swt_fixed_forall; |
| |
| g_type_class_add_private (class, sizeof (SwtFixedPrivate)); |
| } |
| |
| void swt_fixed_restack (SwtFixed *fixed, GtkWidget *widget, GtkWidget *sibling, gboolean above) { |
| SwtFixedPrivate *priv = fixed->priv; |
| GList *list; |
| SwtFixedChild *child, *sibling_child; |
| |
| list = priv->children; |
| while (list) { |
| child = list->data; |
| if (child->widget == widget) break; |
| list = list->next; |
| } |
| |
| if (!list) return; |
| priv->children = g_list_remove_link (priv->children, list); |
| g_list_free_1 (list); |
| |
| list = NULL; |
| if (sibling) { |
| list = priv->children; |
| while (list) { |
| sibling_child = list->data; |
| if (sibling_child->widget == sibling) { |
| break; |
| } |
| list = list->next; |
| } |
| if (list) { |
| if (!above) list = list->next; |
| } |
| } |
| if (!list) { |
| list = above ? priv->children : NULL; |
| } |
| priv->children = g_list_insert_before (priv->children, list, child); |
| |
| /* |
| { |
| GdkWindow *sibling_window = NULL; |
| if (list) { |
| child = list->data; |
| sibling_window = gtk_widget_get_window (child); |
| } |
| gdk_window_restack (gtk_widget_get_window (widget), sibling_window, above); |
| } |
| */ |
| } |
| |
| GtkWidget *swt_fixed_new(void) { |
| |
| return GTK_WIDGET(g_object_new(SWT_TYPE_FIXED, NULL)); |
| } |
| |
| static void swt_fixed_init (SwtFixed *widget) { |
| SwtFixedPrivate *priv; |
| |
| priv = widget->priv = G_TYPE_INSTANCE_GET_PRIVATE (widget, SWT_TYPE_FIXED, SwtFixedPrivate); |
| priv->children = NULL; |
| priv->hadjustment = NULL; |
| priv->vadjustment = NULL; |
| |
| gtk_widget_set_has_surface(GTK_WIDGET(widget), TRUE); |
| } |
| |
| static void swt_fixed_finalize (GObject *object) { |
| SwtFixed *widget = SWT_FIXED (object); |
| SwtFixedPrivate *priv = widget->priv; |
| |
| g_object_unref (priv->hadjustment); |
| g_object_unref (priv->vadjustment); |
| |
| G_OBJECT_CLASS (swt_fixed_parent_class)->finalize (object); |
| } |
| |
| static void swt_fixed_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { |
| SwtFixed *widget = SWT_FIXED (object); |
| SwtFixedPrivate *priv = widget->priv; |
| |
| switch (prop_id) { |
| case PROP_HADJUSTMENT: |
| g_value_set_object (value, priv->hadjustment); |
| break; |
| case PROP_VADJUSTMENT: |
| g_value_set_object (value, priv->vadjustment); |
| break; |
| case PROP_HSCROLL_POLICY: |
| g_value_set_enum (value, priv->hscroll_policy); |
| break; |
| case PROP_VSCROLL_POLICY: |
| g_value_set_enum (value, priv->vscroll_policy); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void swt_fixed_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { |
| SwtFixed *widget = SWT_FIXED (object); |
| SwtFixedPrivate *priv = widget->priv; |
| GtkAdjustment *adjustment; |
| |
| switch (prop_id) { |
| case PROP_HADJUSTMENT: |
| adjustment = g_value_get_object (value); |
| if (adjustment && priv->hadjustment == adjustment) return; |
| if (priv->hadjustment != NULL) g_object_unref (priv->hadjustment); |
| if (adjustment == NULL) adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); |
| priv->hadjustment = g_object_ref_sink (adjustment); |
| g_object_notify (G_OBJECT (widget), "hadjustment"); |
| break; |
| case PROP_VADJUSTMENT: |
| adjustment = g_value_get_object (value); |
| if (adjustment && priv->vadjustment == adjustment) return; |
| if (priv->vadjustment != NULL) g_object_unref (priv->vadjustment); |
| if (adjustment == NULL) adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); |
| priv->vadjustment = g_object_ref_sink (adjustment); |
| g_object_notify (G_OBJECT (widget), "vadjustment"); |
| break; |
| case PROP_HSCROLL_POLICY: |
| priv->hscroll_policy = g_value_get_enum (value); |
| break; |
| case PROP_VSCROLL_POLICY: |
| priv->vscroll_policy = g_value_get_enum (value); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void swt_fixed_realize (GtkWidget *widget) { |
| SwtFixed *fixed = SWT_FIXED (widget); |
| SwtFixedPrivate *priv = fixed->priv; |
| GtkAllocation allocation; |
| GdkSurface *surface; |
| |
| if (!gtk_widget_get_has_surface (widget)) { |
| GTK_WIDGET_CLASS (swt_fixed_parent_class)->realize (widget); |
| return; |
| } |
| |
| gtk_widget_get_allocation (widget, &allocation); |
| |
| surface = gdk_surface_new_child (gtk_widget_get_parent_surface (widget), &allocation); |
| gtk_widget_set_surface(widget, surface); |
| gdk_surface_set_user_data (surface, widget); |
| return GTK_WIDGET_CLASS (swt_fixed_parent_class)->realize (widget); |
| } |
| |
| static void swt_fixed_map (GtkWidget *widget) { |
| SwtFixed *fixed = SWT_FIXED (widget); |
| SwtFixedPrivate *priv = fixed->priv; |
| GList *list; |
| |
| list = priv->children; |
| while (list) { |
| SwtFixedChild *child_data = list->data; |
| GtkWidget *child = child_data->widget; |
| list = list->next; |
| if (gtk_widget_get_visible (child)) { |
| if (!gtk_widget_get_mapped (child)) gtk_widget_map (child); |
| } |
| } |
| if (gtk_widget_get_has_surface (widget)) { |
| //NOTE: contrary to most of GTK, swt_fixed_* container does not raise windows upon showing them. |
| //This has the effect that widgets are drawn *beneath* the previous one. |
| //E.g if this line is changed to gdk_window_show (..) then widgets are drawn on top of the previous one. |
| //This affects mostly only the absolute layout with overlapping widgets, e.g minimizied panels that |
| //pop-out in Eclipse (aka fast-view). |
| //As such, be attentive to swt_fixed_forall(..); traversing children may need to be done in reverse in some |
| //cases. |
| gdk_surface_show_unraised (gtk_widget_get_surface (widget)); |
| } |
| return GTK_WIDGET_CLASS (swt_fixed_parent_class)->map (widget); |
| } |
| |
| static void swt_fixed_measure (GtkWidget *widget, GtkOrientation orientation, int for_size, int *minimum, |
| int *natural, int *minimum_baseline, int *natural_baseline) { |
| natural = 0; |
| natural_baseline = 0; |
| minimum = 0; |
| minimum_baseline = 0; |
| return; |
| } |
| |
| static void swt_fixed_size_allocate (GtkWidget *widget, const GtkAllocation *allocation, int baseline) { |
| SwtFixed *fixed = SWT_FIXED (widget); |
| SwtFixedPrivate *priv = fixed->priv; |
| GList *list; |
| GtkAllocation child_allocation; |
| GtkRequisition requisition; |
| gint w, h; |
| |
| if (gtk_widget_get_has_surface (widget)) { |
| if (gtk_widget_get_realized (widget)) { |
| gdk_surface_move_resize (gtk_widget_get_surface (widget), allocation->x, allocation->y, allocation->width, allocation->height); |
| } |
| } |
| |
| list = priv->children; |
| |
| while (list) { |
| SwtFixedChild *child_data = list->data; |
| GtkWidget *child = child_data->widget; |
| list = list->next; |
| |
| child_allocation.x = child_data->x; |
| child_allocation.y = child_data->y; |
| if (!gtk_widget_get_has_surface (widget)) { |
| child_allocation.x += allocation->x; |
| child_allocation.y += allocation->y; |
| } |
| |
| w = child_data->width; |
| h = child_data->height; |
| if (w == -1 || h == -1) { |
| gtk_widget_get_preferred_size (child, &requisition, NULL); |
| if (w == -1) w = requisition.width; |
| if (h == -1) h = requisition.height; |
| } |
| // Feature in GTK: gtk_widget_preferred_size() has to be called before |
| // gtk_widget_size_allocate otherwise a warning is thrown. See Bug 486068. |
| gtk_widget_get_preferred_size (child, &requisition, NULL); |
| |
| child_allocation.width = w; |
| child_allocation.height = h; |
| |
| gtk_widget_size_allocate (child, &child_allocation, -1); |
| } |
| } |
| |
| void swt_fixed_move (SwtFixed *fixed, GtkWidget *widget, gint x, gint y) { |
| SwtFixedPrivate *priv = fixed->priv; |
| GList *list; |
| |
| list = priv->children; |
| while (list) { |
| SwtFixedChild *child_data = list->data; |
| GtkWidget *child = child_data->widget; |
| if (child == widget) { |
| child_data->x = x; |
| child_data->y = y; |
| break; |
| } |
| list = list->next; |
| } |
| } |
| |
| void swt_fixed_resize (SwtFixed *fixed, GtkWidget *widget, gint width, gint height) { |
| SwtFixedPrivate *priv = fixed->priv; |
| GList *list; |
| |
| list = priv->children; |
| while (list) { |
| SwtFixedChild *child_data = list->data; |
| GtkWidget *child = child_data->widget; |
| if (child == widget) { |
| child_data->width = width; |
| child_data->height = height; |
| |
| /* |
| * Feature in GTK: sometimes the sizing of child SwtFixed widgets |
| * does not happen quickly enough, causing miscalculations in SWT. |
| * Allocate the size of the child directly when swt_fixed_resize() |
| * is called. See bug 487160. |
| */ |
| GtkAllocation allocation, to_allocate; |
| GtkRequisition req; |
| gtk_widget_get_allocation(child, &allocation); |
| |
| // Keep x and y values the same to prevent misplaced containers |
| to_allocate.x = allocation.x; |
| to_allocate.y = allocation.y; |
| to_allocate.width = width; |
| to_allocate.height = height; |
| |
| // Call gtk_widget_get_preferred_size() and finish the allocation. |
| gtk_widget_get_preferred_size (child, &req, NULL); |
| gtk_widget_size_allocate(child, &to_allocate, -1); |
| break; |
| } |
| list = list->next; |
| } |
| } |
| |
| static void swt_fixed_add (GtkContainer *container, GtkWidget *child) { |
| GtkWidget *widget = GTK_WIDGET (container); |
| SwtFixed *fixed = SWT_FIXED (container); |
| SwtFixedPrivate *priv = fixed->priv; |
| SwtFixedChild *child_data; |
| |
| child_data = g_new (SwtFixedChild, 1); |
| child_data->widget = child; |
| child_data->x = child_data->y = 0; |
| child_data->width = child_data->height = -1; |
| |
| priv->children = g_list_append (priv->children, child_data); |
| gtk_widget_set_parent (child, widget); |
| } |
| |
| static void swt_fixed_remove (GtkContainer *container, GtkWidget *widget) { |
| SwtFixed *fixed = SWT_FIXED (container); |
| SwtFixedPrivate *priv = fixed->priv; |
| GList *list; |
| |
| list = priv->children; |
| while (list) { |
| SwtFixedChild *child_data = list->data; |
| GtkWidget *child = child_data->widget; |
| if (child == widget) { |
| gtk_widget_unparent (widget); |
| priv->children = g_list_remove_link (priv->children, list); |
| g_list_free_1 (list); |
| g_free (child_data); |
| break; |
| } |
| list = list->next; |
| } |
| } |
| |
| static void swt_fixed_forall (GtkContainer *container, GtkCallback callback, gpointer callback_data) { |
| SwtFixed *fixed = SWT_FIXED (container); |
| SwtFixedPrivate *priv = fixed->priv; |
| GList *list; |
| |
| list = priv->children; |
| |
| // NOTE: The direction of the list traversal is conditional. |
| // |
| // 1) When we do a *_foreach() traversal (i.e, include_internals==FALSE), we traverse the list as normal |
| // from front to back. |
| // This is used to layout higher level widgets inside containers (e.g row/grid etc..) in the expected way. |
| // If for a non-internal traversal we were to go in reverse, then widgets would get laid out in inverse order. |
| // 2) When we do a *_forall() traversal (i.e, include_internals==TRUE), we traverse the list in *reverse* order. |
| // This is an internal traversal of the internals of a widget. Reverse traversal is necessary for things like |
| // DnD Drop and DnD Motion events to find the correct widget in the case of overlapping widgets on an absolute layout. |
| // Reversal is required because in swt_fixed_map(..) we do not raise the widget when we show it, as a result |
| // the stack is in reverse. |
| |
| while (list) { |
| SwtFixedChild *child_data = list->data; |
| GtkWidget *child = child_data->widget; |
| |
| list = list->next; |
| |
| (* callback) (child, callback_data); |
| } |
| } |