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