2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
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.
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.
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
24 #include "debugging/debugging.h"
27 MapModules& ReferenceAPI_getMapModules();
28 #include "iselection.h"
32 #include "ireference.h"
33 #include "ifiletypes.h"
38 #include "ifilesystem.h"
39 #include "namespace.h"
40 #include "moduleobserver.h"
44 #include <gtk/gtkmain.h>
45 #include <gtk/gtkbox.h>
46 #include <gtk/gtkentry.h>
47 #include <gtk/gtklabel.h>
48 #include <gtk/gtktable.h>
49 #include <gtk/gtktreemodel.h>
50 #include <gtk/gtktreeview.h>
51 #include <gtk/gtkliststore.h>
52 #include <gtk/gtkcellrenderertext.h>
55 #include "transformlib.h"
56 #include "selectionlib.h"
57 #include "instancelib.h"
58 #include "traverselib.h"
60 #include "eclasslib.h"
62 #include "stream/textfilestream.h"
64 #include "uniquenames.h"
65 #include "modulesystem/singletonmodule.h"
66 #include "modulesystem/moduleregistry.h"
67 #include "stream/stringstream.h"
68 #include "signal/signal.h"
70 #include "gtkutil/filechooser.h"
74 #include "filetypes.h"
76 #include "entityinspector.h"
79 #include "camwindow.h"
81 #include "mainframe.h"
82 #include "preferences.h"
83 #include "referencecache.h"
87 #include "brushmodule.h"
99 //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
100 m_names.insert(name_read(c_str()));
107 //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
108 m_names.erase(name_read(c_str()));
112 NameObserver& operator=(const NameObserver& other);
114 NameObserver(UniqueNames& names) : m_names(names)
118 NameObserver(const NameObserver& other) : m_names(other.m_names), m_name(other.m_name)
128 return string_empty(c_str());
130 const char* c_str() const
132 return m_name.c_str();
134 void nameChanged(const char* name)
140 typedef MemberCaller1<NameObserver, const char*, &NameObserver::nameChanged> NameChangedCaller;
143 class BasicNamespace : public Namespace
145 typedef std::map<NameCallback, NameObserver> Names;
147 UniqueNames m_uniqueNames;
151 ASSERT_MESSAGE(m_names.empty(), "namespace: names still registered at shutdown");
153 void attach(const NameCallback& setName, const NameCallbackCallback& attachObserver)
155 std::pair<Names::iterator, bool> result = m_names.insert(Names::value_type(setName, m_uniqueNames));
156 ASSERT_MESSAGE(result.second, "cannot attach name");
157 attachObserver(NameObserver::NameChangedCaller((*result.first).second));
158 //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
160 void detach(const NameCallback& setName, const NameCallbackCallback& detachObserver)
162 Names::iterator i = m_names.find(setName);
163 ASSERT_MESSAGE(i != m_names.end(), "cannot detach name");
164 //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
165 detachObserver(NameObserver::NameChangedCaller((*i).second));
169 void makeUnique(const char* name, const NameCallback& setName) const
172 name_write(buffer, m_uniqueNames.make_unique(name_read(name)));
176 void mergeNames(const BasicNamespace& other) const
178 typedef std::list<NameCallback> SetNameCallbacks;
179 typedef std::map<CopiedString, SetNameCallbacks> NameGroups;
182 UniqueNames uniqueNames(other.m_uniqueNames);
184 for(Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i)
186 groups[(*i).second.c_str()].push_back((*i).first);
189 for(NameGroups::iterator i = groups.begin(); i != groups.end(); ++i)
191 name_t uniqueName(uniqueNames.make_unique(name_read((*i).first.c_str())));
192 uniqueNames.insert(uniqueName);
195 name_write(buffer, uniqueName);
197 //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
199 SetNameCallbacks& setNameCallbacks = (*i).second;
201 for(SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j)
209 BasicNamespace g_defaultNamespace;
210 BasicNamespace g_cloneNamespace;
214 Namespace* m_namespace;
216 typedef Namespace Type;
217 STRING_CONSTANT(Name, "*");
221 m_namespace = &g_defaultNamespace;
223 Namespace* getTable()
229 typedef SingletonModule<NamespaceAPI> NamespaceModule;
230 typedef Static<NamespaceModule> StaticNamespaceModule;
231 StaticRegisterModule staticRegisterDefaultNamespace(StaticNamespaceModule::instance());
234 std::list<Namespaced*> g_cloned;
236 inline Namespaced* Node_getNamespaced(scene::Node& node)
238 return NodeTypeCast<Namespaced>::cast(node);
241 void Node_gatherNamespaced(scene::Node& node)
243 Namespaced* namespaced = Node_getNamespaced(node);
246 g_cloned.push_back(namespaced);
250 class GatherNamespaced : public scene::Traversable::Walker
253 bool pre(scene::Node& node) const
255 Node_gatherNamespaced(node);
260 void Map_gatherNamespaced(scene::Node& root)
262 Node_traverseSubgraph(root, GatherNamespaced());
265 void Map_mergeClonedNames()
267 for(std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i)
269 (*i)->setNamespace(g_cloneNamespace);
271 g_cloneNamespace.mergeNames(g_defaultNamespace);
272 for(std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i)
274 (*i)->setNamespace(g_defaultNamespace);
288 void set(scene::Node* node)
296 scene::Node* get() const
303 void Map_SetValid(Map& map, bool valid);
304 void Map_UpdateTitle(const Map& map);
305 void Map_SetWorldspawn(Map& map, scene::Node* node);
308 class Map : public ModuleObserver
312 Resource* m_resource;
316 void (*m_modified_changed)(const Map&);
318 Signal0 m_mapValidCallbacks;
320 WorldNode m_world_node; // "classname" "worldspawn" !
322 Map() : m_resource(0), m_valid(false), m_modified_changed(Map_UpdateTitle)
330 if(Map_Unnamed(*this))
332 g_map.m_resource->setNode(NewMapRoot("").get_pointer());
333 MapFile* map = Node_getMapFile(*g_map.m_resource->getNode());
344 GlobalSceneGraph().insert_root(*m_resource->getNode());
348 Map_SetValid(g_map, true);
355 Map_SetValid(g_map, false);
356 Map_SetWorldspawn(g_map, 0);
359 GlobalUndoSystem().clear();
361 GlobalSceneGraph().erase_root();
367 Map* g_currentMap = 0;
369 void Map_addValidCallback(Map& map, const SignalHandler& handler)
371 map.m_mapValidCallbacks.connectLast(handler);
374 bool Map_Valid(const Map& map)
379 void Map_SetValid(Map& map, bool valid)
382 map.m_mapValidCallbacks();
386 const char* Map_Name(const Map& map)
388 return map.m_name.c_str();
391 bool Map_Unnamed(const Map& map)
393 return string_equal(Map_Name(map), "unnamed.map");
396 inline const MapFormat& MapFormat_forFile(const char* filename)
398 const char* moduleName = findModuleName(GetFileTypeRegistry(), MapFormat::Name(), path_get_extension(filename));
399 MapFormat* format = Radiant_getMapModules().findModule(moduleName);
400 ASSERT_MESSAGE(format != 0, "map format not found for file " << makeQuoted(filename));
404 const MapFormat& Map_getFormat(const Map& map)
406 return MapFormat_forFile(Map_Name(map));
410 bool Map_Modified(const Map& map)
412 return map.m_modified;
415 void Map_SetModified(Map& map, bool modified)
417 if(map.m_modified ^ modified)
419 map.m_modified = modified;
421 map.m_modified_changed(map);
425 void Map_UpdateTitle(const Map& map)
427 Sys_SetTitle(map.m_name.c_str(), Map_Modified(map));
432 scene::Node* Map_GetWorldspawn(const Map& map)
434 return map.m_world_node.get();
437 void Map_SetWorldspawn(Map& map, scene::Node* node)
439 map.m_world_node.set(node);
444 // need that in a variable, will have to tweak depending on the game
445 float g_MaxWorldCoord = 64*1024;
446 float g_MinWorldCoord = -64*1024;
448 void AddRegionBrushes (void);
449 void RemoveRegionBrushes (void);
455 free all map elements, reinitialize the structures that depend on them
462 g_map.m_resource->detach(g_map);
463 GlobalReferenceCache().release(g_map.m_name.c_str());
464 g_map.m_resource = 0;
469 Brush_unlatchPreferences();
472 class EntityFindByClassname : public scene::Graph::Walker
477 EntityFindByClassname(const char* name, Entity*& entity) : m_name(name), m_entity(entity)
481 bool pre(const scene::Path& path, scene::Instance& instance) const
485 Entity* entity = Node_getEntity(path.top());
487 && string_equal(m_name, entity->getKeyValue("classname")))
496 Entity* Scene_FindEntityByClass(const char* name)
499 GlobalSceneGraph().traverse(EntityFindByClassname(name, entity));
503 Entity *Scene_FindPlayerStart()
505 typedef const char* StaticString;
506 StaticString strings[] = {
508 "info_player_deathmatch",
509 "team_CTF_redplayer",
510 "team_CTF_blueplayer",
512 "team_CTF_bluespawn",
514 typedef const StaticString* StaticStringIterator;
515 for(StaticStringIterator i = strings, end = strings+(sizeof(strings)/sizeof(StaticString)); i != end; ++i)
517 Entity* entity = Scene_FindEntityByClass(*i);
527 // move the view to a start position
531 void FocusViews(const Vector3& point, float angle)
533 CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
534 Camera_setOrigin(camwnd, point);
535 Vector3 angles(Camera_getAngles(camwnd));
536 angles[CAMERA_PITCH] = 0;
537 angles[CAMERA_YAW] = angle;
538 Camera_setAngles(camwnd, angles);
540 XYWnd* xywnd = g_pParentWnd->GetXYWnd();
541 xywnd->SetOrigin(point);
544 #include "stringio.h"
546 void Map_StartPosition()
548 Entity* entity = Scene_FindPlayerStart();
553 string_parse_vector3(entity->getKeyValue("origin"), origin);
554 FocusViews(origin, string_read_float(entity->getKeyValue("angle")));
558 FocusViews(g_vector3_identity, 0);
563 inline bool node_is_worldspawn(scene::Node& node)
565 Entity* entity = Node_getEntity(node);
566 return entity != 0 && string_equal(entity->getKeyValue("classname"), "worldspawn");
570 // use first worldspawn
571 class entity_updateworldspawn : public scene::Traversable::Walker
574 bool pre(scene::Node& node) const
576 if(node_is_worldspawn(node))
578 if(Map_GetWorldspawn(g_map) == 0)
580 Map_SetWorldspawn(g_map, &node);
587 scene::Node* Map_FindWorldspawn(Map& map)
589 Map_SetWorldspawn(map, 0);
591 Node_getTraversable(GlobalSceneGraph().root())->traverse(entity_updateworldspawn());
593 return Map_GetWorldspawn(map);
597 class CollectAllWalker : public scene::Traversable::Walker
600 UnsortedNodeSet& m_nodes;
602 CollectAllWalker(scene::Node& root, UnsortedNodeSet& nodes) : m_root(root), m_nodes(nodes)
605 bool pre(scene::Node& node) const
607 m_nodes.insert(NodeSmartReference(node));
608 Node_getTraversable(m_root)->erase(node);
613 void Node_insertChildFirst(scene::Node& parent, scene::Node& child)
615 UnsortedNodeSet nodes;
616 Node_getTraversable(parent)->traverse(CollectAllWalker(parent, nodes));
617 Node_getTraversable(parent)->insert(child);
619 for(UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i)
621 Node_getTraversable(parent)->insert((*i));
625 scene::Node& createWorldspawn()
627 NodeSmartReference worldspawn(GlobalEntityCreator().createEntity(GlobalEntityClassManager().findOrInsert("worldspawn", true)));
628 Node_insertChildFirst(GlobalSceneGraph().root(), worldspawn);
632 void Map_UpdateWorldspawn(Map& map)
634 if(Map_FindWorldspawn(map) == 0)
636 Map_SetWorldspawn(map, &createWorldspawn());
640 scene::Node& Map_FindOrInsertWorldspawn(Map& map)
642 Map_UpdateWorldspawn(map);
643 return *Map_GetWorldspawn(map);
647 class MapMergeAll : public scene::Traversable::Walker
649 mutable scene::Path m_path;
651 MapMergeAll(const scene::Path& root)
655 bool pre(scene::Node& node) const
657 Node_getTraversable(m_path.top())->insert(node);
658 m_path.push(makeReference(node));
659 selectPath(m_path, true);
662 void post(scene::Node& node) const
668 class MapMergeEntities : public scene::Traversable::Walker
670 mutable scene::Path m_path;
672 MapMergeEntities(const scene::Path& root)
676 bool pre(scene::Node& node) const
678 if(node_is_worldspawn(node))
680 scene::Node* world_node = Map_FindWorldspawn(g_map);
683 Map_SetWorldspawn(g_map, &node);
684 Node_getTraversable(m_path.top().get())->insert(node);
685 m_path.push(makeReference(node));
686 Node_getTraversable(node)->traverse(SelectChildren(m_path));
690 m_path.push(makeReference(*world_node));
691 Node_getTraversable(node)->traverse(MapMergeAll(m_path));
696 Node_getTraversable(m_path.top())->insert(node);
697 m_path.push(makeReference(node));
698 if(node_is_group(node))
700 Node_getTraversable(node)->traverse(SelectChildren(m_path));
704 selectPath(m_path, true);
709 void post(scene::Node& node) const
715 class BasicContainer : public scene::Node::Symbiot
719 NodeTypeCastTable m_casts;
723 NodeContainedCast<BasicContainer, scene::Traversable>::install(m_casts);
725 NodeTypeCastTable& get()
732 TraversableNodeSet m_traverse;
735 typedef LazyStatic<TypeCasts> StaticTypeCasts;
737 scene::Traversable& get(NullType<scene::Traversable>)
742 BasicContainer() : m_node(this, this, StaticTypeCasts::instance().get())
755 /// Merges the map graph rooted at \p node into the global scene-graph.
756 void MergeMap(scene::Node& node)
758 Node_getTraversable(node)->traverse(MapMergeEntities(scene::Path(makeReference(GlobalSceneGraph().root()))));
760 void Map_ImportSelected(TextInputStream& in, const MapFormat& format)
762 NodeSmartReference node((new BasicContainer)->node());
763 format.readGraph(node, in, GlobalEntityCreator());
764 Map_gatherNamespaced(node);
765 Map_mergeClonedNames();
769 inline scene::Cloneable* Node_getCloneable(scene::Node& node)
771 return NodeTypeCast<scene::Cloneable>::cast(node);
774 inline scene::Node& node_clone(scene::Node& node)
776 scene::Cloneable* cloneable = Node_getCloneable(node);
779 return cloneable->clone();
782 return (new scene::NullNode)->node();
785 class CloneAll : public scene::Traversable::Walker
787 mutable scene::Path m_path;
789 CloneAll(scene::Node& root)
790 : m_path(makeReference(root))
793 bool pre(scene::Node& node) const
800 m_path.push(makeReference(node_clone(node)));
801 m_path.top().get().IncRef();
805 void post(scene::Node& node) const
812 Node_getTraversable(m_path.parent())->insert(m_path.top());
814 m_path.top().get().DecRef();
819 scene::Node& Node_Clone(scene::Node& node)
821 scene::Node& clone = node_clone(node);
822 scene::Traversable* traversable = Node_getTraversable(node);
825 traversable->traverse(CloneAll(clone));
831 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
833 class EntityBreakdownWalker : public scene::Graph::Walker
835 EntityBreakdown& m_entitymap;
837 EntityBreakdownWalker(EntityBreakdown& entitymap)
838 : m_entitymap(entitymap)
841 bool pre(const scene::Path& path, scene::Instance& instance) const
843 Entity* entity = Node_getEntity(path.top());
846 const EntityClass& eclass = entity->getEntityClass();
847 if(m_entitymap.find(eclass.name()) == m_entitymap.end())
849 m_entitymap[eclass.name()] = 1;
851 else ++m_entitymap[eclass.name()];
857 void Scene_EntityBreakdown(EntityBreakdown& entitymap)
859 GlobalSceneGraph().traverse(EntityBreakdownWalker(entitymap));
863 WindowPosition g_posMapInfoWnd(c_default_window_pos);
868 GtkEntry* brushes_entry;
869 GtkEntry* entities_entry;
870 GtkListStore* EntityBreakdownWalker;
872 GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Map Info", G_CALLBACK(dialog_delete_callback), &dialog);
874 window_set_position(window, g_posMapInfoWnd);
877 GtkVBox* vbox = create_dialog_vbox(4, 4);
878 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
881 GtkHBox* hbox = create_dialog_hbox(4);
882 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, TRUE, 0);
885 GtkTable* table = create_dialog_table(2, 2, 4, 4);
886 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(table), TRUE, TRUE, 0);
889 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
890 gtk_widget_show(GTK_WIDGET(entry));
891 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 0, 1,
892 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
893 (GtkAttachOptions) (0), 0, 0);
894 gtk_entry_set_editable(entry, FALSE);
896 brushes_entry = entry;
899 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
900 gtk_widget_show(GTK_WIDGET(entry));
901 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2,
902 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
903 (GtkAttachOptions) (0), 0, 0);
904 gtk_entry_set_editable(entry, FALSE);
906 entities_entry = entry;
909 GtkWidget* label = gtk_label_new ("Total Brushes");
910 gtk_widget_show (label);
911 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
912 (GtkAttachOptions) (GTK_FILL),
913 (GtkAttachOptions) (0), 0, 0);
914 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
917 GtkWidget* label = gtk_label_new ("Total Entities");
918 gtk_widget_show (label);
919 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
920 (GtkAttachOptions) (GTK_FILL),
921 (GtkAttachOptions) (0), 0, 0);
922 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
926 GtkVBox* vbox2 = create_dialog_vbox(4);
927 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox2), FALSE, FALSE, 0);
930 GtkButton* button = create_dialog_button("Close", G_CALLBACK(dialog_button_ok), &dialog);
931 gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(button), FALSE, FALSE, 0);
936 GtkWidget* label = gtk_label_new ("Entity breakdown");
937 gtk_widget_show (label);
938 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, TRUE, 0);
939 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
942 GtkScrolledWindow* scr = create_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4);
943 gtk_box_pack_start(GTK_BOX (vbox), GTK_WIDGET(scr), TRUE, TRUE, 0);
946 GtkListStore* store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
948 GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
949 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(view), TRUE);
952 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
953 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Entity", renderer, "text", 0, 0);
954 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
955 gtk_tree_view_column_set_sort_column_id(column, 0);
959 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
960 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Count", renderer, "text", 1, 0);
961 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
962 gtk_tree_view_column_set_sort_column_id(column, 1);
965 gtk_widget_show(view);
967 gtk_container_add(GTK_CONTAINER(scr), view);
969 EntityBreakdownWalker = store;
977 EntityBreakdown entitymap;
978 Scene_EntityBreakdown(entitymap);
980 for(EntityBreakdown::iterator i=entitymap.begin(); i != entitymap.end(); ++i)
983 sprintf (tmp, "%u", Unsigned((*i).second));
985 gtk_list_store_append(GTK_LIST_STORE(EntityBreakdownWalker), &iter);
986 gtk_list_store_set(GTK_LIST_STORE(EntityBreakdownWalker), &iter, 0, (*i).first.c_str(), 1, tmp, -1);
990 g_object_unref(G_OBJECT(EntityBreakdownWalker));
993 sprintf (tmp, "%u", Unsigned(g_brushCount.get()));
994 gtk_entry_set_text (GTK_ENTRY (brushes_entry), tmp);
995 sprintf (tmp, "%u", Unsigned(g_entityCount.get()));
996 gtk_entry_set_text (GTK_ENTRY (entities_entry), tmp);
998 modal_dialog_show(window, dialog);
1001 window_get_position(window, g_posMapInfoWnd);
1003 gtk_widget_destroy(GTK_WIDGET(window));
1011 const char* m_message;
1013 ScopeTimer(const char* message)
1014 : m_message(message)
1020 double elapsed_time = m_timer.elapsed_msec() / 1000.f;
1021 globalOutputStream() << m_message << " timer: " << FloatFormat(elapsed_time, 5, 2) << " second(s) elapsed\n";
1031 void Map_LoadFile (const char *filename)
1033 globalOutputStream() << "Loading map from " << filename << "\n";
1034 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
1037 ScopeTimer timer("map load");
1039 const MapFormat* format = NULL;
1040 const char* moduleName = findModuleName(&GlobalFiletypes(), MapFormat::Name(), path_get_extension(filename));
1041 if(string_not_empty(moduleName))
1042 format = ReferenceAPI_getMapModules().findModule(moduleName);
1044 for(int i = 0; i < Brush_toggleFormatCount(); ++i)
1048 Brush_toggleFormat(i);
1049 g_map.m_name = filename;
1050 Map_UpdateTitle(g_map);
1051 g_map.m_resource = GlobalReferenceCache().capture(g_map.m_name.c_str());
1053 format->wrongFormat = false;
1054 g_map.m_resource->attach(g_map);
1056 if(!format->wrongFormat)
1060 Node_getTraversable(GlobalSceneGraph().root())->traverse(entity_updateworldspawn());
1063 globalOutputStream() << "--- LoadMapFile ---\n";
1064 globalOutputStream() << g_map.m_name.c_str() << "\n";
1066 globalOutputStream() << Unsigned(g_brushCount.get()) << " primitive\n";
1067 globalOutputStream() << Unsigned(g_entityCount.get()) << " entities\n";
1069 //GlobalEntityCreator().printStatistics();
1072 // move the view to a start position
1074 Map_StartPosition();
1076 g_currentMap = &g_map;
1082 virtual bool excluded(scene::Node& node) const = 0;
1085 class ExcludeWalker : public scene::Traversable::Walker
1087 const scene::Traversable::Walker& m_walker;
1088 const Excluder* m_exclude;
1089 mutable bool m_skip;
1091 ExcludeWalker(const scene::Traversable::Walker& walker, const Excluder& exclude)
1092 : m_walker(walker), m_exclude(&exclude), m_skip(false)
1095 bool pre(scene::Node& node) const
1097 if(m_exclude->excluded(node) || node.isRoot())
1108 void post(scene::Node& node) const
1116 m_walker.post(node);
1121 class AnyInstanceSelected : public scene::Instantiable::Visitor
1125 AnyInstanceSelected(bool& selected) : m_selected(selected)
1129 void visit(scene::Instance& instance) const
1131 Selectable* selectable = Instance_getSelectable(instance);
1133 && selectable->isSelected())
1140 bool Node_instanceSelected(scene::Node& node)
1142 scene::Instantiable* instantiable = Node_getInstantiable(node);
1143 ASSERT_NOTNULL(instantiable);
1145 instantiable->forEachInstance(AnyInstanceSelected(selected));
1149 class SelectedDescendantWalker : public scene::Traversable::Walker
1153 SelectedDescendantWalker(bool& selected) : m_selected(selected)
1158 bool pre(scene::Node& node) const
1165 if(Node_instanceSelected(node))
1174 bool Node_selectedDescendant(scene::Node& node)
1177 Node_traverseSubgraph(node, SelectedDescendantWalker(selected));
1181 class SelectionExcluder : public Excluder
1184 bool excluded(scene::Node& node) const
1186 return !Node_selectedDescendant(node);
1190 class IncludeSelectedWalker : public scene::Traversable::Walker
1192 const scene::Traversable::Walker& m_walker;
1193 mutable std::size_t m_selected;
1194 mutable bool m_skip;
1196 bool selectedParent() const
1198 return m_selected != 0;
1201 IncludeSelectedWalker(const scene::Traversable::Walker& walker)
1202 : m_walker(walker), m_selected(0), m_skip(false)
1205 bool pre(scene::Node& node) const
1208 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1209 if(!node.isRoot() && (Node_selectedDescendant(node) || selectedParent()))
1211 if(Node_instanceSelected(node))
1224 void post(scene::Node& node) const
1232 if(Node_instanceSelected(node))
1236 m_walker.post(node);
1241 void Map_Traverse_Selected(scene::Node& root, const scene::Traversable::Walker& walker)
1243 scene::Traversable* traversable = Node_getTraversable(root);
1244 if(traversable != 0)
1247 traversable->traverse(ExcludeWalker(walker, SelectionExcluder()));
1249 traversable->traverse(IncludeSelectedWalker(walker));
1254 void Map_ExportSelected(TextOutputStream& out, const MapFormat& format)
1256 format.writeGraph(GlobalSceneGraph().root(), Map_Traverse_Selected, out);
1259 void Map_Traverse(scene::Node& root, const scene::Traversable::Walker& walker)
1261 scene::Traversable* traversable = Node_getTraversable(root);
1262 if(traversable != 0)
1264 traversable->traverse(walker);
1268 class RegionExcluder : public Excluder
1271 bool excluded(scene::Node& node) const
1273 return node.excluded();
1277 void Map_Traverse_Region(scene::Node& root, const scene::Traversable::Walker& walker)
1279 scene::Traversable* traversable = Node_getTraversable(root);
1280 if(traversable != 0)
1282 traversable->traverse(ExcludeWalker(walker, RegionExcluder()));
1286 bool Map_SaveRegion(const char *filename)
1290 bool success = MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse_Region, filename);
1292 RemoveRegionBrushes();
1298 void Map_RenameAbsolute(const char* absolute)
1300 Resource* resource = GlobalReferenceCache().capture(absolute);
1301 NodeSmartReference clone(NewMapRoot(path_make_relative(absolute, GlobalFileSystem().findRoot(absolute))));
1302 resource->setNode(clone.get_pointer());
1305 //ScopeTimer timer("clone subgraph");
1306 Node_getTraversable(GlobalSceneGraph().root())->traverse(CloneAll(clone));
1309 g_map.m_resource->detach(g_map);
1310 GlobalReferenceCache().release(g_map.m_name.c_str());
1312 g_map.m_resource = resource;
1314 g_map.m_name = absolute;
1315 Map_UpdateTitle(g_map);
1317 g_map.m_resource->attach(g_map);
1320 void Map_Rename(const char* filename)
1322 if(!string_equal(g_map.m_name.c_str(), filename))
1324 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map");
1326 Map_RenameAbsolute(filename);
1328 SceneChangeNotify();
1340 ScopeTimer timer("map save");
1342 return true; // assume success..
1353 //globalOutputStream() << "Map_New\n";
1355 g_map.m_name = "unnamed.map";
1356 Map_UpdateTitle(g_map);
1359 g_map.m_resource = GlobalReferenceCache().capture(g_map.m_name.c_str());
1360 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1361 g_map.m_resource->attach(g_map);
1363 SceneChangeNotify();
1366 FocusViews(g_vector3_identity, 0);
1368 g_currentMap = &g_map;
1371 extern void ConstructRegionBrushes(scene::Node* brushes[6], const Vector3& region_mins, const Vector3& region_maxs);
1373 void ConstructRegionStartpoint(scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs)
1376 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1377 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1378 for now, let's just print an error
1381 Vector3 vOrig(Camera_getOrigin(*g_pParentWnd->GetCamWnd()));
1383 for (int i=0 ; i<3 ; i++)
1385 if (vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i])
1387 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1392 // write the info_playerstart
1394 sprintf(sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2]);
1395 Node_getEntity(*startpoint)->setKeyValue("origin", sTmp);
1396 sprintf(sTmp, "%d", (int)Camera_getAngles(*g_pParentWnd->GetCamWnd())[CAMERA_YAW]);
1397 Node_getEntity(*startpoint)->setKeyValue("angle", sTmp);
1401 ===========================================================
1405 ===========================================================
1408 Vector3 region_mins(g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord);
1409 Vector3 region_maxs(g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord);
1411 scene::Node* region_sides[6];
1412 scene::Node* region_startpoint = 0;
1417 a regioned map will have temp walls put up at the region boundary
1418 \todo TODO TTimo old implementation of region brushes
1419 we still add them straight in the worldspawn and take them out after the map is saved
1420 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1423 void AddRegionBrushes (void)
1429 region_sides[i] = &GlobalBrushCreator().createBrush();
1430 Node_getTraversable(Map_FindOrInsertWorldspawn(g_map))->insert(NodeSmartReference(*region_sides[i]));
1433 region_startpoint = &GlobalEntityCreator().createEntity(GlobalEntityClassManager().findOrInsert("info_player_start", false));
1435 ConstructRegionBrushes(region_sides, region_mins, region_maxs);
1436 ConstructRegionStartpoint(region_startpoint, region_mins, region_maxs);
1438 Node_getTraversable(GlobalSceneGraph().root())->insert(NodeSmartReference(*region_startpoint));
1441 void RemoveRegionBrushes (void)
1443 for(std::size_t i=0; i<6; i++)
1445 Node_getTraversable(*Map_GetWorldspawn(g_map))->erase(*region_sides[i]);
1447 Node_getTraversable(GlobalSceneGraph().root())->erase(*region_startpoint);
1450 inline void exclude_node(scene::Node& node, bool exclude)
1453 ? node.enable(scene::Node::eExcluded)
1454 : node.disable(scene::Node::eExcluded);
1457 class ExcludeAllWalker : public scene::Graph::Walker
1461 ExcludeAllWalker(bool exclude)
1462 : m_exclude(exclude)
1465 bool pre(const scene::Path& path, scene::Instance& instance) const
1467 exclude_node(path.top(), m_exclude);
1473 void Scene_Exclude_All(bool exclude)
1475 GlobalSceneGraph().traverse(ExcludeAllWalker(exclude));
1478 bool Instance_isSelected(const scene::Instance& instance)
1480 const Selectable* selectable = Instance_getSelectable(instance);
1481 return selectable != 0 && selectable->isSelected();
1484 class ExcludeSelectedWalker : public scene::Graph::Walker
1488 ExcludeSelectedWalker(bool exclude)
1489 : m_exclude(exclude)
1492 bool pre(const scene::Path& path, scene::Instance& instance) const
1494 exclude_node(path.top(), (instance.isSelected() || instance.childSelected() || instance.parentSelected()) == m_exclude);
1499 void Scene_Exclude_Selected(bool exclude)
1501 GlobalSceneGraph().traverse(ExcludeSelectedWalker(exclude));
1504 class ExcludeRegionedWalker : public scene::Graph::Walker
1508 ExcludeRegionedWalker(bool exclude)
1509 : m_exclude(exclude)
1512 bool pre(const scene::Path& path, scene::Instance& instance) const
1518 aabb_intersects_aabb(
1519 instance.worldAABB(),
1520 aabb_for_minmax(region_mins, region_maxs)
1530 void Scene_Exclude_Region(bool exclude)
1532 GlobalSceneGraph().traverse(ExcludeRegionedWalker(exclude));
1539 Other filtering options may still be on
1542 void Map_RegionOff()
1544 region_active = false;
1546 region_maxs[0] = g_MaxWorldCoord - 64;
1547 region_mins[0] = g_MinWorldCoord + 64;
1548 region_maxs[1] = g_MaxWorldCoord - 64;
1549 region_mins[1] = g_MinWorldCoord + 64;
1550 region_maxs[2] = g_MaxWorldCoord - 64;
1551 region_mins[2] = g_MinWorldCoord + 64;
1553 Scene_Exclude_All(false);
1556 void Map_ApplyRegion (void)
1558 region_active = true;
1560 Scene_Exclude_Region(false);
1565 ========================
1566 Map_RegionSelectedBrushes
1567 ========================
1569 void Map_RegionSelectedBrushes (void)
1573 if(GlobalSelectionSystem().countSelected() != 0
1574 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive)
1576 region_active = true;
1577 Select_GetBounds (region_mins, region_maxs);
1579 Scene_Exclude_Selected(false);
1581 GlobalSelectionSystem().setSelectedAll(false);
1591 void Map_RegionXY(float x_min, float y_min, float x_max, float y_max)
1595 region_mins[0] = x_min;
1596 region_maxs[0] = x_max;
1597 region_mins[1] = y_min;
1598 region_maxs[1] = y_max;
1599 region_mins[2] = g_MinWorldCoord + 64;
1600 region_maxs[2] = g_MaxWorldCoord - 64;
1605 void Map_RegionBounds(const AABB& bounds)
1609 region_mins = vector3_subtracted(bounds.origin, bounds.extents);
1610 region_maxs = vector3_added(bounds.origin, bounds.extents);
1622 void Map_RegionBrush (void)
1624 if(GlobalSelectionSystem().countSelected() != 0)
1626 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1627 Map_RegionBounds(instance.worldAABB());
1636 bool Map_ImportFile(const char* filename)
1638 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
1640 bool success = false;
1642 if(extension_equal(path_get_extension(filename), "bsp"))
1646 const MapFormat* format = NULL;
1647 const char* moduleName = findModuleName(&GlobalFiletypes(), MapFormat::Name(), path_get_extension(filename));
1648 if(string_not_empty(moduleName))
1649 format = ReferenceAPI_getMapModules().findModule(moduleName);
1652 format->wrongFormat = false;
1653 Resource* resource = GlobalReferenceCache().capture(filename);
1654 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1655 if(!resource->load())
1657 GlobalReferenceCache().release(filename);
1661 if(format->wrongFormat)
1663 GlobalReferenceCache().release(filename);
1666 NodeSmartReference clone(NewMapRoot(""));
1667 Node_getTraversable(*resource->getNode())->traverse(CloneAll(clone));
1668 Map_gatherNamespaced(clone);
1669 Map_mergeClonedNames();
1672 GlobalReferenceCache().release(filename);
1675 SceneChangeNotify();
1681 const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue("q3map2_type");
1682 int n = string_length(path_get_extension(filename));
1683 if(n && (extension_equal(path_get_extension(filename), "bsp") || extension_equal(path_get_extension(filename), "map")))
1685 StringBuffer output;
1686 output.push_string(AppPath_get());
1687 output.push_string("q3map2.");
1688 output.push_string(RADIANT_EXECUTABLE);
1689 output.push_string(" -v -game ");
1690 output.push_string((type && *type) ? type : "quake3");
1691 output.push_string(" -fs_basepath \"");
1692 output.push_string(EnginePath_get());
1693 output.push_string("\" -fs_homepath \"");
1694 output.push_string(g_qeglobals.m_userEnginePath.c_str());
1695 output.push_string("\" -fs_game ");
1696 output.push_string(gamename_get());
1697 output.push_string(" -convert -format ");
1698 output.push_string(Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map");
1699 if(extension_equal(path_get_extension(filename), "map"))
1700 output.push_string(" -readmap ");
1701 output.push_string(" \"");
1702 output.push_string(filename);
1703 output.push_string("\"");
1706 Q_Exec(NULL, output.c_str(), NULL, false, true);
1708 // rebuild filename as "filenamewithoutext_converted.map"
1710 output.push_range(filename, filename + string_length(filename) - (n + 1));
1711 output.push_string("_converted.map");
1712 filename = output.c_str();
1715 Resource* resource = GlobalReferenceCache().capture(filename);
1716 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1717 if(!resource->load())
1719 GlobalReferenceCache().release(filename);
1722 NodeSmartReference clone(NewMapRoot(""));
1723 Node_getTraversable(*resource->getNode())->traverse(CloneAll(clone));
1724 Map_gatherNamespaced(clone);
1725 Map_mergeClonedNames();
1728 GlobalReferenceCache().release(filename);
1731 SceneChangeNotify();
1740 bool Map_SaveFile(const char* filename)
1742 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map");
1743 return MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse, filename);
1751 // Saves selected world brushes and whole entities with partial/full selections
1753 bool Map_SaveSelected(const char* filename)
1755 return MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse_Selected, filename);
1759 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1761 scene::Node& m_parent;
1763 ParentSelectedBrushesToEntityWalker(scene::Node& parent) : m_parent(parent)
1766 bool pre(const scene::Path& path, scene::Instance& instance) const
1768 if(path.top().get_pointer() != &m_parent
1769 && Node_isPrimitive(path.top()))
1771 Selectable* selectable = Instance_getSelectable(instance);
1773 && selectable->isSelected()
1781 void post(const scene::Path& path, scene::Instance& instance) const
1783 if(path.top().get_pointer() != &m_parent
1784 && Node_isPrimitive(path.top()))
1786 Selectable* selectable = Instance_getSelectable(instance);
1788 && selectable->isSelected()
1791 scene::Node& parent = path.parent();
1792 if(&parent != &m_parent)
1794 NodeSmartReference node(path.top().get());
1795 Node_getTraversable(parent)->erase(node);
1796 Node_getTraversable(m_parent)->insert(node);
1803 void Scene_parentSelectedBrushesToEntity(scene::Graph& graph, scene::Node& parent)
1805 graph.traverse(ParentSelectedBrushesToEntityWalker(parent));
1808 class CountSelectedBrushes : public scene::Graph::Walker
1810 std::size_t& m_count;
1811 mutable std::size_t m_depth;
1813 CountSelectedBrushes(std::size_t& count) : m_count(count), m_depth(0)
1817 bool pre(const scene::Path& path, scene::Instance& instance) const
1819 if(++m_depth != 1 && path.top().get().isRoot())
1823 Selectable* selectable = Instance_getSelectable(instance);
1825 && selectable->isSelected()
1826 && Node_isPrimitive(path.top()))
1832 void post(const scene::Path& path, scene::Instance& instance) const
1838 std::size_t Scene_countSelectedBrushes(scene::Graph& graph)
1841 graph.traverse(CountSelectedBrushes(count));
1853 const char* nodetype_get_name(ENodeType type)
1855 if(type == eNodeMap)
1857 if(type == eNodeEntity)
1859 if(type == eNodePrimitive)
1864 ENodeType node_get_nodetype(scene::Node& node)
1866 if(Node_isEntity(node))
1870 if(Node_isPrimitive(node))
1872 return eNodePrimitive;
1874 return eNodeUnknown;
1877 bool contains_entity(scene::Node& node)
1879 return Node_getTraversable(node) != 0 && !Node_isBrush(node) && !Node_isPatch(node) && !Node_isEntity(node);
1882 bool contains_primitive(scene::Node& node)
1884 return Node_isEntity(node) && Node_getTraversable(node) != 0 && Node_getEntity(node)->isContainer();
1887 ENodeType node_get_contains(scene::Node& node)
1889 if(contains_entity(node))
1893 if(contains_primitive(node))
1895 return eNodePrimitive;
1897 return eNodeUnknown;
1900 void Path_parent(const scene::Path& parent, const scene::Path& child)
1902 ENodeType contains = node_get_contains(parent.top());
1903 ENodeType type = node_get_nodetype(child.top());
1905 if(contains != eNodeUnknown && contains == type)
1907 NodeSmartReference node(child.top().get());
1908 Path_deleteTop(child);
1909 Node_getTraversable(parent.top())->insert(node);
1910 SceneChangeNotify();
1914 globalErrorStream() << "failed - " << nodetype_get_name(type) << " cannot be parented to " << nodetype_get_name(contains) << " container.\n";
1918 void Scene_parentSelected()
1920 UndoableCommand undo("parentSelected");
1922 if(GlobalSelectionSystem().countSelected() > 1)
1924 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1926 const scene::Path& m_parent;
1928 ParentSelectedBrushesToEntityWalker(const scene::Path& parent) : m_parent(parent)
1931 void visit(scene::Instance& instance) const
1933 if(&m_parent != &instance.path())
1935 Path_parent(m_parent, instance.path());
1940 ParentSelectedBrushesToEntityWalker visitor(GlobalSelectionSystem().ultimateSelected().path());
1941 GlobalSelectionSystem().foreachSelected(visitor);
1945 globalOutputStream() << "failed - did not find two selected nodes.\n";
1953 if (ConfirmModified("New Map"))
1961 CopiedString g_mapsPath;
1963 const char* getMapsPath()
1965 return g_mapsPath.c_str();
1968 const char* map_open(const char* title)
1970 return file_dialog(GTK_WIDGET(MainFrame_getWindow()), TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false);
1973 const char* map_import(const char* title)
1975 return file_dialog(GTK_WIDGET(MainFrame_getWindow()), TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false);
1978 const char* map_save(const char* title)
1980 return file_dialog(GTK_WIDGET(MainFrame_getWindow()), FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true);
1985 if (!ConfirmModified("Open Map"))
1988 const char* filename = map_open("Open Map");
1992 MRU_AddFile(filename);
1995 Map_LoadFile(filename);
2001 const char* filename = map_import("Import Map");
2005 UndoableCommand undo("mapImport");
2006 Map_ImportFile(filename);
2012 const char* filename = map_save("Save Map");
2016 MRU_AddFile(filename);
2017 Map_Rename(filename);
2030 if(Map_Unnamed(g_map))
2034 else if(Map_Modified(g_map))
2042 const char* filename = map_save("Export Selection");
2046 Map_SaveSelected(filename);
2052 const char* filename = map_save("Export Region");
2056 Map_SaveRegion(filename);
2064 SceneChangeNotify();
2070 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
2071 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
2072 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
2073 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
2075 SceneChangeNotify();
2081 SceneChangeNotify();
2084 void RegionSelected()
2086 Map_RegionSelectedBrushes();
2087 SceneChangeNotify();
2094 class BrushFindByIndexWalker : public scene::Traversable::Walker
2096 mutable std::size_t m_index;
2097 scene::Path& m_path;
2099 BrushFindByIndexWalker(std::size_t index, scene::Path& path)
2100 : m_index(index), m_path(path)
2103 bool pre(scene::Node& node) const
2105 if(Node_isPrimitive(node) && m_index-- == 0)
2107 m_path.push(makeReference(node));
2113 class EntityFindByIndexWalker : public scene::Traversable::Walker
2115 mutable std::size_t m_index;
2116 scene::Path& m_path;
2118 EntityFindByIndexWalker(std::size_t index, scene::Path& path)
2119 : m_index(index), m_path(path)
2122 bool pre(scene::Node& node) const
2124 if(Node_isEntity(node) && m_index-- == 0)
2126 m_path.push(makeReference(node));
2132 void Scene_FindEntityBrush(std::size_t entity, std::size_t brush, scene::Path& path)
2134 path.push(makeReference(GlobalSceneGraph().root()));
2136 Node_getTraversable(path.top())->traverse(EntityFindByIndexWalker(entity, path));
2138 if(path.size() == 2)
2140 scene::Traversable* traversable = Node_getTraversable(path.top());
2141 if(traversable != 0)
2143 traversable->traverse(BrushFindByIndexWalker(brush, path));
2148 inline bool Node_hasChildren(scene::Node& node)
2150 scene::Traversable* traversable = Node_getTraversable(node);
2151 return traversable != 0 && !traversable->empty();
2154 void SelectBrush (int entitynum, int brushnum)
2157 Scene_FindEntityBrush(entitynum, brushnum, path);
2158 if(path.size() == 3 || (path.size() == 2 && !Node_hasChildren(path.top())))
2160 scene::Instance* instance = GlobalSceneGraph().find(path);
2161 ASSERT_MESSAGE(instance != 0, "SelectBrush: path not found in scenegraph");
2162 Selectable* selectable = Instance_getSelectable(*instance);
2163 ASSERT_MESSAGE(selectable != 0, "SelectBrush: path not selectable");
2164 selectable->setSelected(true);
2165 g_pParentWnd->GetXYWnd()->PositionView(instance->worldAABB().origin);
2170 class BrushFindIndexWalker : public scene::Graph::Walker
2172 mutable const scene::Node* m_node;
2173 std::size_t& m_count;
2175 BrushFindIndexWalker(const scene::Node& node, std::size_t& count)
2176 : m_node(&node), m_count(count)
2179 bool pre(const scene::Path& path, scene::Instance& instance) const
2181 if(Node_isPrimitive(path.top()))
2183 if(m_node == path.top().get_pointer())
2196 class EntityFindIndexWalker : public scene::Graph::Walker
2198 mutable const scene::Node* m_node;
2199 std::size_t& m_count;
2201 EntityFindIndexWalker(const scene::Node& node, std::size_t& count)
2202 : m_node(&node), m_count(count)
2205 bool pre(const scene::Path& path, scene::Instance& instance) const
2207 if(Node_isEntity(path.top()))
2209 if(m_node == path.top().get_pointer())
2222 static void GetSelectionIndex (int *ent, int *brush)
2224 std::size_t count_brush = 0;
2225 std::size_t count_entity = 0;
2226 if(GlobalSelectionSystem().countSelected() != 0)
2228 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2230 GlobalSceneGraph().traverse(BrushFindIndexWalker(path.top(), count_brush));
2231 GlobalSceneGraph().traverse(EntityFindIndexWalker(path.parent(), count_entity));
2233 *brush = int(count_brush);
2234 *ent = int(count_entity);
2243 GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Find Brush", G_CALLBACK(dialog_delete_callback), &dialog);
2245 GtkAccelGroup* accel = gtk_accel_group_new();
2246 gtk_window_add_accel_group(window, accel);
2249 GtkVBox* vbox = create_dialog_vbox(4, 4);
2250 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
2252 GtkTable* table = create_dialog_table(2, 2, 4, 4);
2253 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), TRUE, TRUE, 0);
2255 GtkWidget* label = gtk_label_new ("Entity number");
2256 gtk_widget_show (label);
2257 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
2258 (GtkAttachOptions) (0),
2259 (GtkAttachOptions) (0), 0, 0);
2262 GtkWidget* label = gtk_label_new ("Brush number");
2263 gtk_widget_show (label);
2264 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
2265 (GtkAttachOptions) (0),
2266 (GtkAttachOptions) (0), 0, 0);
2269 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
2270 gtk_widget_show(GTK_WIDGET(entry));
2271 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 0, 1,
2272 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2273 (GtkAttachOptions) (0), 0, 0);
2274 gtk_widget_grab_focus(GTK_WIDGET(entry));
2278 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
2279 gtk_widget_show(GTK_WIDGET(entry));
2280 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2,
2281 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2282 (GtkAttachOptions) (0), 0, 0);
2288 GtkHBox* hbox = create_dialog_hbox(4);
2289 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), TRUE, TRUE, 0);
2291 GtkButton* button = create_dialog_button("Find", G_CALLBACK(dialog_button_ok), &dialog);
2292 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0);
2293 widget_make_default(GTK_WIDGET(button));
2294 gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
2297 GtkButton* button = create_dialog_button("Close", G_CALLBACK(dialog_button_cancel), &dialog);
2298 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0);
2299 gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
2304 // Initialize dialog
2308 GetSelectionIndex (&ent, &br);
2309 sprintf (buf, "%i", ent);
2310 gtk_entry_set_text(entity, buf);
2311 sprintf (buf, "%i", br);
2312 gtk_entry_set_text(brush, buf);
2314 if(modal_dialog_show(window, dialog) == eIDOK)
2316 const char *entstr = gtk_entry_get_text(entity);
2317 const char *brushstr = gtk_entry_get_text(brush);
2318 SelectBrush (atoi(entstr), atoi(brushstr));
2321 gtk_widget_destroy(GTK_WIDGET(window));
2324 void Map_constructPreferences(PreferencesPage& page)
2326 page.appendCheckBox("", "Load last map on open", g_bLoadLastMap);
2330 class MapEntityClasses : public ModuleObserver
2332 std::size_t m_unrealised;
2334 MapEntityClasses() : m_unrealised(1)
2339 if(--m_unrealised == 0)
2341 if(g_map.m_resource != 0)
2343 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
2344 g_map.m_resource->realise();
2350 if(++m_unrealised == 1)
2352 if(g_map.m_resource != 0)
2354 g_map.m_resource->flush();
2355 g_map.m_resource->unrealise();
2361 MapEntityClasses g_MapEntityClasses;
2364 class MapModuleObserver : public ModuleObserver
2366 std::size_t m_unrealised;
2368 MapModuleObserver() : m_unrealised(1)
2373 if(--m_unrealised == 0)
2375 ASSERT_MESSAGE(!string_empty(g_qeglobals.m_userGamePath.c_str()), "maps_directory: user-game-path is empty");
2376 StringOutputStream buffer(256);
2377 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2378 Q_mkdir(buffer.c_str());
2379 g_mapsPath = buffer.c_str();
2384 if(++m_unrealised == 1)
2391 MapModuleObserver g_MapModuleObserver;
2393 #include "preferencesystem.h"
2395 CopiedString g_strLastMap;
2396 bool g_bLoadLastMap = false;
2398 void Map_Construct()
2400 GlobalCommands_insert("RegionOff", FreeCaller<RegionOff>());
2401 GlobalCommands_insert("RegionSetXY", FreeCaller<RegionXY>());
2402 GlobalCommands_insert("RegionSetBrush", FreeCaller<RegionBrush>());
2403 GlobalCommands_insert("RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator('R', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
2405 GlobalPreferenceSystem().registerPreference("LastMap", CopiedStringImportStringCaller(g_strLastMap), CopiedStringExportStringCaller(g_strLastMap));
2406 GlobalPreferenceSystem().registerPreference("LoadLastMap", BoolImportStringCaller(g_bLoadLastMap), BoolExportStringCaller(g_bLoadLastMap));
2407 GlobalPreferenceSystem().registerPreference("MapInfoDlg", WindowPositionImportStringCaller(g_posMapInfoWnd), WindowPositionExportStringCaller(g_posMapInfoWnd));
2409 PreferencesDialog_addSettingsPreferences(FreeCaller1<PreferencesPage&, Map_constructPreferences>());
2411 GlobalEntityClassManager().attach(g_MapEntityClasses);
2412 Radiant_attachHomePathsObserver(g_MapModuleObserver);
2417 Radiant_detachHomePathsObserver(g_MapModuleObserver);
2418 GlobalEntityClassManager().detach(g_MapEntityClasses);