a basic but useful Gtk+-based config-store
authorMathieu Lacage <mathieu.lacage@sophia.inria.fr>
Tue, 13 May 2008 17:02:52 -0700
changeset 3063 a1c532e3bf18
parent 3013 81949bbc6349
child 3064 2daf5961f130
a basic but useful Gtk+-based config-store
src/contrib/attribute-iterator.cc
src/contrib/attribute-iterator.h
src/contrib/config-store.cc
src/contrib/config-store.h
src/contrib/gtk-config-store.cc
src/contrib/gtk-config-store.h
src/contrib/wscript
src/wscript
wscript
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/contrib/attribute-iterator.cc	Tue May 13 17:02:52 2008 -0700
@@ -0,0 +1,197 @@
+#include "attribute-iterator.h"
+#include "ns3/config.h"
+#include "ns3/log.h"
+#include "ns3/pointer.h"
+#include "ns3/object-vector.h"
+#include "ns3/string.h"
+#include <fstream>
+
+
+NS_LOG_COMPONENT_DEFINE ("AttributeIterator");
+
+namespace ns3 {
+
+
+AttributeIterator::AttributeIterator ()
+{}
+
+AttributeIterator::~AttributeIterator ()
+{}
+
+void 
+AttributeIterator::Iterate (void)
+{
+  for (uint32_t i = 0; i < Config::GetRootNamespaceObjectN (); ++i)
+    {
+      Ptr<Object> object = Config::GetRootNamespaceObject (i);
+      DoIterate (object);
+    }
+  NS_ASSERT (m_currentPath.empty ());
+  NS_ASSERT (m_examined.empty ());
+}
+
+bool
+AttributeIterator::IsExamined (Ptr<const Object> object)
+{
+  for (uint32_t i = 0; i < m_examined.size (); ++i)
+    {
+      if (object == m_examined[i])
+	{
+	  return true;
+	}
+    }
+  return false;
+}
+
+
+std::string
+AttributeIterator::GetCurrentPath (std::string attr) const
+{
+  std::ostringstream oss;
+  for (uint32_t i = 0; i < m_currentPath.size (); ++i)
+    {
+      oss << "/" << m_currentPath[i];
+    }
+  if (attr != "")
+    {
+      oss << "/" << attr;
+    }
+  return oss.str ();
+}
+
+void 
+AttributeIterator::Push (std::string name)
+{
+  m_currentPath.push_back (name);
+  DoPush (name, GetCurrentPath (""));
+}
+void 
+AttributeIterator::Pop (void)
+{
+  DoPop ();
+  m_currentPath.pop_back ();
+}
+void
+AttributeIterator::Visit (Ptr<Object> object, std::string name)
+{
+  DoVisit (object, name, GetCurrentPath (name));
+}
+
+
+void
+AttributeIterator::DoIterate (Ptr<Object> object)
+{
+  if (IsExamined (object))
+    {
+      return;
+    }
+  TypeId tid = object->GetInstanceTypeId ();
+  Push ("$" + tid.GetName ());
+  NS_LOG_DEBUG ("store " << tid.GetName ());
+  for (uint32_t i = 0; i < tid.GetAttributeN (); ++i)
+    {
+      Ptr<const AttributeChecker> checker = tid.GetAttributeChecker (i);
+      const PointerChecker *ptrChecker = dynamic_cast<const PointerChecker *> (PeekPointer (checker));
+      if (ptrChecker != 0)
+	{
+	  NS_LOG_DEBUG ("pointer attribute " << tid.GetAttributeName (i));
+	  PointerValue ptr;
+	  object->GetAttribute (tid.GetAttributeName (i), ptr);
+	  Ptr<Object> tmp = ptr.Get<Object> ();
+	  if (tmp != 0)
+	    {
+	      Push (tid.GetAttributeName (i));
+	      m_examined.push_back (object);
+	      DoIterate (tmp);
+	      m_examined.pop_back ();
+	      Pop ();
+	    }
+	  continue;
+	}
+      // attempt to cast to an object vector.
+      const ObjectVectorChecker *vectorChecker = dynamic_cast<const ObjectVectorChecker *> (PeekPointer (checker));
+      if (vectorChecker != 0)
+	{
+	  NS_LOG_DEBUG ("vector attribute " << tid.GetAttributeName (i));
+	  ObjectVectorValue vector;
+	  object->GetAttribute (tid.GetAttributeName (i), vector);
+	  Push (tid.GetAttributeName (i));
+	  for (uint32_t j = 0; j < vector.GetN (); ++j)
+	    {
+	      NS_LOG_DEBUG ("vector attribute item " << j);
+	      Ptr<Object> tmp = vector.Get (j);
+	      std::ostringstream oss;
+	      oss << j;
+	      Push (oss.str ());
+	      m_examined.push_back (object);
+	      DoIterate (tmp);
+	      m_examined.pop_back ();
+	      Pop ();
+	    }
+	  Pop ();
+	  continue;
+	}
+      uint32_t flags = tid.GetAttributeFlags (i);
+      Ptr<const AttributeAccessor> accessor = tid.GetAttributeAccessor (i);
+      if ((flags & TypeId::ATTR_GET) && accessor->HasGetter () &&
+	  (flags & TypeId::ATTR_SET) && accessor->HasSetter ())
+	{
+	  Visit (object, tid.GetAttributeName (i));
+	}
+      else
+	{
+	  NS_LOG_DEBUG ("could not store " << tid.GetAttributeName (i));
+	}
+    }
+  Object::AggregateIterator iter = object->GetAggregateIterator ();
+  bool recursiveAggregate = false;
+  while (iter.HasNext ())
+    {
+      Ptr<const Object> tmp = iter.Next ();
+      if (IsExamined (tmp))
+	{
+	  recursiveAggregate = true;
+	}
+    }
+  if (!recursiveAggregate)
+    {
+      iter = object->GetAggregateIterator ();
+      while (iter.HasNext ())
+	{
+	  Ptr<Object> tmp = const_cast<Object *> (PeekPointer (iter.Next ()));
+	  m_examined.push_back (object);
+	  DoIterate (tmp);
+	  m_examined.pop_back ();
+	}
+    }
+  Pop ();
+}
+
+
+
+TextFileAttributeIterator::TextFileAttributeIterator (std::ostream &os)
+  : m_os (os)
+{}
+void 
+TextFileAttributeIterator::DoVisit (Ptr<Object> object, std::string name, std::string path)
+{
+  StringValue str;
+  object->GetAttribute (name, str);
+  m_os << path << " " << str.Get () << std::endl;
+}
+void 
+TextFileAttributeIterator::DoPush (std::string name, std::string path)
+{}
+void 
+TextFileAttributeIterator::DoPop (void)
+{}
+
+void 
+TextFileAttributeIterator::Save (void)
+{
+  Iterate ();
+}
+
+
+
+} // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/contrib/attribute-iterator.h	Tue May 13 17:02:52 2008 -0700
@@ -0,0 +1,49 @@
+#ifndef ATTRIBUTE_ITERATOR_H
+#define ATTRIBUTE_ITERATOR_H
+
+#include "ns3/ptr.h"
+#include "ns3/object.h"
+#include <vector>
+
+namespace ns3 {
+
+class AttributeIterator
+{
+public:
+  AttributeIterator ();
+  virtual ~AttributeIterator ();
+
+  void Iterate (void);
+private:
+  virtual void DoVisit (Ptr<Object> object, std::string name, std::string path) = 0;
+  virtual void DoPush (std::string name, std::string path) = 0;
+  virtual void DoPop (void) = 0;
+
+  void DoIterate (Ptr<Object> object);
+  bool IsExamined (Ptr<const Object> object);
+  std::string GetCurrentPath (std::string attr) const;
+  void Push (std::string name);
+  void Pop (void);
+  void Visit (Ptr<Object> object, std::string name);
+
+
+  std::vector<Ptr<Object> > m_examined;
+  std::vector<std::string> m_currentPath;
+};
+
+class TextFileAttributeIterator : public AttributeIterator
+{
+public:
+  TextFileAttributeIterator (std::ostream &os);
+  void Save (void);
+private:
+  virtual void DoVisit (Ptr<Object> object, std::string name, std::string path);
+  virtual void DoPush (std::string name, std::string path);
+  virtual void DoPop (void);
+  std::ostream &m_os;
+};
+
+
+} // namespace ns3
+
+#endif /* ATTRIBUTE_ITERATOR_H */
--- a/src/contrib/config-store.cc	Sun May 11 21:43:55 2008 -0700
+++ b/src/contrib/config-store.cc	Tue May 13 17:02:52 2008 -0700
@@ -1,9 +1,9 @@
 #include "config-store.h"
