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"
45 #include <gtk/gtkmain.h>
46 #include <gtk/gtkbox.h>
47 #include <gtk/gtkentry.h>
48 #include <gtk/gtklabel.h>
49 #include <gtk/gtktable.h>
50 #include <gtk/gtktreemodel.h>
51 #include <gtk/gtktreeview.h>
52 #include <gtk/gtkliststore.h>
53 #include <gtk/gtkcellrenderertext.h>
56 #include "transformlib.h"
57 #include "selectionlib.h"
58 #include "instancelib.h"
59 #include "traverselib.h"
61 #include "eclasslib.h"
63 #include "stream/textfilestream.h"
65 #include "uniquenames.h"
66 #include "modulesystem/singletonmodule.h"
67 #include "modulesystem/moduleregistry.h"
68 #include "stream/stringstream.h"
69 #include "signal/signal.h"
71 #include "gtkutil/filechooser.h"
75 #include "filetypes.h"
77 #include "entityinspector.h"
80 #include "camwindow.h"
82 #include "mainframe.h"
83 #include "preferences.h"
84 #include "referencecache.h"
88 #include "brushmodule.h"
98 //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
99 m_names.insert( name_read( c_str() ) );
104 //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
105 m_names.erase( name_read( c_str() ) );
109 NameObserver& operator=( const NameObserver& other );
111 NameObserver( UniqueNames& names ) : m_names( names ){
114 NameObserver( const NameObserver& other ) : m_names( other.m_names ), m_name( other.m_name ){
121 return string_empty( c_str() );
123 const char* c_str() const {
124 return m_name.c_str();
126 void nameChanged( const char* name ){
131 typedef MemberCaller1<NameObserver, const char*, &NameObserver::nameChanged> NameChangedCaller;
134 class BasicNamespace : public Namespace
136 typedef std::map<NameCallback, NameObserver> Names;
138 UniqueNames m_uniqueNames;
141 ASSERT_MESSAGE( m_names.empty(), "namespace: names still registered at shutdown" );
143 void attach( const NameCallback& setName, const NameCallbackCallback& attachObserver ){
144 std::pair<Names::iterator, bool> result = m_names.insert( Names::value_type( setName, m_uniqueNames ) );
145 ASSERT_MESSAGE( result.second, "cannot attach name" );
146 attachObserver( NameObserver::NameChangedCaller( ( *result.first ).second ) );
147 //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
149 void detach( const NameCallback& setName, const NameCallbackCallback& detachObserver ){
150 Names::iterator i = m_names.find( setName );
151 ASSERT_MESSAGE( i != m_names.end(), "cannot detach name" );
152 //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
153 detachObserver( NameObserver::NameChangedCaller( ( *i ).second ) );
157 void makeUnique( const char* name, const NameCallback& setName ) const {
159 name_write( buffer, m_uniqueNames.make_unique( name_read( name ) ) );
163 void mergeNames( const BasicNamespace& other ) const {
164 typedef std::list<NameCallback> SetNameCallbacks;
165 typedef std::map<CopiedString, SetNameCallbacks> NameGroups;
168 UniqueNames uniqueNames( other.m_uniqueNames );
170 for ( Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i )
172 groups[( *i ).second.c_str()].push_back( ( *i ).first );
175 for ( NameGroups::iterator i = groups.begin(); i != groups.end(); ++i )
177 name_t uniqueName( uniqueNames.make_unique( name_read( ( *i ).first.c_str() ) ) );
178 uniqueNames.insert( uniqueName );
181 name_write( buffer, uniqueName );
183 //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
185 SetNameCallbacks& setNameCallbacks = ( *i ).second;
187 for ( SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j )
195 BasicNamespace g_defaultNamespace;
196 BasicNamespace g_cloneNamespace;
200 Namespace* m_namespace;
202 typedef Namespace Type;
203 STRING_CONSTANT( Name, "*" );
206 m_namespace = &g_defaultNamespace;
208 Namespace* getTable(){
213 typedef SingletonModule<NamespaceAPI> NamespaceModule;
214 typedef Static<NamespaceModule> StaticNamespaceModule;
215 StaticRegisterModule staticRegisterDefaultNamespace( StaticNamespaceModule::instance() );
218 std::list<Namespaced*> g_cloned;
220 inline Namespaced* Node_getNamespaced( scene::Node& node ){
221 return NodeTypeCast<Namespaced>::cast( node );
224 void Node_gatherNamespaced( scene::Node& node ){
225 Namespaced* namespaced = Node_getNamespaced( node );
226 if ( namespaced != 0 ) {
227 g_cloned.push_back( namespaced );
231 class GatherNamespaced : public scene::Traversable::Walker
234 bool pre( scene::Node& node ) const {
235 Node_gatherNamespaced( node );
240 void Map_gatherNamespaced( scene::Node& root ){
241 Node_traverseSubgraph( root, GatherNamespaced() );
244 void Map_mergeClonedNames(){
245 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
247 ( *i )->setNamespace( g_cloneNamespace );
249 g_cloneNamespace.mergeNames( g_defaultNamespace );
250 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
252 ( *i )->setNamespace( g_defaultNamespace );
265 void set( scene::Node* node ){
274 scene::Node* get() const {
280 void Map_SetValid( Map& map, bool valid );
281 void Map_UpdateTitle( const Map& map );
282 void Map_SetWorldspawn( Map& map, scene::Node* node );
285 class Map : public ModuleObserver
289 Resource* m_resource;
293 void ( *m_modified_changed )( const Map& );
295 Signal0 m_mapValidCallbacks;
297 WorldNode m_world_node; // "classname" "worldspawn" !
299 Map() : m_resource( 0 ), m_valid( false ), m_modified_changed( Map_UpdateTitle ){
303 if ( m_resource != 0 ) {
304 if ( Map_Unnamed( *this ) ) {
305 g_map.m_resource->setNode( NewMapRoot( "" ).get_pointer() );
306 MapFile* map = Node_getMapFile( *g_map.m_resource->getNode() );
316 GlobalSceneGraph().insert_root( *m_resource->getNode() );
320 Map_SetValid( g_map, true );
324 if ( m_resource != 0 ) {
325 Map_SetValid( g_map, false );
326 Map_SetWorldspawn( g_map, 0 );
329 GlobalUndoSystem().clear();
331 GlobalSceneGraph().erase_root();
337 Map* g_currentMap = 0;
339 void Map_addValidCallback( Map& map, const SignalHandler& handler ){
340 map.m_mapValidCallbacks.connectLast( handler );
343 bool Map_Valid( const Map& map ){
347 void Map_SetValid( Map& map, bool valid ){
349 map.m_mapValidCallbacks();
353 const char* Map_Name( const Map& map ){
354 return map.m_name.c_str();
357 bool Map_Unnamed( const Map& map ){
358 return string_equal( Map_Name( map ), "unnamed.map" );
361 inline const MapFormat& MapFormat_forFile( const char* filename ){
362 const char* moduleName = findModuleName( GetFileTypeRegistry(), MapFormat::Name(), path_get_extension( filename ) );
363 MapFormat* format = Radiant_getMapModules().findModule( moduleName );
364 ASSERT_MESSAGE( format != 0, "map format not found for file " << makeQuoted( filename ) );
368 const MapFormat& Map_getFormat( const Map& map ){
369 return MapFormat_forFile( Map_Name( map ) );
373 bool Map_Modified( const Map& map ){
374 return map.m_modified;
377 void Map_SetModified( Map& map, bool modified ){
378 if ( map.m_modified ^ modified ) {
379 map.m_modified = modified;
381 map.m_modified_changed( map );
385 void Map_UpdateTitle( const Map& map ){
386 Sys_SetTitle( map.m_name.c_str(), Map_Modified( map ) );
391 scene::Node* Map_GetWorldspawn( const Map& map ){
392 return map.m_world_node.get();
395 void Map_SetWorldspawn( Map& map, scene::Node* node ){
396 map.m_world_node.set( node );
401 // need that in a variable, will have to tweak depending on the game
402 float g_MaxWorldCoord = 64 * 1024;
403 float g_MinWorldCoord = -64 * 1024;
405 void AddRegionBrushes( void );
406 void RemoveRegionBrushes( void );
413 free all map elements, reinitialize the structures that depend on them
419 g_map.m_resource->detach( g_map );
420 GlobalReferenceCache().release( g_map.m_name.c_str() );
421 g_map.m_resource = 0;
426 Brush_unlatchPreferences();
429 class EntityFindByClassname : public scene::Graph::Walker
434 EntityFindByClassname( const char* name, Entity*& entity ) : m_name( name ), m_entity( entity ){
437 bool pre( const scene::Path& path, scene::Instance& instance ) const {
438 if ( m_entity == 0 ) {
439 Entity* entity = Node_getEntity( path.top() );
441 && string_equal( m_name, entity->getKeyValue( "classname" ) ) ) {
449 Entity* Scene_FindEntityByClass( const char* name ){
451 GlobalSceneGraph().traverse( EntityFindByClassname( name, entity ) );
455 Entity *Scene_FindPlayerStart(){
456 typedef const char* StaticString;
457 StaticString strings[] = {
459 "info_player_deathmatch",
460 "team_CTF_redplayer",
461 "team_CTF_blueplayer",
463 "team_CTF_bluespawn",
465 typedef const StaticString* StaticStringIterator;
466 for ( StaticStringIterator i = strings, end = strings + ( sizeof( strings ) / sizeof( StaticString ) ); i != end; ++i )
468 Entity* entity = Scene_FindEntityByClass( *i );
477 // move the view to a start position
481 void FocusViews( const Vector3& point, float angle ){
482 CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
483 Camera_setOrigin( camwnd, point );
484 Vector3 angles( Camera_getAngles( camwnd ) );
485 angles[CAMERA_PITCH] = 0;
486 angles[CAMERA_YAW] = angle;
487 Camera_setAngles( camwnd, angles );
489 XYWnd* xywnd = g_pParentWnd->GetXYWnd();
490 xywnd->SetOrigin( point );
493 #include "stringio.h"
495 void Map_StartPosition(){
496 Entity* entity = Scene_FindPlayerStart();
500 string_parse_vector3( entity->getKeyValue( "origin" ), origin );
501 FocusViews( origin, string_read_float( entity->getKeyValue( "angle" ) ) );
505 FocusViews( g_vector3_identity, 0 );
510 inline bool node_is_worldspawn( scene::Node& node ){
511 Entity* entity = Node_getEntity( node );
512 return entity != 0 && string_equal( entity->getKeyValue( "classname" ), "worldspawn" );
516 // use first worldspawn
517 class entity_updateworldspawn : public scene::Traversable::Walker
520 bool pre( scene::Node& node ) const {
521 if ( node_is_worldspawn( node ) ) {
522 if ( Map_GetWorldspawn( g_map ) == 0 ) {
523 Map_SetWorldspawn( g_map, &node );
530 scene::Node* Map_FindWorldspawn( Map& map ){
531 Map_SetWorldspawn( map, 0 );
533 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
535 return Map_GetWorldspawn( map );
539 class CollectAllWalker : public scene::Traversable::Walker
542 UnsortedNodeSet& m_nodes;
544 CollectAllWalker( scene::Node& root, UnsortedNodeSet& nodes ) : m_root( root ), m_nodes( nodes ){
546 bool pre( scene::Node& node ) const {
547 m_nodes.insert( NodeSmartReference( node ) );
548 Node_getTraversable( m_root )->erase( node );
553 void Node_insertChildFirst( scene::Node& parent, scene::Node& child ){
554 UnsortedNodeSet nodes;
555 Node_getTraversable( parent )->traverse( CollectAllWalker( parent, nodes ) );
556 Node_getTraversable( parent )->insert( child );
558 for ( UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i )
560 Node_getTraversable( parent )->insert( ( *i ) );
564 scene::Node& createWorldspawn(){
565 NodeSmartReference worldspawn( GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "worldspawn", true ) ) );
566 Node_insertChildFirst( GlobalSceneGraph().root(), worldspawn );
570 void Map_UpdateWorldspawn( Map& map ){
571 if ( Map_FindWorldspawn( map ) == 0 ) {
572 Map_SetWorldspawn( map, &createWorldspawn() );
576 scene::Node& Map_FindOrInsertWorldspawn( Map& map ){
577 Map_UpdateWorldspawn( map );
578 return *Map_GetWorldspawn( map );
582 class MapMergeAll : public scene::Traversable::Walker
584 mutable scene::Path m_path;
586 MapMergeAll( const scene::Path& root )
589 bool pre( scene::Node& node ) const {
590 Node_getTraversable( m_path.top() )->insert( node );
591 m_path.push( makeReference( node ) );
592 selectPath( m_path, true );
595 void post( scene::Node& node ) const {
600 class MapMergeEntities : public scene::Traversable::Walker
602 mutable scene::Path m_path;
604 MapMergeEntities( const scene::Path& root )
607 bool pre( scene::Node& node ) const {
608 if ( node_is_worldspawn( node ) ) {
609 scene::Node* world_node = Map_FindWorldspawn( g_map );
610 if ( world_node == 0 ) {
611 Map_SetWorldspawn( g_map, &node );
612 Node_getTraversable( m_path.top().get() )->insert( node );
613 m_path.push( makeReference( node ) );
614 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
618 m_path.push( makeReference( *world_node ) );
619 Node_getTraversable( node )->traverse( MapMergeAll( m_path ) );
624 Node_getTraversable( m_path.top() )->insert( node );
625 m_path.push( makeReference( node ) );
626 if ( node_is_group( node ) ) {
627 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
631 selectPath( m_path, true );
636 void post( scene::Node& node ) const {
641 class BasicContainer : public scene::Node::Symbiot
645 NodeTypeCastTable m_casts;
648 NodeContainedCast<BasicContainer, scene::Traversable>::install( m_casts );
650 NodeTypeCastTable& get(){
656 TraversableNodeSet m_traverse;
659 typedef LazyStatic<TypeCasts> StaticTypeCasts;
661 scene::Traversable& get( NullType<scene::Traversable>){
665 BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){
675 /// Merges the map graph rooted at \p node into the global scene-graph.
676 void MergeMap( scene::Node& node ){
677 Node_getTraversable( node )->traverse( MapMergeEntities( scene::Path( makeReference( GlobalSceneGraph().root() ) ) ) );
679 void Map_ImportSelected( TextInputStream& in, const MapFormat& format ){
680 NodeSmartReference node( ( new BasicContainer )->node() );
681 format.readGraph( node, in, GlobalEntityCreator() );
682 Map_gatherNamespaced( node );
683 Map_mergeClonedNames();
687 inline scene::Cloneable* Node_getCloneable( scene::Node& node ){
688 return NodeTypeCast<scene::Cloneable>::cast( node );
691 inline scene::Node& node_clone( scene::Node& node ){
692 scene::Cloneable* cloneable = Node_getCloneable( node );
693 if ( cloneable != 0 ) {
694 return cloneable->clone();
697 return ( new scene::NullNode )->node();
700 class CloneAll : public scene::Traversable::Walker
702 mutable scene::Path m_path;
704 CloneAll( scene::Node& root )
705 : m_path( makeReference( root ) ){
707 bool pre( scene::Node& node ) const {
708 if ( node.isRoot() ) {
712 m_path.push( makeReference( node_clone( node ) ) );
713 m_path.top().get().IncRef();
717 void post( scene::Node& node ) const {
718 if ( node.isRoot() ) {
722 Node_getTraversable( m_path.parent() )->insert( m_path.top() );
724 m_path.top().get().DecRef();
729 scene::Node& Node_Clone( scene::Node& node ){
730 scene::Node& clone = node_clone( node );
731 scene::Traversable* traversable = Node_getTraversable( node );
732 if ( traversable != 0 ) {
733 traversable->traverse( CloneAll( clone ) );
739 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
741 class EntityBreakdownWalker : public scene::Graph::Walker
743 EntityBreakdown& m_entitymap;
745 EntityBreakdownWalker( EntityBreakdown& entitymap )
746 : m_entitymap( entitymap ){
748 bool pre( const scene::Path& path, scene::Instance& instance ) const {
749 Entity* entity = Node_getEntity( path.top() );
751 const EntityClass& eclass = entity->getEntityClass();
752 if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
753 m_entitymap[eclass.name()] = 1;
755 else{ ++m_entitymap[eclass.name()]; }
761 void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
762 GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
766 WindowPosition g_posMapInfoWnd( c_default_window_pos );
770 GtkEntry* brushes_entry;
771 GtkEntry* entities_entry;
772 GtkListStore* EntityBreakdownWalker;
774 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Map Info", G_CALLBACK( dialog_delete_callback ), &dialog );
776 window_set_position( window, g_posMapInfoWnd );
779 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
780 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
783 GtkHBox* hbox = create_dialog_hbox( 4 );
784 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
787 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
788 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
791 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
792 gtk_widget_show( GTK_WIDGET( entry ) );
793 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
794 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
795 (GtkAttachOptions) ( 0 ), 0, 0 );
796 gtk_entry_set_editable( entry, FALSE );
798 brushes_entry = entry;
801 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
802 gtk_widget_show( GTK_WIDGET( entry ) );
803 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
804 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
805 (GtkAttachOptions) ( 0 ), 0, 0 );
806 gtk_entry_set_editable( entry, FALSE );
808 entities_entry = entry;
811 GtkWidget* label = gtk_label_new( "Total Brushes" );
812 gtk_widget_show( label );
813 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
814 (GtkAttachOptions) ( GTK_FILL ),
815 (GtkAttachOptions) ( 0 ), 0, 0 );
816 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
819 GtkWidget* label = gtk_label_new( "Total Entities" );
820 gtk_widget_show( label );
821 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
822 (GtkAttachOptions) ( GTK_FILL ),
823 (GtkAttachOptions) ( 0 ), 0, 0 );
824 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
828 GtkVBox* vbox2 = create_dialog_vbox( 4 );
829 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, FALSE, 0 );
832 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
833 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
838 GtkWidget* label = gtk_label_new( "Entity breakdown" );
839 gtk_widget_show( label );
840 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, TRUE, 0 );
841 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
844 GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 );
845 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( scr ), TRUE, TRUE, 0 );
848 GtkListStore* store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING );
850 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
851 gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE );
854 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
855 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Entity", renderer, "text", 0, 0 );
856 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
857 gtk_tree_view_column_set_sort_column_id( column, 0 );
861 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
862 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Count", renderer, "text", 1, 0 );
863 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
864 gtk_tree_view_column_set_sort_column_id( column, 1 );
867 gtk_widget_show( view );
869 gtk_container_add( GTK_CONTAINER( scr ), view );
871 EntityBreakdownWalker = store;
879 EntityBreakdown entitymap;
880 Scene_EntityBreakdown( entitymap );
882 for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i )
885 sprintf( tmp, "%u", Unsigned( ( *i ).second ) );
887 gtk_list_store_append( GTK_LIST_STORE( EntityBreakdownWalker ), &iter );
888 gtk_list_store_set( GTK_LIST_STORE( EntityBreakdownWalker ), &iter, 0, ( *i ).first.c_str(), 1, tmp, -1 );
892 g_object_unref( G_OBJECT( EntityBreakdownWalker ) );
895 sprintf( tmp, "%u", Unsigned( g_brushCount.get() ) );
896 gtk_entry_set_text( GTK_ENTRY( brushes_entry ), tmp );
897 sprintf( tmp, "%u", Unsigned( g_entityCount.get() ) );
898 gtk_entry_set_text( GTK_ENTRY( entities_entry ), tmp );
900 modal_dialog_show( window, dialog );
903 window_get_position( window, g_posMapInfoWnd );
905 gtk_widget_destroy( GTK_WIDGET( window ) );
913 const char* m_message;
915 ScopeTimer( const char* message )
916 : m_message( message ){
920 double elapsed_time = m_timer.elapsed_msec() / 1000.f;
921 globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
931 void Map_LoadFile( const char *filename ){
932 globalOutputStream() << "Loading map from " << filename << "\n";
933 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
935 MRU_AddFile( filename );
938 ScopeTimer timer( "map load" );
940 const MapFormat* format = NULL;
941 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
942 if ( string_not_empty( moduleName ) ) {
943 format = ReferenceAPI_getMapModules().findModule( moduleName );
946 for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
951 Brush_toggleFormat( i );
952 g_map.m_name = filename;
953 Map_UpdateTitle( g_map );
954 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
956 format->wrongFormat = false;
958 g_map.m_resource->attach( g_map );
960 if ( !format->wrongFormat ) {
966 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
969 globalOutputStream() << "--- LoadMapFile ---\n";
970 globalOutputStream() << g_map.m_name.c_str() << "\n";
972 globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
973 globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
975 //GlobalEntityCreator().printStatistics();
978 // move the view to a start position
982 g_currentMap = &g_map;
984 // restart VFS to apply new pak filtering based on mapname
985 // needed for daemon DPK VFS
992 virtual bool excluded( scene::Node& node ) const = 0;
995 class ExcludeWalker : public scene::Traversable::Walker
997 const scene::Traversable::Walker& m_walker;
998 const Excluder* m_exclude;
1001 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
1002 : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
1004 bool pre( scene::Node& node ) const {
1005 if ( m_exclude->excluded( node ) || node.isRoot() ) {
1011 m_walker.pre( node );
1015 void post( scene::Node& node ) const {
1021 m_walker.post( node );
1026 class AnyInstanceSelected : public scene::Instantiable::Visitor
1030 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1033 void visit( scene::Instance& instance ) const {
1034 Selectable* selectable = Instance_getSelectable( instance );
1035 if ( selectable != 0
1036 && selectable->isSelected() ) {
1042 bool Node_instanceSelected( scene::Node& node ){
1043 scene::Instantiable* instantiable = Node_getInstantiable( node );
1044 ASSERT_NOTNULL( instantiable );
1046 instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1050 class SelectedDescendantWalker : public scene::Traversable::Walker
1054 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1058 bool pre( scene::Node& node ) const {
1059 if ( node.isRoot() ) {
1063 if ( Node_instanceSelected( node ) ) {
1071 bool Node_selectedDescendant( scene::Node& node ){
1073 Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1077 class SelectionExcluder : public Excluder
1080 bool excluded( scene::Node& node ) const {
1081 return !Node_selectedDescendant( node );
1085 class IncludeSelectedWalker : public scene::Traversable::Walker
1087 const scene::Traversable::Walker& m_walker;
1088 mutable std::size_t m_selected;
1089 mutable bool m_skip;
1091 bool selectedParent() const {
1092 return m_selected != 0;
1095 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1096 : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1098 bool pre( scene::Node& node ) const {
1100 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1101 if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1102 if ( Node_instanceSelected( node ) ) {
1105 m_walker.pre( node );
1114 void post( scene::Node& node ) const {
1120 if ( Node_instanceSelected( node ) ) {
1123 m_walker.post( node );
1128 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1129 scene::Traversable* traversable = Node_getTraversable( root );
1130 if ( traversable != 0 ) {
1132 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1134 traversable->traverse( IncludeSelectedWalker( walker ) );
1139 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1140 format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1143 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1144 scene::Traversable* traversable = Node_getTraversable( root );
1145 if ( traversable != 0 ) {
1146 traversable->traverse( walker );
1150 class RegionExcluder : public Excluder
1153 bool excluded( scene::Node& node ) const {
1154 return node.excluded();
1158 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1159 scene::Traversable* traversable = Node_getTraversable( root );
1160 if ( traversable != 0 ) {
1161 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1165 bool Map_SaveRegion( const char *filename ){
1168 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1170 RemoveRegionBrushes();
1176 void Map_RenameAbsolute( const char* absolute ){
1177 Resource* resource = GlobalReferenceCache().capture( absolute );
1178 NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1179 resource->setNode( clone.get_pointer() );
1182 //ScopeTimer timer("clone subgraph");
1183 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1186 g_map.m_resource->detach( g_map );
1187 GlobalReferenceCache().release( g_map.m_name.c_str() );
1189 g_map.m_resource = resource;
1191 g_map.m_name = absolute;
1192 Map_UpdateTitle( g_map );
1194 g_map.m_resource->attach( g_map );
1195 // refresh VFS to apply new pak filtering based on mapname
1196 // needed for daemon DPK VFS
1200 void Map_Rename( const char* filename ){
1201 if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1202 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1204 Map_RenameAbsolute( filename );
1206 SceneChangeNotify();
1217 ScopeTimer timer( "map save" );
1219 return true; // assume success..
1229 //globalOutputStream() << "Map_New\n";
1231 g_map.m_name = "unnamed.map";
1232 Map_UpdateTitle( g_map );
1235 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1236 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1237 g_map.m_resource->attach( g_map );
1239 SceneChangeNotify();
1242 FocusViews( g_vector3_identity, 0 );
1244 g_currentMap = &g_map;
1246 // restart VFS to apply new pak filtering based on mapname
1247 // needed for daemon DPK VFS
1251 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
1253 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1255 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1256 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1257 for now, let's just print an error
1260 Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1262 for ( int i = 0 ; i < 3 ; i++ )
1264 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1265 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1270 // write the info_playerstart
1272 sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1273 Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1274 sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1275 Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1279 ===========================================================
1283 ===========================================================
1286 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1287 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1289 scene::Node* region_sides[6];
1290 scene::Node* region_startpoint = 0;
1295 a regioned map will have temp walls put up at the region boundary
1296 \todo TODO TTimo old implementation of region brushes
1297 we still add them straight in the worldspawn and take them out after the map is saved
1298 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1301 void AddRegionBrushes( void ){
1304 for ( i = 0; i < 6; i++ )
1306 region_sides[i] = &GlobalBrushCreator().createBrush();
1307 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1310 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1312 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1313 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1315 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1318 void RemoveRegionBrushes( void ){
1319 for ( std::size_t i = 0; i < 6; i++ )
1321 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1323 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1326 inline void exclude_node( scene::Node& node, bool exclude ){
1328 ? node.enable( scene::Node::eExcluded )
1329 : node.disable( scene::Node::eExcluded );
1332 class ExcludeAllWalker : public scene::Graph::Walker
1336 ExcludeAllWalker( bool exclude )
1337 : m_exclude( exclude ){
1339 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1340 exclude_node( path.top(), m_exclude );
1346 void Scene_Exclude_All( bool exclude ){
1347 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1350 bool Instance_isSelected( const scene::Instance& instance ){
1351 const Selectable* selectable = Instance_getSelectable( instance );
1352 return selectable != 0 && selectable->isSelected();
1355 class ExcludeSelectedWalker : public scene::Graph::Walker
1359 ExcludeSelectedWalker( bool exclude )
1360 : m_exclude( exclude ){
1362 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1363 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1368 void Scene_Exclude_Selected( bool exclude ){
1369 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1372 class ExcludeRegionedWalker : public scene::Graph::Walker
1376 ExcludeRegionedWalker( bool exclude )
1377 : m_exclude( exclude ){
1379 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1384 aabb_intersects_aabb(
1385 instance.worldAABB(),
1386 aabb_for_minmax( region_mins, region_maxs )
1396 void Scene_Exclude_Region( bool exclude ){
1397 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1404 Other filtering options may still be on
1407 void Map_RegionOff(){
1408 region_active = false;
1410 region_maxs[0] = g_MaxWorldCoord - 64;
1411 region_mins[0] = g_MinWorldCoord + 64;
1412 region_maxs[1] = g_MaxWorldCoord - 64;
1413 region_mins[1] = g_MinWorldCoord + 64;
1414 region_maxs[2] = g_MaxWorldCoord - 64;
1415 region_mins[2] = g_MinWorldCoord + 64;
1417 Scene_Exclude_All( false );
1420 void Map_ApplyRegion( void ){
1421 region_active = true;
1423 Scene_Exclude_Region( false );
1428 ========================
1429 Map_RegionSelectedBrushes
1430 ========================
1432 void Map_RegionSelectedBrushes( void ){
1435 if ( GlobalSelectionSystem().countSelected() != 0
1436 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1437 region_active = true;
1438 Select_GetBounds( region_mins, region_maxs );
1440 Scene_Exclude_Selected( false );
1442 GlobalSelectionSystem().setSelectedAll( false );
1452 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1455 region_mins[0] = x_min;
1456 region_maxs[0] = x_max;
1457 region_mins[1] = y_min;
1458 region_maxs[1] = y_max;
1459 region_mins[2] = g_MinWorldCoord + 64;
1460 region_maxs[2] = g_MaxWorldCoord - 64;
1465 void Map_RegionBounds( const AABB& bounds ){
1468 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1469 region_maxs = vector3_added( bounds.origin, bounds.extents );
1481 void Map_RegionBrush( void ){
1482 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1483 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1484 Map_RegionBounds( instance.worldAABB() );
1493 bool Map_ImportFile( const char* filename ){
1494 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1496 bool success = false;
1498 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1503 const MapFormat* format = NULL;
1504 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1505 if ( string_not_empty( moduleName ) ) {
1506 format = ReferenceAPI_getMapModules().findModule( moduleName );
1510 format->wrongFormat = false;
1512 Resource* resource = GlobalReferenceCache().capture( filename );
1513 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1514 if ( !resource->load() ) {
1515 GlobalReferenceCache().release( filename );
1519 if ( format->wrongFormat ) {
1520 GlobalReferenceCache().release( filename );
1524 NodeSmartReference clone( NewMapRoot( "" ) );
1525 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1526 Map_gatherNamespaced( clone );
1527 Map_mergeClonedNames();
1530 GlobalReferenceCache().release( filename );
1533 SceneChangeNotify();
1539 const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
1540 int n = string_length( path_get_extension( filename ) );
1541 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1542 StringBuffer output;
1543 output.push_string( AppPath_get() );
1544 output.push_string( "q3map2." );
1545 output.push_string( RADIANT_EXECUTABLE );
1546 output.push_string( " -v -game " );
1547 output.push_string( ( type && *type ) ? type : "quake3" );
1548 output.push_string( " -fs_basepath \"" );
1549 output.push_string( EnginePath_get() );
1550 output.push_string( "\" -fs_homepath \"" );
1551 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1552 output.push_string( "\" -fs_game " );
1553 output.push_string( gamename_get() );
1554 output.push_string( " -convert -format " );
1555 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1556 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1557 output.push_string( " -readmap " );
1559 output.push_string( " \"" );
1560 output.push_string( filename );
1561 output.push_string( "\"" );
1564 Q_Exec( NULL, output.c_str(), NULL, false, true );
1566 // rebuild filename as "filenamewithoutext_converted.map"
1568 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1569 output.push_string( "_converted.map" );
1570 filename = output.c_str();
1573 Resource* resource = GlobalReferenceCache().capture( filename );
1574 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1575 if ( !resource->load() ) {
1576 GlobalReferenceCache().release( filename );
1579 NodeSmartReference clone( NewMapRoot( "" ) );
1580 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1581 Map_gatherNamespaced( clone );
1582 Map_mergeClonedNames();
1585 GlobalReferenceCache().release( filename );
1588 SceneChangeNotify();
1597 bool Map_SaveFile( const char* filename ){
1598 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1599 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1601 // refresh VFS to apply new pak filtering based on mapname
1602 // needed for daemon DPK VFS
1613 // Saves selected world brushes and whole entities with partial/full selections
1615 bool Map_SaveSelected( const char* filename ){
1616 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1620 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1622 scene::Node& m_parent;
1624 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
1626 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1627 if ( path.top().get_pointer() != &m_parent
1628 && Node_isPrimitive( path.top() ) ) {
1629 Selectable* selectable = Instance_getSelectable( instance );
1630 if ( selectable != 0
1631 && selectable->isSelected()
1632 && path.size() > 1 ) {
1638 void post( const scene::Path& path, scene::Instance& instance ) const {
1639 if ( path.top().get_pointer() != &m_parent
1640 && Node_isPrimitive( path.top() ) ) {
1641 Selectable* selectable = Instance_getSelectable( instance );
1642 if ( selectable != 0
1643 && selectable->isSelected()
1644 && path.size() > 1 ) {
1645 scene::Node& parent = path.parent();
1646 if ( &parent != &m_parent ) {
1647 NodeSmartReference node( path.top().get() );
1648 Node_getTraversable( parent )->erase( node );
1649 Node_getTraversable( m_parent )->insert( node );
1656 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1657 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1660 class CountSelectedBrushes : public scene::Graph::Walker
1662 std::size_t& m_count;
1663 mutable std::size_t m_depth;
1665 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1668 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1669 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1672 Selectable* selectable = Instance_getSelectable( instance );
1673 if ( selectable != 0
1674 && selectable->isSelected()
1675 && Node_isPrimitive( path.top() ) ) {
1680 void post( const scene::Path& path, scene::Instance& instance ) const {
1685 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1687 graph.traverse( CountSelectedBrushes( count ) );
1699 const char* nodetype_get_name( ENodeType type ){
1700 if ( type == eNodeMap ) {
1703 if ( type == eNodeEntity ) {
1706 if ( type == eNodePrimitive ) {
1712 ENodeType node_get_nodetype( scene::Node& node ){
1713 if ( Node_isEntity( node ) ) {
1716 if ( Node_isPrimitive( node ) ) {
1717 return eNodePrimitive;
1719 return eNodeUnknown;
1722 bool contains_entity( scene::Node& node ){
1723 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1726 bool contains_primitive( scene::Node& node ){
1727 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1730 ENodeType node_get_contains( scene::Node& node ){
1731 if ( contains_entity( node ) ) {
1734 if ( contains_primitive( node ) ) {
1735 return eNodePrimitive;
1737 return eNodeUnknown;
1740 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1741 ENodeType contains = node_get_contains( parent.top() );
1742 ENodeType type = node_get_nodetype( child.top() );
1744 if ( contains != eNodeUnknown && contains == type ) {
1745 NodeSmartReference node( child.top().get() );
1746 Path_deleteTop( child );
1747 Node_getTraversable( parent.top() )->insert( node );
1748 SceneChangeNotify();
1752 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1756 void Scene_parentSelected(){
1757 UndoableCommand undo( "parentSelected" );
1759 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1760 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1762 const scene::Path& m_parent;
1764 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1766 void visit( scene::Instance& instance ) const {
1767 if ( &m_parent != &instance.path() ) {
1768 Path_parent( m_parent, instance.path() );
1773 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1774 GlobalSelectionSystem().foreachSelected( visitor );
1778 globalOutputStream() << "failed - did not find two selected nodes.\n";
1785 if ( ConfirmModified( "New Map" ) ) {
1792 CopiedString g_mapsPath;
1794 const char* getMapsPath(){
1795 return g_mapsPath.c_str();
1798 const char* map_open( const char* title ){
1799 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
1802 const char* map_import( const char* title ){
1803 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
1806 const char* map_save( const char* title ){
1807 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
1811 if ( !ConfirmModified( "Open Map" ) ) {
1815 const char* filename = map_open( "Open Map" );
1817 if ( filename != 0 ) {
1820 Map_LoadFile( filename );
1825 const char* filename = map_import( "Import Map" );
1827 if ( filename != 0 ) {
1828 UndoableCommand undo( "mapImport" );
1829 Map_ImportFile( filename );
1834 const char* filename = map_save( "Save Map" );
1836 if ( filename != 0 ) {
1837 MRU_AddFile( filename );
1838 Map_Rename( filename );
1849 if ( Map_Unnamed( g_map ) ) {
1852 else if ( Map_Modified( g_map ) ) {
1858 const char* filename = map_save( "Export Selection" );
1860 if ( filename != 0 ) {
1861 Map_SaveSelected( filename );
1866 const char* filename = map_save( "Export Region" );
1868 if ( filename != 0 ) {
1869 Map_SaveRegion( filename );
1876 SceneChangeNotify();
1881 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1882 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1883 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1884 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1886 SceneChangeNotify();
1891 SceneChangeNotify();
1894 void RegionSelected(){
1895 Map_RegionSelectedBrushes();
1896 SceneChangeNotify();
1903 class BrushFindByIndexWalker : public scene::Traversable::Walker
1905 mutable std::size_t m_index;
1906 scene::Path& m_path;
1908 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
1909 : m_index( index ), m_path( path ){
1911 bool pre( scene::Node& node ) const {
1912 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
1913 m_path.push( makeReference( node ) );
1919 class EntityFindByIndexWalker : public scene::Traversable::Walker
1921 mutable std::size_t m_index;
1922 scene::Path& m_path;
1924 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
1925 : m_index( index ), m_path( path ){
1927 bool pre( scene::Node& node ) const {
1928 if ( Node_isEntity( node ) && m_index-- == 0 ) {
1929 m_path.push( makeReference( node ) );
1935 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
1936 path.push( makeReference( GlobalSceneGraph().root() ) );
1938 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
1940 if ( path.size() == 2 ) {
1941 scene::Traversable* traversable = Node_getTraversable( path.top() );
1942 if ( traversable != 0 ) {
1943 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
1948 inline bool Node_hasChildren( scene::Node& node ){
1949 scene::Traversable* traversable = Node_getTraversable( node );
1950 return traversable != 0 && !traversable->empty();
1953 void SelectBrush( int entitynum, int brushnum ){
1955 Scene_FindEntityBrush( entitynum, brushnum, path );
1956 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
1957 scene::Instance* instance = GlobalSceneGraph().find( path );
1958 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
1959 Selectable* selectable = Instance_getSelectable( *instance );
1960 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
1961 selectable->setSelected( true );
1962 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
1967 class BrushFindIndexWalker : public scene::Graph::Walker
1969 mutable const scene::Node* m_node;
1970 std::size_t& m_count;
1972 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
1973 : m_node( &node ), m_count( count ){
1975 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1976 if ( Node_isPrimitive( path.top() ) ) {
1977 if ( m_node == path.top().get_pointer() ) {
1988 class EntityFindIndexWalker : public scene::Graph::Walker
1990 mutable const scene::Node* m_node;
1991 std::size_t& m_count;
1993 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
1994 : m_node( &node ), m_count( count ){
1996 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1997 if ( Node_isEntity( path.top() ) ) {
1998 if ( m_node == path.top().get_pointer() ) {
2009 static void GetSelectionIndex( int *ent, int *brush ){
2010 std::size_t count_brush = 0;
2011 std::size_t count_entity = 0;
2012 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2013 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2015 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
2016 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
2018 *brush = int(count_brush);
2019 *ent = int(count_entity);
2027 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Find Brush", G_CALLBACK( dialog_delete_callback ), &dialog );
2029 GtkAccelGroup* accel = gtk_accel_group_new();
2030 gtk_window_add_accel_group( window, accel );
2033 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2034 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2036 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2037 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2039 GtkWidget* label = gtk_label_new( "Entity number" );
2040 gtk_widget_show( label );
2041 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2042 (GtkAttachOptions) ( 0 ),
2043 (GtkAttachOptions) ( 0 ), 0, 0 );
2046 GtkWidget* label = gtk_label_new( "Brush number" );
2047 gtk_widget_show( label );
2048 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2049 (GtkAttachOptions) ( 0 ),
2050 (GtkAttachOptions) ( 0 ), 0, 0 );
2053 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2054 gtk_widget_show( GTK_WIDGET( entry ) );
2055 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2056 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2057 (GtkAttachOptions) ( 0 ), 0, 0 );
2058 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2062 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2063 gtk_widget_show( GTK_WIDGET( entry ) );
2064 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2065 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2066 (GtkAttachOptions) ( 0 ), 0, 0 );
2072 GtkHBox* hbox = create_dialog_hbox( 4 );
2073 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2075 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2076 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2077 widget_make_default( GTK_WIDGET( button ) );
2078 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2081 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2082 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2083 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2088 // Initialize dialog
2092 GetSelectionIndex( &ent, &br );
2093 sprintf( buf, "%i", ent );
2094 gtk_entry_set_text( entity, buf );
2095 sprintf( buf, "%i", br );
2096 gtk_entry_set_text( brush, buf );
2098 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2099 const char *entstr = gtk_entry_get_text( entity );
2100 const char *brushstr = gtk_entry_get_text( brush );
2101 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2104 gtk_widget_destroy( GTK_WIDGET( window ) );
2107 void Map_constructPreferences( PreferencesPage& page ){
2108 page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
2112 class MapEntityClasses : public ModuleObserver
2114 std::size_t m_unrealised;
2116 MapEntityClasses() : m_unrealised( 1 ){
2119 if ( --m_unrealised == 0 ) {
2120 if ( g_map.m_resource != 0 ) {
2121 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2122 g_map.m_resource->realise();
2127 if ( ++m_unrealised == 1 ) {
2128 if ( g_map.m_resource != 0 ) {
2129 g_map.m_resource->flush();
2130 g_map.m_resource->unrealise();
2136 MapEntityClasses g_MapEntityClasses;
2139 class MapModuleObserver : public ModuleObserver
2141 std::size_t m_unrealised;
2143 MapModuleObserver() : m_unrealised( 1 ){
2146 if ( --m_unrealised == 0 ) {
2147 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2148 StringOutputStream buffer( 256 );
2149 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2150 Q_mkdir( buffer.c_str() );
2151 g_mapsPath = buffer.c_str();
2155 if ( ++m_unrealised == 1 ) {
2161 MapModuleObserver g_MapModuleObserver;
2163 #include "preferencesystem.h"
2165 CopiedString g_strLastMap;
2166 bool g_bLoadLastMap = false;
2168 void Map_Construct(){
2169 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2170 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2171 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2172 GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2174 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2175 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2176 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2178 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2180 GlobalEntityClassManager().attach( g_MapEntityClasses );
2181 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2185 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2186 GlobalEntityClassManager().detach( g_MapEntityClasses );