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"
39 #include "ifilesystem.h"
40 #include "namespace.h"
41 #include "moduleobserver.h"
42 #include "moduleobservers.h"
46 #include <gtk/gtkmain.h>
47 #include <gtk/gtkbox.h>
48 #include <gtk/gtkentry.h>
49 #include <gtk/gtklabel.h>
50 #include <gtk/gtktable.h>
51 #include <gtk/gtktreemodel.h>
52 #include <gtk/gtktreeview.h>
53 #include <gtk/gtkliststore.h>
54 #include <gtk/gtkcellrenderertext.h>
57 #include "transformlib.h"
58 #include "selectionlib.h"
59 #include "instancelib.h"
60 #include "traverselib.h"
62 #include "eclasslib.h"
64 #include "stream/textfilestream.h"
66 #include "uniquenames.h"
67 #include "modulesystem/singletonmodule.h"
68 #include "modulesystem/moduleregistry.h"
69 #include "stream/stringstream.h"
70 #include "signal/signal.h"
72 #include "gtkutil/filechooser.h"
76 #include "filetypes.h"
78 #include "entityinspector.h"
81 #include "camwindow.h"
83 #include "mainframe.h"
84 #include "preferences.h"
85 #include "referencecache.h"
89 #include "brushmodule.h"
99 //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
100 m_names.insert( name_read( c_str() ) );
105 //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
106 m_names.erase( name_read( c_str() ) );
110 NameObserver& operator=( const NameObserver& other );
112 NameObserver( UniqueNames& names ) : m_names( names ){
115 NameObserver( const NameObserver& other ) : m_names( other.m_names ), m_name( other.m_name ){
122 return string_empty( c_str() );
124 const char* c_str() const {
125 return m_name.c_str();
127 void nameChanged( const char* name ){
132 typedef MemberCaller1<NameObserver, const char*, &NameObserver::nameChanged> NameChangedCaller;
135 class BasicNamespace : public Namespace
137 typedef std::map<NameCallback, NameObserver> Names;
139 UniqueNames m_uniqueNames;
142 ASSERT_MESSAGE( m_names.empty(), "namespace: names still registered at shutdown" );
144 void attach( const NameCallback& setName, const NameCallbackCallback& attachObserver ){
145 std::pair<Names::iterator, bool> result = m_names.insert( Names::value_type( setName, m_uniqueNames ) );
146 ASSERT_MESSAGE( result.second, "cannot attach name" );
147 attachObserver( NameObserver::NameChangedCaller( ( *result.first ).second ) );
148 //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
150 void detach( const NameCallback& setName, const NameCallbackCallback& detachObserver ){
151 Names::iterator i = m_names.find( setName );
152 ASSERT_MESSAGE( i != m_names.end(), "cannot detach name" );
153 //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
154 detachObserver( NameObserver::NameChangedCaller( ( *i ).second ) );
158 void makeUnique( const char* name, const NameCallback& setName ) const {
160 name_write( buffer, m_uniqueNames.make_unique( name_read( name ) ) );
164 void mergeNames( const BasicNamespace& other ) const {
165 typedef std::list<NameCallback> SetNameCallbacks;
166 typedef std::map<CopiedString, SetNameCallbacks> NameGroups;
169 UniqueNames uniqueNames( other.m_uniqueNames );
171 for ( Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i )
173 groups[( *i ).second.c_str()].push_back( ( *i ).first );
176 for ( NameGroups::iterator i = groups.begin(); i != groups.end(); ++i )
178 name_t uniqueName( uniqueNames.make_unique( name_read( ( *i ).first.c_str() ) ) );
179 uniqueNames.insert( uniqueName );
182 name_write( buffer, uniqueName );
184 //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
186 SetNameCallbacks& setNameCallbacks = ( *i ).second;
188 for ( SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j )
196 BasicNamespace g_defaultNamespace;
197 BasicNamespace g_cloneNamespace;
201 Namespace* m_namespace;
203 typedef Namespace Type;
204 STRING_CONSTANT( Name, "*" );
207 m_namespace = &g_defaultNamespace;
209 Namespace* getTable(){
214 typedef SingletonModule<NamespaceAPI> NamespaceModule;
215 typedef Static<NamespaceModule> StaticNamespaceModule;
216 StaticRegisterModule staticRegisterDefaultNamespace( StaticNamespaceModule::instance() );
219 std::list<Namespaced*> g_cloned;
221 inline Namespaced* Node_getNamespaced( scene::Node& node ){
222 return NodeTypeCast<Namespaced>::cast( node );
225 void Node_gatherNamespaced( scene::Node& node ){
226 Namespaced* namespaced = Node_getNamespaced( node );
227 if ( namespaced != 0 ) {
228 g_cloned.push_back( namespaced );
232 class GatherNamespaced : public scene::Traversable::Walker
235 bool pre( scene::Node& node ) const {
236 Node_gatherNamespaced( node );
241 void Map_gatherNamespaced( scene::Node& root ){
242 Node_traverseSubgraph( root, GatherNamespaced() );
245 void Map_mergeClonedNames(){
246 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
248 ( *i )->setNamespace( g_cloneNamespace );
250 g_cloneNamespace.mergeNames( g_defaultNamespace );
251 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
253 ( *i )->setNamespace( g_defaultNamespace );
266 void set( scene::Node* node ){
275 scene::Node* get() const {
281 void Map_SetValid( Map& map, bool valid );
282 void Map_UpdateTitle( const Map& map );
283 void Map_SetWorldspawn( Map& map, scene::Node* node );
286 class Map : public ModuleObserver
290 Resource* m_resource;
294 void ( *m_modified_changed )( const Map& );
296 Signal0 m_mapValidCallbacks;
298 WorldNode m_world_node; // "classname" "worldspawn" !
300 Map() : m_resource( 0 ), m_valid( false ), m_modified_changed( Map_UpdateTitle ){
304 if ( m_resource != 0 ) {
305 if ( Map_Unnamed( *this ) ) {
306 g_map.m_resource->setNode( NewMapRoot( "" ).get_pointer() );
307 MapFile* map = Node_getMapFile( *g_map.m_resource->getNode() );
317 GlobalSceneGraph().insert_root( *m_resource->getNode() );
321 Map_SetValid( g_map, true );
325 if ( m_resource != 0 ) {
326 Map_SetValid( g_map, false );
327 Map_SetWorldspawn( g_map, 0 );
330 GlobalUndoSystem().clear();
332 GlobalSceneGraph().erase_root();
338 Map* g_currentMap = 0;
340 void Map_addValidCallback( Map& map, const SignalHandler& handler ){
341 map.m_mapValidCallbacks.connectLast( handler );
344 bool Map_Valid( const Map& map ){
348 void Map_SetValid( Map& map, bool valid ){
350 map.m_mapValidCallbacks();
354 const char* Map_Name( const Map& map ){
355 return map.m_name.c_str();
358 bool Map_Unnamed( const Map& map ){
359 return string_equal( Map_Name( map ), "unnamed.map" );
362 inline const MapFormat& MapFormat_forFile( const char* filename ){
363 const char* moduleName = findModuleName( GetFileTypeRegistry(), MapFormat::Name(), path_get_extension( filename ) );
364 MapFormat* format = Radiant_getMapModules().findModule( moduleName );
365 ASSERT_MESSAGE( format != 0, "map format not found for file " << makeQuoted( filename ) );
369 const MapFormat& Map_getFormat( const Map& map ){
370 return MapFormat_forFile( Map_Name( map ) );
374 bool Map_Modified( const Map& map ){
375 return map.m_modified;
378 void Map_SetModified( Map& map, bool modified ){
379 if ( map.m_modified ^ modified ) {
380 map.m_modified = modified;
382 map.m_modified_changed( map );
386 void Map_UpdateTitle( const Map& map ){
387 Sys_SetTitle( map.m_name.c_str(), Map_Modified( map ) );
392 scene::Node* Map_GetWorldspawn( const Map& map ){
393 return map.m_world_node.get();
396 void Map_SetWorldspawn( Map& map, scene::Node* node ){
397 map.m_world_node.set( node );
402 // need that in a variable, will have to tweak depending on the game
403 float g_MaxWorldCoord = 64 * 1024;
404 float g_MinWorldCoord = -64 * 1024;
406 void AddRegionBrushes( void );
407 void RemoveRegionBrushes( void );
409 /* Map open/close observers */
411 ModuleObservers g_mapPathObservers;
413 class MapFileObserver : public ModuleObserver
416 // Restart VFS to apply new pak filtering based on mapname
417 // needed for daemon dpk vfs
423 MapFileObserver g_mapFileObserver;
425 void BindMapFileObservers(){
426 g_mapPathObservers.attach( g_mapFileObserver );
429 void UnBindMapFileObservers(){
430 g_mapPathObservers.detach( g_mapFileObserver );
437 free all map elements, reinitialize the structures that depend on them
443 g_map.m_resource->detach( g_map );
444 GlobalReferenceCache().release( g_map.m_name.c_str() );
445 g_map.m_resource = 0;
450 Brush_unlatchPreferences();
451 g_mapPathObservers.unrealise();
454 class EntityFindByClassname : public scene::Graph::Walker
459 EntityFindByClassname( const char* name, Entity*& entity ) : m_name( name ), m_entity( entity ){
462 bool pre( const scene::Path& path, scene::Instance& instance ) const {
463 if ( m_entity == 0 ) {
464 Entity* entity = Node_getEntity( path.top() );
466 && string_equal( m_name, entity->getKeyValue( "classname" ) ) ) {
474 Entity* Scene_FindEntityByClass( const char* name ){
476 GlobalSceneGraph().traverse( EntityFindByClassname( name, entity ) );
480 Entity *Scene_FindPlayerStart(){
481 typedef const char* StaticString;
482 StaticString strings[] = {
484 "info_player_deathmatch",
485 "team_CTF_redplayer",
486 "team_CTF_blueplayer",
488 "team_CTF_bluespawn",
490 typedef const StaticString* StaticStringIterator;
491 for ( StaticStringIterator i = strings, end = strings + ( sizeof( strings ) / sizeof( StaticString ) ); i != end; ++i )
493 Entity* entity = Scene_FindEntityByClass( *i );
502 // move the view to a start position
506 void FocusViews( const Vector3& point, float angle ){
507 CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
508 Camera_setOrigin( camwnd, point );
509 Vector3 angles( Camera_getAngles( camwnd ) );
510 angles[CAMERA_PITCH] = 0;
511 angles[CAMERA_YAW] = angle;
512 Camera_setAngles( camwnd, angles );
514 XYWnd* xywnd = g_pParentWnd->GetXYWnd();
515 xywnd->SetOrigin( point );
518 #include "stringio.h"
520 void Map_StartPosition(){
521 Entity* entity = Scene_FindPlayerStart();
525 string_parse_vector3( entity->getKeyValue( "origin" ), origin );
526 FocusViews( origin, string_read_float( entity->getKeyValue( "angle" ) ) );
530 FocusViews( g_vector3_identity, 0 );
535 inline bool node_is_worldspawn( scene::Node& node ){
536 Entity* entity = Node_getEntity( node );
537 return entity != 0 && string_equal( entity->getKeyValue( "classname" ), "worldspawn" );
541 // use first worldspawn
542 class entity_updateworldspawn : public scene::Traversable::Walker
545 bool pre( scene::Node& node ) const {
546 if ( node_is_worldspawn( node ) ) {
547 if ( Map_GetWorldspawn( g_map ) == 0 ) {
548 Map_SetWorldspawn( g_map, &node );
555 scene::Node* Map_FindWorldspawn( Map& map ){
556 Map_SetWorldspawn( map, 0 );
558 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
560 return Map_GetWorldspawn( map );
564 class CollectAllWalker : public scene::Traversable::Walker
567 UnsortedNodeSet& m_nodes;
569 CollectAllWalker( scene::Node& root, UnsortedNodeSet& nodes ) : m_root( root ), m_nodes( nodes ){
571 bool pre( scene::Node& node ) const {
572 m_nodes.insert( NodeSmartReference( node ) );
573 Node_getTraversable( m_root )->erase( node );
578 void Node_insertChildFirst( scene::Node& parent, scene::Node& child ){
579 UnsortedNodeSet nodes;
580 Node_getTraversable( parent )->traverse( CollectAllWalker( parent, nodes ) );
581 Node_getTraversable( parent )->insert( child );
583 for ( UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i )
585 Node_getTraversable( parent )->insert( ( *i ) );
589 scene::Node& createWorldspawn(){
590 NodeSmartReference worldspawn( GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "worldspawn", true ) ) );
591 Node_insertChildFirst( GlobalSceneGraph().root(), worldspawn );
595 void Map_UpdateWorldspawn( Map& map ){
596 if ( Map_FindWorldspawn( map ) == 0 ) {
597 Map_SetWorldspawn( map, &createWorldspawn() );
601 scene::Node& Map_FindOrInsertWorldspawn( Map& map ){
602 Map_UpdateWorldspawn( map );
603 return *Map_GetWorldspawn( map );
607 class MapMergeAll : public scene::Traversable::Walker
609 mutable scene::Path m_path;
611 MapMergeAll( const scene::Path& root )
614 bool pre( scene::Node& node ) const {
615 Node_getTraversable( m_path.top() )->insert( node );
616 m_path.push( makeReference( node ) );
617 selectPath( m_path, true );
620 void post( scene::Node& node ) const {
625 class MapMergeEntities : public scene::Traversable::Walker
627 mutable scene::Path m_path;
629 MapMergeEntities( const scene::Path& root )
632 bool pre( scene::Node& node ) const {
633 if ( node_is_worldspawn( node ) ) {
634 scene::Node* world_node = Map_FindWorldspawn( g_map );
635 if ( world_node == 0 ) {
636 Map_SetWorldspawn( g_map, &node );
637 Node_getTraversable( m_path.top().get() )->insert( node );
638 m_path.push( makeReference( node ) );
639 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
643 m_path.push( makeReference( *world_node ) );
644 Node_getTraversable( node )->traverse( MapMergeAll( m_path ) );
649 Node_getTraversable( m_path.top() )->insert( node );
650 m_path.push( makeReference( node ) );
651 if ( node_is_group( node ) ) {
652 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
656 selectPath( m_path, true );
661 void post( scene::Node& node ) const {
666 class BasicContainer : public scene::Node::Symbiot
670 NodeTypeCastTable m_casts;
673 NodeContainedCast<BasicContainer, scene::Traversable>::install( m_casts );
675 NodeTypeCastTable& get(){
681 TraversableNodeSet m_traverse;
684 typedef LazyStatic<TypeCasts> StaticTypeCasts;
686 scene::Traversable& get( NullType<scene::Traversable>){
690 BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){
700 /// Merges the map graph rooted at \p node into the global scene-graph.
701 void MergeMap( scene::Node& node ){
702 Node_getTraversable( node )->traverse( MapMergeEntities( scene::Path( makeReference( GlobalSceneGraph().root() ) ) ) );
704 void Map_ImportSelected( TextInputStream& in, const MapFormat& format ){
705 NodeSmartReference node( ( new BasicContainer )->node() );
706 format.readGraph( node, in, GlobalEntityCreator() );
707 Map_gatherNamespaced( node );
708 Map_mergeClonedNames();
712 inline scene::Cloneable* Node_getCloneable( scene::Node& node ){
713 return NodeTypeCast<scene::Cloneable>::cast( node );
716 inline scene::Node& node_clone( scene::Node& node ){
717 scene::Cloneable* cloneable = Node_getCloneable( node );
718 if ( cloneable != 0 ) {
719 return cloneable->clone();
722 return ( new scene::NullNode )->node();
725 class CloneAll : public scene::Traversable::Walker
727 mutable scene::Path m_path;
729 CloneAll( scene::Node& root )
730 : m_path( makeReference( root ) ){
732 bool pre( scene::Node& node ) const {
733 if ( node.isRoot() ) {
737 m_path.push( makeReference( node_clone( node ) ) );
738 m_path.top().get().IncRef();
742 void post( scene::Node& node ) const {
743 if ( node.isRoot() ) {
747 Node_getTraversable( m_path.parent() )->insert( m_path.top() );
749 m_path.top().get().DecRef();
754 scene::Node& Node_Clone( scene::Node& node ){
755 scene::Node& clone = node_clone( node );
756 scene::Traversable* traversable = Node_getTraversable( node );
757 if ( traversable != 0 ) {
758 traversable->traverse( CloneAll( clone ) );
764 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
766 class EntityBreakdownWalker : public scene::Graph::Walker
768 EntityBreakdown& m_entitymap;
770 EntityBreakdownWalker( EntityBreakdown& entitymap )
771 : m_entitymap( entitymap ){
773 bool pre( const scene::Path& path, scene::Instance& instance ) const {
774 Entity* entity = Node_getEntity( path.top() );
776 const EntityClass& eclass = entity->getEntityClass();
777 if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
778 m_entitymap[eclass.name()] = 1;
780 else{ ++m_entitymap[eclass.name()]; }
786 void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
787 GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
791 WindowPosition g_posMapInfoWnd( c_default_window_pos );
795 GtkEntry* brushes_entry;
796 GtkEntry* entities_entry;
797 GtkListStore* EntityBreakdownWalker;
799 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Map Info", G_CALLBACK( dialog_delete_callback ), &dialog );
801 window_set_position( window, g_posMapInfoWnd );
804 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
805 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
808 GtkHBox* hbox = create_dialog_hbox( 4 );
809 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
812 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
813 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
816 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
817 gtk_widget_show( GTK_WIDGET( entry ) );
818 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
819 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
820 (GtkAttachOptions) ( 0 ), 0, 0 );
821 gtk_entry_set_editable( entry, FALSE );
823 brushes_entry = entry;
826 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
827 gtk_widget_show( GTK_WIDGET( entry ) );
828 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
829 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
830 (GtkAttachOptions) ( 0 ), 0, 0 );
831 gtk_entry_set_editable( entry, FALSE );
833 entities_entry = entry;
836 GtkWidget* label = gtk_label_new( "Total Brushes" );
837 gtk_widget_show( label );
838 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
839 (GtkAttachOptions) ( GTK_FILL ),
840 (GtkAttachOptions) ( 0 ), 0, 0 );
841 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
844 GtkWidget* label = gtk_label_new( "Total Entities" );
845 gtk_widget_show( label );
846 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
847 (GtkAttachOptions) ( GTK_FILL ),
848 (GtkAttachOptions) ( 0 ), 0, 0 );
849 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
853 GtkVBox* vbox2 = create_dialog_vbox( 4 );
854 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, FALSE, 0 );
857 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
858 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
863 GtkWidget* label = gtk_label_new( "Entity breakdown" );
864 gtk_widget_show( label );
865 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, TRUE, 0 );
866 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
869 GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 );
870 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( scr ), TRUE, TRUE, 0 );
873 GtkListStore* store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING );
875 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
876 gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE );
879 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
880 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Entity", renderer, "text", 0, 0 );
881 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
882 gtk_tree_view_column_set_sort_column_id( column, 0 );
886 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
887 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Count", renderer, "text", 1, 0 );
888 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
889 gtk_tree_view_column_set_sort_column_id( column, 1 );
892 gtk_widget_show( view );
894 gtk_container_add( GTK_CONTAINER( scr ), view );
896 EntityBreakdownWalker = store;
904 EntityBreakdown entitymap;
905 Scene_EntityBreakdown( entitymap );
907 for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i )
910 sprintf( tmp, "%u", Unsigned( ( *i ).second ) );
912 gtk_list_store_append( GTK_LIST_STORE( EntityBreakdownWalker ), &iter );
913 gtk_list_store_set( GTK_LIST_STORE( EntityBreakdownWalker ), &iter, 0, ( *i ).first.c_str(), 1, tmp, -1 );
917 g_object_unref( G_OBJECT( EntityBreakdownWalker ) );
920 sprintf( tmp, "%u", Unsigned( g_brushCount.get() ) );
921 gtk_entry_set_text( GTK_ENTRY( brushes_entry ), tmp );
922 sprintf( tmp, "%u", Unsigned( g_entityCount.get() ) );
923 gtk_entry_set_text( GTK_ENTRY( entities_entry ), tmp );
925 modal_dialog_show( window, dialog );
928 window_get_position( window, g_posMapInfoWnd );
930 gtk_widget_destroy( GTK_WIDGET( window ) );
938 const char* m_message;
940 ScopeTimer( const char* message )
941 : m_message( message ){
945 double elapsed_time = m_timer.elapsed_msec() / 1000.f;
946 globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
956 void Map_LoadFile( const char *filename ){
957 globalOutputStream() << "Loading map from " << filename << "\n";
958 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
961 ScopeTimer timer( "map load" );
963 const MapFormat* format = NULL;
964 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
965 if ( string_not_empty( moduleName ) ) {
966 format = ReferenceAPI_getMapModules().findModule( moduleName );
969 for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
974 Brush_toggleFormat( i );
975 g_map.m_name = filename;
976 g_mapPathObservers.realise();
977 Map_UpdateTitle( g_map );
978 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
980 format->wrongFormat = false;
982 g_map.m_resource->attach( g_map );
984 if ( !format->wrongFormat ) {
990 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
993 globalOutputStream() << "--- LoadMapFile ---\n";
994 globalOutputStream() << g_map.m_name.c_str() << "\n";
996 globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
997 globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
999 //GlobalEntityCreator().printStatistics();
1002 // move the view to a start position
1004 Map_StartPosition();
1006 g_currentMap = &g_map;
1012 virtual bool excluded( scene::Node& node ) const = 0;
1015 class ExcludeWalker : public scene::Traversable::Walker
1017 const scene::Traversable::Walker& m_walker;
1018 const Excluder* m_exclude;
1019 mutable bool m_skip;
1021 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
1022 : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
1024 bool pre( scene::Node& node ) const {
1025 if ( m_exclude->excluded( node ) || node.isRoot() ) {
1031 m_walker.pre( node );
1035 void post( scene::Node& node ) const {
1041 m_walker.post( node );
1046 class AnyInstanceSelected : public scene::Instantiable::Visitor
1050 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1053 void visit( scene::Instance& instance ) const {
1054 Selectable* selectable = Instance_getSelectable( instance );
1055 if ( selectable != 0
1056 && selectable->isSelected() ) {
1062 bool Node_instanceSelected( scene::Node& node ){
1063 scene::Instantiable* instantiable = Node_getInstantiable( node );
1064 ASSERT_NOTNULL( instantiable );
1066 instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1070 class SelectedDescendantWalker : public scene::Traversable::Walker
1074 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1078 bool pre( scene::Node& node ) const {
1079 if ( node.isRoot() ) {
1083 if ( Node_instanceSelected( node ) ) {
1091 bool Node_selectedDescendant( scene::Node& node ){
1093 Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1097 class SelectionExcluder : public Excluder
1100 bool excluded( scene::Node& node ) const {
1101 return !Node_selectedDescendant( node );
1105 class IncludeSelectedWalker : public scene::Traversable::Walker
1107 const scene::Traversable::Walker& m_walker;
1108 mutable std::size_t m_selected;
1109 mutable bool m_skip;
1111 bool selectedParent() const {
1112 return m_selected != 0;
1115 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1116 : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1118 bool pre( scene::Node& node ) const {
1120 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1121 if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1122 if ( Node_instanceSelected( node ) ) {
1125 m_walker.pre( node );
1134 void post( scene::Node& node ) const {
1140 if ( Node_instanceSelected( node ) ) {
1143 m_walker.post( node );
1148 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1149 scene::Traversable* traversable = Node_getTraversable( root );
1150 if ( traversable != 0 ) {
1152 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1154 traversable->traverse( IncludeSelectedWalker( walker ) );
1159 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1160 format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1163 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1164 scene::Traversable* traversable = Node_getTraversable( root );
1165 if ( traversable != 0 ) {
1166 traversable->traverse( walker );
1170 class RegionExcluder : public Excluder
1173 bool excluded( scene::Node& node ) const {
1174 return node.excluded();
1178 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1179 scene::Traversable* traversable = Node_getTraversable( root );
1180 if ( traversable != 0 ) {
1181 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1185 bool Map_SaveRegion( const char *filename ){
1188 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1190 RemoveRegionBrushes();
1196 void Map_RenameAbsolute( const char* absolute ){
1197 Resource* resource = GlobalReferenceCache().capture( absolute );
1198 NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1199 resource->setNode( clone.get_pointer() );
1202 //ScopeTimer timer("clone subgraph");
1203 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1206 g_map.m_resource->detach( g_map );
1207 GlobalReferenceCache().release( g_map.m_name.c_str() );
1208 g_mapPathObservers.unrealise();
1210 g_map.m_resource = resource;
1212 g_map.m_name = absolute;
1213 g_mapPathObservers.realise();
1214 Map_UpdateTitle( g_map );
1216 g_map.m_resource->attach( g_map );
1219 void Map_Rename( const char* filename ){
1220 if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1221 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1223 Map_RenameAbsolute( filename );
1225 SceneChangeNotify();
1236 ScopeTimer timer( "map save" );
1238 return true; // assume success..
1248 //globalOutputStream() << "Map_New\n";
1250 g_map.m_name = "unnamed.map";
1251 g_mapPathObservers.realise();
1252 Map_UpdateTitle( g_map );
1255 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1256 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1257 g_map.m_resource->attach( g_map );
1259 SceneChangeNotify();
1262 FocusViews( g_vector3_identity, 0 );
1264 g_currentMap = &g_map;
1267 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
1269 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1271 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1272 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1273 for now, let's just print an error
1276 Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1278 for ( int i = 0 ; i < 3 ; i++ )
1280 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1281 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1286 // write the info_playerstart
1288 sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1289 Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1290 sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1291 Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1295 ===========================================================
1299 ===========================================================
1302 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1303 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1305 scene::Node* region_sides[6];
1306 scene::Node* region_startpoint = 0;
1311 a regioned map will have temp walls put up at the region boundary
1312 \todo TODO TTimo old implementation of region brushes
1313 we still add them straight in the worldspawn and take them out after the map is saved
1314 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1317 void AddRegionBrushes( void ){
1320 for ( i = 0; i < 6; i++ )
1322 region_sides[i] = &GlobalBrushCreator().createBrush();
1323 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1326 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1328 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1329 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1331 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1334 void RemoveRegionBrushes( void ){
1335 for ( std::size_t i = 0; i < 6; i++ )
1337 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1339 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1342 inline void exclude_node( scene::Node& node, bool exclude ){
1344 ? node.enable( scene::Node::eExcluded )
1345 : node.disable( scene::Node::eExcluded );
1348 class ExcludeAllWalker : public scene::Graph::Walker
1352 ExcludeAllWalker( bool exclude )
1353 : m_exclude( exclude ){
1355 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1356 exclude_node( path.top(), m_exclude );
1362 void Scene_Exclude_All( bool exclude ){
1363 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1366 bool Instance_isSelected( const scene::Instance& instance ){
1367 const Selectable* selectable = Instance_getSelectable( instance );
1368 return selectable != 0 && selectable->isSelected();
1371 class ExcludeSelectedWalker : public scene::Graph::Walker
1375 ExcludeSelectedWalker( bool exclude )
1376 : m_exclude( exclude ){
1378 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1379 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1384 void Scene_Exclude_Selected( bool exclude ){
1385 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1388 class ExcludeRegionedWalker : public scene::Graph::Walker
1392 ExcludeRegionedWalker( bool exclude )
1393 : m_exclude( exclude ){
1395 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1400 aabb_intersects_aabb(
1401 instance.worldAABB(),
1402 aabb_for_minmax( region_mins, region_maxs )
1412 void Scene_Exclude_Region( bool exclude ){
1413 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1420 Other filtering options may still be on
1423 void Map_RegionOff(){
1424 region_active = false;
1426 region_maxs[0] = g_MaxWorldCoord - 64;
1427 region_mins[0] = g_MinWorldCoord + 64;
1428 region_maxs[1] = g_MaxWorldCoord - 64;
1429 region_mins[1] = g_MinWorldCoord + 64;
1430 region_maxs[2] = g_MaxWorldCoord - 64;
1431 region_mins[2] = g_MinWorldCoord + 64;
1433 Scene_Exclude_All( false );
1436 void Map_ApplyRegion( void ){
1437 region_active = true;
1439 Scene_Exclude_Region( false );
1444 ========================
1445 Map_RegionSelectedBrushes
1446 ========================
1448 void Map_RegionSelectedBrushes( void ){
1451 if ( GlobalSelectionSystem().countSelected() != 0
1452 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1453 region_active = true;
1454 Select_GetBounds( region_mins, region_maxs );
1456 Scene_Exclude_Selected( false );
1458 GlobalSelectionSystem().setSelectedAll( false );
1468 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1471 region_mins[0] = x_min;
1472 region_maxs[0] = x_max;
1473 region_mins[1] = y_min;
1474 region_maxs[1] = y_max;
1475 region_mins[2] = g_MinWorldCoord + 64;
1476 region_maxs[2] = g_MaxWorldCoord - 64;
1481 void Map_RegionBounds( const AABB& bounds ){
1484 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1485 region_maxs = vector3_added( bounds.origin, bounds.extents );
1497 void Map_RegionBrush( void ){
1498 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1499 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1500 Map_RegionBounds( instance.worldAABB() );
1509 bool Map_ImportFile( const char* filename ){
1510 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1512 bool success = false;
1514 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1519 const MapFormat* format = NULL;
1520 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1521 if ( string_not_empty( moduleName ) ) {
1522 format = ReferenceAPI_getMapModules().findModule( moduleName );
1526 format->wrongFormat = false;
1528 Resource* resource = GlobalReferenceCache().capture( filename );
1529 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1530 if ( !resource->load() ) {
1531 GlobalReferenceCache().release( filename );
1535 if ( format->wrongFormat ) {
1536 GlobalReferenceCache().release( filename );
1540 NodeSmartReference clone( NewMapRoot( "" ) );
1541 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1542 Map_gatherNamespaced( clone );
1543 Map_mergeClonedNames();
1546 GlobalReferenceCache().release( filename );
1549 SceneChangeNotify();
1555 const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
1556 int n = string_length( path_get_extension( filename ) );
1557 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1558 StringBuffer output;
1559 output.push_string( AppPath_get() );
1560 output.push_string( "q3map2." );
1561 output.push_string( RADIANT_EXECUTABLE );
1562 output.push_string( " -v -game " );
1563 output.push_string( ( type && *type ) ? type : "quake3" );
1564 output.push_string( " -fs_basepath \"" );
1565 output.push_string( EnginePath_get() );
1566 output.push_string( "\" -fs_homepath \"" );
1567 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1568 output.push_string( "\" -fs_game " );
1569 output.push_string( gamename_get() );
1570 output.push_string( " -convert -format " );
1571 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1572 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1573 output.push_string( " -readmap " );
1575 output.push_string( " \"" );
1576 output.push_string( filename );
1577 output.push_string( "\"" );
1580 Q_Exec( NULL, output.c_str(), NULL, false, true );
1582 // rebuild filename as "filenamewithoutext_converted.map"
1584 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1585 output.push_string( "_converted.map" );
1586 filename = output.c_str();
1589 Resource* resource = GlobalReferenceCache().capture( filename );
1590 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1591 if ( !resource->load() ) {
1592 GlobalReferenceCache().release( filename );
1595 NodeSmartReference clone( NewMapRoot( "" ) );
1596 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1597 Map_gatherNamespaced( clone );
1598 Map_mergeClonedNames();
1601 GlobalReferenceCache().release( filename );
1604 SceneChangeNotify();
1613 bool Map_SaveFile( const char* filename ){
1614 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1615 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1623 // Saves selected world brushes and whole entities with partial/full selections
1625 bool Map_SaveSelected( const char* filename ){
1626 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1630 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1632 scene::Node& m_parent;
1634 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
1636 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1637 if ( path.top().get_pointer() != &m_parent
1638 && Node_isPrimitive( path.top() ) ) {
1639 Selectable* selectable = Instance_getSelectable( instance );
1640 if ( selectable != 0
1641 && selectable->isSelected()
1642 && path.size() > 1 ) {
1648 void post( const scene::Path& path, scene::Instance& instance ) const {
1649 if ( path.top().get_pointer() != &m_parent
1650 && Node_isPrimitive( path.top() ) ) {
1651 Selectable* selectable = Instance_getSelectable( instance );
1652 if ( selectable != 0
1653 && selectable->isSelected()
1654 && path.size() > 1 ) {
1655 scene::Node& parent = path.parent();
1656 if ( &parent != &m_parent ) {
1657 NodeSmartReference node( path.top().get() );
1658 Node_getTraversable( parent )->erase( node );
1659 Node_getTraversable( m_parent )->insert( node );
1666 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1667 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1670 class CountSelectedBrushes : public scene::Graph::Walker
1672 std::size_t& m_count;
1673 mutable std::size_t m_depth;
1675 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1678 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1679 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1682 Selectable* selectable = Instance_getSelectable( instance );
1683 if ( selectable != 0
1684 && selectable->isSelected()
1685 && Node_isPrimitive( path.top() ) ) {
1690 void post( const scene::Path& path, scene::Instance& instance ) const {
1695 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1697 graph.traverse( CountSelectedBrushes( count ) );
1709 const char* nodetype_get_name( ENodeType type ){
1710 if ( type == eNodeMap ) {
1713 if ( type == eNodeEntity ) {
1716 if ( type == eNodePrimitive ) {
1722 ENodeType node_get_nodetype( scene::Node& node ){
1723 if ( Node_isEntity( node ) ) {
1726 if ( Node_isPrimitive( node ) ) {
1727 return eNodePrimitive;
1729 return eNodeUnknown;
1732 bool contains_entity( scene::Node& node ){
1733 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1736 bool contains_primitive( scene::Node& node ){
1737 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1740 ENodeType node_get_contains( scene::Node& node ){
1741 if ( contains_entity( node ) ) {
1744 if ( contains_primitive( node ) ) {
1745 return eNodePrimitive;
1747 return eNodeUnknown;
1750 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1751 ENodeType contains = node_get_contains( parent.top() );
1752 ENodeType type = node_get_nodetype( child.top() );
1754 if ( contains != eNodeUnknown && contains == type ) {
1755 NodeSmartReference node( child.top().get() );
1756 Path_deleteTop( child );
1757 Node_getTraversable( parent.top() )->insert( node );
1758 SceneChangeNotify();
1762 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1766 void Scene_parentSelected(){
1767 UndoableCommand undo( "parentSelected" );
1769 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1770 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1772 const scene::Path& m_parent;
1774 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1776 void visit( scene::Instance& instance ) const {
1777 if ( &m_parent != &instance.path() ) {
1778 Path_parent( m_parent, instance.path() );
1783 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1784 GlobalSelectionSystem().foreachSelected( visitor );
1788 globalOutputStream() << "failed - did not find two selected nodes.\n";
1795 if ( ConfirmModified( "New Map" ) ) {
1802 CopiedString g_mapsPath;
1804 const char* getMapsPath(){
1805 return g_mapsPath.c_str();
1808 const char* map_open( const char* title ){
1809 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
1812 const char* map_import( const char* title ){
1813 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
1816 const char* map_save( const char* title ){
1817 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
1821 if ( !ConfirmModified( "Open Map" ) ) {
1825 const char* filename = map_open( "Open Map" );
1827 if ( filename != 0 ) {
1828 MRU_AddFile( filename );
1831 Map_LoadFile( filename );
1836 const char* filename = map_import( "Import Map" );
1838 if ( filename != 0 ) {
1839 UndoableCommand undo( "mapImport" );
1840 Map_ImportFile( filename );
1845 const char* filename = map_save( "Save Map" );
1847 if ( filename != 0 ) {
1848 MRU_AddFile( filename );
1849 Map_Rename( filename );
1860 if ( Map_Unnamed( g_map ) ) {
1863 else if ( Map_Modified( g_map ) ) {
1869 const char* filename = map_save( "Export Selection" );
1871 if ( filename != 0 ) {
1872 Map_SaveSelected( filename );
1877 const char* filename = map_save( "Export Region" );
1879 if ( filename != 0 ) {
1880 Map_SaveRegion( filename );
1887 SceneChangeNotify();
1892 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1893 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1894 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1895 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1897 SceneChangeNotify();
1902 SceneChangeNotify();
1905 void RegionSelected(){
1906 Map_RegionSelectedBrushes();
1907 SceneChangeNotify();
1914 class BrushFindByIndexWalker : public scene::Traversable::Walker
1916 mutable std::size_t m_index;
1917 scene::Path& m_path;
1919 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
1920 : m_index( index ), m_path( path ){
1922 bool pre( scene::Node& node ) const {
1923 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
1924 m_path.push( makeReference( node ) );
1930 class EntityFindByIndexWalker : public scene::Traversable::Walker
1932 mutable std::size_t m_index;
1933 scene::Path& m_path;
1935 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
1936 : m_index( index ), m_path( path ){
1938 bool pre( scene::Node& node ) const {
1939 if ( Node_isEntity( node ) && m_index-- == 0 ) {
1940 m_path.push( makeReference( node ) );
1946 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
1947 path.push( makeReference( GlobalSceneGraph().root() ) );
1949 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
1951 if ( path.size() == 2 ) {
1952 scene::Traversable* traversable = Node_getTraversable( path.top() );
1953 if ( traversable != 0 ) {
1954 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
1959 inline bool Node_hasChildren( scene::Node& node ){
1960 scene::Traversable* traversable = Node_getTraversable( node );
1961 return traversable != 0 && !traversable->empty();
1964 void SelectBrush( int entitynum, int brushnum ){
1966 Scene_FindEntityBrush( entitynum, brushnum, path );
1967 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
1968 scene::Instance* instance = GlobalSceneGraph().find( path );
1969 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
1970 Selectable* selectable = Instance_getSelectable( *instance );
1971 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
1972 selectable->setSelected( true );
1973 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
1978 class BrushFindIndexWalker : public scene::Graph::Walker
1980 mutable const scene::Node* m_node;
1981 std::size_t& m_count;
1983 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
1984 : m_node( &node ), m_count( count ){
1986 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1987 if ( Node_isPrimitive( path.top() ) ) {
1988 if ( m_node == path.top().get_pointer() ) {
1999 class EntityFindIndexWalker : public scene::Graph::Walker
2001 mutable const scene::Node* m_node;
2002 std::size_t& m_count;
2004 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
2005 : m_node( &node ), m_count( count ){
2007 bool pre( const scene::Path& path, scene::Instance& instance ) const {
2008 if ( Node_isEntity( path.top() ) ) {
2009 if ( m_node == path.top().get_pointer() ) {
2020 static void GetSelectionIndex( int *ent, int *brush ){
2021 std::size_t count_brush = 0;
2022 std::size_t count_entity = 0;
2023 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2024 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2026 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
2027 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
2029 *brush = int(count_brush);
2030 *ent = int(count_entity);
2038 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Find Brush", G_CALLBACK( dialog_delete_callback ), &dialog );
2040 GtkAccelGroup* accel = gtk_accel_group_new();
2041 gtk_window_add_accel_group( window, accel );
2044 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2045 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2047 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2048 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2050 GtkWidget* label = gtk_label_new( "Entity number" );
2051 gtk_widget_show( label );
2052 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2053 (GtkAttachOptions) ( 0 ),
2054 (GtkAttachOptions) ( 0 ), 0, 0 );
2057 GtkWidget* label = gtk_label_new( "Brush number" );
2058 gtk_widget_show( label );
2059 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2060 (GtkAttachOptions) ( 0 ),
2061 (GtkAttachOptions) ( 0 ), 0, 0 );
2064 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2065 gtk_widget_show( GTK_WIDGET( entry ) );
2066 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2067 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2068 (GtkAttachOptions) ( 0 ), 0, 0 );
2069 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2073 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2074 gtk_widget_show( GTK_WIDGET( entry ) );
2075 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2076 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2077 (GtkAttachOptions) ( 0 ), 0, 0 );
2083 GtkHBox* hbox = create_dialog_hbox( 4 );
2084 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2086 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2087 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2088 widget_make_default( GTK_WIDGET( button ) );
2089 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2092 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2093 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2094 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2099 // Initialize dialog
2103 GetSelectionIndex( &ent, &br );
2104 sprintf( buf, "%i", ent );
2105 gtk_entry_set_text( entity, buf );
2106 sprintf( buf, "%i", br );
2107 gtk_entry_set_text( brush, buf );
2109 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2110 const char *entstr = gtk_entry_get_text( entity );
2111 const char *brushstr = gtk_entry_get_text( brush );
2112 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2115 gtk_widget_destroy( GTK_WIDGET( window ) );
2118 void Map_constructPreferences( PreferencesPage& page ){
2119 page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
2123 class MapEntityClasses : public ModuleObserver
2125 std::size_t m_unrealised;
2127 MapEntityClasses() : m_unrealised( 1 ){
2130 if ( --m_unrealised == 0 ) {
2131 if ( g_map.m_resource != 0 ) {
2132 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2133 g_map.m_resource->realise();
2138 if ( ++m_unrealised == 1 ) {
2139 if ( g_map.m_resource != 0 ) {
2140 g_map.m_resource->flush();
2141 g_map.m_resource->unrealise();
2147 MapEntityClasses g_MapEntityClasses;
2150 class MapModuleObserver : public ModuleObserver
2152 std::size_t m_unrealised;
2154 MapModuleObserver() : m_unrealised( 1 ){
2157 if ( --m_unrealised == 0 ) {
2158 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2159 StringOutputStream buffer( 256 );
2160 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2161 Q_mkdir( buffer.c_str() );
2162 g_mapsPath = buffer.c_str();
2166 if ( ++m_unrealised == 1 ) {
2172 MapModuleObserver g_MapModuleObserver;
2174 #include "preferencesystem.h"
2176 CopiedString g_strLastMap;
2177 bool g_bLoadLastMap = false;
2179 void Map_Construct(){
2180 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2181 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2182 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2183 GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2185 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2186 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2187 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2189 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2191 GlobalEntityClassManager().attach( g_MapEntityClasses );
2192 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2196 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2197 GlobalEntityClassManager().detach( g_MapEntityClasses );