+#include "attribute-iterator.h"
 #include "ns3/string.h"
+#include "ns3/log.h"
+#include "ns3/attribute-list.h"
 #include "ns3/config.h"
-#include "ns3/object-vector.h"
-#include "ns3/pointer.h"
-#include "ns3/log.h"
 #include <string>
 #include <fstream>
 #include <iostream>
@@ -61,134 +61,15 @@
 void 
 ConfigStore::StoreTo (std::string filename)
 {
+
   std::ofstream os;
   os.open (filename.c_str (), std::ios::out);
-  for (uint32_t i = 0; i < Config::GetRootNamespaceObjectN (); ++i)
-    {
-      Ptr<Object> object = Config::GetRootNamespaceObject (i);
-      Store (os, object);
-    }
+  TextFileAttributeIterator iter = TextFileAttributeIterator (os);
+  iter.Save ();
   os.close ();
-  NS_ASSERT (m_currentPath.empty ());
-  NS_ASSERT (m_examined.empty ());
   exit (0);
 }
 
-bool
-ConfigStore::IsExamined (Ptr<const Object> object)
-{
-  for (uint32_t i = 0; i < m_examined.size (); ++i)
-    {
-      if (object == m_examined[i])
-	{
-	  return true;
-	}
-    }
-  return false;
-}
-
-std::string
-ConfigStore::GetCurrentPath (std::string attr) const
-{
-  std::ostringstream oss;
-  for (uint32_t i = 0; i < m_currentPath.size (); ++i)
-    {
-      oss << "/" << m_currentPath[i];
-    }
-  oss << "/" << attr;
-  return oss.str ();
-}
-
-void
-ConfigStore::Store (std::ostream &os, Ptr<const Object> object)
-{
-  if (IsExamined (object))
-    {
-      return;
-    }
-  TypeId tid = object->GetInstanceTypeId ();
-  m_currentPath.push_back ("$" + tid.GetName ());
-  NS_LOG_DEBUG ("store " << tid.GetName ());
-  for (uint32_t i = 0; i < tid.GetAttributeN (); ++i)
-    {
-      Ptr<const AttributeChecker> checker = tid.GetAttributeChecker (i);
-      const PointerChecker *ptrChecker = dynamic_cast<const PointerChecker *> (PeekPointer (checker));
-      if (ptrChecker != 0)
-	{
-	  NS_LOG_DEBUG ("pointer attribute " << tid.GetAttributeName (i));
-	  PointerValue ptr;
-	  object->GetAttribute (tid.GetAttributeName (i), ptr);
-	  Ptr<const Object> tmp = ptr.Get<Object> ();
-	  if (tmp != 0)
-	    {
-	      m_currentPath.push_back (tid.GetAttributeName (i));
-	      m_examined.push_back (object);
-	      Store (os, tmp);
-	      m_examined.pop_back ();
-	      m_currentPath.pop_back ();
-	    }
-	  continue;
-	}
-      // attempt to cast to an object vector.
-      const ObjectVectorChecker *vectorChecker = dynamic_cast<const ObjectVectorChecker *> (PeekPointer (checker));
-      if (vectorChecker != 0)
-	{
-	  NS_LOG_DEBUG ("vector attribute " << tid.GetAttributeName (i));
-	  ObjectVectorValue vector;
-	  object->GetAttribute (tid.GetAttributeName (i), vector);
-	  for (uint32_t j = 0; j < vector.GetN (); ++j)
-	    {
-	      NS_LOG_DEBUG ("vector attribute item " << j);
-	      Ptr<const Object> tmp = vector.Get (j);
-	      std::ostringstream oss;
-	      oss << tid.GetAttributeName (i) << "/" << j;
-	      m_currentPath.push_back (oss.str ());
-	      m_examined.push_back (object);
-	      Store (os, tmp);
-	      m_examined.pop_back ();
-	      m_currentPath.pop_back ();
-	    }
-	  continue;
-	}
-      uint32_t flags = tid.GetAttributeFlags (i);
-      Ptr<const AttributeAccessor> accessor = tid.GetAttributeAccessor (i);
-      if ((flags & TypeId::ATTR_GET) && accessor->HasGetter () &&
-	  (flags & TypeId::ATTR_SET) && accessor->HasSetter ())
-	{
-	  StringValue str;
-	  object->GetAttribute (tid.GetAttributeName (i), str);
-	  os << GetCurrentPath (tid.GetAttributeName (i)) << " " << str.Get () << std::endl;
-	}
-      else
-	{
-	  NS_LOG_DEBUG ("could not store " << tid.GetAttributeName (i));
-	}
-    }
-  Object::AggregateIterator iter = object->GetAggregateIterator ();
-  bool recursiveAggregate = false;
-  while (iter.HasNext ())
-    {
-      Ptr<const Object> tmp = iter.Next ();
-      if (IsExamined (tmp))
-	{
-	  recursiveAggregate = true;
-	}
-    }
-  if (!recursiveAggregate)
-    {
-      iter = object->GetAggregateIterator ();
-      while (iter.HasNext ())
-	{
-	  Ptr<const Object> tmp = iter.Next ();
-	  m_examined.push_back (object);
-	  Store (os, tmp);
-	  m_examined.pop_back ();
-	}
-    }
-  m_currentPath.pop_back ();
-}
-
-
 void 
 ConfigStore::Configure (void)
 {
--- a/src/contrib/config-store.h	Sun May 11 21:43:55 2008 -0700
+++ b/src/contrib/config-store.h	Tue May 13 17:02:52 2008 -0700
@@ -2,8 +2,6 @@
 #define CONFIG_STORE_H
 
 #include "ns3/object-base.h"
-#include "ns3/object.h"
-#include <vector>
 
 namespace ns3 {
 
@@ -29,14 +27,9 @@
 private:
   void LoadFrom (std::string filename);
   void StoreTo (std::string filename);
-  void Store (std::ostream &os, Ptr<const Object> object);
-  bool IsExamined (Ptr<const Object> object);
-  std::string GetCurrentPath (std::string attr) const;
 
   std::string m_loadFilename;
   std::string m_storeFilename;
-  std::vector<Ptr<const Object> > m_examined;
-  std::vector<std::string> m_currentPath;
 };
 
 }  // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/contrib/gtk-config-store.cc	Tue May 13 17:02:52 2008 -0700
