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";
1051 // move the view to a start position
1053 Map_StartPosition();
1055 g_currentMap = &g_map;
1061 virtual bool excluded(scene::Node& node) const = 0;
1064 class ExcludeWalker : public scene::Traversable::Walker
1066 const scene::Traversable::Walker& m_walker;
1067 const Excluder* m_exclude;
1068 mutable bool m_skip;
1070 ExcludeWalker(const scene::Traversable::Walker& walker, const Excluder& exclude)
1071 : m_walker(walker), m_exclude(&exclude), m_skip(false)
1074 bool pre(scene::Node& node) const
1076 if(m_exclude->excluded(node) || node.isRoot())
1087 void post(scene::Node& node) const
1095 m_walker.post(node);
1100 class AnyInstanceSelected : public scene::Instantiable::Visitor
1104 AnyInstanceSelected(bool& selected) : m_selected(selected)
1108 void visit(scene::Instance& instance) const
1110 Selectable* selectable = Instance_getSelectable(instance);
1112 && selectable->isSelected())
1119 bool Node_instanceSelected(scene::Node& node)
1121 scene::Instantiable* instantiable = Node_getInstantiable(node);
1122 ASSERT_NOTNULL(instantiable);
1124 instantiable->forEachInstance(AnyInstanceSelected(selected));
1128 class SelectedDescendantWalker : public scene::Traversable::Walker
1132 SelectedDescendantWalker(bool& selected) : m_selected(selected)
1137 bool pre(scene::Node& node) const
1144 if(Node_instanceSelected(node))
1153 bool Node_selectedDescendant(scene::Node& node)
1156 Node_traverseSubgraph(node, SelectedDescendantWalker(selected));
1160 class SelectionExcluder : public Excluder
1163 bool excluded(scene::Node& node) const
1165 return !Node_selectedDescendant(node);
1169 class IncludeSelectedWalker : public scene::Traversable::Walker
1171 const scene::Traversable::Walker& m_walker;
1172 mutable std::size_t m_selected;
1173 mutable bool m_skip;
1175 bool selectedParent() const
1177 return m_selected != 0;
1180 IncludeSelectedWalker(const scene::Traversable::Walker& walker)
1181 : m_walker(walker), m_selected(0), m_skip(false)
1184 bool pre(scene::Node& node) const
1187 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1188 if(!node.isRoot() && (Node_selectedDescendant(node) || selectedParent()))
1190 if(Node_instanceSelected(node))
1203 void post(scene::Node& node) const
1211 if(Node_instanceSelected(node))
1215 m_walker.post(node);
1220 void Map_Traverse_Selected(scene::Node& root, const scene::Traversable::Walker& walker)
1222 scene::Traversable* traversable = Node_getTraversable(root);
1223 if(traversable != 0)
1226 traversable->traverse(ExcludeWalker(walker, SelectionExcluder()));
1228 traversable->traverse(IncludeSelectedWalker(walker));
1233 void Map_ExportSelected(TextOutputStream& out, const MapFormat& format)
1235 format.writeGraph(GlobalSceneGraph().root(), Map_Traverse_Selected, out);
1238 void Map_Traverse(scene::Node& root, const scene::Traversable::Walker& walker)
1240 scene::Traversable* traversable = Node_getTraversable(root);
1241 if(traversable != 0)
1243 traversable->traverse(walker);
1247 class RegionExcluder : public Excluder
1250 bool excluded(scene::Node& node) const
1252 return node.excluded();
1256 void Map_Traverse_Region(scene::Node& root, const scene::Traversable::Walker& walker)
1258 scene::Traversable* traversable = Node_getTraversable(root);
1259 if(traversable != 0)
1261 traversable->traverse(ExcludeWalker(walker, RegionExcluder()));
1265 bool Map_SaveRegion(const char *filename)
1269 bool success = MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse_Region, filename);
1271 RemoveRegionBrushes();
1277 void Map_RenameAbsolute(const char* absolute)
1279 Resource* resource = GlobalReferenceCache().capture(absolute);
1280 NodeSmartReference clone(NewMapRoot(path_make_relative(absolute, GlobalFileSystem().findRoot(absolute))));
1281 resource->setNode(clone.get_pointer());
1284 //ScopeTimer timer("clone subgraph");
1285 Node_getTraversable(GlobalSceneGraph().root())->traverse(CloneAll(clone));
1288 g_map.m_resource->detach(g_map);
1289 GlobalReferenceCache().release(g_map.m_name.c_str());
1291 g_map.m_resource = resource;
1293 g_map.m_name = absolute;
1294 Map_UpdateTitle(g_map);
1296 g_map.m_resource->attach(g_map);
1299 void Map_Rename(const char* filename)
1301 if(!string_equal(g_map.m_name.c_str(), filename))
1303 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map");
1305 Map_RenameAbsolute(filename);
1307 SceneChangeNotify();
1319 ScopeTimer timer("map save");
1321 return true; // assume success..
1332 //globalOutputStream() << "Map_New\n";
1334 g_map.m_name = "unnamed.map";
1335 Map_UpdateTitle(g_map);
1338 g_map.m_resource = GlobalReferenceCache().capture(g_map.m_name.c_str());
1339 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1340 g_map.m_resource->attach(g_map);
1342 SceneChangeNotify();
1345 FocusViews(g_vector3_identity, 0);
1347 g_currentMap = &g_map;
1350 extern void ConstructRegionBrushes(scene::Node* brushes[6], const Vector3& region_mins, const Vector3& region_maxs);
1352 void ConstructRegionStartpoint(scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs)
1355 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1356 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1357 for now, let's just print an error
1360 Vector3 vOrig(Camera_getOrigin(*g_pParentWnd->GetCamWnd()));
1362 for (int i=0 ; i<3 ; i++)
1364 if (vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i])
1366 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1371 // write the info_playerstart
1373 sprintf(sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2]);
1374 Node_getEntity(*startpoint)->setKeyValue("origin", sTmp);
1375 sprintf(sTmp, "%d", (int)Camera_getAngles(*g_pParentWnd->GetCamWnd())[CAMERA_YAW]);
1376 Node_getEntity(*startpoint)->setKeyValue("angle", sTmp);
1380 ===========================================================
1384 ===========================================================
1387 Vector3 region_mins(g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord);
1388 Vector3 region_maxs(g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord);
1390 scene::Node* region_sides[6];
1391 scene::Node* region_startpoint = 0;
1396 a regioned map will have temp walls put up at the region boundary
1397 \todo TODO TTimo old implementation of region brushes
1398 we still add them straight in the worldspawn and take them out after the map is saved
1399 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1402 void AddRegionBrushes (void)
1408 region_sides[i] = &GlobalBrushCreator().createBrush();
1409 Node_getTraversable(Map_FindOrInsertWorldspawn(g_map))->insert(*region_sides[i]);
1412 region_startpoint = &GlobalEntityCreator().createEntity(GlobalEntityClassManager().findOrInsert("info_player_start", false));
1414 ConstructRegionBrushes(region_sides, region_mins, region_maxs);
1415 ConstructRegionStartpoint(region_startpoint, region_mins, region_maxs);
1417 Node_getTraversable(GlobalSceneGraph().root())->insert(*region_startpoint);
1420 void RemoveRegionBrushes (void)
1422 for(std::size_t i=0; i<6; i++)
1424 Node_getTraversable(*Map_GetWorldspawn(g_map))->erase(*region_sides[i]);
1426 Node_getTraversable(GlobalSceneGraph().root())->erase(*region_startpoint);
1429 inline void exclude_node(scene::Node& node, bool exclude)
1432 ? node.enable(scene::Node::eExcluded)
1433 : node.disable(scene::Node::eExcluded);
1436 class ExcludeAllWalker : public scene::Graph::Walker
1440 ExcludeAllWalker(bool exclude)
1441 : m_exclude(exclude)
1444 bool pre(const scene::Path& path, scene::Instance& instance) const
1446 exclude_node(path.top(), m_exclude);
1452 void Scene_Exclude_All(bool exclude)
1454 GlobalSceneGraph().traverse(ExcludeAllWalker(exclude));
1457 bool Instance_isSelected(const scene::Instance& instance)
1459 const Selectable* selectable = Instance_getSelectable(instance);
1460 return selectable != 0 && selectable->isSelected();
1463 class ExcludeSelectedWalker : public scene::Graph::Walker
1467 ExcludeSelectedWalker(bool exclude)
1468 : m_exclude(exclude)
1471 bool pre(const scene::Path& path, scene::Instance& instance) const
1473 exclude_node(path.top(), (instance.isSelected() || instance.childSelected() || instance.parentSelected()) == m_exclude);
1478 void Scene_Exclude_Selected(bool exclude)
1480 GlobalSceneGraph().traverse(ExcludeSelectedWalker(exclude));
1483 class ExcludeRegionedWalker : public scene::Graph::Walker
1487 ExcludeRegionedWalker(bool exclude)
1488 : m_exclude(exclude)
1491 bool pre(const scene::Path& path, scene::Instance& instance) const
1497 aabb_intersects_aabb(
1498 instance.worldAABB(),
1499 aabb_for_minmax(region_mins, region_maxs)
1509 void Scene_Exclude_Region(bool exclude)
1511 GlobalSceneGraph().traverse(ExcludeRegionedWalker(exclude));
1518 Other filtering options may still be on
1521 void Map_RegionOff()
1523 region_active = false;
1525 region_maxs[0] = g_MaxWorldCoord - 64;
1526 region_mins[0] = g_MinWorldCoord + 64;
1527 region_maxs[1] = g_MaxWorldCoord - 64;
1528 region_mins[1] = g_MinWorldCoord + 64;
1529 region_maxs[2] = g_MaxWorldCoord - 64;
1530 region_mins[2] = g_MinWorldCoord + 64;
1532 Scene_Exclude_All(false);
1535 void Map_ApplyRegion (void)
1537 region_active = true;
1539 Scene_Exclude_Region(false);
1544 ========================
1545 Map_RegionSelectedBrushes
1546 ========================
1548 void Map_RegionSelectedBrushes (void)
1552 if(GlobalSelectionSystem().countSelected() != 0
1553 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive)
1555 region_active = true;
1556 Select_GetBounds (region_mins, region_maxs);
1558 Scene_Exclude_Selected(false);
1560 GlobalSelectionSystem().setSelectedAll(false);
1570 void Map_RegionXY(float x_min, float y_min, float x_max, float y_max)
1574 region_mins[0] = x_min;
1575 region_maxs[0] = x_max;
1576 region_mins[1] = y_min;
1577 region_maxs[1] = y_max;
1578 region_mins[2] = g_MinWorldCoord + 64;
1579 region_maxs[2] = g_MaxWorldCoord - 64;
1584 void Map_RegionBounds(const AABB& bounds)
1588 region_mins = vector3_subtracted(bounds.origin, bounds.extents);
1589 region_maxs = vector3_added(bounds.origin, bounds.extents);
1601 void Map_RegionBrush (void)
1603 if(GlobalSelectionSystem().countSelected() != 0)
1605 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1606 Map_RegionBounds(instance.worldAABB());
1615 bool Map_ImportFile(const char* filename)
1617 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
1619 bool success = false;
1621 Resource* resource = GlobalReferenceCache().capture(filename);
1622 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1623 if(resource->load())
1625 NodeSmartReference clone(NewMapRoot(""));
1628 //ScopeTimer timer("clone subgraph");
1629 Node_getTraversable(*resource->getNode())->traverse(CloneAll(clone));
1632 Map_gatherNamespaced(clone);
1633 Map_mergeClonedNames();
1637 GlobalReferenceCache().release(filename);
1640 SceneChangeNotify();
1650 bool Map_SaveFile(const char* filename)
1652 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map");
1653 return MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse, filename);
1661 // Saves selected world brushes and whole entities with partial/full selections
1663 bool Map_SaveSelected(const char* filename)
1665 return MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse_Selected, filename);
1669 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1671 scene::Node& m_parent;
1673 ParentSelectedBrushesToEntityWalker(scene::Node& parent) : m_parent(parent)
1676 bool pre(const scene::Path& path, scene::Instance& instance) const
1678 if(path.top().get_pointer() != &m_parent
1679 && Node_isPrimitive(path.top()))
1681 Selectable* selectable = Instance_getSelectable(instance);
1683 && selectable->isSelected()
1691 void post(const scene::Path& path, scene::Instance& instance) const
1693 if(path.top().get_pointer() != &m_parent
1694 && Node_isPrimitive(path.top()))
1696 Selectable* selectable = Instance_getSelectable(instance);
1698 && selectable->isSelected()
1701 scene::Node& parent = path.parent();
1702 if(&parent != &m_parent)
1704 NodeSmartReference node(path.top().get());
1705 Node_getTraversable(parent)->erase(node);
1706 Node_getTraversable(m_parent)->insert(node);
1713 void Scene_parentSelectedBrushesToEntity(scene::Graph& graph, scene::Node& parent)
1715 graph.traverse(ParentSelectedBrushesToEntityWalker(parent));
1718 class CountSelectedBrushes : public scene::Graph::Walker
1720 std::size_t& m_count;
1721 mutable std::size_t m_depth;
1723 CountSelectedBrushes(std::size_t& count) : m_count(count), m_depth(0)
1727 bool pre(const scene::Path& path, scene::Instance& instance) const
1729 if(++m_depth != 1 && path.top().get().isRoot())
1733 Selectable* selectable = Instance_getSelectable(instance);
1735 && selectable->isSelected()
1736 && Node_isPrimitive(path.top()))
1742 void post(const scene::Path& path, scene::Instance& instance) const
1748 std::size_t Scene_countSelectedBrushes(scene::Graph& graph)
1751 graph.traverse(CountSelectedBrushes(count));
1763 const char* nodetype_get_name(ENodeType type)
1765 if(type == eNodeMap)
1767 if(type == eNodeEntity)
1769 if(type == eNodePrimitive)
1774 ENodeType node_get_nodetype(scene::Node& node)
1776 if(Node_isEntity(node))
1780 if(Node_isPrimitive(node))
1782 return eNodePrimitive;
1784 return eNodeUnknown;
1787 bool contains_entity(scene::Node& node)
1789 return Node_getTraversable(node) != 0 && !Node_isBrush(node) && !Node_isPatch(node) && !Node_isEntity(node);
1792 bool contains_primitive(scene::Node& node)
1794 return Node_isEntity(node) && Node_getTraversable(node) != 0 && Node_getEntity(node)->isContainer();
1797 ENodeType node_get_contains(scene::Node& node)
1799 if(contains_entity(node))
1803 if(contains_primitive(node))
1805 return eNodePrimitive;
1807 return eNodeUnknown;
1810 void Path_parent(const scene::Path& parent, const scene::Path& child)
1812 ENodeType contains = node_get_contains(parent.top());
1813 ENodeType type = node_get_nodetype(child.top());
1815 if(contains != eNodeUnknown && contains == type)
1817 NodeSmartReference node(child.top().get());
1818 Path_deleteTop(child);
1819 Node_getTraversable(parent.top())->insert(node);
1820 SceneChangeNotify();
1824 globalErrorStream() << "failed - " << nodetype_get_name(type) << " cannot be parented to " << nodetype_get_name(contains) << " container.\n";
1828 void Scene_parentSelected()
1830 UndoableCommand undo("parentSelected");
1832 if(GlobalSelectionSystem().countSelected() > 1)
1834 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1836 const scene::Path& m_parent;
1838 ParentSelectedBrushesToEntityWalker(const scene::Path& parent) : m_parent(parent)
1841 void visit(scene::Instance& instance) const
1843 if(&m_parent != &instance.path())
1845 Path_parent(m_parent, instance.path());
1850 ParentSelectedBrushesToEntityWalker visitor(GlobalSelectionSystem().ultimateSelected().path());
1851 GlobalSelectionSystem().foreachSelected(visitor);
1855 globalOutputStream() << "failed - did not find two selected nodes.\n";
1863 if (ConfirmModified("New Map"))
1871 void maps_directory(StringOutputStream& buffer)
1873 ASSERT_MESSAGE(!string_empty(g_qeglobals.m_userGamePath.c_str()), "maps_directory: user-game-path is empty");
1874 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
1875 Q_mkdir(buffer.c_str());
1878 const char* map_open(const char* title)
1880 StringOutputStream buf(256);
1881 maps_directory(buf);
1882 return file_dialog(GTK_WIDGET(MainFrame_getWindow()), TRUE, title, buf.c_str(), MapFormat::Name());
1885 const char* map_save(const char* title)
1887 StringOutputStream buf(256);
1888 maps_directory(buf);
1889 return file_dialog(GTK_WIDGET(MainFrame_getWindow()), FALSE, title, buf.c_str(), MapFormat::Name());
1894 if (!ConfirmModified("Open Map"))
1897 const char* filename = map_open("Open Map");
1901 MRU_AddFile(filename);
1904 Map_LoadFile(filename);
1910 const char* filename = map_open("Import Map");
1914 UndoableCommand undo("mapImport");
1915 Map_ImportFile(filename);
1921 const char* filename = map_save("Save Map");
1925 MRU_AddFile(filename);
1926 Map_Rename(filename);
1939 if(Map_Unnamed(g_map))
1943 else if(Map_Modified(g_map))
1951 const char* filename = map_save("Export Selection");
1955 Map_SaveSelected(filename);
1961 const char* filename = map_save("Export Region");
1965 Map_SaveRegion(filename);
1973 SceneChangeNotify();
1979 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1980 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
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()
1984 SceneChangeNotify();
1990 SceneChangeNotify();
1993 void RegionSelected()
1995 Map_RegionSelectedBrushes();
1996 SceneChangeNotify();
2003 class BrushFindByIndexWalker : public scene::Traversable::Walker
2005 mutable std::size_t m_index;
2006 scene::Path& m_path;
2008 BrushFindByIndexWalker(std::size_t index, scene::Path& path)
2009 : m_index(index), m_path(path)
2012 bool pre(scene::Node& node) const
2014 if(Node_isPrimitive(node) && m_index-- == 0)
2016 m_path.push(makeReference(node));
2022 class EntityFindByIndexWalker : public scene::Traversable::Walker
2024 mutable std::size_t m_index;
2025 scene::Path& m_path;
2027 EntityFindByIndexWalker(std::size_t index, scene::Path& path)
2028 : m_index(index), m_path(path)
2031 bool pre(scene::Node& node) const
2033 if(Node_isEntity(node) && m_index-- == 0)
2035 m_path.push(makeReference(node));
2041 void Scene_FindEntityBrush(std::size_t entity, std::size_t brush, scene::Path& path)
2043 path.push(makeReference(GlobalSceneGraph().root()));
2045 Node_getTraversable(path.top())->traverse(EntityFindByIndexWalker(entity, path));
2047 if(path.size() == 2)
2049 scene::Traversable* traversable = Node_getTraversable(path.top());
2050 if(traversable != 0)
2052 traversable->traverse(BrushFindByIndexWalker(brush, path));
2057 inline bool Node_hasChildren(scene::Node& node)
2059 scene::Traversable* traversable = Node_getTraversable(node);
2060 return traversable != 0 && !traversable->empty();
2063 void SelectBrush (int entitynum, int brushnum)
2066 Scene_FindEntityBrush(entitynum, brushnum, path);
2067 if(path.size() == 3 || (path.size() == 2 && !Node_hasChildren(path.top())))
2069 scene::Instance* instance = GlobalSceneGraph().find(path);
2070 ASSERT_MESSAGE(instance != 0, "SelectBrush: path not found in scenegraph");
2071 Selectable* selectable = Instance_getSelectable(*instance);
2072 ASSERT_MESSAGE(selectable != 0, "SelectBrush: path not selectable");
2073 selectable->setSelected(true);
2074 g_pParentWnd->GetXYWnd()->PositionView(instance->worldAABB().origin);
2079 class BrushFindIndexWalker : public scene::Graph::Walker
2081 mutable const scene::Node* m_node;
2082 std::size_t& m_count;
2084 BrushFindIndexWalker(const scene::Node& node, std::size_t& count)
2085 : m_node(&node), m_count(count)
2088 bool pre(const scene::Path& path, scene::Instance& instance) const
2090 if(Node_isPrimitive(path.top()))
2092 if(m_node == path.top().get_pointer())
2105 class EntityFindIndexWalker : public scene::Graph::Walker
2107 mutable const scene::Node* m_node;
2108 std::size_t& m_count;
2110 EntityFindIndexWalker(const scene::Node& node, std::size_t& count)
2111 : m_node(&node), m_count(count)
2114 bool pre(const scene::Path& path, scene::Instance& instance) const
2116 if(Node_isEntity(path.top()))
2118 if(m_node == path.top().get_pointer())
2131 static void GetSelectionIndex (int *ent, int *brush)
2133 std::size_t count_brush = 0;
2134 std::size_t count_entity = 0;
2135 if(GlobalSelectionSystem().countSelected() != 0)
2137 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2139 GlobalSceneGraph().traverse(BrushFindIndexWalker(path.top(), count_brush));
2140 GlobalSceneGraph().traverse(EntityFindIndexWalker(path.parent(), count_entity));
2142 *brush = int(count_brush);
2143 *ent = int(count_entity);
2152 GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Find Brush", G_CALLBACK(dialog_delete_callback), &dialog);
2154 GtkAccelGroup* accel = gtk_accel_group_new();
2155 gtk_window_add_accel_group(window, accel);
2158 GtkVBox* vbox = create_dialog_vbox(4, 4);
2159 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
2161 GtkTable* table = create_dialog_table(2, 2, 4, 4);
2162 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), TRUE, TRUE, 0);
2164 GtkWidget* label = gtk_label_new ("Entity number");
2165 gtk_widget_show (label);
2166 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
2167 (GtkAttachOptions) (0),
2168 (GtkAttachOptions) (0), 0, 0);
2171 GtkWidget* label = gtk_label_new ("Brush number");
2172 gtk_widget_show (label);
2173 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
2174 (GtkAttachOptions) (0),
2175 (GtkAttachOptions) (0), 0, 0);
2178 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
2179 gtk_widget_show(GTK_WIDGET(entry));
2180 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 0, 1,
2181 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2182 (GtkAttachOptions) (0), 0, 0);
2183 gtk_widget_grab_focus(GTK_WIDGET(entry));
2187 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
2188 gtk_widget_show(GTK_WIDGET(entry));
2189 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2,
2190 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2191 (GtkAttachOptions) (0), 0, 0);
2197 GtkHBox* hbox = create_dialog_hbox(4);
2198 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), TRUE, TRUE, 0);
2200 GtkButton* button = create_dialog_button("Find", G_CALLBACK(dialog_button_ok), &dialog);
2201 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0);
2202 widget_make_default(GTK_WIDGET(button));
2203 gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
2206 GtkButton* button = create_dialog_button("Close", G_CALLBACK(dialog_button_cancel), &dialog);
2207 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0);
2208 gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
2213 // Initialize dialog
2217 GetSelectionIndex (&ent, &br);
2218 sprintf (buf, "%i", ent);
2219 gtk_entry_set_text(entity, buf);
2220 sprintf (buf, "%i", br);
2221 gtk_entry_set_text(brush, buf);
2223 if(modal_dialog_show(window, dialog) == eIDOK)
2225 const char *entstr = gtk_entry_get_text(entity);
2226 const char *brushstr = gtk_entry_get_text(brush);
2227 SelectBrush (atoi(entstr), atoi(brushstr));
2230 gtk_widget_destroy(GTK_WIDGET(window));
2234 class MapEntityClasses : public ModuleObserver
2236 std::size_t m_unrealised;
2238 MapEntityClasses() : m_unrealised(1)
2243 if(--m_unrealised == 0)
2245 if(g_map.m_resource != 0)
2247 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
2248 g_map.m_resource->realise();
2254 if(++m_unrealised == 1)
2256 if(g_map.m_resource != 0)
2258 g_map.m_resource->flush();
2259 g_map.m_resource->unrealise();
2265 MapEntityClasses g_MapEntityClasses;
2268 void Map_constructPreferences(PreferencesPage& page)
2270 page.appendCheckBox("", "Load last map on open", g_bLoadLastMap);
2273 #include "preferencesystem.h"
2275 CopiedString g_strLastMap;
2276 bool g_bLoadLastMap = false;
2278 void Map_Construct()
2280 GlobalCommands_insert("RegionOff", FreeCaller<RegionOff>());
2281 GlobalCommands_insert("RegionSetXY", FreeCaller<RegionXY>());
2282 GlobalCommands_insert("RegionSetBrush", FreeCaller<RegionBrush>());
2283 GlobalCommands_insert("RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator('R', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
2285 GlobalPreferenceSystem().registerPreference("LastMap", CopiedStringImportStringCaller(g_strLastMap), CopiedStringExportStringCaller(g_strLastMap));
2286 GlobalPreferenceSystem().registerPreference("LoadLastMap", BoolImportStringCaller(g_bLoadLastMap), BoolExportStringCaller(g_bLoadLastMap));
2287 GlobalPreferenceSystem().registerPreference("MapInfoDlg", WindowPositionImportStringCaller(g_posMapInfoWnd), WindowPositionExportStringCaller(g_posMapInfoWnd));
2289 PreferencesDialog_addSettingsPreferences(FreeCaller1<PreferencesPage&, Map_constructPreferences>());
2291 GlobalEntityClassManager().attach(g_MapEntityClasses);
2296 GlobalEntityClassManager().detach(g_MapEntityClasses);