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" );
936 ScopeTimer timer( "map load" );
938 const MapFormat* format = NULL;
939 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
940 if ( string_not_empty( moduleName ) ) {
941 format = ReferenceAPI_getMapModules().findModule( moduleName );
944 for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
949 Brush_toggleFormat( i );
950 g_map.m_name = filename;
951 Map_UpdateTitle( g_map );
952 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
954 format->wrongFormat = false;
956 g_map.m_resource->attach( g_map );
958 if ( !format->wrongFormat ) {
964 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
967 globalOutputStream() << "--- LoadMapFile ---\n";
968 globalOutputStream() << g_map.m_name.c_str() << "\n";
970 globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
971 globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
973 //GlobalEntityCreator().printStatistics();
976 // move the view to a start position
980 g_currentMap = &g_map;
982 // restart VFS to apply new pak filtering based on mapname
983 // needed for daemon DPK VFS
990 virtual bool excluded( scene::Node& node ) const = 0;
993 class ExcludeWalker : public scene::Traversable::Walker
995 const scene::Traversable::Walker& m_walker;
996 const Excluder* m_exclude;
999 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
1000 : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
1002 bool pre( scene::Node& node ) const {
1003 if ( m_exclude->excluded( node ) || node.isRoot() ) {
1009 m_walker.pre( node );
1013 void post( scene::Node& node ) const {
1019 m_walker.post( node );
1024 class AnyInstanceSelected : public scene::Instantiable::Visitor
1028 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1031 void visit( scene::Instance& instance ) const {
1032 Selectable* selectable = Instance_getSelectable( instance );
1033 if ( selectable != 0
1034 && selectable->isSelected() ) {
1040 bool Node_instanceSelected( scene::Node& node ){
1041 scene::Instantiable* instantiable = Node_getInstantiable( node );
1042 ASSERT_NOTNULL( instantiable );
1044 instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1048 class SelectedDescendantWalker : public scene::Traversable::Walker
1052 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1056 bool pre( scene::Node& node ) const {
1057 if ( node.isRoot() ) {
1061 if ( Node_instanceSelected( node ) ) {
1069 bool Node_selectedDescendant( scene::Node& node ){
1071 Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1075 class SelectionExcluder : public Excluder
1078 bool excluded( scene::Node& node ) const {
1079 return !Node_selectedDescendant( node );
1083 class IncludeSelectedWalker : public scene::Traversable::Walker
1085 const scene::Traversable::Walker& m_walker;
1086 mutable std::size_t m_selected;
1087 mutable bool m_skip;
1089 bool selectedParent() const {
1090 return m_selected != 0;
1093 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1094 : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1096 bool pre( scene::Node& node ) const {
1098 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1099 if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1100 if ( Node_instanceSelected( node ) ) {
1103 m_walker.pre( node );
1112 void post( scene::Node& node ) const {
1118 if ( Node_instanceSelected( node ) ) {
1121 m_walker.post( node );
1126 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1127 scene::Traversable* traversable = Node_getTraversable( root );
1128 if ( traversable != 0 ) {
1130 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1132 traversable->traverse( IncludeSelectedWalker( walker ) );
1137 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1138 format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1141 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1142 scene::Traversable* traversable = Node_getTraversable( root );
1143 if ( traversable != 0 ) {
1144 traversable->traverse( walker );
1148 class RegionExcluder : public Excluder
1151 bool excluded( scene::Node& node ) const {
1152 return node.excluded();
1156 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1157 scene::Traversable* traversable = Node_getTraversable( root );
1158 if ( traversable != 0 ) {
1159 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1163 bool Map_SaveRegion( const char *filename ){
1166 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1168 RemoveRegionBrushes();
1174 void Map_RenameAbsolute( const char* absolute ){
1175 Resource* resource = GlobalReferenceCache().capture( absolute );
1176 NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1177 resource->setNode( clone.get_pointer() );
1180 //ScopeTimer timer("clone subgraph");
1181 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1184 g_map.m_resource->detach( g_map );
1185 GlobalReferenceCache().release( g_map.m_name.c_str() );
1187 g_map.m_resource = resource;
1189 g_map.m_name = absolute;
1190 Map_UpdateTitle( g_map );
1192 g_map.m_resource->attach( g_map );
1193 // refresh VFS to apply new pak filtering based on mapname
1194 // needed for daemon DPK VFS
1198 void Map_Rename( const char* filename ){
1199 if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1200 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1202 Map_RenameAbsolute( filename );
1204 SceneChangeNotify();
1215 ScopeTimer timer( "map save" );
1217 return true; // assume success..
1227 //globalOutputStream() << "Map_New\n";
1229 g_map.m_name = "unnamed.map";
1230 Map_UpdateTitle( g_map );
1233 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1234 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1235 g_map.m_resource->attach( g_map );
1237 SceneChangeNotify();
1240 FocusViews( g_vector3_identity, 0 );
1242 g_currentMap = &g_map;
1244 // restart VFS to apply new pak filtering based on mapname
1245 // needed for daemon DPK VFS
1249 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
1251 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1253 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1254 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1255 for now, let's just print an error
1258 Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1260 for ( int i = 0 ; i < 3 ; i++ )
1262 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1263 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1268 // write the info_playerstart
1270 sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1271 Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1272 sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1273 Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1277 ===========================================================
1281 ===========================================================
1284 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1285 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1287 scene::Node* region_sides[6];
1288 scene::Node* region_startpoint = 0;
1293 a regioned map will have temp walls put up at the region boundary
1294 \todo TODO TTimo old implementation of region brushes
1295 we still add them straight in the worldspawn and take them out after the map is saved
1296 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1299 void AddRegionBrushes( void ){
1302 for ( i = 0; i < 6; i++ )
1304 region_sides[i] = &GlobalBrushCreator().createBrush();
1305 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1308 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1310 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1311 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1313 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1316 void RemoveRegionBrushes( void ){
1317 for ( std::size_t i = 0; i < 6; i++ )
1319 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1321 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1324 inline void exclude_node( scene::Node& node, bool exclude ){
1326 ? node.enable( scene::Node::eExcluded )
1327 : node.disable( scene::Node::eExcluded );
1330 class ExcludeAllWalker : public scene::Graph::Walker
1334 ExcludeAllWalker( bool exclude )
1335 : m_exclude( exclude ){
1337 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1338 exclude_node( path.top(), m_exclude );
1344 void Scene_Exclude_All( bool exclude ){
1345 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1348 bool Instance_isSelected( const scene::Instance& instance ){
1349 const Selectable* selectable = Instance_getSelectable( instance );
1350 return selectable != 0 && selectable->isSelected();
1353 class ExcludeSelectedWalker : public scene::Graph::Walker
1357 ExcludeSelectedWalker( bool exclude )
1358 : m_exclude( exclude ){
1360 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1361 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1366 void Scene_Exclude_Selected( bool exclude ){
1367 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1370 class ExcludeRegionedWalker : public scene::Graph::Walker
1374 ExcludeRegionedWalker( bool exclude )
1375 : m_exclude( exclude ){
1377 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1382 aabb_intersects_aabb(
1383 instance.worldAABB(),
1384 aabb_for_minmax( region_mins, region_maxs )
1394 void Scene_Exclude_Region( bool exclude ){
1395 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1402 Other filtering options may still be on
1405 void Map_RegionOff(){
1406 region_active = false;
1408 region_maxs[0] = g_MaxWorldCoord - 64;
1409 region_mins[0] = g_MinWorldCoord + 64;
1410 region_maxs[1] = g_MaxWorldCoord - 64;
1411 region_mins[1] = g_MinWorldCoord + 64;
1412 region_maxs[2] = g_MaxWorldCoord - 64;
1413 region_mins[2] = g_MinWorldCoord + 64;
1415 Scene_Exclude_All( false );
1418 void Map_ApplyRegion( void ){
1419 region_active = true;
1421 Scene_Exclude_Region( false );
1426 ========================
1427 Map_RegionSelectedBrushes
1428 ========================
1430 void Map_RegionSelectedBrushes( void ){
1433 if ( GlobalSelectionSystem().countSelected() != 0
1434 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1435 region_active = true;
1436 Select_GetBounds( region_mins, region_maxs );
1438 Scene_Exclude_Selected( false );
1440 GlobalSelectionSystem().setSelectedAll( false );
1450 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1453 region_mins[0] = x_min;
1454 region_maxs[0] = x_max;
1455 region_mins[1] = y_min;
1456 region_maxs[1] = y_max;
1457 region_mins[2] = g_MinWorldCoord + 64;
1458 region_maxs[2] = g_MaxWorldCoord - 64;
1463 void Map_RegionBounds( const AABB& bounds ){
1466 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1467 region_maxs = vector3_added( bounds.origin, bounds.extents );
1479 void Map_RegionBrush( void ){
1480 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1481 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1482 Map_RegionBounds( instance.worldAABB() );
1491 bool Map_ImportFile( const char* filename ){
1492 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1494 bool success = false;
1496 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1501 const MapFormat* format = NULL;
1502 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1503 if ( string_not_empty( moduleName ) ) {
1504 format = ReferenceAPI_getMapModules().findModule( moduleName );
1508 format->wrongFormat = false;
1510 Resource* resource = GlobalReferenceCache().capture( filename );
1511 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1512 if ( !resource->load() ) {
1513 GlobalReferenceCache().release( filename );
1517 if ( format->wrongFormat ) {
1518 GlobalReferenceCache().release( filename );
1522 NodeSmartReference clone( NewMapRoot( "" ) );
1523 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1524 Map_gatherNamespaced( clone );
1525 Map_mergeClonedNames();
1528 GlobalReferenceCache().release( filename );
1531 SceneChangeNotify();
1537 const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
1538 int n = string_length( path_get_extension( filename ) );
1539 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1540 StringBuffer output;
1541 output.push_string( AppPath_get() );
1542 output.push_string( "q3map2." );
1543 output.push_string( RADIANT_EXECUTABLE );
1544 output.push_string( " -v -game " );
1545 output.push_string( ( type && *type ) ? type : "quake3" );
1546 output.push_string( " -fs_basepath \"" );
1547 output.push_string( EnginePath_get() );
1548 output.push_string( "\" -fs_homepath \"" );
1549 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1550 output.push_string( "\" -fs_game " );
1551 output.push_string( gamename_get() );
1552 output.push_string( " -convert -format " );
1553 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1554 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1555 output.push_string( " -readmap " );
1557 output.push_string( " \"" );
1558 output.push_string( filename );
1559 output.push_string( "\"" );
1562 Q_Exec( NULL, output.c_str(), NULL, false, true );
1564 // rebuild filename as "filenamewithoutext_converted.map"
1566 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1567 output.push_string( "_converted.map" );
1568 filename = output.c_str();
1571 Resource* resource = GlobalReferenceCache().capture( filename );
1572 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1573 if ( !resource->load() ) {
1574 GlobalReferenceCache().release( filename );
1577 NodeSmartReference clone( NewMapRoot( "" ) );
1578 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1579 Map_gatherNamespaced( clone );
1580 Map_mergeClonedNames();
1583 GlobalReferenceCache().release( filename );
1586 SceneChangeNotify();
1595 bool Map_SaveFile( const char* filename ){
1596 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1597 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1599 // refresh VFS to apply new pak filtering based on mapname
1600 // needed for daemon DPK VFS
1611 // Saves selected world brushes and whole entities with partial/full selections
1613 bool Map_SaveSelected( const char* filename ){
1614 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1618 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1620 scene::Node& m_parent;
1622 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
1624 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1625 if ( path.top().get_pointer() != &m_parent
1626 && Node_isPrimitive( path.top() ) ) {
1627 Selectable* selectable = Instance_getSelectable( instance );
1628 if ( selectable != 0
1629 && selectable->isSelected()
1630 && path.size() > 1 ) {
1636 void post( 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 ) {
1643 scene::Node& parent = path.parent();
1644 if ( &parent != &m_parent ) {
1645 NodeSmartReference node( path.top().get() );
1646 Node_getTraversable( parent )->erase( node );
1647 Node_getTraversable( m_parent )->insert( node );
1654 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1655 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1658 class CountSelectedBrushes : public scene::Graph::Walker
1660 std::size_t& m_count;
1661 mutable std::size_t m_depth;
1663 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1666 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1667 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1670 Selectable* selectable = Instance_getSelectable( instance );
1671 if ( selectable != 0
1672 && selectable->isSelected()
1673 && Node_isPrimitive( path.top() ) ) {
1678 void post( const scene::Path& path, scene::Instance& instance ) const {
1683 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1685 graph.traverse( CountSelectedBrushes( count ) );
1697 const char* nodetype_get_name( ENodeType type ){
1698 if ( type == eNodeMap ) {
1701 if ( type == eNodeEntity ) {
1704 if ( type == eNodePrimitive ) {
1710 ENodeType node_get_nodetype( scene::Node& node ){
1711 if ( Node_isEntity( node ) ) {
1714 if ( Node_isPrimitive( node ) ) {
1715 return eNodePrimitive;
1717 return eNodeUnknown;
1720 bool contains_entity( scene::Node& node ){
1721 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1724 bool contains_primitive( scene::Node& node ){
1725 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1728 ENodeType node_get_contains( scene::Node& node ){
1729 if ( contains_entity( node ) ) {
1732 if ( contains_primitive( node ) ) {
1733 return eNodePrimitive;
1735 return eNodeUnknown;
1738 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1739 ENodeType contains = node_get_contains( parent.top() );
1740 ENodeType type = node_get_nodetype( child.top() );
1742 if ( contains != eNodeUnknown && contains == type ) {
1743 NodeSmartReference node( child.top().get() );
1744 Path_deleteTop( child );
1745 Node_getTraversable( parent.top() )->insert( node );
1746 SceneChangeNotify();
1750 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1754 void Scene_parentSelected(){
1755 UndoableCommand undo( "parentSelected" );
1757 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1758 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1760 const scene::Path& m_parent;
1762 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1764 void visit( scene::Instance& instance ) const {
1765 if ( &m_parent != &instance.path() ) {
1766 Path_parent( m_parent, instance.path() );
1771 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1772 GlobalSelectionSystem().foreachSelected( visitor );
1776 globalOutputStream() << "failed - did not find two selected nodes.\n";
1783 if ( ConfirmModified( "New Map" ) ) {
1790 CopiedString g_mapsPath;
1792 const char* getMapsPath(){
1793 return g_mapsPath.c_str();
1796 const char* map_open( const char* title ){
1797 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
1800 const char* map_import( const char* title ){
1801 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
1804 const char* map_save( const char* title ){
1805 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
1809 if ( !ConfirmModified( "Open Map" ) ) {
1813 const char* filename = map_open( "Open Map" );
1815 if ( filename != 0 ) {
1816 MRU_AddFile( filename );
1819 Map_LoadFile( filename );
1824 const char* filename = map_import( "Import Map" );
1826 if ( filename != 0 ) {
1827 UndoableCommand undo( "mapImport" );
1828 Map_ImportFile( filename );
1833 const char* filename = map_save( "Save Map" );
1835 if ( filename != 0 ) {
1836 MRU_AddFile( filename );
1837 Map_Rename( filename );
1848 if ( Map_Unnamed( g_map ) ) {
1851 else if ( Map_Modified( g_map ) ) {
1857 const char* filename = map_save( "Export Selection" );
1859 if ( filename != 0 ) {
1860 Map_SaveSelected( filename );
1865 const char* filename = map_save( "Export Region" );
1867 if ( filename != 0 ) {
1868 Map_SaveRegion( filename );
1875 SceneChangeNotify();
1880 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1881 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1882 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1883 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1885 SceneChangeNotify();
1890 SceneChangeNotify();
1893 void RegionSelected(){
1894 Map_RegionSelectedBrushes();
1895 SceneChangeNotify();
1902 class BrushFindByIndexWalker : public scene::Traversable::Walker
1904 mutable std::size_t m_index;
1905 scene::Path& m_path;
1907 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
1908 : m_index( index ), m_path( path ){
1910 bool pre( scene::Node& node ) const {
1911 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
1912 m_path.push( makeReference( node ) );
1918 class EntityFindByIndexWalker : public scene::Traversable::Walker
1920 mutable std::size_t m_index;
1921 scene::Path& m_path;
1923 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
1924 : m_index( index ), m_path( path ){
1926 bool pre( scene::Node& node ) const {
1927 if ( Node_isEntity( node ) && m_index-- == 0 ) {
1928 m_path.push( makeReference( node ) );
1934 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
1935 path.push( makeReference( GlobalSceneGraph().root() ) );
1937 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
1939 if ( path.size() == 2 ) {
1940 scene::Traversable* traversable = Node_getTraversable( path.top() );
1941 if ( traversable != 0 ) {
1942 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
1947 inline bool Node_hasChildren( scene::Node& node ){
1948 scene::Traversable* traversable = Node_getTraversable( node );
1949 return traversable != 0 && !traversable->empty();
1952 void SelectBrush( int entitynum, int brushnum ){
1954 Scene_FindEntityBrush( entitynum, brushnum, path );
1955 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
1956 scene::Instance* instance = GlobalSceneGraph().find( path );
1957 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
1958 Selectable* selectable = Instance_getSelectable( *instance );
1959 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
1960 selectable->setSelected( true );
1961 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
1966 class BrushFindIndexWalker : public scene::Graph::Walker
1968 mutable const scene::Node* m_node;
1969 std::size_t& m_count;
1971 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
1972 : m_node( &node ), m_count( count ){
1974 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1975 if ( Node_isPrimitive( path.top() ) ) {
1976 if ( m_node == path.top().get_pointer() ) {
1987 class EntityFindIndexWalker : public scene::Graph::Walker
1989 mutable const scene::Node* m_node;
1990 std::size_t& m_count;
1992 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
1993 : m_node( &node ), m_count( count ){
1995 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1996 if ( Node_isEntity( path.top() ) ) {
1997 if ( m_node == path.top().get_pointer() ) {
2008 static void GetSelectionIndex( int *ent, int *brush ){
2009 std::size_t count_brush = 0;
2010 std::size_t count_entity = 0;
2011 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2012 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2014 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
2015 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
2017 *brush = int(count_brush);
2018 *ent = int(count_entity);
2026 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Find Brush", G_CALLBACK( dialog_delete_callback ), &dialog );
2028 GtkAccelGroup* accel = gtk_accel_group_new();
2029 gtk_window_add_accel_group( window, accel );
2032 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2033 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2035 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2036 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2038 GtkWidget* label = gtk_label_new( "Entity number" );
2039 gtk_widget_show( label );
2040 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2041 (GtkAttachOptions) ( 0 ),
2042 (GtkAttachOptions) ( 0 ), 0, 0 );
2045 GtkWidget* label = gtk_label_new( "Brush number" );
2046 gtk_widget_show( label );
2047 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2048 (GtkAttachOptions) ( 0 ),
2049 (GtkAttachOptions) ( 0 ), 0, 0 );
2052 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2053 gtk_widget_show( GTK_WIDGET( entry ) );
2054 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2055 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2056 (GtkAttachOptions) ( 0 ), 0, 0 );
2057 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2061 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2062 gtk_widget_show( GTK_WIDGET( entry ) );
2063 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2064 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2065 (GtkAttachOptions) ( 0 ), 0, 0 );
2071 GtkHBox* hbox = create_dialog_hbox( 4 );
2072 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2074 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2075 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2076 widget_make_default( GTK_WIDGET( button ) );
2077 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2080 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2081 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2082 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2087 // Initialize dialog
2091 GetSelectionIndex( &ent, &br );
2092 sprintf( buf, "%i", ent );
2093 gtk_entry_set_text( entity, buf );
2094 sprintf( buf, "%i", br );
2095 gtk_entry_set_text( brush, buf );
2097 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2098 const char *entstr = gtk_entry_get_text( entity );
2099 const char *brushstr = gtk_entry_get_text( brush );
2100 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2103 gtk_widget_destroy( GTK_WIDGET( window ) );
2106 void Map_constructPreferences( PreferencesPage& page ){
2107 page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
2111 class MapEntityClasses : public ModuleObserver
2113 std::size_t m_unrealised;
2115 MapEntityClasses() : m_unrealised( 1 ){
2118 if ( --m_unrealised == 0 ) {
2119 if ( g_map.m_resource != 0 ) {
2120 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2121 g_map.m_resource->realise();
2126 if ( ++m_unrealised == 1 ) {
2127 if ( g_map.m_resource != 0 ) {
2128 g_map.m_resource->flush();
2129 g_map.m_resource->unrealise();
2135 MapEntityClasses g_MapEntityClasses;
2138 class MapModuleObserver : public ModuleObserver
2140 std::size_t m_unrealised;
2142 MapModuleObserver() : m_unrealised( 1 ){
2145 if ( --m_unrealised == 0 ) {
2146 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2147 StringOutputStream buffer( 256 );
2148 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2149 Q_mkdir( buffer.c_str() );
2150 g_mapsPath = buffer.c_str();
2154 if ( ++m_unrealised == 1 ) {
2160 MapModuleObserver g_MapModuleObserver;
2162 #include "preferencesystem.h"
2164 CopiedString g_strLastMap;
2165 bool g_bLoadLastMap = false;
2167 void Map_Construct(){
2168 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2169 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2170 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2171 GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2173 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2174 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2175 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2177 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2179 GlobalEntityClassManager().attach( g_MapEntityClasses );
2180 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2184 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2185 GlobalEntityClassManager().detach( g_MapEntityClasses );