@@ -0,0 +1,267 @@
+#include "gtk-config-store.h"
+#include "attribute-iterator.h"
+#include "ns3/config.h"
+#include "ns3/string.h"
+#include <gtk/gtk.h>
+#include <fstream>
+
+
+namespace ns3 {
+
+enum {
+  COL_NAME = 0,
+  COL_VALUE,
+  COL_LAST
+};
+
+struct AttributeNode
+{
+  std::string name;
+  std::string path;
+  Ptr<Object> object;
+};
+
+class ModelCreator : public AttributeIterator
+{
+public:
+  ModelCreator ();
+
+  void Build (GtkTreeStore *treestore);
+private:
+  virtual void DoVisit (Ptr<Object> object, std::string name, std::string path);
+  virtual void DoPush (std::string name, std::string path);
+  virtual void DoPop (void);
+
+  GtkTreeStore *m_treestore;
+  std::vector<GtkTreeIter *> m_iters;
+};
+
+ModelCreator::ModelCreator ()
+{}
+void 
+ModelCreator::Build (GtkTreeStore *treestore)
+{
+  m_treestore = treestore;
+  m_iters.push_back (NULL);
+  Iterate ();
+  NS_ASSERT (m_iters.size () == 1);
+}
+void 
+ModelCreator::DoVisit (Ptr<Object> object, std::string name, std::string path)
+{
+  StringValue str;
+  object->GetAttribute (name, str);
+  GtkTreeIter *parent = m_iters.back ();
+  GtkTreeIter current;
+  gtk_tree_store_append (m_treestore, &current, parent);
+  AttributeNode *node = new AttributeNode ();
+  node->name = name;
+  node->path = path + "/" + str.Get ();
+  node->object = object;
+  gtk_tree_store_set (m_treestore, &current,
+		      COL_NAME, g_strdup (name.c_str ()), 
+		      COL_VALUE, node,
+		      -1);
+}
+void 
+ModelCreator::DoPush (std::string name, std::string path)
+{
+  GtkTreeIter *parent = m_iters.back ();
+  GtkTreeIter *current = g_new (GtkTreeIter, 1);
+  gtk_tree_store_append (m_treestore, current, parent);
+  gtk_tree_store_set (m_treestore, current,
+		      COL_NAME, g_strdup (name.c_str ()),
+		      COL_VALUE, NULL, 
+                     -1);
+  m_iters.push_back (current);
+}
+void 
+ModelCreator::DoPop (void)
+{
+  GtkTreeIter *current = m_iters.back ();
+  g_free (current);
+  m_iters.pop_back ();
+}
+
+static void
+attribute_cell_data_function (GtkTreeViewColumn *col,
+			      GtkCellRenderer   *renderer,
+			      GtkTreeModel      *model,
+			      GtkTreeIter       *iter,
+			      gpointer           user_data)
+{
+  AttributeNode *node = 0;
+  gtk_tree_model_get (model, iter, COL_VALUE, &node, -1);
+  if (node != 0)
+    {
+      StringValue str;
+      node->object->GetAttribute (node->name, str);
+      g_object_set(renderer, "text", str.Get ().c_str (), NULL);
+    }
+  else
+    {
+      g_object_set(renderer, "text", "", NULL);
+    }
+}
+
+static void
+cell_edited_callback (GtkCellRendererText *cell,
+		      gchar               *path_string,
+		      gchar               *new_text,
+		      gpointer             user_data)
+{
+  GtkTreeModel *model = GTK_TREE_MODEL (user_data);
+  GtkTreeIter iter;
+  gtk_tree_model_get_iter_from_string (model, &iter, path_string);
+  AttributeNode *node;
+  gtk_tree_model_get (model, &iter, COL_VALUE, &node, -1);
+  node->object->SetAttribute (node->name, StringValue (new_text));
+}
+
+
+static GtkWidget *
+create_view_and_model (void)
+{
+  GtkTreeViewColumn   *col;
+  GtkCellRenderer     *renderer;
+  GtkWidget           *view;
+
+  GtkTreeStore *model = gtk_tree_store_new (COL_LAST, G_TYPE_STRING, G_TYPE_POINTER);
+  ModelCreator creator;
+  creator.Build (model);
+
+
+  view = gtk_tree_view_new();
+  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), GTK_TREE_VIEW_GRID_LINES_BOTH);
+
+  col = gtk_tree_view_column_new();
+  gtk_tree_view_column_set_title(col, "Object Attributes");
+  gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start(col, renderer, TRUE);
+  gtk_tree_view_column_add_attribute(col, renderer, "text", COL_NAME);
+
+  col = gtk_tree_view_column_new();
+  gtk_tree_view_column_set_title(col, "Attribute Value");
+  gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
+  renderer = gtk_cell_renderer_text_new();
+  g_object_set(renderer, "editable", TRUE, NULL);
+  g_signal_connect(renderer, "edited", (GCallback) cell_edited_callback, model);
+  gtk_tree_view_column_pack_start(col, renderer, TRUE);
+  gtk_tree_view_column_set_cell_data_func(col, renderer, attribute_cell_data_function, NULL, NULL);
+
+
+  gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL (model));
+
+  g_object_unref(model); /* destroy model automatically with view */
+
+
+  return view;
+}
+
+static void
+save_clicked (GtkButton *button,
+	      gpointer   user_data)
+{
+  GtkWidget *parent_window = GTK_WIDGET (user_data);
+  GtkWidget *dialog;
+
+  dialog = gtk_file_chooser_dialog_new ("Save File",
+					GTK_WINDOW (parent_window),
+					GTK_FILE_CHOOSER_ACTION_SAVE,
+					GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+					GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+					NULL);
+  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
+
+  gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "config.txt");
+
+
+  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
+    {
+      char *filename;
+
+      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+      std::ofstream os;
+      os.open (filename);
+      TextFileAttributeIterator file = TextFileAttributeIterator (os);
+      file.Save ();
+      os.close ();
+      g_free (filename);
+    }
+
+  gtk_widget_destroy (dialog);
+}
+
+static void
+load_clicked (GtkButton *button,
+	      gpointer   user_data)
+{
+  GtkWidget *parent_window = GTK_WIDGET (user_data);
+  GtkWidget *dialog;
+
+  dialog = gtk_file_chooser_dialog_new ("Open File",
+					GTK_WINDOW (parent_window),
+					GTK_FILE_CHOOSER_ACTION_OPEN,
+					GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+					GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+					NULL);
+
+  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
+    {
+      char *filename;
+
+      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+      std::ifstream is;
+      is.open (filename, std::ios::in);
+      std::string path, value;
+      while (is.good())
+	{
+	  is >> path >> value;
+	  Config::Set (path, StringValue (value));
+	}
+      g_free (filename);
+    }
+
+  gtk_widget_destroy (dialog);
+}
+
+GtkConfigStore::GtkConfigStore ()
+{}
+
+void 
+GtkConfigStore::Configure (void)
+{
+  GtkWidget *window;
+  GtkWidget *view;
+
+  gtk_init (0, 0);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  g_signal_connect (window, "delete_event", gtk_main_quit, NULL); /* dirty */
+
+  view = create_view_and_model ();
+
+  GtkWidget *vbox = gtk_vbox_new (FALSE, 5);
+  gtk_box_pack_start (GTK_BOX (vbox), view, TRUE, TRUE, 0);
+  gtk_box_pack_end (GTK_BOX (vbox), gtk_hseparator_new (), FALSE, FALSE, 0);
+  GtkWidget *hbox = gtk_hbox_new (FALSE, 5);
+  gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+  GtkWidget *save = gtk_button_new_with_label ("Save");
+  g_signal_connect (save, "clicked",  (GCallback) save_clicked, window);
+  gtk_box_pack_end (GTK_BOX (hbox), save, FALSE, FALSE, 0);
+  GtkWidget *load = gtk_button_new_with_label ("Load");
+  g_signal_connect (load, "clicked",  (GCallback) load_clicked, window);
+  gtk_box_pack_end (GTK_BOX (hbox), load, FALSE, FALSE, 0);
+  GtkWidget *exit = gtk_button_new_with_label ("Exit");
+  g_signal_connect (exit, "clicked",  (GCallback) gtk_main_quit, NULL);
+  gtk_box_pack_end (GTK_BOX (hbox), exit, FALSE, FALSE, 0);
+
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+
+  gtk_widget_show_all (window);
+
+  gtk_main ();
+}
+
+} // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/contrib/gtk-config-store.h	Tue May 13 17:02:52 2008 -0700
@@ -0,0 +1,17 @@
+#ifndef GTK_CONFIG_STORE_H
+#define GTK_CONFIG_STORE_H
+
+namespace ns3 {
+
+class GtkConfigStore
+{
+public:
+  GtkConfigStore ();
+
+  void Configure (void);
+};
+
+
+} // namespace ns3
+
+#endif /* GTK_CONFIG_STORE_H */
--- a/src/contrib/wscript	Sun May 11 21:43:55 2008 -0700
+++ b/src/contrib/wscript	Tue May 13 17:02:52 2008 -0700
@@ -1,11 +1,20 @@
 ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
