1 #include "GraphTreeModel.h"
2 
3 #include <gtk/gtk.h>
4 #include "scenelib.h"
5 #include <iostream>
6 #include "nameable.h"
7 
8 #include "GraphTreeModelPopulator.h"
9 #include "GraphTreeModelSelectionUpdater.h"
10 
11 namespace ui {
12 
13 namespace {
14 
Node_getNameable(scene::Node & node)15 inline Nameable* Node_getNameable (scene::Node& node)
16 {
17 	return dynamic_cast<Nameable*>(&node);
18 }
19 
getNodeName(scene::Node & node)20 inline std::string getNodeName (scene::Node& node)
21 {
22 	Nameable* nameable = Node_getNameable(node);
23 	return (nameable != NULL) ? nameable->name() : "node";
24 }
25 
26 }
27 
sortName(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer userdata)28 gint GraphTreeModel::sortName (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
29 {
30 	gint ret = 0;
31 	gchar* name1, *name2;
32 
33 	gtk_tree_model_get(model, a, COL_NAME, &name1, -1);
34 	gtk_tree_model_get(model, b, COL_NAME, &name2, -1);
35 
36 	if (name1 == NULL || name2 == NULL) {
37 		if (name1 == NULL && name2 == NULL)
38 			return ret;
39 
40 		ret = (name1 == NULL) ? -1 : 1;
41 	} else {
42 		ret = g_utf8_collate(name1, name2);
43 	}
44 
45 	g_free(name1);
46 	g_free(name2);
47 
48 	return ret;
49 }
50 
GraphTreeModel()51 GraphTreeModel::GraphTreeModel () :
52 		_model(gtk_tree_store_new(NUM_COLS, G_TYPE_POINTER, G_TYPE_STRING))
53 {
54 	GtkTreeSortable *sortable = GTK_TREE_SORTABLE(_model);
55 	gtk_tree_sortable_set_sort_func(sortable, COL_NAME, sortName, NULL, NULL);
56 	gtk_tree_sortable_set_sort_column_id(sortable, COL_NAME, GTK_SORT_ASCENDING);
57 	// Subscribe to the scenegraph to get notified about insertions/deletions
58 	GlobalSceneGraph().addSceneObserver(this);
59 }
60 
~GraphTreeModel()61 GraphTreeModel::~GraphTreeModel ()
62 {
63 	GlobalSceneGraph().removeSceneObserver(this);
64 	// Remove everything before shutting down
65 	clear();
66 }
67 
insert(const scene::Instance & instance)68 const GraphTreeNode* GraphTreeModel::insert (const scene::Instance& instance)
69 {
70 	// Create a new GraphTreeNode
71 	GraphTreeNode* node = new GraphTreeNode(instance);
72 
73 	// Insert this iterator below a possible parent iterator
74 	gtk_tree_store_insert(_model, node->getIter(), findParentIter(instance), 0);
75 
76 	// Fill in the values
77 	gtk_tree_store_set(_model, node->getIter(), COL_INSTANCE_POINTER, &instance, COL_NAME,
78 			getNodeCaption(instance.path().top()).c_str(), -1);
79 
80 	// Insert this iterator into the node map to facilitate lookups
81 	std::pair<NodeMap::iterator, bool> result = _nodemap.insert(NodeMap::value_type(instance.path().top(), node));
82 
83 	// Return the GraphTreeNode reference
84 	return result.first->second;
85 }
86 
erase(const scene::Instance & instance)87 void GraphTreeModel::erase (const scene::Instance& instance)
88 {
89 	NodeMap::iterator found = _nodemap.find(instance.path().top());
90 
91 	if (found != _nodemap.end()) {
92 		// Remove this from the GtkTreeStore...
93 		gtk_tree_store_remove(_model, found->second->getIter());
94 
95 		delete found->second;
96 
97 		// ...and from our lookup table
98 		_nodemap.erase(found);
99 	}
100 }
101 
find(const scene::Instance & instance)102 GraphTreeNode* GraphTreeModel::find (const scene::Instance& instance)
103 {
104 	NodeMap::const_iterator found = _nodemap.find(instance.path().top());
105 	return (found != _nodemap.end()) ? found->second : NULL;
106 }
107 
clear()108 void GraphTreeModel::clear ()
109 {
110 	// Remove everything, GTK plus nodemap
111 	gtk_tree_store_clear(_model);
112 	for (NodeMap::iterator i = _nodemap.begin(); i != _nodemap.end(); ++i) {
113 		delete i->second;
114 	}
115 	_nodemap.clear();
116 }
117 
refresh()118 void GraphTreeModel::refresh ()
119 {
120 	// Instantiate a scenegraph walker and visit every node in the graph
121 	// The walker also clears the graph in its constructor
122 	GraphTreeModelPopulator populator(*this);
123 	GlobalSceneGraph().traverse(populator);
124 }
125 
updateSelectionStatus(GtkTreeSelection * selection)126 void GraphTreeModel::updateSelectionStatus (GtkTreeSelection* selection)
127 {
128 	GraphTreeModelSelectionUpdater updater(*this, selection);
129 	GlobalSceneGraph().traverse(updater);
130 }
131 
updateSelectionStatus(GtkTreeSelection * selection,scene::Instance & instance)132 void GraphTreeModel::updateSelectionStatus (GtkTreeSelection* selection, scene::Instance& instance)
133 {
134 	NodeMap::const_iterator found = _nodemap.find(instance.path().top());
135 
136 	if (found != _nodemap.end()) {
137 		if (Instance_isSelected(instance)) {
138 			gtk_tree_selection_select_iter(selection, found->second->getIter());
139 		} else {
140 			gtk_tree_selection_unselect_iter(selection, found->second->getIter());
141 		}
142 	}
143 }
144 
findParentNode(const scene::Instance & instance)145 GraphTreeNode* GraphTreeModel::findParentNode (const scene::Instance& instance)
146 {
147 	const scene::Path& path = instance.path();
148 
149 	if (path.size() <= 1) {
150 		// No parent, return the NULL pointer
151 		return NULL;
152 	}
153 
154 	// Try to find the node
155 	NodeMap::const_iterator found = _nodemap.find(path.parent());
156 
157 	// Return NULL (empty shared_ptr) if not found
158 	return (found != _nodemap.end()) ? found->second : NULL;
159 }
160 
findParentIter(const scene::Instance & instance)161 GtkTreeIter* GraphTreeModel::findParentIter (const scene::Instance& instance)
162 {
163 	// Find the parent's GraphTreeNode
164 	GraphTreeNode* nodePtr = findParentNode(instance);
165 	// Return a NULL nodeptr if not found
166 	return (nodePtr != NULL) ? nodePtr->getIter() : NULL;
167 }
168 
getNodeCaption(scene::Node & node)169 std::string GraphTreeModel::getNodeCaption (scene::Node& node)
170 {
171 	return getNodeName(node);
172 }
173 
operator GtkTreeModel*()174 GraphTreeModel::operator GtkTreeModel* ()
175 {
176 	return GTK_TREE_MODEL(_model);
177 }
178 
179 // Gets called when a new <instance> is inserted into the scenegraph
onSceneNodeInsert(const scene::Instance & instance)180 void GraphTreeModel::onSceneNodeInsert (const scene::Instance& instance)
181 {
182 	insert(instance); // wrap to the actual insert() method
183 }
184 
185 // Gets called when <instance> is removed from the scenegraph
onSceneNodeErase(const scene::Instance & instance)186 void GraphTreeModel::onSceneNodeErase (const scene::Instance& instance)
187 {
188 	erase(instance); // wrap to the actual erase() method
189 }
190 
191 } // namespace ui
192