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