1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include "entitylist.h"
23
24 #include "iselection.h"
25
26 #include <gtk/gtktreemodel.h>
27 #include <gtk/gtktreeview.h>
28 #include <gtk/gtktreeselection.h>
29 #include <gtk/gtkcellrenderertext.h>
30
31 #include "string/string.h"
32 #include "scenelib.h"
33 #include "nameable.h"
34 #include "generic/callback.h"
35 #include "generic/object.h"
36
37 #include "gtkutil/widget.h"
38 #include "gtkutil/window.h"
39 #include "gtkutil/idledraw.h"
40 #include "gtkutil/accelerator.h"
41 #include "gtkutil/closure.h"
42
43 #include "treemodel.h"
44
45 void RedrawEntityList();
46 typedef FreeCaller<RedrawEntityList> RedrawEntityListCaller;
47
48 typedef struct _GtkTreeView GtkTreeView;
49
50 class EntityList
51 {
52 public:
53 enum EDirty
54 {
55 eDefault,
56 eSelection,
57 eInsertRemove,
58 };
59
60 EDirty m_dirty;
61
62 IdleDraw m_idleDraw;
63 WindowPositionTracker m_positionTracker;
64
65 GtkWindow* m_window;
66 GtkTreeView* m_tree_view;
67 GraphTreeModel* m_tree_model;
68 bool m_selection_disabled;
69
EntityList()70 EntityList() :
71 m_dirty(EntityList::eDefault),
72 m_idleDraw(RedrawEntityListCaller()),
73 m_window(0),
74 m_selection_disabled(false)
75 {
76 }
77
visible() const78 bool visible() const
79 {
80 return GTK_WIDGET_VISIBLE(GTK_WIDGET(m_window));
81 }
82 };
83
84 namespace
85 {
86 EntityList* g_EntityList;
87
getEntityList()88 inline EntityList& getEntityList()
89 {
90 ASSERT_NOTNULL(g_EntityList);
91 return *g_EntityList;
92 }
93 }
94
95
Node_getNameable(scene::Node & node)96 inline Nameable* Node_getNameable(scene::Node& node)
97 {
98 return NodeTypeCast<Nameable>::cast(node);
99 }
100
node_get_name(scene::Node & node)101 const char* node_get_name(scene::Node& node)
102 {
103 Nameable* nameable = Node_getNameable(node);
104 return (nameable != 0)
105 ? nameable->name()
106 : "node";
107 }
108
109 template<typename value_type>
gtk_tree_model_get_pointer(GtkTreeModel * model,GtkTreeIter * iter,gint column,value_type ** pointer)110 inline void gtk_tree_model_get_pointer(GtkTreeModel* model, GtkTreeIter* iter, gint column, value_type** pointer)
111 {
112 GValue value = GValue_default();
113 gtk_tree_model_get_value(model, iter, column, &value);
114 *pointer = (value_type*)g_value_get_pointer(&value);
115 }
116
117
118
entitylist_treeviewcolumn_celldatafunc(GtkTreeViewColumn * column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)119 void entitylist_treeviewcolumn_celldatafunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, gpointer data)
120 {
121 scene::Node* node;
122 gtk_tree_model_get_pointer(model, iter, 0, &node);
123 scene::Instance* instance;
124 gtk_tree_model_get_pointer(model, iter, 1, &instance);
125 if(node != 0)
126 {
127 gtk_cell_renderer_set_fixed_size(renderer, -1, -1);
128 char* name = const_cast<char*>(node_get_name(*node));
129 g_object_set(G_OBJECT(renderer), "text", name, "visible", TRUE, 0);
130
131 //globalOutputStream() << "rendering cell " << makeQuoted(name) << "\n";
132 GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(getEntityList().m_tree_view));
133 if(instance->childSelected())
134 {
135 g_object_set(G_OBJECT(renderer), "cell-background-gdk", &style->base[GTK_STATE_ACTIVE], 0);
136 }
137 else
138 {
139 g_object_set(G_OBJECT(renderer), "cell-background-gdk", &style->base[GTK_STATE_NORMAL], 0);
140 }
141 }
142 else
143 {
144 gtk_cell_renderer_set_fixed_size(renderer, -1, 0);
145 g_object_set(G_OBJECT(renderer), "text", "", "visible", FALSE, 0);
146 }
147 }
148
entitylist_tree_select(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,gpointer data)149 static gboolean entitylist_tree_select(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer data)
150 {
151 GtkTreeIter iter;
152 gtk_tree_model_get_iter(model, &iter, path);
153 scene::Node* node;
154 gtk_tree_model_get_pointer(model, &iter, 0, &node);
155 scene::Instance* instance;
156 gtk_tree_model_get_pointer(model, &iter, 1, &instance);
157 Selectable* selectable = Instance_getSelectable(*instance);
158
159 if(node == 0)
160 {
161 if(path_currently_selected != FALSE)
162 {
163 getEntityList().m_selection_disabled = true;
164 GlobalSelectionSystem().setSelectedAll(false);
165 getEntityList().m_selection_disabled = false;
166 }
167 }
168 else if(selectable != 0)
169 {
170 getEntityList().m_selection_disabled = true;
171 selectable->setSelected(path_currently_selected == FALSE);
172 getEntityList().m_selection_disabled = false;
173 return TRUE;
174 }
175
176 return FALSE;
177 }
178
entitylist_tree_select_null(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,gpointer data)179 static gboolean entitylist_tree_select_null(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer data)
180 {
181 return TRUE;
182 }
183
EntityList_ConnectSignals(GtkTreeView * view)184 void EntityList_ConnectSignals(GtkTreeView* view)
185 {
186 GtkTreeSelection* select = gtk_tree_view_get_selection(view);
187 gtk_tree_selection_set_select_function(select, entitylist_tree_select, NULL, 0);
188 }
189
EntityList_DisconnectSignals(GtkTreeView * view)190 void EntityList_DisconnectSignals(GtkTreeView* view)
191 {
192 GtkTreeSelection* select = gtk_tree_view_get_selection(view);
193 gtk_tree_selection_set_select_function(select, entitylist_tree_select_null, 0, 0);
194 }
195
196
197
treemodel_update_selection(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)198 gboolean treemodel_update_selection(GtkTreeModel* model, GtkTreePath* path, GtkTreeIter* iter, gpointer data)
199 {
200 GtkTreeView* view = reinterpret_cast<GtkTreeView*>(data);
201
202 scene::Instance* instance;
203 gtk_tree_model_get_pointer(model, iter, 1, &instance);
204 Selectable* selectable = Instance_getSelectable(*instance);
205
206 if(selectable != 0)
207 {
208 GtkTreeSelection* selection = gtk_tree_view_get_selection(view);
209 if(selectable->isSelected())
210 {
211 gtk_tree_selection_select_path(selection, path);
212 }
213 else
214 {
215 gtk_tree_selection_unselect_path(selection, path);
216 }
217 }
218
219 return FALSE;
220 }
221
EntityList_UpdateSelection(GtkTreeModel * model,GtkTreeView * view)222 void EntityList_UpdateSelection(GtkTreeModel* model, GtkTreeView* view)
223 {
224 EntityList_DisconnectSignals(view);
225 gtk_tree_model_foreach(model, treemodel_update_selection, view);
226 EntityList_ConnectSignals(view);
227 }
228
229
RedrawEntityList()230 void RedrawEntityList()
231 {
232 switch(getEntityList().m_dirty)
233 {
234 case EntityList::eInsertRemove:
235 case EntityList::eSelection:
236 EntityList_UpdateSelection(GTK_TREE_MODEL(getEntityList().m_tree_model), getEntityList().m_tree_view);
237 default:
238 break;
239 }
240 getEntityList().m_dirty = EntityList::eDefault;
241 }
242
entitylist_queue_draw()243 void entitylist_queue_draw()
244 {
245 getEntityList().m_idleDraw.queueDraw();
246 }
247
EntityList_SelectionUpdate()248 void EntityList_SelectionUpdate()
249 {
250 if(getEntityList().m_selection_disabled)
251 return;
252
253 if(getEntityList().m_dirty < EntityList::eSelection)
254 getEntityList().m_dirty = EntityList::eSelection;
255 entitylist_queue_draw();
256 }
257
EntityList_SelectionChanged(const Selectable & selectable)258 void EntityList_SelectionChanged(const Selectable& selectable)
259 {
260 EntityList_SelectionUpdate();
261 }
262
entitylist_treeview_rowcollapsed(GtkTreeView * view,GtkTreeIter * iter,GtkTreePath * path,gpointer user_data)263 void entitylist_treeview_rowcollapsed(GtkTreeView* view, GtkTreeIter* iter, GtkTreePath* path, gpointer user_data)
264 {
265 }
266
entitylist_treeview_row_expanded(GtkTreeView * view,GtkTreeIter * iter,GtkTreePath * path,gpointer user_data)267 void entitylist_treeview_row_expanded(GtkTreeView* view, GtkTreeIter* iter, GtkTreePath* path, gpointer user_data)
268 {
269 EntityList_SelectionUpdate();
270 }
271
272
EntityList_SetShown(bool shown)273 void EntityList_SetShown(bool shown)
274 {
275 widget_set_visible(GTK_WIDGET(getEntityList().m_window), shown);
276 }
277
EntityList_toggleShown()278 void EntityList_toggleShown()
279 {
280 EntityList_SetShown(!getEntityList().visible());
281 }
282
graph_tree_model_compare_name(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer user_data)283 gint graph_tree_model_compare_name(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
284 {
285 scene::Node* first;
286 gtk_tree_model_get(model, a, 0, (gpointer*)&first, -1);
287 scene::Node* second;
288 gtk_tree_model_get(model, b, 0, (gpointer*)&second, -1);
289 int result = 0;
290 if(first != 0 && second != 0)
291 {
292 result = string_compare(node_get_name(*first), node_get_name(*second));
293 }
294 if(result == 0)
295 {
296 return (first < second) ? -1 : (second < first) ? 1 : 0;
297 }
298 return result;
299 }
300
301 extern GraphTreeModel* scene_graph_get_tree_model();
AttachEntityTreeModel()302 void AttachEntityTreeModel()
303 {
304 getEntityList().m_tree_model = scene_graph_get_tree_model();
305
306 gtk_tree_view_set_model(getEntityList().m_tree_view, GTK_TREE_MODEL(getEntityList().m_tree_model));
307 }
308
DetachEntityTreeModel()309 void DetachEntityTreeModel()
310 {
311 getEntityList().m_tree_model = 0;
312
313 gtk_tree_view_set_model(getEntityList().m_tree_view, 0);
314 }
315
EntityList_constructWindow(GtkWindow * main_window)316 void EntityList_constructWindow(GtkWindow* main_window)
317 {
318 ASSERT_MESSAGE(getEntityList().m_window == 0, "error");
319
320 GtkWindow* window = create_persistent_floating_window("Entity List", main_window);
321
322 gtk_window_add_accel_group(window, global_accel);
323
324 getEntityList().m_positionTracker.connect(window);
325
326
327 getEntityList().m_window = window;
328
329 {
330 GtkScrolledWindow* scr = create_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
331 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(scr));
332
333 {
334 GtkWidget* view = gtk_tree_view_new();
335 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
336
337 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
338 GtkTreeViewColumn* column = gtk_tree_view_column_new();
339 gtk_tree_view_column_pack_start(column, renderer, TRUE);
340 gtk_tree_view_column_set_cell_data_func(column, renderer, entitylist_treeviewcolumn_celldatafunc, 0, 0);
341
342 GtkTreeSelection* select = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
343 gtk_tree_selection_set_mode(select, GTK_SELECTION_MULTIPLE);
344
345 g_signal_connect(G_OBJECT(view), "row_expanded", G_CALLBACK(entitylist_treeview_row_expanded), 0);
346 g_signal_connect(G_OBJECT(view), "row_collapsed", G_CALLBACK(entitylist_treeview_rowcollapsed), 0);
347
348 gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
349
350 gtk_widget_show(view);
351 gtk_container_add (GTK_CONTAINER(scr), view);
352 getEntityList().m_tree_view = GTK_TREE_VIEW(view);
353 }
354 }
355
356 EntityList_ConnectSignals(getEntityList().m_tree_view);
357 AttachEntityTreeModel();
358 }
359
EntityList_destroyWindow()360 void EntityList_destroyWindow()
361 {
362 DetachEntityTreeModel();
363 EntityList_DisconnectSignals(getEntityList().m_tree_view);
364 destroy_floating_window(getEntityList().m_window);
365 }
366
367 #include "preferencesystem.h"
368
369 #include "iselection.h"
370
371 namespace
372 {
373 scene::Node* nullNode = 0;
374 }
375
376 class NullSelectedInstance : public scene::Instance, public Selectable
377 {
378 class TypeCasts
379 {
380 InstanceTypeCastTable m_casts;
381 public:
TypeCasts()382 TypeCasts()
383 {
384 InstanceStaticCast<NullSelectedInstance, Selectable>::install(m_casts);
385 }
get()386 InstanceTypeCastTable& get()
387 {
388 return m_casts;
389 }
390 };
391
392 public:
393 typedef LazyStatic<TypeCasts> StaticTypeCasts;
394
NullSelectedInstance()395 NullSelectedInstance() : Instance(scene::Path(makeReference(*nullNode)), 0, this, StaticTypeCasts::instance().get())
396 {
397 }
398
setSelected(bool select)399 void setSelected(bool select)
400 {
401 ERROR_MESSAGE("error");
402 }
isSelected() const403 bool isSelected() const
404 {
405 return true;
406 }
407 };
408
409 typedef LazyStatic<NullSelectedInstance> StaticNullSelectedInstance;
410
411
EntityList_Construct()412 void EntityList_Construct()
413 {
414 graph_tree_model_insert(scene_graph_get_tree_model(), StaticNullSelectedInstance::instance());
415
416 g_EntityList = new EntityList;
417
418 getEntityList().m_positionTracker.setPosition(c_default_window_pos);
419
420 GlobalPreferenceSystem().registerPreference("EntityInfoDlg", WindowPositionTrackerImportStringCaller(getEntityList().m_positionTracker), WindowPositionTrackerExportStringCaller(getEntityList().m_positionTracker));
421
422 GlobalSelectionSystem().addSelectionChangeCallback(FreeCaller1<const Selectable&, EntityList_SelectionChanged>());
423 }
EntityList_Destroy()424 void EntityList_Destroy()
425 {
426 delete g_EntityList;
427
428 graph_tree_model_erase(scene_graph_get_tree_model(), StaticNullSelectedInstance::instance());
429 }
430