/* * glade-gtk-grid.c - GladeWidgetAdaptor for GtkGrid widget * * Copyright (C) 2011 Openismus GmbH * * Authors: * Tristan Van Berkom * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "glade-fixed.h" #include "glade-grid-editor.h" typedef struct { gint left_attach; gint top_attach; gint width; gint height; } GladeGridAttachments; typedef enum { GROUP_ACTION_INSERT_ROW, GROUP_ACTION_INSERT_COLUMN, GROUP_ACTION_REMOVE_COLUMN, GROUP_ACTION_REMOVE_ROW } GroupAction; static gboolean glade_gtk_grid_configure_begin (GladeFixed *fixed, GladeWidget *child, GtkWidget *grid); static gboolean glade_gtk_grid_configure_end (GladeFixed *fixed, GladeWidget *child, GtkWidget *grid); static gboolean glade_gtk_grid_configure_child (GladeFixed *fixed, GladeWidget *child, GdkRectangle *rect, GtkWidget *grid); GladeEditable * glade_gtk_grid_create_editable (GladeWidgetAdaptor * adaptor, GladeEditorPageType type) { if (type == GLADE_PAGE_GENERAL) return (GladeEditable *) glade_grid_editor_new (); return GWA_GET_CLASS (GTK_TYPE_CONTAINER)->create_editable (adaptor, type); } static void glade_gtk_grid_get_child_attachments (GtkWidget *grid, GtkWidget *child, GladeGridAttachments *grid_child) { gtk_container_child_get (GTK_CONTAINER (grid), child, "left-attach", &grid_child->left_attach, "width", &grid_child->width, "top-attach", &grid_child->top_attach, "height", &grid_child->height, NULL); } static gboolean glade_gtk_grid_has_child (GtkGrid *grid, GList *children, guint left_attach, guint top_attach) { gboolean ret = FALSE; GList *list; for (list = children; list && list->data; list = list->next) { GladeGridAttachments attach; GtkWidget *widget = list->data; glade_gtk_grid_get_child_attachments (GTK_WIDGET (grid), widget, &attach); if (left_attach >= attach.left_attach && left_attach < attach.left_attach + attach.width && top_attach >= attach.top_attach && top_attach < attach.top_attach + attach.height) { ret = TRUE; break; } } return ret; } static void glade_gtk_grid_refresh_placeholders (GtkGrid *grid, gboolean load_finished) { GladeWidget *widget; GladeProject *project; GtkContainer *container; GList *list, *children; guint n_columns, n_rows; gint i, j; widget = glade_widget_get_from_gobject (grid); project = glade_widget_get_project (widget); /* Wait for project to finish loading */ if ((project && glade_project_is_loading (project)) && !load_finished) return; glade_widget_property_get (widget, "n-columns", &n_columns); glade_widget_property_get (widget, "n-rows", &n_rows); container = GTK_CONTAINER (grid); children = gtk_container_get_children (container); for (list = children; list && list->data; list = list->next) { GtkWidget *child = list->data; if (GLADE_IS_PLACEHOLDER (child)) gtk_container_remove (container, child); } g_list_free (children); children = gtk_container_get_children (container); for (i = 0; i < n_columns; i++) for (j = 0; j < n_rows; j++) if (glade_gtk_grid_has_child (grid, children, i, j) == FALSE) gtk_grid_attach (grid, glade_placeholder_new (), i, j, 1, 1); if (gtk_widget_get_realized (GTK_WIDGET (grid))) gtk_container_check_resize (container); g_list_free (children); } static void glade_gtk_grid_parse_finished (GladeProject *project, GObject *container) { GladeWidget *gwidget = glade_widget_get_from_gobject (container); GladeGridAttachments attach; GList *list, *children; gint row = 0, column = 0, n_row = 0, n_column = 0; children = gtk_container_get_children (GTK_CONTAINER (container)); for (list = children; list; list = list->next) { GtkWidget *widget = list->data; if (GLADE_IS_PLACEHOLDER (widget)) continue; glade_gtk_grid_get_child_attachments (GTK_WIDGET (container), widget, &attach); n_row = attach.top_attach + attach.height; n_column = attach.left_attach + attach.width; if (row < n_row) row = n_row; if (column < n_column) column = n_column; } if (column) glade_widget_property_set (gwidget, "n-columns", column); if (row) glade_widget_property_set (gwidget, "n-rows", row); g_list_free (children); /* Refresh placeholders only once after the project is finished parsing */ glade_gtk_grid_refresh_placeholders (GTK_GRID (container), TRUE); } void glade_gtk_grid_post_create (GladeWidgetAdaptor *adaptor, GObject *container, GladeCreateReason reason) { GladeWidget *gwidget = glade_widget_get_from_gobject (container); g_signal_connect (G_OBJECT (gwidget), "configure-child", G_CALLBACK (glade_gtk_grid_configure_child), container); g_signal_connect (G_OBJECT (gwidget), "configure-begin", G_CALLBACK (glade_gtk_grid_configure_begin), container); g_signal_connect (G_OBJECT (gwidget), "configure-end", G_CALLBACK (glade_gtk_grid_configure_end), container); if (reason == GLADE_CREATE_LOAD) g_signal_connect (glade_widget_get_project (gwidget), "parse-finished", G_CALLBACK (glade_gtk_grid_parse_finished), container); } void glade_gtk_grid_destroy_object (GladeWidgetAdaptor *adaptor, GObject *object) { GladeWidget *widget = glade_widget_get_from_gobject (object); GladeProject *project = glade_widget_get_project (widget); if (project) g_signal_handlers_disconnect_by_func (project, glade_gtk_grid_parse_finished, object); } static gboolean glade_gtk_grid_widget_exceeds_bounds (GtkGrid *grid, gint n_rows, gint n_cols) { GList *list, *children; gboolean ret = FALSE; children = gtk_container_get_children (GTK_CONTAINER (grid)); for (list = children; list && list->data; list = g_list_next (list)) { GladeGridAttachments attach; GtkWidget *widget = list->data; if (GLADE_IS_PLACEHOLDER (widget)) continue; glade_gtk_grid_get_child_attachments (GTK_WIDGET (grid), widget, &attach); if (attach.left_attach + attach.width > n_cols || attach.top_attach + attach.height > n_rows) { ret = TRUE; break; } } g_list_free (children); return ret; } static void gtk_grid_children_callback (GtkWidget *widget, gpointer client_data) { GList **children; children = (GList **) client_data; *children = g_list_prepend (*children, widget); } GList * glade_gtk_grid_get_children (GladeWidgetAdaptor *adaptor, GtkContainer *container) { GList *children = NULL; g_return_val_if_fail (GTK_IS_GRID (container), NULL); gtk_container_forall (container, gtk_grid_children_callback, &children); /* GtkGrid has the children list already reversed */ return children; } void glade_gtk_grid_add_child (GladeWidgetAdaptor *adaptor, GObject *object, GObject *child) { g_return_if_fail (GTK_IS_GRID (object)); g_return_if_fail (GTK_IS_WIDGET (child)); gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (child)); glade_gtk_grid_refresh_placeholders (GTK_GRID (object), FALSE); } void glade_gtk_grid_remove_child (GladeWidgetAdaptor *adaptor, GObject *object, GObject *child) { g_return_if_fail (GTK_IS_GRID (object)); g_return_if_fail (GTK_IS_WIDGET (child)); gtk_container_remove (GTK_CONTAINER (object), GTK_WIDGET (child)); glade_gtk_grid_refresh_placeholders (GTK_GRID (object), FALSE); } void glade_gtk_grid_replace_child (GladeWidgetAdaptor *adaptor, GObject *container, GObject *current, GObject *new_widget) { g_return_if_fail (GTK_IS_GRID (container)); g_return_if_fail (GTK_IS_WIDGET (current)); g_return_if_fail (GTK_IS_WIDGET (new_widget)); /* Chain Up */ GWA_GET_CLASS (GTK_TYPE_CONTAINER)->replace_child (adaptor, container, current, new_widget); /* If we are replacing a GladeWidget, we must refresh placeholders * because the widget may have spanned multiple rows/columns, we must * not do so in the case we are pasting multiple widgets into a grid, * where destroying placeholders results in default packing properties * (since the remaining placeholder templates no longer exist, only the * first pasted widget would have proper packing properties). */ if (!GLADE_IS_PLACEHOLDER (new_widget)) glade_gtk_grid_refresh_placeholders (GTK_GRID (container), FALSE); } static void glade_gtk_grid_set_n_common (GObject *object, const GValue *value, gboolean for_rows) { GladeWidget *widget; GtkGrid *grid; guint new_size, n_columns, n_rows; grid = GTK_GRID (object); widget = glade_widget_get_from_gobject (GTK_WIDGET (grid)); glade_widget_property_get (widget, "n-columns", &n_columns); glade_widget_property_get (widget, "n-rows", &n_rows); new_size = g_value_get_uint (value); if (new_size < 1) return; if (glade_gtk_grid_widget_exceeds_bounds (grid, for_rows ? new_size : n_rows, for_rows ? n_columns : new_size)) /* Refuse to shrink if it means orphaning widgets */ return; /* Fill grid with placeholders */ glade_gtk_grid_refresh_placeholders (grid, FALSE); } void glade_gtk_grid_set_property (GladeWidgetAdaptor *adaptor, GObject *object, const gchar *id, const GValue *value) { if (!strcmp (id, "n-rows")) glade_gtk_grid_set_n_common (object, value, TRUE); else if (!strcmp (id, "n-columns")) glade_gtk_grid_set_n_common (object, value, FALSE); else GWA_GET_CLASS (GTK_TYPE_CONTAINER)->set_property (adaptor, object, id, value); } static gboolean glade_gtk_grid_verify_n_common (GObject *object, const GValue *value, gboolean for_rows) { GtkGrid *grid = GTK_GRID (object); GladeWidget *widget; guint n_columns, n_rows, new_size = g_value_get_uint (value); widget = glade_widget_get_from_gobject (GTK_WIDGET (grid)); glade_widget_property_get (widget, "n-columns", &n_columns); glade_widget_property_get (widget, "n-rows", &n_rows); if (glade_gtk_grid_widget_exceeds_bounds (grid, for_rows ? new_size : n_rows, for_rows ? n_columns : new_size)) /* Refuse to shrink if it means orphaning widgets */ return FALSE; return TRUE; } gboolean glade_gtk_grid_verify_property (GladeWidgetAdaptor *adaptor, GObject *object, const gchar *id, const GValue *value) { if (!strcmp (id, "n-rows")) return glade_gtk_grid_verify_n_common (object, value, TRUE); else if (!strcmp (id, "n-columns")) return glade_gtk_grid_verify_n_common (object, value, FALSE); else if (GWA_GET_CLASS (GTK_TYPE_CONTAINER)->verify_property) GWA_GET_CLASS (GTK_TYPE_CONTAINER)->verify_property (adaptor, object, id, value); return TRUE; } void glade_gtk_grid_set_child_property (GladeWidgetAdaptor *adaptor, GObject *container, GObject *child, const gchar *property_name, GValue *value) { g_return_if_fail (GTK_IS_GRID (container)); g_return_if_fail (GTK_IS_WIDGET (child)); g_return_if_fail (property_name != NULL && value != NULL); GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_set_property (adaptor, container, child, property_name, value); if (strcmp (property_name, "left-attach") == 0 || strcmp (property_name, "top-attach") == 0 || strcmp (property_name, "width") == 0 || strcmp (property_name, "height") == 0) { /* Refresh placeholders */ glade_gtk_grid_refresh_placeholders (GTK_GRID (container), FALSE); } } static gboolean glade_gtk_grid_verify_attach_common (GObject *object, GValue *value, const gchar *prop, const gchar *parent_prop) { GladeWidget *widget, *parent; guint parent_val; gint val, prop_val; widget = glade_widget_get_from_gobject (object); g_return_val_if_fail (GLADE_IS_WIDGET (widget), TRUE); parent = glade_widget_get_parent (widget); g_return_val_if_fail (GLADE_IS_WIDGET (parent), TRUE); val = g_value_get_int (value); glade_widget_property_get (widget, prop, &prop_val); glade_widget_property_get (parent, parent_prop, &parent_val); if (val < 0 || (val+prop_val) > parent_val) return FALSE; return TRUE; } gboolean glade_gtk_grid_child_verify_property (GladeWidgetAdaptor *adaptor, GObject *container, GObject *child, const gchar *id, GValue *value) { if (!strcmp (id, "left-attach")) return glade_gtk_grid_verify_attach_common (child, value, "width", "n-columns"); else if (!strcmp (id, "width")) return glade_gtk_grid_verify_attach_common (child, value, "left-attach", "n-columns"); else if (!strcmp (id, "top-attach")) return glade_gtk_grid_verify_attach_common (child, value, "height", "n-rows"); else if (!strcmp (id, "height")) return glade_gtk_grid_verify_attach_common (child, value, "top-attach", "n-rows"); else if (GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_verify_property) GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_verify_property (adaptor, container, child, id, value); return TRUE; } static void glade_gtk_grid_child_insert_remove_action (GladeWidgetAdaptor *adaptor, GObject *container, GObject *object, GroupAction group_action, const gchar *n_row_col, const gchar *attach1, /* should be smaller (top/left) attachment */ const gchar *attach2, /* should be larger (bot/right) attachment */ gboolean remove, gboolean after) { GladeWidget *parent; GList *children, *l; gint child_pos, size, offset; gtk_container_child_get (GTK_CONTAINER (container), GTK_WIDGET (object), attach1, &child_pos, NULL); parent = glade_widget_get_from_gobject (container); switch (group_action) { case GROUP_ACTION_INSERT_ROW: glade_command_push_group (_("Insert Row on %s"), glade_widget_get_name (parent)); break; case GROUP_ACTION_INSERT_COLUMN: glade_command_push_group (_("Insert Column on %s"), glade_widget_get_name (parent)); break; case GROUP_ACTION_REMOVE_COLUMN: glade_command_push_group (_("Remove Column on %s"), glade_widget_get_name (parent)); break; case GROUP_ACTION_REMOVE_ROW: glade_command_push_group (_("Remove Row on %s"), glade_widget_get_name (parent)); break; default: g_assert_not_reached (); } children = glade_widget_adaptor_get_children (adaptor, container); /* Make sure widgets does not get destroyed */ g_list_foreach (children, (GFunc) g_object_ref, NULL); glade_widget_property_get (parent, n_row_col, &size); if (remove) { GList *del = NULL; /* Remove children first */ for (l = children; l; l = g_list_next (l)) { GladeWidget *gchild = glade_widget_get_from_gobject (l->data); gint pos1, pos2; /* Skip placeholders */ if (gchild == NULL) continue; glade_widget_pack_property_get (gchild, attach1, &pos1); glade_widget_pack_property_get (gchild, attach2, &pos2); pos2 += pos1; if ((pos1 + 1 == pos2) && ((after ? pos2 : pos1) == child_pos)) { del = g_list_prepend (del, gchild); } } if (del) { glade_command_delete (del); g_list_free (del); } offset = -1; } else { /* Expand the grid */ glade_command_set_property (glade_widget_get_property (parent, n_row_col), size + 1); offset = 1; } /* Reorder children */ for (l = children; l; l = g_list_next (l)) { GladeWidget *gchild = glade_widget_get_from_gobject (l->data); gint pos; /* Skip placeholders */ if (gchild == NULL) continue; /* if removing, do top/left before bot/right */ if (remove) { /* adjust top-left attachment */ glade_widget_pack_property_get (gchild, attach1, &pos); if (pos > child_pos || (after && pos == child_pos)) { glade_command_set_property (glade_widget_get_pack_property (gchild, attach1), pos + offset); } } /* if inserting, do bot/right before top/left */ else { /* adjust top-left attachment */ glade_widget_pack_property_get (gchild, attach1, &pos); if ((after && pos > child_pos) || (!after && pos >= child_pos)) { glade_command_set_property (glade_widget_get_pack_property (gchild, attach1), pos + offset); } } } if (remove) { /* Shrink the grid */ glade_command_set_property (glade_widget_get_property (parent, n_row_col), size - 1); } g_list_foreach (children, (GFunc) g_object_unref, NULL); g_list_free (children); glade_command_pop_group (); } void glade_gtk_grid_child_action_activate (GladeWidgetAdaptor *adaptor, GObject *container, GObject *object, const gchar *action_path) { if (strcmp (action_path, "insert_row/after") == 0) { glade_gtk_grid_child_insert_remove_action (adaptor, container, object, GROUP_ACTION_INSERT_ROW, "n-rows", "top-attach", "height", FALSE, TRUE); } else if (strcmp (action_path, "insert_row/before") == 0) { glade_gtk_grid_child_insert_remove_action (adaptor, container, object, GROUP_ACTION_INSERT_ROW, "n-rows", "top-attach", "height", FALSE, FALSE); } else if (strcmp (action_path, "insert_column/after") == 0) { glade_gtk_grid_child_insert_remove_action (adaptor, container, object, GROUP_ACTION_INSERT_COLUMN, "n-columns", "left-attach", "width", FALSE, TRUE); } else if (strcmp (action_path, "insert_column/before") == 0) { glade_gtk_grid_child_insert_remove_action (adaptor, container, object, GROUP_ACTION_INSERT_COLUMN, "n-columns", "left-attach", "width", FALSE, FALSE); } else if (strcmp (action_path, "remove_column") == 0) { glade_gtk_grid_child_insert_remove_action (adaptor, container, object, GROUP_ACTION_REMOVE_COLUMN, "n-columns", "left-attach", "width", TRUE, FALSE); } else if (strcmp (action_path, "remove_row") == 0) { glade_gtk_grid_child_insert_remove_action (adaptor, container, object, GROUP_ACTION_REMOVE_ROW, "n-rows", "top-attach", "height", TRUE, FALSE); } else GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_action_activate (adaptor, container, object, action_path); } /*************************************************************** * GladeFixed drag/resize * ***************************************************************/ typedef struct { GladeWidget *widget; gint left_attach; gint top_attach; gint width; gint height; } GladeGridChild; typedef enum { DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT } GladeGridDir; static GladeGridChild grid_edit = { 0, }; static GladeGridChild grid_cur_attach = { 0, }; /* Takes a point (x or y depending on 'row') relative to * grid, and returns the row or column in which the point * was found. */ static gint glade_gtk_grid_get_row_col_from_point (GtkGrid *grid, gboolean row, gint point) { GladeGridAttachments attach; GtkAllocation allocation; GList *list, *children; gint span, trans_point, size, base, end; children = gtk_container_get_children (GTK_CONTAINER (grid)); for (list = children; list; list = list->next) { GtkWidget *widget = list->data; glade_gtk_grid_get_child_attachments (GTK_WIDGET (grid), widget, &attach); if (row) gtk_widget_translate_coordinates (GTK_WIDGET (grid), widget, 0, point, NULL, &trans_point); else gtk_widget_translate_coordinates (GTK_WIDGET (grid), widget, point, 0, &trans_point, NULL); gtk_widget_get_allocation (widget, &allocation); /* Find any widget in our row/column */ end = row ? allocation.height : allocation.width; if (trans_point >= 0 && /* should be trans_point < end ... test FIXME ! */ trans_point < end) { base = row ? attach.top_attach : attach.left_attach; size = row ? allocation.height : allocation.width; span = row ? attach.height : attach.width; return base + (trans_point * span / size); } } g_list_free (children); return -1; } static gboolean glade_gtk_grid_point_crosses_threshold (GtkGrid *grid, gboolean row, gint num, GladeGridDir dir, gint point) { GladeGridAttachments attach; GtkAllocation allocation; GList *list, *children; gint span, trans_point, size, rowcol_size, base; children = gtk_container_get_children (GTK_CONTAINER (grid)); for (list = children; list; list = list->next) { GtkWidget *widget = list->data; glade_gtk_grid_get_child_attachments (GTK_WIDGET (grid), widget, &attach); /* Find any widget in our row/column */ if ((row && num >= attach.top_attach && num < attach.top_attach + attach.height) || (!row && num >= attach.left_attach && num < attach.left_attach + attach.width)) { if (row) gtk_widget_translate_coordinates (GTK_WIDGET (grid), widget, 0, point, NULL, &trans_point); else gtk_widget_translate_coordinates (GTK_WIDGET (grid), widget, point, 0, &trans_point, NULL); span = row ? attach.height : attach.width; gtk_widget_get_allocation (widget, &allocation); size = row ? allocation.height : allocation.width; base = row ? attach.top_attach : attach.left_attach; rowcol_size = size / span; trans_point -= (num - base) * rowcol_size; #if 0 g_print ("dir: %s, widget size: %d, rowcol size: %d, " "requested rowcol: %d, widget base rowcol: %d, trim: %d, " "widget point: %d, thresh: %d\n", dir == DIR_UP ? "up" : dir == DIR_DOWN ? "down" : dir == DIR_LEFT ? "left" : "right", size, rowcol_size, num, base, (num - base) * rowcol_size, trans_point, dir == DIR_UP || dir == DIR_LEFT ? (rowcol_size / 2) : (rowcol_size / 2)); #endif switch (dir) { case DIR_UP: case DIR_LEFT: return trans_point <= (rowcol_size / 2); case DIR_DOWN: case DIR_RIGHT: return trans_point >= (rowcol_size / 2); default: break; } } } g_list_free (children); return FALSE; } static gboolean glade_gtk_grid_get_attachments (GladeFixed *fixed, GtkGrid *grid, GdkRectangle *rect, GladeGridChild *configure) { GladeWidget *widget = GLADE_WIDGET (fixed); gint center_x, center_y, row, column; guint n_columns, n_rows; center_x = rect->x + (rect->width / 2); center_y = rect->y + (rect->height / 2); column = glade_gtk_grid_get_row_col_from_point (grid, FALSE, center_x); row = glade_gtk_grid_get_row_col_from_point (grid, TRUE, center_y); /* its a start, now try to grow when the rect extents * reach at least half way into the next row/column */ configure->left_attach = column; configure->width = 1; configure->top_attach = row; configure->height = 1; glade_widget_property_get (widget, "n-columns", &n_columns); glade_widget_property_get (widget, "n-rows", &n_rows); if (column >= 0 && row >= 0) { /* Check and expand left */ while (configure->left_attach > 0) { if (rect->x < fixed->child_x_origin && fixed->operation != GLADE_CURSOR_DRAG && GLADE_FIXED_CURSOR_LEFT (fixed->operation) == FALSE) break; if (glade_gtk_grid_point_crosses_threshold (grid, FALSE, configure->left_attach - 1, DIR_LEFT, rect->x) == FALSE) break; configure->left_attach--; configure->width++; } /* Check and expand right */ while (configure->left_attach + configure->width < n_columns) { if (rect->x + rect->width > fixed->child_x_origin + fixed->child_width_origin && fixed->operation != GLADE_CURSOR_DRAG && GLADE_FIXED_CURSOR_RIGHT (fixed->operation) == FALSE) break; if (glade_gtk_grid_point_crosses_threshold (grid, FALSE, configure->left_attach + configure->width, DIR_RIGHT, rect->x + rect->width) == FALSE) break; configure->width++; } /* Check and expand top */ while (configure->top_attach > 0) { if (rect->y < fixed->child_y_origin && fixed->operation != GLADE_CURSOR_DRAG && GLADE_FIXED_CURSOR_TOP (fixed->operation) == FALSE) break; if (glade_gtk_grid_point_crosses_threshold (grid, TRUE, configure->top_attach - 1, DIR_UP, rect->y) == FALSE) break; configure->top_attach--; configure->height++; } /* Check and expand bottom */ while (configure->top_attach + configure->height < n_rows) { if (rect->y + rect->height > fixed->child_y_origin + fixed->child_height_origin && fixed->operation != GLADE_CURSOR_DRAG && GLADE_FIXED_CURSOR_BOTTOM (fixed->operation) == FALSE) break; if (glade_gtk_grid_point_crosses_threshold (grid, TRUE, configure->top_attach + configure->height, DIR_DOWN, rect->y + rect->height) == FALSE) break; configure->height++; } } return column >= 0 && row >= 0; } static gboolean glade_gtk_grid_configure_child (GladeFixed *fixed, GladeWidget *child, GdkRectangle *rect, GtkWidget *grid) { GladeGridChild configure = { child, }; /* Sometimes we are unable to find a widget in the appropriate column, * usually because a placeholder hasnt had its size allocation yet. */ if (glade_gtk_grid_get_attachments (fixed, GTK_GRID (grid), rect, &configure)) { if (memcmp (&configure, &grid_cur_attach, sizeof (GladeGridChild)) != 0) { glade_property_push_superuser (); glade_widget_pack_property_set (child, "left-attach", configure.left_attach); glade_widget_pack_property_set (child, "width", configure.width); glade_widget_pack_property_set (child, "top-attach", configure.top_attach); glade_widget_pack_property_set (child, "height", configure.height); glade_property_pop_superuser (); memcpy (&grid_cur_attach, &configure, sizeof (GladeGridChild)); } } return TRUE; } static gboolean glade_gtk_grid_configure_begin (GladeFixed *fixed, GladeWidget *child, GtkWidget *grid) { grid_edit.widget = child; glade_widget_pack_property_get (child, "left-attach", &grid_edit.left_attach); glade_widget_pack_property_get (child, "width", &grid_edit.width); glade_widget_pack_property_get (child, "top-attach", &grid_edit.top_attach); glade_widget_pack_property_get (child, "height", &grid_edit.height); memcpy (&grid_cur_attach, &grid_edit, sizeof (GladeGridChild)); return TRUE; } static gboolean glade_gtk_grid_configure_end (GladeFixed *fixed, GladeWidget *child, GtkWidget *grid) { GladeGridChild new_child = { child, }; glade_widget_pack_property_get (child, "left-attach", &new_child.left_attach); glade_widget_pack_property_get (child, "width", &new_child.width); glade_widget_pack_property_get (child, "top-attach", &new_child.top_attach); glade_widget_pack_property_get (child, "height", &new_child.height); /* Compare the meaningfull part of the current edit. */ if (memcmp (&new_child, &grid_edit, sizeof (GladeGridChild)) != 0) { GValue left_attach_value = { 0, }; GValue width_attach_value = { 0, }; GValue top_attach_value = { 0, }; GValue height_attach_value = { 0, }; GValue new_left_attach_value = { 0, }; GValue new_width_attach_value = { 0, }; GValue new_top_attach_value = { 0, }; GValue new_height_attach_value = { 0, }; GladeProperty *left_attach_prop, *width_attach_prop, *top_attach_prop, *height_attach_prop; left_attach_prop = glade_widget_get_pack_property (child, "left-attach"); width_attach_prop = glade_widget_get_pack_property (child, "width"); top_attach_prop = glade_widget_get_pack_property (child, "top-attach"); height_attach_prop = glade_widget_get_pack_property (child, "height"); g_return_val_if_fail (GLADE_IS_PROPERTY (left_attach_prop), FALSE); g_return_val_if_fail (GLADE_IS_PROPERTY (width_attach_prop), FALSE); g_return_val_if_fail (GLADE_IS_PROPERTY (top_attach_prop), FALSE); g_return_val_if_fail (GLADE_IS_PROPERTY (height_attach_prop), FALSE); glade_property_get_value (left_attach_prop, &new_left_attach_value); glade_property_get_value (width_attach_prop, &new_width_attach_value); glade_property_get_value (top_attach_prop, &new_top_attach_value); glade_property_get_value (height_attach_prop, &new_height_attach_value); g_value_init (&left_attach_value, G_TYPE_INT); g_value_init (&width_attach_value, G_TYPE_INT); g_value_init (&top_attach_value, G_TYPE_INT); g_value_init (&height_attach_value, G_TYPE_INT); g_value_set_int (&left_attach_value, grid_edit.left_attach); g_value_set_int (&width_attach_value, grid_edit.width); g_value_set_int (&top_attach_value, grid_edit.top_attach); g_value_set_int (&height_attach_value, grid_edit.height); glade_command_push_group (_("Placing %s inside %s"), glade_widget_get_name (child), glade_widget_get_name (GLADE_WIDGET (fixed))); glade_command_set_properties (left_attach_prop, &left_attach_value, &new_left_attach_value, width_attach_prop, &width_attach_value, &new_width_attach_value, top_attach_prop, &top_attach_value, &new_top_attach_value, height_attach_prop, &height_attach_value, &new_height_attach_value, NULL); glade_command_pop_group (); g_value_unset (&left_attach_value); g_value_unset (&width_attach_value); g_value_unset (&top_attach_value); g_value_unset (&height_attach_value); g_value_unset (&new_left_attach_value); g_value_unset (&new_width_attach_value); g_value_unset (&new_top_attach_value); g_value_unset (&new_height_attach_value); } return TRUE; }