Index: cpp/frams/param/paramtree.cpp
===================================================================
--- cpp/frams/param/paramtree.cpp	(revision 723)
+++ cpp/frams/param/paramtree.cpp	(revision 723)
@@ -0,0 +1,73 @@
+#include "paramtree.h"
+
+ParamTree::Node *ParamTree::addNode(ParamTree::Node* parent, const SString& name, int group)
+{
+	std::shared_ptr<Node> new_it(new Node(this, parent, name, group));
+	Node *last = parent->first_child.get();
+	if (last)
+	{
+		Node *next;
+		while (true)
+		{
+			next = last->next_sibling.get();
+			if (next == NULL) break;
+			last = next;
+		}
+		last->next_sibling = new_it;
+	}
+	else
+		parent->first_child = new_it;
+	return new_it.get();
+}
+
+ParamTree::Node *ParamTree::findNode(ParamTree::Node *parent, const SString& name)
+{
+	for (ParamTree::Node *it = parent->first_child.get(); it != NULL; it = it->next_sibling.get())
+		if (it->name == name) return it;
+	return NULL;
+}
+
+static int countChars(const char* str, char ch)
+{
+	int count = 0;
+	for (; *str; str++)
+		if (*str == ch) count++;
+	return count;
+}
+
+ParamTree::ParamTree(ParamInterface &_pi)
+	:pi(_pi)
+{
+	int g;
+	int level = 0, hasmorelevels = 1;
+	root.tree = this;
+	root.name = "[Tree root]";
+	for (; hasmorelevels; level++)
+	{
+		hasmorelevels = 0;
+		for (g = 0; g<pi.getGroupCount(); g++)
+		{
+			const char* grname = pi.grname(g);
+			int colons = countChars(grname, ':');
+			if (colons>level) hasmorelevels = 1;
+			if (colons != level) continue;
+
+			SString name = grname; // "abc:def:ghi"
+			Node *parentnode = &root;
+			int i = 0;
+			// search for parentnode for level 'level'
+			for (int haslevel = 0; haslevel < level; haslevel++)
+			{
+				i = name.indexOf(':', 0); // can't fail!
+				// "na:me" -> prefix="na",name="me"
+				SString prefix = name.substr(0, i);
+				// search for parent node in listview ('prefix')
+				Node *it = findNode(parentnode, prefix);
+				name = name.substr(i + 1);
+				if (it) parentnode = it;
+				else parentnode = addNode(parentnode, prefix, -1);
+			}
+			addNode(parentnode, name, g);
+		}
+	}
+}
Index: cpp/frams/param/paramtree.h
===================================================================
--- cpp/frams/param/paramtree.h	(revision 723)
+++ cpp/frams/param/paramtree.h	(revision 723)
@@ -0,0 +1,57 @@
+#ifndef _PARAMTREE_H_
+#define _PARAMTREE_H_
+
+#include <frams/param/param.h>
+#include <vector>
+#include <memory>
+
+// building a tree of Param groups, based on the convention used in the Framsticks GUI application:
+// group names containing colon characters ":" are hierarchical paths (like filesystem)
+// groups referencing common subpaths connect to their common parent nodes
+// group ordering is preserved
+// duplicate group names are not allowed
+//
+// input: ParamInterface object, only using getGroupCount() and grname()
+// output: tree structure in ParamTree.root, traverse using first_child/next_sibling
+//
+// example input: "a" "b" "c" "a:b:c" (connects to existing "a") "b:c" (connects to existing "b") "b:d" (connects to existing "b")
+// example output:
+//
+//   +-+ a         (node name="a", group name="a")
+//   | '-+ b       (dummy node, there is no "a:b" group)
+//   |   '-- c     (node name="c", group name="a:b:c")
+//   +-+ b
+//   | +-- c
+//   | '-- d
+//   +-- c
+//
+// sample code: see frams/_demos/paramtree_stdin_test.cpp
+
+class ParamTree
+{
+public:
+	class Node;
+	typedef std::shared_ptr<Node> NodePtr;
+	class Node
+	{
+	public:
+		ParamTree *tree;
+		Node *parent;
+		SString name; //path component name
+		int group_index; //original group index or -1 for dummy nodes
+		NodePtr first_child;
+		NodePtr next_sibling;
+
+		Node(ParamTree *_tree = NULL, Node *_parent = NULL, const SString &_name = "", int _group_index = -1)
+			:tree(_tree), parent(_parent), name(_name), group_index(_group_index) {}
+	};
+	Node root;
+	ParamInterface &pi;
+
+	Node *addNode(Node* parent, const SString &name, int group);
+	Node *findNode(Node *parent, const SString& name);
+
+	ParamTree(ParamInterface &_pi);
+};
+
+#endif
