summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan Pablo Ugarte <juanpablougarte@gmail.com>2013-11-15 22:45:17 -0300
committerJuan Pablo Ugarte <juanpablougarte@gmail.com>2014-04-23 17:26:14 -0300
commitbde9aabb1654330e6f36efb0b7bbbe8f646c1629 (patch)
treec75f9a1d61d54fed85397ef59fa87425d747bfe5
parentbcd3c34fdf749701e7d7b144d492510d6f67b1f7 (diff)
downloadglade-bde9aabb1654330e6f36efb0b7bbbe8f646c1629.tar.gz
Backport 2bc40ad87be072aac2759755df63707d43f8415c.
Thanks to Thomas Martitz for the backport. Sort object dependancy before saving using a topological sorting algorithm _glade_tsort() instead of g_list_sort() with glade_widget_depends() which is not a transitive property. Closes Bug 709609 "[PATCH] Change way of sorting before writing XML output." Fixes Bug 711858 "editing glade project results in long CPU usage spikes after upgrading to 3.16 and GTK+3.10" This backport includes 1401a4bb43a3e56d642c34d5dc7d5ee86217cb3d which reverted part of 2bc40ad87be072aac2759755df63707d43f8415c. Conflicts: gladeui/Makefile.am gladeui/glade-project.c gladeui/glade-widget-adaptor.c gladeui/glade-widget.c plugins/gtk+/glade-gtk-entry.c plugins/gtk+/glade-gtk-size-group.c plugins/gtk+/glade-gtk-tree-view.c plugins/gtk+/glade-gtk-widget.c plugins/gtk+/gtk+.xml.in
-rw-r--r--gladeui/Makefile.am3
-rw-r--r--gladeui/glade-project.c365
-rw-r--r--gladeui/glade-property-class.c17
-rw-r--r--gladeui/glade-property-class.h4
-rw-r--r--gladeui/glade-property.c25
-rw-r--r--gladeui/glade-property.h5
-rw-r--r--gladeui/glade-tsort.c177
-rw-r--r--gladeui/glade-tsort.h51
-rw-r--r--gladeui/glade-widget-adaptor.c37
-rw-r--r--gladeui/glade-widget-private.h36
-rw-r--r--gladeui/glade-widget.c33
-rw-r--r--gladeui/glade-widget.h3
-rw-r--r--plugins/gtk+/glade-gtk.c32
-rw-r--r--plugins/gtk+/gtk+.xml.in3
14 files changed, 627 insertions, 164 deletions
diff --git a/gladeui/Makefile.am b/gladeui/Makefile.am
index 1e4e5b81..98ca9a65 100644
--- a/gladeui/Makefile.am
+++ b/gladeui/Makefile.am
@@ -56,6 +56,7 @@ libgladeui_1_la_SOURCES = \
glade-popup.h \
glade-marshallers.h \
glade-accumulators.h \
+ glade-tsort.c \
glade-widget-action.c \
glade-name-context.c \
glade-displayable-values.c \
@@ -124,6 +125,8 @@ noinst_HEADERS = \
glade-object-stub.h \
glade-popup.h \
glade-accumulators.h
+ glade-tsort.h \
+ glade-widget-private.h
if PLATFORM_WIN32
libgladeui_1_la_LDFLAGS += -no-undefined
diff --git a/gladeui/glade-project.c b/gladeui/glade-project.c
index 850e7bfa..e75d5025 100644
--- a/gladeui/glade-project.c
+++ b/gladeui/glade-project.c
@@ -51,6 +51,9 @@
#include "glade-name-context.h"
#include "glade-object-stub.h"
+#include "glade-widget-private.h"
+#include "glade-tsort.h"
+
#define VALID_ITER(project, iter) ((iter)!= NULL && G_IS_OBJECT ((iter)->user_data) && ((GladeProject*)(project))->priv->stamp == (iter)->stamp)
enum
@@ -211,6 +214,8 @@ static void glade_project_verify_adaptor (GladeProject *
gboolean forwidget,
GladeSupportMask *mask);
+static GladeXmlContext *glade_project_write (GladeProject *project);
+
static GladeWidget *search_ancestry_by_name (GladeWidget *toplevel,
const gchar *name);
@@ -1795,69 +1800,6 @@ glade_project_write_resource_path (GladeProject *project,
}
}
-static gint
-sort_project_dependancies (GObject *a, GObject *b)
-{
- GladeWidget *ga, *gb;
-
- ga = glade_widget_get_from_gobject (a);
- gb = glade_widget_get_from_gobject (b);
-
- if (glade_widget_adaptor_depends (ga->adaptor, ga, gb))
- return 1;
- else if (glade_widget_adaptor_depends (gb->adaptor, gb, ga))
- return -1;
- else
- return strcmp (ga->name, gb->name);
-}
-
-static GladeXmlContext *
-glade_project_write (GladeProject *project)
-{
- GladeXmlContext *context;
- GladeXmlDoc *doc;
- GladeXmlNode *root; /* *comment_node; */
- GList *list;
-
- doc = glade_xml_doc_new ();
- context = glade_xml_context_new (doc, NULL);
- root = glade_xml_node_new (context, GLADE_XML_TAG_PROJECT (project->priv->format));
- glade_xml_doc_set_root (doc, root);
-
- glade_project_update_comment (project);
- /* comment_node = glade_xml_node_new_comment (context, project->priv->comment); */
-
- /* XXX Need to append this to the doc ! not the ROOT !
- glade_xml_node_append_child (root, comment_node); */
-
- glade_project_write_required_libs (project, context, root);
-
- glade_project_write_naming_policy (project, context, root);
-
- glade_project_write_resource_path (project, context, root);
-
- /* Sort the whole thing */
- project->priv->objects =
- g_list_sort (project->priv->objects,
- (GCompareFunc)sort_project_dependancies);
-
- for (list = project->priv->objects; list; list = list->next)
- {
- GladeWidget *widget;
-
- widget = glade_widget_get_from_gobject (list->data);
-
- /*
- * Append toplevel widgets. Each widget then takes
- * care of appending its children.
- */
- if (widget->parent == NULL)
- glade_widget_write (widget, context, root);
- }
-
- return context;
-}
-
/**
* glade_project_save:
* @project: a #GladeProject
@@ -2485,6 +2427,224 @@ glade_project_verify_adaptor (GladeProject *project,
}
+
+static gint
+glade_widgets_name_cmp (gconstpointer ga, gconstpointer gb)
+{
+ return g_strcmp0 (glade_widget_get_name ((gpointer)ga), glade_widget_get_name ((gpointer)gb));
+}
+
+static inline gboolean
+glade_project_widget_hard_depends (GladeWidget *widget, GladeWidget *another)
+{
+ GList *l;
+
+ for (l = _glade_widget_peek_prop_refs (another); l; l = g_list_next (l))
+ {
+ GladePropertyClass *klass;
+
+ /* If one of the properties that reference @another is
+ * owned by @widget then @widget depends on @another
+ */
+ if (glade_property_get_widget (l->data) == widget &&
+ (klass = glade_property_get_class (l->data)) &&
+ glade_property_class_get_construct_only (klass))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static _NodeEdge *
+glade_project_get_graph_deps (GladeProject *project)
+{
+ GladeProjectPrivate *priv = project->priv;
+ GList *l, *objects = NULL;
+ _NodeEdge *edges = NULL;
+
+ /* Create list of GladeWidgets */
+ for (l = priv->objects; l; l = g_list_next (l))
+ objects = g_list_prepend (objects, glade_widget_get_from_gobject (l->data));
+
+ /* Sort objects alphabetically */
+ objects = g_list_sort (objects, glade_widgets_name_cmp);
+
+ /* Create edges of the directed graph */
+ for (l = objects; l; l = g_list_next (l))
+ {
+ GladeWidget *predecessor = l->data;
+ GladeWidget *predecessor_top;
+ GList *ll;
+
+ predecessor_top = glade_widget_get_toplevel (predecessor);
+
+ for (ll = objects; ll; ll = g_list_next (ll))
+ {
+ GladeWidget *successor = ll->data;
+ GladeWidget *successor_top;
+
+ successor_top = glade_widget_get_toplevel (successor);
+
+ /* Ignore object within the same toplevel */
+ if (predecessor_top == successor_top)
+ continue;
+
+ /* Check if succesor depends on predecessor and add a corresponding
+ * edge to the dependency graph.
+ * Note that we add the implicit dependency on their respective
+ * toplevels because that is what we care!
+ */
+ if (glade_widget_depends (successor, predecessor))
+ edges = _node_edge_prepend (edges, predecessor_top, successor_top);
+ }
+ }
+
+ g_list_free (objects);
+
+ return edges;
+}
+
+static GList *
+glade_project_get_nodes_from_edges (GladeProject *project, _NodeEdge *edges)
+{
+ _NodeEdge *edge, *hard_edges = NULL;
+ GList *cycles = NULL;
+
+ /* Collect widgets with circular dependencies */
+ for (edge = edges; edge; edge = edge->next)
+ {
+ if (glade_widget_get_parent (edge->successor) == edge->predecessor ||
+ glade_project_widget_hard_depends (edge->predecessor, edge->successor))
+ hard_edges = _node_edge_prepend (hard_edges, edge->predecessor, edge->successor);
+
+ /* Just toplevels */
+ if (glade_widget_get_parent (edge->successor))
+ continue;
+
+ if (!g_list_find (cycles, edge->successor))
+ cycles = g_list_prepend (cycles, edge->successor);
+ }
+
+ /* Sort them alphabetically */
+ cycles = g_list_sort (cycles, glade_widgets_name_cmp);
+
+ if (!hard_edges)
+ return cycles;
+
+ /* Sort them by hard deps */
+ cycles = _glade_tsort (&cycles, &hard_edges);
+
+ if (hard_edges)
+ {
+ GList *hard_cycles = NULL;
+
+ /* Collect widgets with hard circular dependencies */
+ for (edge = hard_edges; edge; edge = edge->next)
+ {
+ /* Just toplevels */
+ if (glade_widget_get_parent (edge->successor))
+ continue;
+
+ if (!g_list_find (hard_cycles, edge->successor))
+ hard_cycles = g_list_prepend (hard_cycles, edge->successor);
+ }
+
+ /* And append to the end of the cycles list */
+ cycles = g_list_concat (cycles, g_list_sort (hard_cycles, glade_widgets_name_cmp));
+
+ /* Opps! there is at least one hard circular dependency,
+ * GtkBuilder will fail to set one of the properties that create the cycle
+ */
+
+ _node_edge_free (hard_edges);
+ }
+
+ return cycles;
+}
+
+static GList *
+glade_project_get_ordered_toplevels (GladeProject *project)
+{
+ GladeProjectPrivate *priv = project->priv;
+ GList *l, *sorted_tree, *tree = NULL;
+ _NodeEdge *edges;
+
+ /* Create list of toplevels GladeWidgets */
+ for (l = priv->tree; l; l = g_list_next (l))
+ tree = g_list_prepend (tree, glade_widget_get_from_gobject (l->data));
+
+ /* Sort toplevels alphabetically */
+ tree = g_list_sort (tree, glade_widgets_name_cmp);
+
+ edges = glade_project_get_graph_deps (project);
+
+ /* Sort tree */
+ sorted_tree = _glade_tsort (&tree, &edges);
+
+ if (edges)
+ {
+ GList *cycles = glade_project_get_nodes_from_edges (project, edges);
+
+ /* And append to the end of the sorted list */
+ sorted_tree = g_list_concat (sorted_tree, cycles);
+
+ _node_edge_free (edges);
+ }
+
+ /* No need to free tree as tsort will consume the list */
+
+ return sorted_tree;
+}
+
+static GladeXmlContext *
+glade_project_write (GladeProject *project)
+{
+ GladeXmlContext *context;
+ GladeXmlDoc *doc;
+ GladeXmlNode *root; /* *comment_node; */
+ GList *list;
+ GList *toplevels;
+
+ doc = glade_xml_doc_new ();
+ context = glade_xml_context_new (doc, NULL);
+ root = glade_xml_node_new (context, GLADE_XML_TAG_PROJECT (project->priv->format));
+ glade_xml_doc_set_root (doc, root);
+
+ glade_project_update_comment (project);
+ /* comment_node = glade_xml_node_new_comment (context, project->priv->comment); */
+
+ /* XXX Need to append this to the doc ! not the ROOT !
+ glade_xml_node_append_child (root, comment_node); */
+
+ glade_project_write_required_libs (project, context, root);
+
+ glade_project_write_naming_policy (project, context, root);
+
+ glade_project_write_resource_path (project, context, root);
+
+ /* Get sorted toplevels */
+ toplevels = glade_project_get_ordered_toplevels (project);
+
+ for (list = toplevels; list; list = g_list_next (list))
+ {
+ GladeWidget *widget = list->data;
+
+ /*
+ * Append toplevel widgets. Each widget then takes
+ * care of appending its children.
+ */
+ if (!glade_widget_get_parent (widget))
+ glade_widget_write (widget, context, root);
+ else
+ g_warning ("Tried to save a non toplevel object '%s' at xml root",
+ glade_widget_get_name (widget));
+ }
+
+ g_list_free (toplevels);
+
+ return context;
+}
+
/**
* glade_project_verify_widget_adaptor:
* @project: A #GladeProject
@@ -2928,58 +3088,57 @@ glade_project_check_reordered (GladeProject *project,
GladeWidget *parent,
GList *old_order)
{
- GList *new_order, *l, *ll;
- gint *order, n_children, i;
- GtkTreeIter iter;
- GtkTreePath *path;
+ GList *new_order, *l, *ll;
+ gint *order, n_children, i;
+ GtkTreeIter iter;
+ GtkTreePath *path;
- g_return_if_fail (GLADE_IS_PROJECT (project));
- g_return_if_fail (GLADE_IS_WIDGET (parent));
- g_return_if_fail (glade_project_has_object (project,
- glade_widget_get_object (parent)));
+ g_return_if_fail (GLADE_IS_PROJECT (project));
+ g_return_if_fail (GLADE_IS_WIDGET (parent));
+ g_return_if_fail (glade_project_has_object (project,
+ glade_widget_get_object (parent)));
- new_order = glade_widget_get_children (parent);
+ new_order = glade_widget_get_children (parent);
- /* Check if the list changed */
- for (l = old_order, ll = new_order;
- l && ll;
- l = l->next, ll = ll->next)
- {
- if (l->data != ll->data)
- break;
- }
+ /* Check if the list changed */
+ for (l = old_order, ll = new_order; l && ll; l = l->next, ll = ll->next)
+ {
+ if (l->data != ll->data)
+ break;
+ }
- if (l || ll)
- {
- n_children = glade_project_count_children (project, parent);
- order = g_new (gint, n_children);
+ if (l || ll)
+ {
+ n_children = glade_project_count_children (project, parent);
+ order = g_new (gint, n_children);
+
+ for (i = 0, l = new_order; l; l = l->next)
+ {
+ GObject *obj = l->data;
- for (i = 0, l = new_order; l; l = l->next)
- {
- GObject *obj = l->data;
+ if (glade_project_has_object (project, obj))
+ {
+ GList *node = g_list_find (old_order, obj);
- if (glade_project_has_object (project, obj))
- {
- GList *node = g_list_find (old_order, obj);
+ g_assert (node);
- g_assert (node);
+ order[i] = g_list_position (old_order, node);
- order[i] = g_list_position (old_order, node);
+ i++;
+ }
+ }
- i++;
- }
- }
+ /* Signal that the rows were reordered */
+ glade_project_model_get_iter_for_object (project, glade_widget_get_object (parent), &iter);
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (project), &iter);
+ gtk_tree_model_rows_reordered (GTK_TREE_MODEL (project), path, &iter, order);
+ gtk_tree_path_free (path);
- /* Signal that the rows were reordered */
- glade_project_model_get_iter_for_object (project, glade_widget_get_object (parent), &iter);
- path = gtk_tree_model_get_path (GTK_TREE_MODEL (project), &iter);
- gtk_tree_model_rows_reordered (GTK_TREE_MODEL (project), path, &iter, order);
- gtk_tree_path_free (path);
+ g_free (order);
- g_free (order);
- }
+ }
- g_list_free (new_order);
+ g_list_free (new_order);
}
/**
diff --git a/gladeui/glade-property-class.c b/gladeui/glade-property-class.c
index 6a66716a..9c9dc18b 100644
--- a/gladeui/glade-property-class.c
+++ b/gladeui/glade-property-class.c
@@ -1762,3 +1762,20 @@ glade_property_class_compare (GladePropertyClass *klass,
return retval;
}
+
+void
+glade_property_class_set_construct_only (GladePropertyClass *property_class,
+ gboolean construct_only)
+{
+ g_return_if_fail (GLADE_IS_PROPERTY_CLASS (property_class));
+
+ property_class->construct_only = construct_only;
+}
+
+gboolean
+glade_property_class_get_construct_only (GladePropertyClass *property_class)
+{
+ g_return_val_if_fail (GLADE_IS_PROPERTY_CLASS (property_class), FALSE);
+
+ return property_class->construct_only;
+}
diff --git a/gladeui/glade-property-class.h b/gladeui/glade-property-class.h
index b2ec3efc..cdfcbc2c 100644
--- a/gladeui/glade-property-class.h
+++ b/gladeui/glade-property-class.h
@@ -278,6 +278,10 @@ gint glade_property_class_compare (GladePropertyC
const GValue *value2,
GladeProjectFormat fmt);
+void glade_property_class_set_construct_only (GladePropertyClass *property_class,
+ gboolean construct_only);
+gboolean glade_property_class_get_construct_only (GladePropertyClass *property_class);
+
G_END_DECLS
#endif /* __GLADE_PROPERTY_CLASS_H__ */
diff --git a/gladeui/glade-property.c b/gladeui/glade-property.c
index 88009c38..0043b75a 100644
--- a/gladeui/glade-property.c
+++ b/gladeui/glade-property.c
@@ -1465,6 +1465,31 @@ glade_property_get_enabled (GladeProperty *property)
return property->enabled;
}
+GladePropertyClass *
+glade_property_get_class (GladeProperty *property)
+{
+ g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
+
+ return property->klass;
+}
+
+void
+glade_property_set_widget (GladeProperty *property,
+ GladeWidget *widget)
+{
+ g_return_if_fail (GLADE_IS_PROPERTY (property));
+
+ property->widget = widget;
+}
+
+GladeWidget *
+glade_property_get_widget (GladeProperty *property)
+{
+ g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
+
+ return property->widget;
+}
+
static gint glade_property_su_stack = 0;
diff --git a/gladeui/glade-property.h b/gladeui/glade-property.h
index f8fd63d3..66768ff8 100644
--- a/gladeui/glade-property.h
+++ b/gladeui/glade-property.h
@@ -191,6 +191,11 @@ void glade_property_set_enabled (GladeProperty
gboolean glade_property_get_enabled (GladeProperty *property);
+GladePropertyClass *glade_property_get_class (GladeProperty *property);
+
+GladeWidget *glade_property_get_widget (GladeProperty *property);
+void glade_property_set_widget (GladeProperty *property,
+ GladeWidget *widget);
void glade_property_i18n_set_comment (GladeProperty *property,
const gchar *str);
diff --git a/gladeui/glade-tsort.c b/gladeui/glade-tsort.c
new file mode 100644
index 00000000..439da33e
--- /dev/null
+++ b/gladeui/glade-tsort.c
@@ -0,0 +1,177 @@
+/*
+ * glade-tsort.c: a topological sorting algorithm implementation
+ *
+ * Copyright (C) 2013 Juan Pablo Ugarte.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ *
+ * Authors:
+ * Juan Pablo Ugarte <juanpablougarte@gmail.com>
+ */
+
+#include "glade-tsort.h"
+
+/**
+ * _node_edge_prepend:
+ * @node: a _NodeEdge pointer or NULL
+ * @predecessor:
+ * @successor:
+ *
+ * Adds a new node with the values @predecessor and @successor to the start of
+ * @node list.
+ *
+ * Returns: the new start of the node list.
+ */
+_NodeEdge *
+_node_edge_prepend (_NodeEdge *node,
+ gpointer predecessor,
+ gpointer successor)
+{
+ _NodeEdge *edge = g_slice_new (_NodeEdge);
+
+ edge->predecessor = predecessor;
+ edge->successor = successor;
+ edge->next = node;
+
+ return edge;
+}
+
+void
+_node_edge_free (_NodeEdge *node)
+{
+ g_slice_free_chain (_NodeEdge, node, next);
+}
+
+static inline void
+tsort_remove_non_start_nodes (GList **nodes, _NodeEdge *edges)
+{
+ _NodeEdge *edge;
+
+ for (edge = edges; edge; edge = edge->next)
+ *nodes = g_list_remove (*nodes, edge->successor);
+}
+
+
+static inline gboolean
+tsort_node_has_no_incoming_edge (gpointer node, _NodeEdge *edges)
+{
+ _NodeEdge *edge;
+
+ for (edge = edges; edge; edge = edge->next)
+ {
+ if (node == edge->successor)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * _glade_tsort:
+ * @nodes: list of pointers to sort
+ * @edges: pointer to the list of #_NodeEdge that conform the dependency
+ * graph of @nodes.
+ *
+ * Topological sorting implementation.
+ * After calling this funtion only graph cycles (circular dependencies) are left
+ * in @edges list. So if @edges is NULL it means the returned list has all the
+ * elements topologically sorted if not it means there are at least one
+ * circular dependency.
+ *
+ * L ← Empty list that will contain the sorted elements
+ * S ← Set of all nodes with no incoming edges
+ * while S is non-empty do
+ * remove a node n from S
+ * insert n into L
+ * for each node m with an edge e from n to m do
+ * remove edge e from the graph
+ * if m has no other incoming edges then
+ * insert m into S
+ * return L (a topologically sorted order if graph has no edges)
+ *
+ * see: http://en.wikipedia.org/wiki/Topological_sorting
+ *
+ * Returns: a new list sorted by dependency including nodes only present in @edges
+ */
+GList *
+_glade_tsort (GList **nodes, _NodeEdge **edges)
+{
+ GList *sorted_nodes;
+
+ /* L ← Empty list that will contain the sorted elements */
+ sorted_nodes = NULL;
+
+ /* S ← Set of all nodes with no incoming edges */
+ tsort_remove_non_start_nodes (nodes, *edges);
+
+ /* while S is non-empty do */
+ while (*nodes)
+ {
+ _NodeEdge *edge, *next, *prev = NULL;
+ gpointer n;
+
+ /* remove a node n from S */
+ n = (*nodes)->data;
+ *nodes = g_list_delete_link (*nodes, *nodes);
+
+ /* insert n into L */
+ /*if (!glade_widget_get_parent (n)) this would be a specific optimization */
+ sorted_nodes = g_list_prepend (sorted_nodes, n);
+
+ /* for each node m ... */
+ for (edge = *edges; edge; edge = next)
+ {
+ next = edge->next;
+
+ /* ... with an edge e from n to m do */
+ if (edge->predecessor == n)
+ {
+ /* remove edge e from the graph */
+ if (prev)
+ prev->next = (prev->next) ? prev->next->next : NULL;
+ else
+ /* edge is the first element in the list so we only need to
+ * change the start of the list */
+ *edges = next;
+
+ /* if m has no other incoming edges then */
+ if (tsort_node_has_no_incoming_edge (edge->successor, *edges))
+ /* insert m into S */
+ *nodes = g_list_prepend (*nodes, edge->successor);
+
+ g_slice_free (_NodeEdge, edge);
+ }
+ else
+ /* Keep a pointer to the previous node in the iteration to make
+ * things easier when removing a node
+ */
+ prev = edge;
+ }
+ }
+
+ /* if graph has edges then return error (graph has at least one cycle) */
+#if 0 /* We rather not return NULL, caller must check if edge */
+ if (*edges)
+ {
+ g_list_free (sorted_nodes);
+ g_slice_free_chain (_NodeEdge, *edges, next);
+ *edges = NULL;
+ return NULL;
+ }
+#endif
+
+ /* return L (a topologically sorted order if edge is NULL) */
+ return g_list_reverse (sorted_nodes);
+}
diff --git a/gladeui/glade-tsort.h b/gladeui/glade-tsort.h
new file mode 100644
index 00000000..3026e2d5
--- /dev/null
+++ b/gladeui/glade-tsort.h
@@ -0,0 +1,51 @@
+/*
+ * glade-tsort.h: a topological sorting algorithm implementation
+ *
+ * Copyright (C) 2013 Juan Pablo Ugarte.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ *
+ * Authors:
+ * Juan Pablo Ugarte <juanpablougarte@gmail.com>
+ */
+
+#ifndef __GLADE_TSORT_H__
+#define __GLADE_TSORT_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct __NodeEdge _NodeEdge;
+
+struct __NodeEdge
+{
+ gpointer predecessor;
+ gpointer successor;
+ _NodeEdge *next;
+};
+
+_NodeEdge *_node_edge_prepend (_NodeEdge *node,
+ gpointer predecessor,
+ gpointer successor);
+
+void _node_edge_free (_NodeEdge *node);
+
+GList *_glade_tsort (GList **nodes,
+ _NodeEdge **edges);
+
+G_END_DECLS
+
+#endif /* __GLADE_TSORT_H__ */
diff --git a/gladeui/glade-widget-adaptor.c b/gladeui/glade-widget-adaptor.c
index 6777e183..0f9c2e47 100644
--- a/gladeui/glade-widget-adaptor.c
+++ b/gladeui/glade-widget-adaptor.c
@@ -42,6 +42,7 @@
#include "glade-accumulators.h"
#include "glade-displayable-values.h"
#include "glade-editor-table.h"
+#include "glade-widget-private.h"
/* For g_file_exists */
#include <sys/types.h>
@@ -859,32 +860,21 @@ glade_widget_adaptor_object_child_action_activate (GladeWidgetAdaptor *adaptor,
static gboolean
glade_widget_adaptor_object_depends (GladeWidgetAdaptor *adaptor,
- GladeWidget *widget,
- GladeWidget *another)
+ GladeWidget *widget,
+ GladeWidget *another)
{
- gboolean depends = FALSE;
- GList *l;
+ GList *l;
- for (l = another->prop_refs; !depends && l; l = l->next)
- {
- GladeProperty *property = l->data;
- GladeWidget *prop_widget = property->widget;
-
- /* If one of the properties that reference @another is
- * owned by @widget then @widget depends on @another
- */
- if (prop_widget == widget)
- depends = TRUE;
+ for (l = _glade_widget_peek_prop_refs (another); l; l = g_list_next (l))
+ {
+ /* If one of the properties that reference @another is
+ * owned by @widget then @widget depends on @another
+ */
+ if (glade_property_get_widget (l->data) == widget)
+ return TRUE;
+ }
- /* Or if the widget that owns a property which references
- * @another is somewhere inside @widget... then @widget
- * also depends on @another.
- */
- else if (glade_widget_is_ancestor (prop_widget, widget))
- depends = TRUE;
- }
-
- return depends;
+ return FALSE;
}
static void
@@ -3724,4 +3714,3 @@ glade_widget_adaptor_create_editable (GladeWidgetAdaptor *adaptor,
return GLADE_WIDGET_ADAPTOR_GET_CLASS
(adaptor)->create_editable (adaptor, type);
}
-
diff --git a/gladeui/glade-widget-private.h b/gladeui/glade-widget-private.h
new file mode 100644
index 00000000..12869f47
--- /dev/null
+++ b/gladeui/glade-widget-private.h
@@ -0,0 +1,36 @@
+/*
+ * glade-widget-private.h
+ *
+ * Copyright (C) 2013 Juan Pablo Ugarte
+ *
+ * Authors:
+ * Juan Pablo Ugarte <juanpablougarte@gmail.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __GLADE_WIDGET_PRIVATE_H__
+#define __GLADE_WIDGET_PRIVATE_H__
+
+#include "glade-widget.h"
+
+G_BEGIN_DECLS
+
+GList *_glade_widget_peek_prop_refs (GladeWidget *widget);
+
+G_END_DECLS
+
+#endif /* __GLADE_WIDGET_PRIVATE_H__ */
diff --git a/gladeui/glade-widget.c b/gladeui/glade-widget.c
index d8045bef..f7ee5866 100644
--- a/gladeui/glade-widget.c
+++ b/gladeui/glade-widget.c
@@ -45,7 +45,7 @@
#include "glade-accumulators.h"
#include "glade-project.h"
#include "glade-widget-adaptor.h"
-#include "glade-widget.h"
+#include "glade-widget-private.h"
#include "glade-marshallers.h"
#include "glade-property.h"
#include "glade-property-class.h"
@@ -1920,6 +1920,14 @@ glade_widget_create_packing_properties (GladeWidget *container, GladeWidget *wid
return g_list_reverse (packing_props);
}
+/* Private API */
+
+GList *
+_glade_widget_peek_prop_refs (GladeWidget *widget)
+{
+ return widget->prop_refs;
+}
+
/*******************************************************************************
API
*******************************************************************************/
@@ -4105,6 +4113,29 @@ glade_widget_is_ancestor (GladeWidget *widget,
return FALSE;
}
+/**
+ * glade_widget_depends:
+ * @widget: a #GladeWidget
+ * @other: another #GladeWidget
+ *
+ * Determines whether @widget is somehow dependent on @other, in
+ * which case it should be serialized after @other.
+ *
+ * A widget is dependent on another widget.
+ * It does not take into account for children dependencies.
+ *
+ * Return value: %TRUE if @widget depends on @other.
+ **/
+gboolean
+glade_widget_depends (GladeWidget *widget,
+ GladeWidget *other)
+{
+ g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE);
+ g_return_val_if_fail (GLADE_IS_WIDGET (other), FALSE);
+
+ return glade_widget_adaptor_depends (widget->adaptor, widget, other);
+}
+
static gint glade_widget_su_stack = 0;
diff --git a/gladeui/glade-widget.h b/gladeui/glade-widget.h
index 95d86015..348edc8d 100644
--- a/gladeui/glade-widget.h
+++ b/gladeui/glade-widget.h
@@ -98,7 +98,6 @@ struct _GladeWidget
*/
GList *locked_widgets; /* A list of widgets this widget has locked down.
*/
-
/* Construct parameters: */
GladeWidget *construct_template;
GladeCreateReason construct_reason;
@@ -279,6 +278,8 @@ gchar *glade_widget_generate_path_name (GladeWidget *w
gboolean glade_widget_is_ancestor (GladeWidget *widget,
GladeWidget *ancestor);
+gboolean glade_widget_depends (GladeWidget *widget,
+ GladeWidget *other);
/*******************************************************************************
Project, object property references
diff --git a/plugins/gtk+/glade-gtk.c b/plugins/gtk+/glade-gtk.c
index c784341f..d797467b 100644
--- a/plugins/gtk+/glade-gtk.c
+++ b/plugins/gtk+/glade-gtk.c
@@ -4804,17 +4804,6 @@ glade_gtk_expander_write_child (GladeWidgetAdaptor *adaptor,
/* -------------------------------- GtkEntry -------------------------------- */
-gboolean
-glade_gtk_entry_depends (GladeWidgetAdaptor *adaptor,
- GladeWidget *widget,
- GladeWidget *another)
-{
- if (GTK_IS_ENTRY_BUFFER (another->object))
- return TRUE;
-
- return GWA_GET_CLASS (GTK_TYPE_WIDGET)->depends (adaptor, widget, another);
-}
-
static void
glade_gtk_entry_changed (GtkEditable *editable, GladeWidget *gentry)
@@ -10072,16 +10061,6 @@ glade_gtk_radio_button_set_property (GladeWidgetAdaptor *adaptor,
}
/*--------------------------- GtkSizeGroup ---------------------------------*/
-gboolean
-glade_gtk_size_group_depends (GladeWidgetAdaptor *adaptor,
- GladeWidget *widget,
- GladeWidget *another)
-{
- if (GTK_IS_WIDGET (another->object))
- return TRUE;
-
- return GWA_GET_CLASS (G_TYPE_OBJECT)->depends (adaptor, widget, another);
-}
#define GLADE_TAG_SIZEGROUP_WIDGETS "widgets"
#define GLADE_TAG_SIZEGROUP_WIDGET "widget"
@@ -12270,17 +12249,6 @@ glade_gtk_treeview_remove_child (GladeWidgetAdaptor *adaptor,
gtk_tree_view_remove_column (view, column);
}
-gboolean
-glade_gtk_treeview_depends (GladeWidgetAdaptor *adaptor,
- GladeWidget *widget,
- GladeWidget *another)
-{
- if (GTK_IS_TREE_MODEL (another->object))
- return TRUE;
-
- return GWA_GET_CLASS (GTK_TYPE_CONTAINER)->depends (adaptor, widget, another);
-}
-
/*--------------------------- GtkAdjustment ---------------------------------*/
void
glade_gtk_adjustment_write_widget (GladeWidgetAdaptor *adaptor,
diff --git a/plugins/gtk+/gtk+.xml.in b/plugins/gtk+/gtk+.xml.in
index f5719f9a..56482f00 100644
--- a/plugins/gtk+/gtk+.xml.in
+++ b/plugins/gtk+/gtk+.xml.in
@@ -859,7 +859,6 @@ embedded in another object</_tooltip>
<create-editable-function>glade_gtk_entry_create_editable</create-editable-function>
<set-property-function>glade_gtk_entry_set_property</set-property-function>
<read-widget-function>glade_gtk_entry_read_widget</read-widget-function>
- <depends-function>glade_gtk_entry_depends</depends-function>
<signals>
<signal id="icon-press" since="2.16"/>
@@ -2017,7 +2016,6 @@ embedded in another object</_tooltip>
<!-- Objects -->
<glade-widget-class name="GtkSizeGroup" generic-name="sizegroup" _title="Size Group" libglade-unsupported="True" toplevel="True">
- <depends-function>glade_gtk_size_group_depends</depends-function>
<read-widget-function>glade_gtk_size_group_read_widget</read-widget-function>
<write-widget-function>glade_gtk_size_group_write_widget</write-widget-function>
<set-property-function>glade_gtk_size_group_set_property</set-property-function>
@@ -2203,7 +2201,6 @@ embedded in another object</_tooltip>
<add-child-function>glade_gtk_treeview_add_child</add-child-function>
<remove-child-function>glade_gtk_treeview_remove_child</remove-child-function>
<action-activate-function>glade_gtk_treeview_action_activate</action-activate-function>
- <depends-function>glade_gtk_treeview_depends</depends-function>
<actions>
<action id="launch_editor" _name="Edit&#8230;" stock="gtk-edit" important="True"/>