[glade/glade-3-8] Backport 2bc40ad87be072aac2759755df63707d43f8415c.



commit bde9aabb1654330e6f36efb0b7bbbe8f646c1629
Author: Juan Pablo Ugarte <juanpablougarte gmail com>
Date:   Fri Nov 15 22:45:17 2013 -0300

    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

 gladeui/Makefile.am            |    3 +
 gladeui/glade-project.c        |  365 ++++++++++++++++++++++++++++-----------
 gladeui/glade-property-class.c |   17 ++
 gladeui/glade-property-class.h |    4 +
 gladeui/glade-property.c       |   25 +++
 gladeui/glade-property.h       |    5 +
 gladeui/glade-tsort.c          |  177 +++++++++++++++++++
 gladeui/glade-tsort.h          |   51 ++++++
 gladeui/glade-widget-adaptor.c |   37 ++---
 gladeui/glade-widget-private.h |   36 ++++
 gladeui/glade-widget.c         |   33 ++++-
 gladeui/glade-widget.h         |    3 +-
 plugins/gtk+/glade-gtk.c       |   32 ----
 plugins/gtk+/gtk+.xml.in       |    3 -
 14 files changed, 627 insertions(+), 164 deletions(-)
---
diff --git a/gladeui/Makefile.am b/gladeui/Makefile.am
index 1e4e5b8..98ca9a6 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 850e7bf..e75d502 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 6a66716..9c9dc18 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 b2ec3ef..cdfcbc2 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 88009c3..0043b75 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 f8fd63d..66768ff 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 0000000..439da33
--- /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 0000000..3026e2d
--- /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 6777e18..0f9c2e4 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 0000000..12869f4
--- /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 d8045be..f7ee586 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 95d8601..348edc8 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 c784341..d797467 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 f5719f9..56482f0 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"/>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]