/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* eel-wrap-box.c - A table that can wrap its contents as needed. Copyright (C) 2000 Eazel, Inc. The Mate 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. The Mate 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 the Mate Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. Authors: Ramiro Estrugo <ramiro@eazel.com> */ #include <config.h> #include "eel-wrap-table.h" #include "eel-art-extensions.h" #include "eel-art-gtk-extensions.h" #include "eel-gtk-extensions.h" #include <gtk/gtk.h> /* Arguments */ enum { PROP_0, PROP_X_SPACING, PROP_Y_SPACING, PROP_X_JUSTIFICATION, PROP_Y_JUSTIFICATION, PROP_HOMOGENEOUS }; /* Detail member struct */ struct EelWrapTableDetails { guint x_spacing; guint y_spacing; EelJustification x_justification; EelJustification y_justification; gboolean homogeneous; GList *children; guint is_scrolled : 1; guint cols; }; /* Private EelWrapTable methods */ static EelDimensions wrap_table_irect_max_dimensions (const EelDimensions *one, const EelDimensions *two); static EelDimensions wrap_table_get_max_child_dimensions (const EelWrapTable *wrap_table); static EelDimensions wrap_table_get_content_dimensions (const EelWrapTable *wrap_table); static EelIRect wrap_table_get_content_bounds (const EelWrapTable *wrap_table); static gboolean wrap_table_child_visible_in (GtkWidget *child, GtkWidget *scrolled); static gboolean wrap_table_child_focus_in (GtkWidget *widget, GdkEventFocus *event, gpointer data); static void wrap_table_layout (EelWrapTable *wrap_table); G_DEFINE_TYPE (EelWrapTable, eel_wrap_table, GTK_TYPE_CONTAINER) static void eel_wrap_table_init (EelWrapTable *wrap_table) { gtk_widget_set_has_window (GTK_WIDGET (wrap_table), FALSE); wrap_table->details = G_TYPE_INSTANCE_GET_PRIVATE (wrap_table, EEL_TYPE_WRAP_TABLE, EelWrapTableDetails); wrap_table->details->x_justification = EEL_JUSTIFICATION_BEGINNING; wrap_table->details->y_justification = EEL_JUSTIFICATION_END; wrap_table->details->cols = 1; } static void eel_wrap_table_finalize (GObject *object) { EelWrapTable *wrap_table; wrap_table = EEL_WRAP_TABLE (object); g_list_free (wrap_table->details->children); G_OBJECT_CLASS (eel_wrap_table_parent_class)->finalize (object); } /* GObjectClass methods */ static void eel_wrap_table_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { EelWrapTable *wrap_table; g_assert (EEL_IS_WRAP_TABLE (object)); wrap_table = EEL_WRAP_TABLE (object); switch (property_id) { case PROP_X_SPACING: eel_wrap_table_set_x_spacing (wrap_table, g_value_get_uint (value)); break; case PROP_Y_SPACING: eel_wrap_table_set_y_spacing (wrap_table, g_value_get_uint (value)); break; case PROP_X_JUSTIFICATION: eel_wrap_table_set_x_justification (wrap_table, g_value_get_enum (value)); break; case PROP_Y_JUSTIFICATION: eel_wrap_table_set_y_justification (wrap_table, g_value_get_enum (value)); break; case PROP_HOMOGENEOUS: eel_wrap_table_set_homogeneous (wrap_table, g_value_get_boolean (value)); break; default: g_assert_not_reached (); } } static void eel_wrap_table_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { EelWrapTable *wrap_table; g_assert (EEL_IS_WRAP_TABLE (object)); wrap_table = EEL_WRAP_TABLE (object); switch (property_id) { case PROP_X_SPACING: g_value_set_uint (value, eel_wrap_table_get_x_spacing (wrap_table)); break; case PROP_Y_SPACING: g_value_set_uint (value, eel_wrap_table_get_y_spacing (wrap_table)); break; case PROP_X_JUSTIFICATION: g_value_set_enum (value, eel_wrap_table_get_x_justification (wrap_table)); break; case PROP_Y_JUSTIFICATION: g_value_set_enum (value, eel_wrap_table_get_y_justification (wrap_table)); break; case PROP_HOMOGENEOUS: g_value_set_boolean (value, eel_wrap_table_get_homogeneous (wrap_table)); break; default: g_assert_not_reached (); } } /* GtkWidgetClass methods */ static void eel_wrap_table_size_request (GtkWidget *widget, GtkRequisition *requisition) { EelWrapTable *wrap_table; EelDimensions content_dimensions; g_assert (EEL_IS_WRAP_TABLE (widget)); g_assert (requisition != NULL); wrap_table = EEL_WRAP_TABLE (widget); content_dimensions = wrap_table_get_content_dimensions (wrap_table); /* The -1 tells Satan to use as much space as is available */ requisition->width = -1; requisition->height = content_dimensions.height + gtk_container_get_border_width (GTK_CONTAINER (widget)) * 2; } #if GTK_CHECK_VERSION (3, 0, 0) static void eel_wrap_table_get_preferred_width (GtkWidget *widget, gint *minimum_width, gint *natural_width) { GtkRequisition req; eel_wrap_table_size_request (widget, &req); *minimum_width = *natural_width = req.width; } static void eel_wrap_table_get_preferred_height (GtkWidget *widget, gint *minimum_height, gint *natural_height) { GtkRequisition req; eel_wrap_table_size_request (widget, &req); *minimum_height = *natural_height = req.height; } #endif static void eel_wrap_table_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { EelWrapTable *wrap_table; g_assert (EEL_IS_WRAP_TABLE (widget)); g_assert (allocation != NULL); wrap_table = EEL_WRAP_TABLE (widget); gtk_widget_set_allocation (widget, allocation); wrap_table_layout (wrap_table); } static int #if GTK_CHECK_VERSION (3, 0, 0) eel_wrap_table_draw (GtkWidget *widget, cairo_t *cr) #else eel_wrap_table_expose_event (GtkWidget *widget, GdkEventExpose *event) #endif { EelWrapTable *wrap_table; GList *iterator; g_assert (EEL_IS_WRAP_TABLE (widget)); g_assert (gtk_widget_get_realized (widget)); #if !GTK_CHECK_VERSION (3, 0, 0) g_assert (event != NULL); #endif wrap_table = EEL_WRAP_TABLE (widget); for (iterator = wrap_table->details->children; iterator; iterator = iterator->next) { g_assert (GTK_IS_WIDGET (iterator->data)); #if GTK_CHECK_VERSION (3, 0, 0) gtk_container_propagate_draw (GTK_CONTAINER (widget), GTK_WIDGET (iterator->data), cr); #else gtk_container_propagate_expose (GTK_CONTAINER (widget), GTK_WIDGET (iterator->data), event); #endif } return FALSE; } static void eel_wrap_table_map (GtkWidget *widget) { EelWrapTable *wrap_table; GList *iterator; g_assert (EEL_IS_WRAP_TABLE (widget)); wrap_table = EEL_WRAP_TABLE (widget); gtk_widget_set_mapped (widget, TRUE); for (iterator = wrap_table->details->children; iterator; iterator = iterator->next) { GtkWidget *item; item = iterator->data; if (gtk_widget_get_visible (item) && !gtk_widget_get_mapped (item)) { gtk_widget_map (item); } } } static void eel_wrap_table_unmap (GtkWidget *widget) { EelWrapTable *wrap_table; GList *iterator; g_assert (EEL_IS_WRAP_TABLE (widget)); wrap_table = EEL_WRAP_TABLE (widget); gtk_widget_set_mapped (widget, FALSE); for (iterator = wrap_table->details->children; iterator; iterator = iterator->next) { GtkWidget *item; item = iterator->data; if (gtk_widget_get_visible (item) && gtk_widget_get_mapped (item)) { gtk_widget_unmap (item); } } } static void eel_wrap_table_realize (GtkWidget *widget) { g_assert (EEL_IS_WRAP_TABLE (widget)); GTK_WIDGET_CLASS (eel_wrap_table_parent_class)->realize (widget); gtk_widget_queue_resize (widget); } /* GtkContainerClass methods */ static void eel_wrap_table_add (GtkContainer *container, GtkWidget *child) { EelWrapTable *wrap_table; GtkWidget *widget; g_assert (container != NULL); g_assert (EEL_IS_WRAP_TABLE (container)); g_assert (GTK_IS_WIDGET (child)); widget = GTK_WIDGET (container); wrap_table = EEL_WRAP_TABLE (container); gtk_widget_set_parent (child, GTK_WIDGET (container)); wrap_table->details->children = g_list_append (wrap_table->details->children, child); if (gtk_widget_get_realized (widget)) { gtk_widget_realize (child); } if (gtk_widget_get_visible (widget) && gtk_widget_get_visible (child)) { if (gtk_widget_get_mapped (widget)) { gtk_widget_map (child); } gtk_widget_queue_resize (child); } if (wrap_table->details->is_scrolled) { g_signal_connect (child, "focus_in_event", G_CALLBACK (wrap_table_child_focus_in), wrap_table); } } static void eel_wrap_table_remove (GtkContainer *container, GtkWidget *child) { EelWrapTable *wrap_table; gboolean child_was_visible; g_assert (EEL_IS_WRAP_TABLE (container)); g_assert (GTK_IS_WIDGET (child)); wrap_table = EEL_WRAP_TABLE (container);; child_was_visible = gtk_widget_get_visible (child); gtk_widget_unparent (child); wrap_table->details->children = g_list_remove (wrap_table->details->children, child); if (child_was_visible) { gtk_widget_queue_resize (GTK_WIDGET (container)); } if (wrap_table->details->is_scrolled) { g_signal_handlers_disconnect_by_func ( child, G_CALLBACK (wrap_table_child_focus_in), wrap_table); } } static void eel_wrap_table_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { EelWrapTable *wrap_table; GList *node; GList *next; g_assert (EEL_IS_WRAP_TABLE (container)); g_assert (callback != NULL); wrap_table = EEL_WRAP_TABLE (container);; for (node = wrap_table->details->children; node != NULL; node = next) { g_assert (GTK_IS_WIDGET (node->data)); next = node->next; (* callback) (GTK_WIDGET (node->data), callback_data); } } static GType eel_wrap_table_child_type (GtkContainer *container) { return GTK_TYPE_WIDGET; } /* Class init methods */ static void eel_wrap_table_class_init (EelWrapTableClass *wrap_table_class) { GObjectClass *gobject_class = G_OBJECT_CLASS (wrap_table_class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (wrap_table_class); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (wrap_table_class); /* GObjectClass */ gobject_class->finalize = eel_wrap_table_finalize; gobject_class->set_property = eel_wrap_table_set_property; gobject_class->get_property = eel_wrap_table_get_property; /* GtkWidgetClass */ widget_class->size_allocate = eel_wrap_table_size_allocate; #if GTK_CHECK_VERSION (3, 0, 0) widget_class->get_preferred_width = eel_wrap_table_get_preferred_width; widget_class->get_preferred_height = eel_wrap_table_get_preferred_height; widget_class->draw = eel_wrap_table_draw; #else widget_class->size_request = eel_wrap_table_size_request; widget_class->expose_event = eel_wrap_table_expose_event; #endif widget_class->map = eel_wrap_table_map; widget_class->unmap = eel_wrap_table_unmap; widget_class->realize = eel_wrap_table_realize; /* GtkContainerClass */ container_class->add = eel_wrap_table_add; container_class->remove = eel_wrap_table_remove; container_class->forall = eel_wrap_table_forall; container_class->child_type = eel_wrap_table_child_type; /* Arguments */ g_object_class_install_property (gobject_class, PROP_X_SPACING, g_param_spec_uint ("x_spacing", NULL, NULL, 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_Y_SPACING, g_param_spec_uint ("y_spacing", NULL, NULL, 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_X_JUSTIFICATION, g_param_spec_enum ("x_justification", NULL, NULL, GTK_TYPE_JUSTIFICATION, GTK_JUSTIFY_LEFT, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_Y_JUSTIFICATION, g_param_spec_enum ("y_justification", NULL, NULL, GTK_TYPE_JUSTIFICATION, GTK_JUSTIFY_LEFT, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_HOMOGENEOUS, g_param_spec_boolean ("homogenous", NULL, NULL, FALSE, G_PARAM_READWRITE)); g_type_class_add_private (wrap_table_class, sizeof (EelWrapTableDetails)); } /* Private EelWrapTable methods */ static int wrap_table_get_num_fitting (int available, int spacing, int max_child_size) { int num; g_assert (max_child_size > 0); g_assert (spacing >= 0); available = MAX (available, 0); num = (available + spacing) / (max_child_size + spacing); num = MAX (num, 1); return num; } static void wrap_table_layout (EelWrapTable *wrap_table) { GList *iterator; EelIPoint pos; EelDimensions max_child_dimensions; EelIRect content_bounds; guint num_cols; GtkAllocation allocation; g_assert (EEL_IS_WRAP_TABLE (wrap_table)); max_child_dimensions = wrap_table_get_max_child_dimensions (wrap_table); max_child_dimensions.width = MAX (max_child_dimensions.width, 1); max_child_dimensions.height = MAX (max_child_dimensions.height, 1); content_bounds = wrap_table_get_content_bounds (wrap_table); pos.x = content_bounds.x0; pos.y = content_bounds.y0; gtk_widget_get_allocation (GTK_WIDGET (wrap_table), &allocation); num_cols = wrap_table_get_num_fitting (allocation.width - gtk_container_get_border_width (GTK_CONTAINER (wrap_table)) * 2, wrap_table->details->x_spacing, max_child_dimensions.width); if (num_cols != wrap_table->details->cols) { wrap_table->details->cols = num_cols; gtk_widget_queue_resize (GTK_WIDGET (wrap_table)); return; } for (iterator = wrap_table->details->children; iterator; iterator = iterator->next) { GtkWidget *item; item = iterator->data; if (gtk_widget_get_visible (item)) { GtkAllocation item_allocation; if (wrap_table->details->homogeneous) { item_allocation.x = pos.x; item_allocation.y = pos.y; item_allocation.width = max_child_dimensions.width; item_allocation.height = max_child_dimensions.height; if ((pos.x + max_child_dimensions.width) > content_bounds.x1) { pos.x = content_bounds.x0 + wrap_table->details->x_spacing + max_child_dimensions.width; pos.y += (max_child_dimensions.height + wrap_table->details->y_spacing); item_allocation.x = content_bounds.x0; item_allocation.y = pos.y; } else { pos.x += (wrap_table->details->x_spacing + max_child_dimensions.width); } } else { GtkRequisition item_requisition; gtk_widget_size_request (item, &item_requisition); item_allocation.x = pos.x; item_allocation.y = pos.y; item_allocation.width = item_requisition.width; item_allocation.height = item_requisition.height; g_assert (item_allocation.width <= max_child_dimensions.width); g_assert (item_allocation.height <= max_child_dimensions.height); if ((pos.x + max_child_dimensions.width) > content_bounds.x1) { pos.x = content_bounds.x0 + wrap_table->details->x_spacing + max_child_dimensions.width; pos.y += (max_child_dimensions.height + wrap_table->details->y_spacing); item_allocation.x = content_bounds.x0; item_allocation.y = pos.y; } else { pos.x += (wrap_table->details->x_spacing + max_child_dimensions.width); } switch (wrap_table->details->x_justification) { case EEL_JUSTIFICATION_MIDDLE: item_allocation.x += (max_child_dimensions.width - (int) item_allocation.width) / 2; break; case EEL_JUSTIFICATION_END: item_allocation.x += (max_child_dimensions.width - (int) item_allocation.width); break; default: break; } switch (wrap_table->details->y_justification) { case EEL_JUSTIFICATION_MIDDLE: item_allocation.y += (max_child_dimensions.height - (int) item_allocation.height) / 2; break; case EEL_JUSTIFICATION_END: item_allocation.y += (max_child_dimensions.height - (int) item_allocation.height); break; default: break; } } gtk_widget_size_allocate (item, &item_allocation); } } } static EelDimensions wrap_table_irect_max_dimensions (const EelDimensions *one, const EelDimensions *two) { EelDimensions max_dimensions; g_assert (one != NULL); g_assert (two != NULL); max_dimensions.width = MAX (one->width, two->width); max_dimensions.height = MAX (one->height, two->height); return max_dimensions; } static EelDimensions wrap_table_get_max_child_dimensions (const EelWrapTable *wrap_table) { EelDimensions max_dimensions; GList *iterator; g_assert (EEL_IS_WRAP_TABLE (wrap_table)); max_dimensions = eel_dimensions_empty; for (iterator = wrap_table->details->children; iterator; iterator = iterator->next) { GtkWidget *child; child = iterator->data; if (gtk_widget_get_visible (child)) { GtkRequisition child_requisition; EelDimensions child_dimensions; gtk_widget_size_request (child, &child_requisition); child_dimensions.width = (int) child_requisition.width; child_dimensions.height = (int) child_requisition.height; max_dimensions = wrap_table_irect_max_dimensions (&child_dimensions, &max_dimensions); } } return max_dimensions; } static EelDimensions wrap_table_get_content_dimensions (const EelWrapTable *wrap_table) { EelDimensions content_dimensions; guint num_children; g_assert (EEL_IS_WRAP_TABLE (wrap_table)); content_dimensions = eel_dimensions_empty; num_children = g_list_length (wrap_table->details->children); if (num_children > 0) { EelDimensions max_child_dimensions; EelDimensions dimensions; int num_cols; int num_rows; dimensions = eel_gtk_widget_get_dimensions (GTK_WIDGET (wrap_table)); max_child_dimensions = wrap_table_get_max_child_dimensions (wrap_table); max_child_dimensions.width = MAX (max_child_dimensions.width, 1); max_child_dimensions.height = MAX (max_child_dimensions.height, 1); num_cols = wrap_table_get_num_fitting (dimensions.width - gtk_container_get_border_width (GTK_CONTAINER (wrap_table)) * 2, wrap_table->details->x_spacing, max_child_dimensions.width); num_rows = num_children / num_cols; num_rows = MAX (num_rows, 1); if ((num_children % num_rows) > 0) { num_rows++; } content_dimensions.width = dimensions.width; content_dimensions.height = num_rows * max_child_dimensions.height; content_dimensions.width += (num_cols - 1) * wrap_table->details->x_spacing; content_dimensions.height += (num_rows - 1) * wrap_table->details->y_spacing; } return content_dimensions; } static EelIRect wrap_table_get_content_bounds (const EelWrapTable *wrap_table) { EelIRect content_bounds; guint border; g_assert (EEL_IS_WRAP_TABLE (wrap_table)); content_bounds = eel_gtk_widget_get_bounds (GTK_WIDGET (wrap_table)); border = gtk_container_get_border_width (GTK_CONTAINER (wrap_table)); content_bounds.x0 += border; content_bounds.y0 += border; content_bounds.x1 -= border; content_bounds.y1 -= border; return content_bounds; } /** * wrap_table_child_visible_in * * Get child position relative to parent, then determine whether * the whole child rectangle is visible in the scrolled window **/ static gboolean wrap_table_child_visible_in (GtkWidget *child, GtkWidget *scrolled) { gint x, y; GtkAllocation child_alloc, scroll_alloc; gtk_widget_translate_coordinates (child, scrolled, 0, 0, &x, &y); gtk_widget_get_allocation(child, &child_alloc); gtk_widget_get_allocation(scrolled, &scroll_alloc); return (x >= 0 && y >= 0) && x + child_alloc.width <= scroll_alloc.width && y + child_alloc.height <= scroll_alloc.height; } static gboolean wrap_table_child_focus_in (GtkWidget *widget, GdkEventFocus *event, gpointer data) { gint x, y; GtkWidget *container, *viewport; GtkAdjustment *hadj, *vadj; container = gtk_widget_get_parent (widget); if (container) { viewport = gtk_widget_get_parent (container); } g_assert (container && viewport); g_assert (GTK_IS_VIEWPORT (viewport)); g_return_val_if_fail (gtk_widget_get_realized (viewport), FALSE); if (!wrap_table_child_visible_in (widget, viewport)) { hadj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (viewport)); vadj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (viewport)); gtk_widget_translate_coordinates (widget, container, 0, 0, &x, &y); #if GTK_CHECK_VERSION (3, 0, 0) gtk_adjustment_set_value (hadj, MIN (x, gtk_adjustment_get_upper(hadj) - gtk_adjustment_get_page_size(hadj))); gtk_adjustment_set_value (vadj, MIN (y, gtk_adjustment_get_upper(vadj) - gtk_adjustment_get_page_size(vadj))); #else gtk_adjustment_set_value (hadj, MIN (x, hadj->upper - hadj->page_size)); gtk_adjustment_set_value (vadj, MIN (y, vadj->upper - vadj->page_size)); #endif } return FALSE; } /** * eel_wrap_table_new: * */ GtkWidget* eel_wrap_table_new (gboolean homogeneous) { EelWrapTable *wrap_table; wrap_table = EEL_WRAP_TABLE (gtk_widget_new (eel_wrap_table_get_type (), NULL)); eel_wrap_table_set_homogeneous (wrap_table, homogeneous); return GTK_WIDGET (wrap_table); } /** * eel_wrap_table_set_x_spacing: * @wrap_table: A EelWrapTable. * @x_spacing: The new horizontal spacing between wraps. * */ void eel_wrap_table_set_x_spacing (EelWrapTable *wrap_table, guint x_spacing) { g_return_if_fail (EEL_IS_WRAP_TABLE (wrap_table)); if (wrap_table->details->x_spacing == x_spacing) { return; } wrap_table->details->x_spacing = x_spacing; gtk_widget_queue_resize (GTK_WIDGET (wrap_table)); } /** * eel_wrap_table_get_item_spacing: * @wrap_table: A EelWrapTable. * * Returns: The horizontal spacing between wraps. */ guint eel_wrap_table_get_x_spacing (const EelWrapTable *wrap_table) { g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), 0); return wrap_table->details->x_spacing; } /** * eel_wrap_table_set_y_spacing: * @wrap_table: A EelWrapTable. * @y_spacing: The new horizontal spacing between wraps. * */ void eel_wrap_table_set_y_spacing (EelWrapTable *wrap_table, guint y_spacing) { g_return_if_fail (EEL_IS_WRAP_TABLE (wrap_table)); if (wrap_table->details->y_spacing == y_spacing) { return; } wrap_table->details->y_spacing = y_spacing; gtk_widget_queue_resize (GTK_WIDGET (wrap_table)); } /** * eel_wrap_table_get_item_spacing: * @wrap_table: A EelWrapTable. * * Returns: The horizontal spacing between wraps. */ guint eel_wrap_table_get_y_spacing (const EelWrapTable *wrap_table) { g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), 0); return wrap_table->details->y_spacing; } /** * eel_wrap_table_find_child_at_event_point: * @wrap_table: A EelWrapTable. * @x: Event x; * @y: Event y; * * Returns: Child found at given coordinates or NULL of no child is found. */ GtkWidget * eel_wrap_table_find_child_at_event_point (const EelWrapTable *wrap_table, int x, int y) { GList *iterator; g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), NULL); for (iterator = wrap_table->details->children; iterator; iterator = iterator->next) { GtkWidget *child; child = iterator->data; if (gtk_widget_get_visible (child)) { EelIRect child_bounds; child_bounds = eel_gtk_widget_get_bounds (child); if (eel_irect_contains_point (child_bounds, x, y)) { return child; } } } return NULL; } /** * eel_wrap_table_set_x_justification: * @wrap_table: A EelWrapTable. * @x_justification: The new horizontal justification between wraps. * */ void eel_wrap_table_set_x_justification (EelWrapTable *wrap_table, EelJustification x_justification) { g_return_if_fail (EEL_IS_WRAP_TABLE (wrap_table)); g_return_if_fail (x_justification >= EEL_JUSTIFICATION_BEGINNING); g_return_if_fail (x_justification <= EEL_JUSTIFICATION_END); if (wrap_table->details->x_justification == x_justification) { return; } wrap_table->details->x_justification = x_justification; gtk_widget_queue_resize (GTK_WIDGET (wrap_table)); } /** * eel_wrap_table_get_item_justification: * @wrap_table: A EelWrapTable. * * Returns: The horizontal justification between wraps. */ EelJustification eel_wrap_table_get_x_justification (const EelWrapTable *wrap_table) { g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), 0); return wrap_table->details->x_justification; } /** * eel_wrap_table_set_y_justification: * @wrap_table: A EelWrapTable. * @y_justification: The new horizontal justification between wraps. * */ void eel_wrap_table_set_y_justification (EelWrapTable *wrap_table, EelJustification y_justification) { g_return_if_fail (EEL_IS_WRAP_TABLE (wrap_table)); g_return_if_fail (y_justification >= EEL_JUSTIFICATION_BEGINNING); g_return_if_fail (y_justification <= EEL_JUSTIFICATION_END); if (wrap_table->details->y_justification == y_justification) { return; } wrap_table->details->y_justification = y_justification; gtk_widget_queue_resize (GTK_WIDGET (wrap_table)); } /** * eel_wrap_table_get_item_justification: * @wrap_table: A EelWrapTable. * * Returns: The horizontal justification between wraps. */ EelJustification eel_wrap_table_get_y_justification (const EelWrapTable *wrap_table) { g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), 0); return wrap_table->details->y_justification; } /** * eel_wrap_table_set_homogeneous: * @wrap_table: A EelWrapTable. * @homogeneous: The new horizontal spacing between wraps. * */ void eel_wrap_table_set_homogeneous (EelWrapTable *wrap_table, gboolean homogeneous) { if (EEL_IS_WRAP_TABLE (wrap_table) && wrap_table->details->homogeneous != homogeneous) { wrap_table->details->homogeneous = homogeneous; gtk_widget_queue_resize (GTK_WIDGET (wrap_table)); } } /** * eel_wrap_table_get_item_spacing: * @wrap_table: A EelWrapTable. * * Returns: The horizontal spacing between wraps. */ gboolean eel_wrap_table_get_homogeneous (const EelWrapTable *wrap_table) { g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), FALSE); return wrap_table->details->homogeneous; } /** * eel_wrap_table_reorder_child: * @wrap_table: A EelWrapTable. * @child: Child to reorder. * @position: New position to put child at. * * Reorder the given chilren into the given position. * * Position is interpreted as follows: * * 0 - Place child at start of table. * -1 - Place child at end of table. * n - Place child at nth position. Count starts at 0. */ void eel_wrap_table_reorder_child (EelWrapTable *wrap_table, GtkWidget *child, int position) { GList *node; gboolean found_child = FALSE; g_return_if_fail (EEL_IS_WRAP_TABLE (wrap_table)); g_return_if_fail (g_list_length (wrap_table->details->children) > 0); if (position == -1) { position = g_list_length (wrap_table->details->children) - 1; } g_return_if_fail (position >= 0); g_return_if_fail ((guint) position < g_list_length (wrap_table->details->children)); for (node = wrap_table->details->children; node != NULL; node = node->next) { GtkWidget *next_child; next_child = node->data; if (next_child == child) { g_assert (found_child == FALSE); found_child = TRUE; } } g_return_if_fail (found_child); wrap_table->details->children = g_list_remove (wrap_table->details->children, child); wrap_table->details->children = g_list_insert (wrap_table->details->children, child, position); gtk_widget_queue_resize (GTK_WIDGET (wrap_table)); } /** * eel_wrap_table_get_num_children: * @wrap_table: A EelWrapTable. * * Returns: The number of children being managed by the wrap table. */ guint eel_wrap_table_get_num_children (const EelWrapTable *wrap_table) { g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), 0); return g_list_length (wrap_table->details->children); } GtkWidget * eel_scrolled_wrap_table_new (gboolean homogenous, GtkShadowType shadow_type, GtkWidget **wrap_table_out) { GtkWidget *scrolled_window; GtkWidget *wrap_table; GtkWidget *viewport; g_return_val_if_fail (wrap_table_out != NULL, NULL); scrolled_window = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); viewport = gtk_viewport_new (gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled_window)), gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window))); gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), shadow_type); gtk_container_add (GTK_CONTAINER (scrolled_window), viewport); wrap_table = eel_wrap_table_new (homogenous); gtk_container_add (GTK_CONTAINER (viewport), wrap_table); gtk_widget_show (wrap_table); gtk_widget_show (viewport); EEL_WRAP_TABLE (wrap_table)->details->is_scrolled = 1; *wrap_table_out = wrap_table; return scrolled_window; }