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 #include "iselection.h"
31 #include "ireference.h"
32 #include "ifiletypes.h"
37 #include "ifilesystem.h"
38 #include "namespace.h"
39 #include "moduleobserver.h"
43 #include <gtk/gtkmain.h>
44 #include <gtk/gtkbox.h>
45 #include <gtk/gtkentry.h>
46 #include <gtk/gtklabel.h>
47 #include <gtk/gtktable.h>
48 #include <gtk/gtktreemodel.h>
49 #include <gtk/gtktreeview.h>
50 #include <gtk/gtkliststore.h>
51 #include <gtk/gtkcellrenderertext.h>
54 #include "transformlib.h"
55 #include "selectionlib.h"
56 #include "instancelib.h"
57 #include "traverselib.h"
59 #include "eclasslib.h"
61 #include "stream/textfilestream.h"
63 #include "uniquenames.h"
64 #include "modulesystem/singletonmodule.h"
65 #include "modulesystem/moduleregistry.h"
66 #include "stream/stringstream.h"
68 #include "gtkutil/filechooser.h"
72 #include "filetypes.h"
74 #include "entityinspector.h"
77 #include "camwindow.h"
79 #include "mainframe.h"
80 #include "preferences.h"
81 #include "referencecache.h"
95 //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
96 m_names.insert(name_read(c_str()));
103 //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
104 m_names.erase(name_read(c_str()));
108 NameObserver& operator=(const NameObserver& other);
110 NameObserver(UniqueNames& names) : m_names(names)
114 NameObserver(const NameObserver& other) : m_names(other.m_names), m_name(other.m_name)
124 return string_empty(c_str());
126 const char* c_str() const
128 return m_name.c_str();
130 void nameChanged(const char* name)
136 typedef MemberCaller1<NameObserver, const char*, &NameObserver::nameChanged> NameChangedCaller;
139 class BasicNamespace : public Namespace
141 typedef std::map<NameCallback, NameObserver> Names;
143 UniqueNames m_uniqueNames;
147 ASSERT_MESSAGE(m_names.empty(), "namespace: names still registered at shutdown");
149 void attach(const NameCallback& setName, const NameCallbackCallback& attachObserver)
151 std::pair<Names::iterator, bool> result = m_names.insert(Names::value_type(setName, m_uniqueNames));
152 ASSERT_MESSAGE(result.second, "cannot attach name");
153 attachObserver(NameObserver::NameChangedCaller((*result.first).second));
154 //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
156 void detach(const NameCallback& setName, const NameCallbackCallback& detachObserver)
158 Names::iterator i = m_names.find(setName);
159 ASSERT_MESSAGE(i != m_names.end(), "cannot detach name");
160 //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
161 detachObserver(NameObserver::NameChangedCaller((*i).second));
165 void makeUnique(const char* name, const NameCallback& setName) const
168 name_write(buffer, m_uniqueNames.make_unique(name_read(name)));
172 void mergeNames(const BasicNamespace& other) const
174 typedef std::list<NameCallback> SetNameCallbacks;
175 typedef std::map<CopiedString, SetNameCallbacks> NameGroups;
178 UniqueNames uniqueNames(other.m_uniqueNames);
180 for(Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i)
182 groups[(*i).second.c_str()].push_back((*i).first);
185 for(NameGroups::iterator i = groups.begin(); i != groups.end(); ++i)
187 name_t uniqueName(uniqueNames.make_unique(name_read((*i).first.c_str())));
188 uniqueNames.insert(uniqueName);
191 name_write(buffer, uniqueName);
193 //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
195 SetNameCallbacks& setNameCallbacks = (*i).second;
197 for(SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j)
205 BasicNamespace g_defaultNamespace;
206 BasicNamespace g_cloneNamespace;
210 Namespace* m_namespace;
212 typedef Namespace Type;
213 STRING_CONSTANT(Name, "*");
217 m_namespace = &g_defaultNamespace;
219 Namespace* getTable()
225 typedef SingletonModule<NamespaceAPI> NamespaceModule;
226 typedef Static<NamespaceModule> StaticNamespaceModule;
227 StaticRegisterModule staticRegisterDefaultNamespace(StaticNamespaceModule::instance());
230 std::list<Namespaced*> g_cloned;
232 inline Namespaced* Node_getNamespaced(scene::Node& node)
234 return NodeTypeCast<Namespaced>::cast(node);
237 void Node_gatherNamespaced(scene::Node& node)
239 Namespaced* namespaced = Node_getNamespaced(node);
242 g_cloned.push_back(namespaced);
246 class GatherNamespaced : public scene::Traversable::Walker
249 bool pre(scene::Node& node) const
251 Node_gatherNamespaced(node);
256 void Map_gatherNamespaced(scene::Node& root)
258 Node_traverseSubgraph(root, GatherNamespaced());
261 void Map_mergeClonedNames()
263 for(std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i)
265 (*i)->setNamespace(g_cloneNamespace);
267 g_cloneNamespace.mergeNames(g_defaultNamespace);
268 for(std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i)
270 (*i)->setNamespace(g_defaultNamespace);
284 void set(scene::Node* node)
292 scene::Node* get() const
299 void Map_SetValid(Map& map, bool valid);
300 void Map_UpdateTitle(const Map& map);
301 void Map_SetWorldspawn(Map& map, scene::Node* node);
304 class Map : public ModuleObserver
308 Resource* m_resource;
312 void (*m_modified_changed)(const Map&);
314 typedef std::vector<Callback> MapValidCallbacks;
315 MapValidCallbacks m_mapValidCallbacks;
317 WorldNode m_world_node; // "classname" "worldspawn" !
319 Map() : m_resource(0), m_valid(false), m_modified_changed(Map_UpdateTitle)
327 if(Map_Unnamed(*this))
329 g_map.m_resource->setNode(NewMapRoot("").get_pointer());
330 MapFile* map = Node_getMapFile(*g_map.m_resource->getNode());
341 GlobalSceneGraph().insert_root(*m_resource->getNode());
345 Map_SetValid(g_map, true);
352 Map_SetValid(g_map, false);
353 Map_SetWorldspawn(g_map, 0);
356 GlobalUndoSystem().clear();
358 GlobalSceneGraph().erase_root();
364 Map* g_currentMap = 0;
366 void Map_addValidCallback(Map& map, const Callback& callback)
368 map.m_mapValidCallbacks.push_back(callback);
371 bool Map_Valid(const Map& map)
376 void Map_SetValid(Map& map, bool valid)
379 std::for_each(map.m_mapValidCallbacks.begin(), map.m_mapValidCallbacks.end(), CallbackInvoke());
383 const char* Map_Name(const Map& map)
385 return map.m_name.c_str();
388 bool Map_Unnamed(const Map& map)
390 return string_equal(Map_Name(map), "unnamed.map");
393 inline const MapFormat& MapFormat_forFile(const char* filename)
395 const char* moduleName = findModuleName(GetFileTypeRegistry(), MapFormat::Name(), path_get_extension(filename));
396 MapFormat* format = Radiant_getMapModules().findModule(moduleName);
397 ASSERT_MESSAGE(format != 0, "map format not found for file " << makeQuoted(filename));
401 const MapFormat& Map_getFormat(const Map& map)
403 return MapFormat_forFile(Map_Name(map));
407 bool Map_Modified(const Map& map)
409 return map.m_modified;
412 void Map_SetModified(Map& map, bool modified)
414 if(map.m_modified ^ modified)
416 map.m_modified = modified;
418 map.m_modified_changed(map);
422 void Map_UpdateTitle(const Map& map)
424 Sys_SetTitle(map.m_name.c_str(), Map_Modified(map));
429 scene::Node* Map_GetWorldspawn(const Map& map)
431 return map.m_world_node.get();
434 void Map_SetWorldspawn(Map& map, scene::Node* node)
436 map.m_world_node.set(node);
441 // need that in a variable, will have to tweak depending on the game
442 float g_MaxWorldCoord = 64*1024;
443 float g_MinWorldCoord = -64*1024;
445 void AddRegionBrushes (void);
446 void RemoveRegionBrushes (void);
452 free all map elements, reinitialize the structures that depend on them
459 g_map.m_resource->detach(g_map);
460 GlobalReferenceCache().release(g_map.m_name.c_str());
461 g_map.m_resource = 0;
468 class EntityFindByClassname : public scene::Graph::Walker
473 EntityFindByClassname(const char* name, Entity*& entity) : m_name(name), m_entity(entity)
477 bool pre(const scene::Path& path, scene::Instance& instance) const
481 Entity* entity = Node_getEntity(path.top());
483 && string_equal(m_name, entity->getKeyValue("classname")))
492 Entity* Scene_FindEntityByClass(const char* name)
495 GlobalSceneGraph().traverse(EntityFindByClassname(name, entity));
499 Entity *Scene_FindPlayerStart()
501 typedef const char* StaticString;
502 StaticString strings[] = {
504 "info_player_deathmatch",
505 "team_CTF_redplayer",
506 "team_CTF_blueplayer",
508 "team_CTF_bluespawn",
510 typedef const StaticString* StaticStringIterator;
511 for(StaticStringIterator i = strings, end = strings+(sizeof(strings)/sizeof(StaticString)); i != end; ++i)
513 Entity* entity = Scene_FindEntityByClass(*i);
523 // move the view to a start position
527 void FocusViews(const Vector3& point, float angle)
529 CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
530 Camera_setOrigin(camwnd, point);
531 Vector3 angles(Camera_getAngles(camwnd));
532 angles[CAMERA_PITCH] = 0;
533 angles[CAMERA_YAW] = angle;
534 Camera_setAngles(camwnd, angles);
536 XYWnd* xywnd = g_pParentWnd->GetXYWnd();
537 xywnd->SetOrigin(point);
540 #include "stringio.h"
542 void Map_StartPosition()
544 Entity* entity = Scene_FindPlayerStart();
549 string_parse_vector3(entity->getKeyValue("origin"), origin);
550 FocusViews(origin, string_read_float(entity->getKeyValue("angle")));
554 FocusViews(g_vector3_identity, 0);
559 inline bool node_is_worldspawn(scene::Node& node)
561 Entity* entity = Node_getEntity(node);
562 return entity != 0 && string_equal(entity->getKeyValue("classname"), "worldspawn");
566 // use first worldspawn
567 class entity_updateworldspawn : public scene::Traversable::Walker
570 bool pre(scene::Node& node) const
572 if(node_is_worldspawn(node))
574 if(Map_GetWorldspawn(g_map) == 0)
576 Map_SetWorldspawn(g_map, &node);
583 scene::Node* Map_FindWorldspawn(Map& map)
585 Map_SetWorldspawn(map, 0);
587 Node_getTraversable(GlobalSceneGraph().root())->traverse(entity_updateworldspawn());
589 return Map_GetWorldspawn(map);
593 class CollectAllWalker : public scene::Traversable::Walker
596 UnsortedNodeSet& m_nodes;
598 CollectAllWalker(scene::Node& root, UnsortedNodeSet& nodes) : m_root(root), m_nodes(nodes)
601 bool pre(scene::Node& node) const
603 m_nodes.insert(NodeSmartReference(node));
604 Node_getTraversable(m_root)->erase(node);
609 void Node_insertChildFirst(scene::Node& parent, scene::Node& child)
611 UnsortedNodeSet nodes;
612 Node_getTraversable(parent)->traverse(CollectAllWalker(parent, nodes));
613 Node_getTraversable(parent)->insert(child);
615 for(UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i)
617 Node_getTraversable(parent)->insert((*i));
621 scene::Node& createWorldspawn()
623 NodeSmartReference worldspawn(GlobalEntityCreator().createEntity(GlobalEntityClassManager().findOrInsert("worldspawn", true)));
624 Node_insertChildFirst(GlobalSceneGraph().root(), worldspawn);
628 void Map_UpdateWorldspawn(Map& map)
630 if(Map_FindWorldspawn(map) == 0)
632 Map_SetWorldspawn(map, &createWorldspawn());
636 scene::Node& Map_FindOrInsertWorldspawn(Map& map)
638 Map_UpdateWorldspawn(map);
639 return *Map_GetWorldspawn(map);
643 class MapMergeAll : public scene::Traversable::Walker
645 mutable scene::Path m_path;
647 MapMergeAll(const scene::Path& root)
651 bool pre(scene::Node& node) const
653 Node_getTraversable(m_path.top())->insert(node);
654 m_path.push(makeReference(node));
655 selectPath(m_path, true);
658 void post(scene::Node& node) const
664 class MapMergeEntities : public scene::Traversable::Walker
666 mutable scene::Path m_path;
668 MapMergeEntities(const scene::Path& root)
672 bool pre(scene::Node& node) const
674 if(node_is_worldspawn(node))
676 scene::Node* world_node = Map_FindWorldspawn(g_map);
679 Map_SetWorldspawn(g_map, &node);
680 Node_getTraversable(m_path.top().get())->insert(node);
681 m_path.push(makeReference(node));
682 Node_getTraversable(node)->traverse(SelectChildren(m_path));
686 m_path.push(makeReference(*world_node));
687 Node_getTraversable(node)->traverse(MapMergeAll(m_path));
692 Node_getTraversable(m_path.top())->insert(node);
693 m_path.push(makeReference(node));
694 if(node_is_group(node))
696 Node_getTraversable(node)->traverse(SelectChildren(m_path));
700 selectPath(m_path, true);
705 void post(scene::Node& node) const
711 class BasicContainer : public scene::Node::Symbiot
715 NodeTypeCastTable m_casts;
719 NodeContainedCast<BasicContainer, scene::Traversable>::install(m_casts);
721 NodeTypeCastTable& get()
728 TraversableNodeSet m_traverse;
731 typedef LazyStatic<TypeCasts> StaticTypeCasts;
733 scene::Traversable& get(NullType<scene::Traversable>)
738 BasicContainer() : m_node(this, this, StaticTypeCasts::instance().get())
751 /// Merges the map graph rooted at \p node into the global scene-graph.
752 void MergeMap(scene::Node& node)
754 Node_getTraversable(node)->traverse(MapMergeEntities(scene::Path(makeReference(GlobalSceneGraph().root()))));
756 void Map_ImportSelected(TextInputStream& in, const MapFormat& format)
758 NodeSmartReference node((new BasicContainer)->node());
759 format.readGraph(node, in, GlobalEntityCreator());
760 Map_gatherNamespaced(node);
761 Map_mergeClonedNames();
765 inline scene::Cloneable* Node_getCloneable(scene::Node& node)
767 return NodeTypeCast<scene::Cloneable>::cast(node);
770 inline scene::Node& node_clone(scene::Node& node)
772 scene::Cloneable* cloneable = Node_getCloneable(node);
775 return cloneable->clone();
778 return (new scene::NullNode)->node();
781 class CloneAll : public scene::Traversable::Walker
783 mutable scene::Path m_path;
785 CloneAll(scene::Node& root)
786 : m_path(makeReference(root))
789 bool pre(scene::Node& node) const
796 m_path.push(makeReference(node_clone(node)));
797 m_path.top().get().IncRef();
801 void post(scene::Node& node) const
808 Node_getTraversable(m_path.parent())->insert(m_path.top());
810 m_path.top().get().DecRef();
815 scene::Node& Node_Clone(scene::Node& node)
817 scene::Node& clone = node_clone(node);
818 scene::Traversable* traversable = Node_getTraversable(node);
821 traversable->traverse(CloneAll(clone));
827 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
829 class EntityBreakdownWalker : public scene::Graph::Walker
831 EntityBreakdown& m_entitymap;
833 EntityBreakdownWalker(EntityBreakdown& entitymap)
834 : m_entitymap(entitymap)
837 bool pre(const scene::Path& path, scene::Instance& instance) const
839 Entity* entity = Node_getEntity(path.top());
842 const EntityClass& eclass = entity->getEntityClass();
843 if(m_entitymap.find(eclass.name()) == m_entitymap.end())
845 m_entitymap[eclass.name()] = 1;
847 else ++m_entitymap[eclass.name()];
853 void Scene_EntityBreakdown(EntityBreakdown& entitymap)
855 GlobalSceneGraph().traverse(EntityBreakdownWalker(entitymap));
859 WindowPosition g_posMapInfoWnd(c_default_window_pos);
864 GtkEntry* brushes_entry;
865 GtkEntry* entities_entry;
866 GtkListStore* EntityBreakdownWalker;
868 GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Map Info", G_CALLBACK(dialog_delete_callback), &dialog);
870 window_set_position(window, g_posMapInfoWnd);
873 GtkVBox* vbox = create_dialog_vbox(4, 4);
874 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
877 GtkHBox* hbox = create_dialog_hbox(4);
878 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, TRUE, 0);
881 GtkTable* table = create_dialog_table(2, 2, 4, 4);
882 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(table), TRUE, TRUE, 0);
885 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
886 gtk_widget_show(GTK_WIDGET(entry));
887 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 0, 1,
888 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
889 (GtkAttachOptions) (0), 0, 0);
890 gtk_entry_set_editable(entry, FALSE);
892 brushes_entry = entry;
895 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
896 gtk_widget_show(GTK_WIDGET(entry));
897 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2,
898 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
899 (GtkAttachOptions) (0), 0, 0);
900 gtk_entry_set_editable(entry, FALSE);
902 entities_entry = entry;
905 GtkWidget* label = gtk_label_new ("Total Brushes");
906 gtk_widget_show (label);
907 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
908 (GtkAttachOptions) (GTK_FILL),
909 (GtkAttachOptions) (0), 0, 0);
910 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
913 GtkWidget* label = gtk_label_new ("Total Entities");
914 gtk_widget_show (label);
915 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
916 (GtkAttachOptions) (GTK_FILL),
917 (GtkAttachOptions) (0), 0, 0);
918 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
922 GtkVBox* vbox2 = create_dialog_vbox(4);
923 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox2), FALSE, FALSE, 0);
926 GtkButton* button = create_dialog_button("Close", G_CALLBACK(dialog_button_ok), &dialog);
927 gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(button), FALSE, FALSE, 0);
932 GtkWidget* label = gtk_label_new ("Entity breakdown");
933 gtk_widget_show (label);
934 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, TRUE, 0);
935 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
938 GtkScrolledWindow* scr = create_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4);
939 gtk_box_pack_start(GTK_BOX (vbox), GTK_WIDGET(scr), TRUE, TRUE, 0);
942 GtkListStore* store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
944 GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
945 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(view), TRUE);
948 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
949 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Entity", renderer, "text", 0, 0);
950 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
951 gtk_tree_view_column_set_sort_column_id(column, 0);
955 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
956 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Count", renderer, "text", 1, 0);
957 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
958 gtk_tree_view_column_set_sort_column_id(column, 1);
961 gtk_widget_show(view);
963 gtk_container_add(GTK_CONTAINER(scr), view);
965 EntityBreakdownWalker = store;
973 EntityBreakdown entitymap;
974 Scene_EntityBreakdown(entitymap);
976 for(EntityBreakdown::iterator i=entitymap.begin(); i != entitymap.end(); ++i)
979 sprintf (tmp, "%u", Unsigned((*i).second));
981 gtk_list_store_append(GTK_LIST_STORE(EntityBreakdownWalker), &iter);
982 gtk_list_store_set(GTK_LIST_STORE(EntityBreakdownWalker), &iter, 0, (*i).first.c_str(), 1, tmp, -1);
986 g_object_unref(G_OBJECT(EntityBreakdownWalker));
989 sprintf (tmp, "%u", Unsigned(g_brushCount.get()));
990 gtk_entry_set_text (GTK_ENTRY (brushes_entry), tmp);
991 sprintf (tmp, "%u", Unsigned(g_entityCount.get()));
992 gtk_entry_set_text (GTK_ENTRY (entities_entry), tmp);
994 modal_dialog_show(window, dialog);
997 window_get_position(window, g_posMapInfoWnd);
999 gtk_widget_destroy(GTK_WIDGET(window));
1007 const char* m_message;
1009 ScopeTimer(const char* message)
1010 : m_message(message)
1016 double elapsed_time = m_timer.elapsed_msec() / 1000.f;
1017 globalOutputStream() << m_message << " timer: " << FloatFormat(elapsed_time, 5, 2) << " second(s) elapsed\n";
1027 void Map_LoadFile (const char *filename)
1029 globalOutputStream() << "Loading map from " << filename << "\n";
1030 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
1032 g_map.m_name = filename;
1033 Map_UpdateTitle(g_map);
1036 ScopeTimer timer("map load");
1038 g_map.m_resource = GlobalReferenceCache().capture(g_map.m_name.c_str());
1039 g_map.m_resource->attach(g_map);
1041 Node_getTraversable(GlobalSceneGraph().root())->traverse(entity_updateworldspawn());
1044 globalOutputStream() << "--- LoadMapFile ---\n";
1045 globalOutputStream() << g_map.m_name.c_str() << "\n";
1047 globalOutputStream() << makeLeftJustified(Unsigned(g_brushCount.get()), 5) << " primitive\n";
1048 globalOutputStream() << makeLeftJustified(Unsigned(g_entityCount.get()), 5) << " entities\n";
1050 //GlobalEntityCreator().printStatistics();
1053 // move the view to a start position
1055 Map_StartPosition();
1057 g_currentMap = &g_map;
1063 virtual bool excluded(scene::Node& node) const = 0;
1066 class ExcludeWalker : public scene::Traversable::Walker
1068 const scene::Traversable::Walker& m_walker;
1069 const Excluder* m_exclude;
1070 mutable bool m_skip;
1072 ExcludeWalker(const scene::Traversable::Walker& walker, const Excluder& exclude)
1073 : m_walker(walker), m_exclude(&exclude), m_skip(false)
1076 bool pre(scene::Node& node) const
1078 if(m_exclude->excluded(node) || node.isRoot())
1089 void post(scene::Node& node) const
1097 m_walker.post(node);
1102 class AnyInstanceSelected : public scene::Instantiable::Visitor
1106 AnyInstanceSelected(bool& selected) : m_selected(selected)
1110 void visit(scene::Instance& instance) const
1112 Selectable* selectable = Instance_getSelectable(instance);
1114 && selectable->isSelected())
1121 bool Node_instanceSelected(scene::Node& node)
1123 scene::Instantiable* instantiable = Node_getInstantiable(node);
1124 ASSERT_NOTNULL(instantiable);
1126 instantiable->forEachInstance(AnyInstanceSelected(selected));
1130 class SelectedDescendantWalker : public scene::Traversable::Walker
1134 SelectedDescendantWalker(bool& selected) : m_selected(selected)
1139 bool pre(scene::Node& node) const
1146 if(Node_instanceSelected(node))
1155 bool Node_selectedDescendant(scene::Node& node)
1158 Node_traverseSubgraph(node, SelectedDescendantWalker(selected));
1162 class SelectionExcluder : public Excluder
1165 bool excluded(scene::Node& node) const
1167 return !Node_selectedDescendant(node);
1171 class IncludeSelectedWalker : public scene::Traversable::Walker
1173 const scene::Traversable::Walker& m_walker;
1174 mutable std::size_t m_selected;
1175 mutable bool m_skip;
1177 bool selectedParent() const
1179 return m_selected != 0;
1182 IncludeSelectedWalker(const scene::Traversable::Walker& walker)
1183 : m_walker(walker), m_selected(0), m_skip(false)
1186 bool pre(scene::Node& node) const
1189 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1190 if(!node.isRoot() && (Node_selectedDescendant(node) || selectedParent()))
1192 if(Node_instanceSelected(node))
1205 void post(scene::Node& node) const
1213 if(Node_instanceSelected(node))
1217 m_walker.post(node);
1222 void Map_Traverse_Selected(scene::Node& root, const scene::Traversable::Walker& walker)
1224 scene::Traversable* traversable = Node_getTraversable(root);
1225 if(traversable != 0)
1228 traversable->traverse(ExcludeWalker(walker, SelectionExcluder()));
1230 traversable->traverse(IncludeSelectedWalker(walker));
1235 void Map_ExportSelected(TextOutputStream& out, const MapFormat& format)
1237 format.writeGraph(GlobalSceneGraph().root(), Map_Traverse_Selected, out);
1240 void Map_Traverse(scene::Node& root, const scene::Traversable::Walker& walker)
1242 scene::Traversable* traversable = Node_getTraversable(root);
1243 if(traversable != 0)
1245 traversable->traverse(walker);
1249 class RegionExcluder : public Excluder
1252 bool excluded(scene::Node& node) const
1254 return node.excluded();
1258 void Map_Traverse_Region(scene::Node& root, const scene::Traversable::Walker& walker)
1260 scene::Traversable* traversable = Node_getTraversable(root);
1261 if(traversable != 0)
1263 traversable->traverse(ExcludeWalker(walker, RegionExcluder()));
1267 bool Map_SaveRegion(const char *filename)
1271 bool success = MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse_Region, filename);
1273 RemoveRegionBrushes();
1279 void Map_RenameAbsolute(const char* absolute)
1281 Resource* resource = GlobalReferenceCache().capture(absolute);
1282 NodeSmartReference clone(NewMapRoot(path_make_relative(absolute, GlobalFileSystem().findRoot(absolute))));
1283 resource->setNode(clone.get_pointer());
1286 //ScopeTimer timer("clone subgraph");
1287 Node_getTraversable(GlobalSceneGraph().root())->traverse(CloneAll(clone));
1290 g_map.m_resource->detach(g_map);
1291 GlobalReferenceCache().release(g_map.m_name.c_str());
1293 g_map.m_resource = resource;
1295 g_map.m_name = absolute;
1296 Map_UpdateTitle(g_map);
1298 g_map.m_resource->attach(g_map);
1301 void Map_Rename(const char* filename)
1303 if(!string_equal(g_map.m_name.c_str(), filename))
1305 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map");
1307 Map_RenameAbsolute(filename);
1309 SceneChangeNotify();
1321 ScopeTimer timer("map save");
1323 return true; // assume success..
1334 //globalOutputStream() << "Map_New\n";
1336 g_map.m_name = "unnamed.map";
1337 Map_UpdateTitle(g_map);
1340 g_map.m_resource = GlobalReferenceCache().capture(g_map.m_name.c_str());
1341 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1342 g_map.m_resource->attach(g_map);
1344 SceneChangeNotify();
1347 FocusViews(g_vector3_identity, 0);
1349 g_currentMap = &g_map;
1352 extern void ConstructRegionBrushes(scene::Node* brushes[6], const Vector3& region_mins, const Vector3& region_maxs);
1354 void ConstructRegionStartpoint(scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs)
1357 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1358 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1359 for now, let's just print an error
1362 Vector3 vOrig(Camera_getOrigin(*g_pParentWnd->GetCamWnd()));
1364 for (int i=0 ; i<3 ; i++)
1366 if (vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i])
1368 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1373 // write the info_playerstart
1375 sprintf(sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2]);
1376 Node_getEntity(*startpoint)->setKeyValue("origin", sTmp);
1377 sprintf(sTmp, "%d", (int)Camera_getAngles(*g_pParentWnd->GetCamWnd())[CAMERA_YAW]);
1378 Node_getEntity(*startpoint)->setKeyValue("angle", sTmp);
1382 ===========================================================
1386 ===========================================================
1389 Vector3 region_mins(g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord);
1390 Vector3 region_maxs(g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord);
1392 scene::Node* region_sides[6];
1393 scene::Node* region_startpoint = 0;
1398 a regioned map will have temp walls put up at the region boundary
1399 \todo TODO TTimo old implementation of region brushes
1400 we still add them straight in the worldspawn and take them out after the map is saved
1401 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1404 void AddRegionBrushes (void)
1410 region_sides[i] = &GlobalBrushCreator().createBrush();
1411 Node_getTraversable(Map_FindOrInsertWorldspawn(g_map))->insert(*region_sides[i]);
1414 region_startpoint = &GlobalEntityCreator().createEntity(GlobalEntityClassManager().findOrInsert("info_player_start", false));
1416 ConstructRegionBrushes(region_sides, region_mins, region_maxs);
1417 ConstructRegionStartpoint(region_startpoint, region_mins, region_maxs);
1419 Node_getTraversable(GlobalSceneGraph().root())->insert(*region_startpoint);
1422 void RemoveRegionBrushes (void)
1424 for(std::size_t i=0; i<6; i++)
1426 Node_getTraversable(*Map_GetWorldspawn(g_map))->erase(*region_sides[i]);
1428 Node_getTraversable(GlobalSceneGraph().root())->erase(*region_startpoint);
1431 inline void exclude_node(scene::Node& node, bool exclude)
1434 ? node.enable(scene::Node::eExcluded)
1435 : node.disable(scene::Node::eExcluded);
1438 class ExcludeAllWalker : public scene::Graph::Walker
1442 ExcludeAllWalker(bool exclude)
1443 : m_exclude(exclude)
1446 bool pre(const scene::Path& path, scene::Instance& instance) const
1448 exclude_node(path.top(), m_exclude);
1454 void Scene_Exclude_All(bool exclude)
1456 GlobalSceneGraph().traverse(ExcludeAllWalker(exclude));
1459 bool Instance_isSelected(const scene::Instance& instance)
1461 const Selectable* selectable = Instance_getSelectable(instance);
1462 return selectable != 0 && selectable->isSelected();
1465 class ExcludeSelectedWalker : public scene::Graph::Walker
1469 ExcludeSelectedWalker(bool exclude)
1470 : m_exclude(exclude)
1473 bool pre(const scene::Path& path, scene::Instance& instance) const
1475 exclude_node(path.top(), (instance.isSelected() || instance.childSelected() || instance.parentSelected()) == m_exclude);
1480 void Scene_Exclude_Selected(bool exclude)
1482 GlobalSceneGraph().traverse(ExcludeSelectedWalker(exclude));
1485 class ExcludeRegionedWalker : public scene::Graph::Walker
1489 ExcludeRegionedWalker(bool exclude)
1490 : m_exclude(exclude)
1493 bool pre(const scene::Path& path, scene::Instance& instance) const
1499 aabb_intersects_aabb(
1500 instance.worldAABB(),
1501 aabb_for_minmax(region_mins, region_maxs)
1511 void Scene_Exclude_Region(bool exclude)
1513 GlobalSceneGraph().traverse(ExcludeRegionedWalker(exclude));
1520 Other filtering options may still be on
1523 void Map_RegionOff()
1525 region_active = false;
1527 region_maxs[0] = g_MaxWorldCoord - 64;
1528 region_mins[0] = g_MinWorldCoord + 64;
1529 region_maxs[1] = g_MaxWorldCoord - 64;
1530 region_mins[1] = g_MinWorldCoord + 64;
1531 region_maxs[2] = g_MaxWorldCoord - 64;
1532 region_mins[2] = g_MinWorldCoord + 64;
1534 Scene_Exclude_All(false);
1537 void Map_ApplyRegion (void)
1539 region_active = true;
1541 Scene_Exclude_Region(false);
1546 ========================
1547 Map_RegionSelectedBrushes
1548 ========================
1550 void Map_RegionSelectedBrushes (void)
1554 if(GlobalSelectionSystem().countSelected() != 0
1555 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive)
1557 region_active = true;
1558 Select_GetBounds (region_mins, region_maxs);
1560 Scene_Exclude_Selected(false);
1562 GlobalSelectionSystem().setSelectedAll(false);
1572 void Map_RegionXY(float x_min, float y_min, float x_max, float y_max)
1576 region_mins[0] = x_min;
1577 region_maxs[0] = x_max;
1578 region_mins[1] = y_min;
1579 region_maxs[1] = y_max;
1580 region_mins[2] = g_MinWorldCoord + 64;
1581 region_maxs[2] = g_MaxWorldCoord - 64;
1586 void Map_RegionBounds(const AABB& bounds)
1590 region_mins = vector3_subtracted(bounds.origin, bounds.extents);
1591 region_maxs = vector3_added(bounds.origin, bounds.extents);
1603 void Map_RegionBrush (void)
1605 if(GlobalSelectionSystem().countSelected() != 0)
1607 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1608 Map_RegionBounds(instance.worldAABB());
1617 bool Map_ImportFile(const char* filename)
1619 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
1621 bool success = false;
1623 Resource* resource = GlobalReferenceCache().capture(filename);
1624 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1625 if(resource->load())
1627 NodeSmartReference clone(NewMapRoot(""));
1630 //ScopeTimer timer("clone subgraph");
1631 Node_getTraversable(*resource->getNode())->traverse(CloneAll(clone));
1634 Map_gatherNamespaced(clone);
1635 Map_mergeClonedNames();
1639 GlobalReferenceCache().release(filename);
1642 SceneChangeNotify();
1652 bool Map_SaveFile(const char* filename)
1654 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map");
1655 return MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse, filename);
1663 // Saves selected world brushes and whole entities with partial/full selections
1665 bool Map_SaveSelected(const char* filename)
1667 return MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse_Selected, filename);
1671 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1673 scene::Node& m_parent;
1675 ParentSelectedBrushesToEntityWalker(scene::Node& parent) : m_parent(parent)
1678 bool pre(const scene::Path& path, scene::Instance& instance) const
1680 if(path.top().get_pointer() != &m_parent
1681 && Node_isPrimitive(path.top()))
1683 Selectable* selectable = Instance_getSelectable(instance);
1685 && selectable->isSelected()
1693 void post(const scene::Path& path, scene::Instance& instance) const
1695 if(path.top().get_pointer() != &m_parent
1696 && Node_isPrimitive(path.top()))
1698 Selectable* selectable = Instance_getSelectable(instance);
1700 && selectable->isSelected()
1703 scene::Node& parent = path.parent();
1704 if(&parent != &m_parent)
1706 NodeSmartReference node(path.top().get());
1707 Node_getTraversable(parent)->erase(node);
1708 Node_getTraversable(m_parent)->insert(node);
1715 void Scene_parentSelectedBrushesToEntity(scene::Graph& graph, scene::Node& parent)
1717 graph.traverse(ParentSelectedBrushesToEntityWalker(parent));
1720 class CountSelectedBrushes : public scene::Graph::Walker
1722 std::size_t& m_count;
1723 mutable std::size_t m_depth;
1725 CountSelectedBrushes(std::size_t& count) : m_count(count), m_depth(0)
1729 bool pre(const scene::Path& path, scene::Instance& instance) const
1731 if(++m_depth != 1 && path.top().get().isRoot())
1735 Selectable* selectable = Instance_getSelectable(instance);
1737 && selectable->isSelected()
1738 && Node_isPrimitive(path.top()))
1744 void post(const scene::Path& path, scene::Instance& instance) const
1750 std::size_t Scene_countSelectedBrushes(scene::Graph& graph)
1753 graph.traverse(CountSelectedBrushes(count));
1765 const char* nodetype_get_name(ENodeType type)
1767 if(type == eNodeMap)
1769 if(type == eNodeEntity)
1771 if(type == eNodePrimitive)
1776 ENodeType node_get_nodetype(scene::Node& node)
1778 if(Node_isEntity(node))
1782 if(Node_isPrimitive(node))
1784 return eNodePrimitive;
1786 return eNodeUnknown;
1789 bool contains_entity(scene::Node& node)
1791 return Node_getTraversable(node) != 0 && !Node_isBrush(node) && !Node_isPatch(node) && !Node_isEntity(node);
1794 bool contains_primitive(scene::Node& node)
1796 return Node_isEntity(node) && Node_getTraversable(node) != 0 && Node_getEntity(node)->isContainer();
1799 ENodeType node_get_contains(scene::Node& node)
1801 if(contains_entity(node))
1805 if(contains_primitive(node))
1807 return eNodePrimitive;
1809 return eNodeUnknown;
1812 void Path_parent(const scene::Path& parent, const scene::Path& child)
1814 ENodeType contains = node_get_contains(parent.top());
1815 ENodeType type = node_get_nodetype(child.top());
1817 if(contains != eNodeUnknown && contains == type)
1819 NodeSmartReference node(child.top().get());
1820 Path_deleteTop(child);
1821 Node_getTraversable(parent.top())->insert(node);
1822 SceneChangeNotify();
1826 globalErrorStream() << "failed - " << nodetype_get_name(type) << " cannot be parented to " << nodetype_get_name(contains) << " container.\n";
1830 void Scene_parentSelected()
1832 UndoableCommand undo("parentSelected");
1834 if(GlobalSelectionSystem().countSelected() > 1)
1836 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1838 const scene::Path& m_parent;
1840 ParentSelectedBrushesToEntityWalker(const scene::Path& parent) : m_parent(parent)
1843 void visit(scene::Instance& instance) const
1845 if(&m_parent != &instance.path())
1847 Path_parent(m_parent, instance.path());
1852 ParentSelectedBrushesToEntityWalker visitor(GlobalSelectionSystem().ultimateSelected().path());
1853 GlobalSelectionSystem().foreachSelected(visitor);
1857 globalOutputStream() << "failed - did not find two selected nodes.\n";
1865 if (ConfirmModified("New Map"))
1873 void maps_directory(StringOutputStream& buffer)
1875 ASSERT_MESSAGE(!string_empty(g_qeglobals.m_userGamePath.c_str()), "maps_directory: user-game-path is empty");
1876 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
1877 Q_mkdir(buffer.c_str());
1880 const char* map_open(const char* title)
1882 StringOutputStream buf(256);
1883 maps_directory(buf);
1884 return file_dialog(GTK_WIDGET(MainFrame_getWindow()), TRUE, title, buf.c_str(), MapFormat::Name());
1887 const char* map_save(const char* title)
1889 StringOutputStream buf(256);
1890 maps_directory(buf);
1891 return file_dialog(GTK_WIDGET(MainFrame_getWindow()), FALSE, title, buf.c_str(), MapFormat::Name());
1896 if (!ConfirmModified("Open Map"))
1899 const char* filename = map_open("Open Map");
1903 MRU_AddFile(filename);
1906 Map_LoadFile(filename);
1912 const char* filename = map_open("Import Map");
1916 UndoableCommand undo("mapImport");
1917 Map_ImportFile(filename);
1923 const char* filename = map_save("Save Map");
1927 MRU_AddFile(filename);
1928 Map_Rename(filename);
1941 if(Map_Unnamed(g_map))
1945 else if(Map_Modified(g_map))
1953 const char* filename = map_save("Export Selection");
1957 Map_SaveSelected(filename);
1963 const char* filename = map_save("Export Region");
1967 Map_SaveRegion(filename);
1975 SceneChangeNotify();
1981 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1982 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1983 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1984 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1986 SceneChangeNotify();
1992 SceneChangeNotify();
1995 void RegionSelected()
1997 Map_RegionSelectedBrushes();
1998 SceneChangeNotify();
2005 class BrushFindByIndexWalker : public scene::Traversable::Walker
2007 mutable std::size_t m_index;
2008 scene::Path& m_path;
2010 BrushFindByIndexWalker(std::size_t index, scene::Path& path)
2011 : m_index(index), m_path(path)
2014 bool pre(scene::Node& node) const
2016 if(Node_isPrimitive(node) && m_index-- == 0)
2018 m_path.push(makeReference(node));
2024 class EntityFindByIndexWalker : public scene::Traversable::Walker
2026 mutable std::size_t m_index;
2027 scene::Path& m_path;
2029 EntityFindByIndexWalker(std::size_t index, scene::Path& path)
2030 : m_index(index), m_path(path)
2033 bool pre(scene::Node& node) const
2035 if(Node_isEntity(node) && m_index-- == 0)
2037 m_path.push(makeReference(node));
2043 void Scene_FindEntityBrush(std::size_t entity, std::size_t brush, scene::Path& path)
2045 path.push(makeReference(GlobalSceneGraph().root()));
2047 Node_getTraversable(path.top())->traverse(EntityFindByIndexWalker(entity, path));
2049 if(path.size() == 2)
2051 scene::Traversable* traversable = Node_getTraversable(path.top());
2052 if(traversable != 0)
2054 traversable->traverse(BrushFindByIndexWalker(brush, path));
2059 inline bool Node_hasChildren(scene::Node& node)
2061 scene::Traversable* traversable = Node_getTraversable(node);
2062 return traversable != 0 && !traversable->empty();
2065 void SelectBrush (int entitynum, int brushnum)
2068 Scene_FindEntityBrush(entitynum, brushnum, path);
2069 if(path.size() == 3 || (path.size() == 2 && !Node_hasChildren(path.top())))
2071 scene::Instance* instance = GlobalSceneGraph().find(path);
2072 ASSERT_MESSAGE(instance != 0, "SelectBrush: path not found in scenegraph");
2073 Selectable* selectable = Instance_getSelectable(*instance);
2074 ASSERT_MESSAGE(selectable != 0, "SelectBrush: path not selectable");
2075 selectable->setSelected(true);
2076 g_pParentWnd->GetXYWnd()->PositionView(instance->worldAABB().origin);
2081 class BrushFindIndexWalker : public scene::Graph::Walker
2083 mutable const scene::Node* m_node;
2084 std::size_t& m_count;
2086 BrushFindIndexWalker(const scene::Node& node, std::size_t& count)
2087 : m_node(&node), m_count(count)
2090 bool pre(const scene::Path& path, scene::Instance& instance) const
2092 if(Node_isPrimitive(path.top()))
2094 if(m_node == path.top().get_pointer())
2107 class EntityFindIndexWalker : public scene::Graph::Walker
2109 mutable const scene::Node* m_node;
2110 std::size_t& m_count;
2112 EntityFindIndexWalker(const scene::Node& node, std::size_t& count)
2113 : m_node(&node), m_count(count)
2116 bool pre(const scene::Path& path, scene::Instance& instance) const
2118 if(Node_isEntity(path.top()))
2120 if(m_node == path.top().get_pointer())
2133 static void GetSelectionIndex (int *ent, int *brush)
2135 std::size_t count_brush = 0;
2136 std::size_t count_entity = 0;
2137 if(GlobalSelectionSystem().countSelected() != 0)
2139 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2141 GlobalSceneGraph().traverse(BrushFindIndexWalker(path.top(), count_brush));
2142 GlobalSceneGraph().traverse(EntityFindIndexWalker(path.parent(), count_entity));
2144 *brush = int(count_brush);
2145 *ent = int(count_entity);
2154 GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Find Brush", G_CALLBACK(dialog_delete_callback), &dialog);
2156 GtkAccelGroup* accel = gtk_accel_group_new();
2157 gtk_window_add_accel_group(window, accel);
2160 GtkVBox* vbox = create_dialog_vbox(4, 4);
2161 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
2163 GtkTable* table = create_dialog_table(2, 2, 4, 4);
2164 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), TRUE, TRUE, 0);
2166 GtkWidget* label = gtk_label_new ("Entity number");
2167 gtk_widget_show (label);
2168 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
2169 (GtkAttachOptions) (0),
2170 (GtkAttachOptions) (0), 0, 0);
2173 GtkWidget* label = gtk_label_new ("Brush number");
2174 gtk_widget_show (label);
2175 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
2176 (GtkAttachOptions) (0),
2177 (GtkAttachOptions) (0), 0, 0);
2180 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
2181 gtk_widget_show(GTK_WIDGET(entry));
2182 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 0, 1,
2183 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2184 (GtkAttachOptions) (0), 0, 0);
2185 gtk_widget_grab_focus(GTK_WIDGET(entry));
2189 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
2190 gtk_widget_show(GTK_WIDGET(entry));
2191 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2,
2192 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2193 (GtkAttachOptions) (0), 0, 0);
2199 GtkHBox* hbox = create_dialog_hbox(4);
2200 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), TRUE, TRUE, 0);
2202 GtkButton* button = create_dialog_button("Find", G_CALLBACK(dialog_button_ok), &dialog);
2203 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0);
2204 widget_make_default(GTK_WIDGET(button));
2205 gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
2208 GtkButton* button = create_dialog_button("Close", G_CALLBACK(dialog_button_cancel), &dialog);
2209 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0);
2210 gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
2215 // Initialize dialog
2219 GetSelectionIndex (&ent, &br);
2220 sprintf (buf, "%i", ent);
2221 gtk_entry_set_text(entity, buf);
2222 sprintf (buf, "%i", br);
2223 gtk_entry_set_text(brush, buf);
2225 if(modal_dialog_show(window, dialog) == eIDOK)
2227 const char *entstr = gtk_entry_get_text(entity);
2228 const char *brushstr = gtk_entry_get_text(brush);
2229 SelectBrush (atoi(entstr), atoi(brushstr));
2232 gtk_widget_destroy(GTK_WIDGET(window));
2236 class MapEntityClasses : public ModuleObserver
2238 std::size_t m_unrealised;
2240 MapEntityClasses() : m_unrealised(1)
2245 if(--m_unrealised == 0)
2247 if(g_map.m_resource != 0)
2249 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
2250 g_map.m_resource->realise();
2256 if(++m_unrealised == 1)
2258 if(g_map.m_resource != 0)
2260 g_map.m_resource->flush();
2261 g_map.m_resource->unrealise();
2267 MapEntityClasses g_MapEntityClasses;
2270 void Map_constructPreferences(PreferencesPage& page)
2272 page.appendCheckBox("", "Load last map on open", g_bLoadLastMap);
2275 #include "preferencesystem.h"
2277 CopiedString g_strLastMap;
2278 bool g_bLoadLastMap = false;
2280 void Map_Construct()
2282 GlobalCommands_insert("RegionOff", FreeCaller<RegionOff>());
2283 GlobalCommands_insert("RegionSetXY", FreeCaller<RegionXY>());
2284 GlobalCommands_insert("RegionSetBrush", FreeCaller<RegionBrush>());
2285 GlobalCommands_insert("RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator('R', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
2287 GlobalPreferenceSystem().registerPreference("LastMap", CopiedStringImportStringCaller(g_strLastMap), CopiedStringExportStringCaller(g_strLastMap));
2288 GlobalPreferenceSystem().registerPreference("LoadLastMap", BoolImportStringCaller(g_bLoadLastMap), BoolExportStringCaller(g_bLoadLastMap));
2289 GlobalPreferenceSystem().registerPreference("MapInfoDlg", WindowPositionImportStringCaller(g_posMapInfoWnd), WindowPositionExportStringCaller(g_posMapInfoWnd));
2291 PreferencesDialog_addSettingsPreferences(FreeCaller1<PreferencesPage&, Map_constructPreferences>());
2293 GlobalEntityClassManager().attach(g_MapEntityClasses);
2298 GlobalEntityClassManager().detach(g_MapEntityClasses);