+def configure(conf):
+    check = conf.create_pkgconfig_configurator()
+    check.name = 'gtk+-2.0'
+    check.uselib = 'GTK_CONFIG_STORE'
+    check.mandatory = False
+    conf.env['ENABLE_GTK_CONFIG_STORE'] = check.run()
+
+
 def build(bld):
     module = bld.create_ns3_module('contrib', ['simulator'])
     module.source = [
         'event-garbage-collector.cc',
         'gnuplot.cc',
         'delay-jitter-estimation.cc',
+        'attribute-iterator.cc',
         'config-store.cc',
         ]
 
@@ -17,3 +26,8 @@
         'delay-jitter-estimation.h',
         'config-store.h',
         ]
+
+    if bld.env()['ENABLE_GTK_CONFIG_STORE']:
+        headers.source.append ('gtk-config-store.h')
+        module.source.append ('gtk-config-store.cc')
+        module.uselib = 'GTK_CONFIG_STORE'
--- a/src/wscript	Sun May 11 21:43:55 2008 -0700
+++ b/src/wscript	Tue May 13 17:02:52 2008 -0700
@@ -47,6 +47,7 @@
 def configure(conf):
     conf.sub_config('core')
     conf.sub_config('simulator')
+    conf.sub_config('contrib')
 
     blddir = os.path.abspath(os.path.join(conf.m_blddir, conf.env.variant()))
     conf.env['NS3_MODULE_PATH'] = [blddir]
--- a/wscript	Sun May 11 21:43:55 2008 -0700
+++ b/wscript	Tue May 13 17:02:52 2008 -0700
@@ -305,6 +305,7 @@
         lib.add_objects = list(modules)
     else:
         lib.add_objects = list(env['NS3_MODULES'])
+        lib.uselib_local = list(env['NS3_MODULES'])
 
 
 def get_command_template():