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"
38 #include "ifilesystem.h"
39 #include "namespace.h"
40 #include "moduleobserver.h"
44 #include <gtk/gtkmain.h>
45 #include <gtk/gtkbox.h>
46 #include <gtk/gtkentry.h>
47 #include <gtk/gtklabel.h>
48 #include <gtk/gtktable.h>
49 #include <gtk/gtktreemodel.h>
50 #include <gtk/gtktreeview.h>
51 #include <gtk/gtkliststore.h>
52 #include <gtk/gtkcellrenderertext.h>
53 #include "uilib/uilib.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<std::string, 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 );
412 free all map elements, reinitialize the structures that depend on them
418 g_map.m_resource->detach( g_map );
419 GlobalReferenceCache().release( g_map.m_name.c_str() );
420 g_map.m_resource = 0;
425 Brush_unlatchPreferences();
428 class EntityFindByClassname : public scene::Graph::Walker
433 EntityFindByClassname( const char* name, Entity*& entity ) : m_name( name ), m_entity( entity ){
436 bool pre( const scene::Path& path, scene::Instance& instance ) const {
437 if ( m_entity == 0 ) {
438 Entity* entity = Node_getEntity( path.top() );
440 && string_equal( m_name, entity->getKeyValue( "classname" ) ) ) {
448 Entity* Scene_FindEntityByClass( const char* name ){
450 GlobalSceneGraph().traverse( EntityFindByClassname( name, entity ) );
454 Entity *Scene_FindPlayerStart(){
455 typedef const char* StaticString;
456 StaticString strings[] = {
458 "info_player_deathmatch",
459 "team_CTF_redplayer",
460 "team_CTF_blueplayer",
462 "team_CTF_bluespawn",
464 typedef const StaticString* StaticStringIterator;
465 for ( StaticStringIterator i = strings, end = strings + ( sizeof( strings ) / sizeof( StaticString ) ); i != end; ++i )
467 Entity* entity = Scene_FindEntityByClass( *i );
476 // move the view to a start position
480 void FocusViews( const Vector3& point, float angle ){
481 CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
482 Camera_setOrigin( camwnd, point );
483 Vector3 angles( Camera_getAngles( camwnd ) );
484 angles[CAMERA_PITCH] = 0;
485 angles[CAMERA_YAW] = angle;
486 Camera_setAngles( camwnd, angles );
488 XYWnd* xywnd = g_pParentWnd->GetXYWnd();
489 xywnd->SetOrigin( point );
492 #include "stringio.h"
494 void Map_StartPosition(){
495 Entity* entity = Scene_FindPlayerStart();
499 string_parse_vector3( entity->getKeyValue( "origin" ), origin );
500 FocusViews( origin, string_read_float( entity->getKeyValue( "angle" ) ) );
504 FocusViews( g_vector3_identity, 0 );
509 inline bool node_is_worldspawn( scene::Node& node ){
510 Entity* entity = Node_getEntity( node );
511 return entity != 0 && string_equal( entity->getKeyValue( "classname" ), "worldspawn" );
515 // use first worldspawn
516 class entity_updateworldspawn : public scene::Traversable::Walker
519 bool pre( scene::Node& node ) const {
520 if ( node_is_worldspawn( node ) ) {
521 if ( Map_GetWorldspawn( g_map ) == 0 ) {
522 Map_SetWorldspawn( g_map, &node );
529 scene::Node* Map_FindWorldspawn( Map& map ){
530 Map_SetWorldspawn( map, 0 );
532 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
534 return Map_GetWorldspawn( map );
538 class CollectAllWalker : public scene::Traversable::Walker
541 UnsortedNodeSet& m_nodes;
543 CollectAllWalker( scene::Node& root, UnsortedNodeSet& nodes ) : m_root( root ), m_nodes( nodes ){
545 bool pre( scene::Node& node ) const {
546 m_nodes.insert( NodeSmartReference( node ) );
547 Node_getTraversable( m_root )->erase( node );
552 void Node_insertChildFirst( scene::Node& parent, scene::Node& child ){
553 UnsortedNodeSet nodes;
554 Node_getTraversable( parent )->traverse( CollectAllWalker( parent, nodes ) );
555 Node_getTraversable( parent )->insert( child );
557 for ( UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i )
559 Node_getTraversable( parent )->insert( ( *i ) );
563 scene::Node& createWorldspawn(){
564 NodeSmartReference worldspawn( GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "worldspawn", true ) ) );
565 Node_insertChildFirst( GlobalSceneGraph().root(), worldspawn );
569 void Map_UpdateWorldspawn( Map& map ){
570 if ( Map_FindWorldspawn( map ) == 0 ) {
571 Map_SetWorldspawn( map, &createWorldspawn() );
575 scene::Node& Map_FindOrInsertWorldspawn( Map& map ){
576 Map_UpdateWorldspawn( map );
577 return *Map_GetWorldspawn( map );
581 class MapMergeAll : public scene::Traversable::Walker
583 mutable scene::Path m_path;
585 MapMergeAll( const scene::Path& root )
588 bool pre( scene::Node& node ) const {
589 Node_getTraversable( m_path.top() )->insert( node );
590 m_path.push( makeReference( node ) );
591 selectPath( m_path, true );
594 void post( scene::Node& node ) const {
599 class MapMergeEntities : public scene::Traversable::Walker
601 mutable scene::Path m_path;
603 MapMergeEntities( const scene::Path& root )
606 bool pre( scene::Node& node ) const {
607 if ( node_is_worldspawn( node ) ) {
608 scene::Node* world_node = Map_FindWorldspawn( g_map );
609 if ( world_node == 0 ) {
610 Map_SetWorldspawn( g_map, &node );
611 Node_getTraversable( m_path.top().get() )->insert( node );
612 m_path.push( makeReference( node ) );
613 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
617 m_path.push( makeReference( *world_node ) );
618 Node_getTraversable( node )->traverse( MapMergeAll( m_path ) );
623 Node_getTraversable( m_path.top() )->insert( node );
624 m_path.push( makeReference( node ) );
625 if ( node_is_group( node ) ) {
626 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
630 selectPath( m_path, true );
635 void post( scene::Node& node ) const {
640 class BasicContainer : public scene::Node::Symbiot
644 NodeTypeCastTable m_casts;
647 NodeContainedCast<BasicContainer, scene::Traversable>::install( m_casts );
649 NodeTypeCastTable& get(){
655 TraversableNodeSet m_traverse;
658 typedef LazyStatic<TypeCasts> StaticTypeCasts;
660 scene::Traversable& get( NullType<scene::Traversable>){
664 BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){
674 /// Merges the map graph rooted at \p node into the global scene-graph.
675 void MergeMap( scene::Node& node ){
676 Node_getTraversable( node )->traverse( MapMergeEntities( scene::Path( makeReference( GlobalSceneGraph().root() ) ) ) );
678 void Map_ImportSelected( TextInputStream& in, const MapFormat& format ){
679 NodeSmartReference node( ( new BasicContainer )->node() );
680 format.readGraph( node, in, GlobalEntityCreator() );
681 Map_gatherNamespaced( node );
682 Map_mergeClonedNames();
686 inline scene::Cloneable* Node_getCloneable( scene::Node& node ){
687 return NodeTypeCast<scene::Cloneable>::cast( node );
690 inline scene::Node& node_clone( scene::Node& node ){
691 scene::Cloneable* cloneable = Node_getCloneable( node );
692 if ( cloneable != 0 ) {
693 return cloneable->clone();
696 return ( new scene::NullNode )->node();
699 class CloneAll : public scene::Traversable::Walker
701 mutable scene::Path m_path;
703 CloneAll( scene::Node& root )
704 : m_path( makeReference( root ) ){
706 bool pre( scene::Node& node ) const {
707 if ( node.isRoot() ) {
711 m_path.push( makeReference( node_clone( node ) ) );
712 m_path.top().get().IncRef();
716 void post( scene::Node& node ) const {
717 if ( node.isRoot() ) {
721 Node_getTraversable( m_path.parent() )->insert( m_path.top() );
723 m_path.top().get().DecRef();
728 scene::Node& Node_Clone( scene::Node& node ){
729 scene::Node& clone = node_clone( node );
730 scene::Traversable* traversable = Node_getTraversable( node );
731 if ( traversable != 0 ) {
732 traversable->traverse( CloneAll( clone ) );
738 typedef std::map<std::string, std::size_t> EntityBreakdown;
740 class EntityBreakdownWalker : public scene::Graph::Walker
742 EntityBreakdown& m_entitymap;
744 EntityBreakdownWalker( EntityBreakdown& entitymap )
745 : m_entitymap( entitymap ){
747 bool pre( const scene::Path& path, scene::Instance& instance ) const {
748 Entity* entity = Node_getEntity( path.top() );
750 const EntityClass& eclass = entity->getEntityClass();
751 if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
752 m_entitymap[eclass.name()] = 1;
754 else{ ++m_entitymap[eclass.name()]; }
760 void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
761 GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
765 WindowPosition g_posMapInfoWnd( c_default_window_pos );
769 GtkEntry* brushes_entry;
770 GtkEntry* entities_entry;
771 GtkListStore* EntityBreakdownWalker;
773 ui::Window window = MainFrame_getWindow().create_dialog_window("Map Info", G_CALLBACK(dialog_delete_callback ), &dialog );
775 window_set_position( window, g_posMapInfoWnd );
778 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
779 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
782 GtkHBox* hbox = create_dialog_hbox( 4 );
783 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
786 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
787 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
790 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
791 gtk_widget_show( GTK_WIDGET( entry ) );
792 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
793 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
794 (GtkAttachOptions) ( 0 ), 0, 0 );
795 gtk_entry_set_editable( entry, FALSE );
797 brushes_entry = entry;
800 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
801 gtk_widget_show( GTK_WIDGET( entry ) );
802 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
803 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
804 (GtkAttachOptions) ( 0 ), 0, 0 );
805 gtk_entry_set_editable( entry, FALSE );
807 entities_entry = entry;
810 ui::Widget label = ui::Label( "Total Brushes" );
811 gtk_widget_show( label );
812 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
813 (GtkAttachOptions) ( GTK_FILL ),
814 (GtkAttachOptions) ( 0 ), 0, 0 );
815 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
818 ui::Widget label = ui::Label( "Total Entities" );
819 gtk_widget_show( label );
820 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
821 (GtkAttachOptions) ( GTK_FILL ),
822 (GtkAttachOptions) ( 0 ), 0, 0 );
823 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
827 GtkVBox* vbox2 = create_dialog_vbox( 4 );
828 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, FALSE, 0 );
831 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
832 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
837 ui::Widget label = ui::Label( "Entity breakdown" );
838 gtk_widget_show( label );
839 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, TRUE, 0 );
840 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
843 GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 );
844 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( scr ), TRUE, TRUE, 0 );
847 GtkListStore* store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING );
849 ui::Widget view = ui::TreeView(ui::TreeModel( GTK_TREE_MODEL( store ) ));
850 gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE );
853 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
854 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Entity", renderer, "text", 0, 0 );
855 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
856 gtk_tree_view_column_set_sort_column_id( column, 0 );
860 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
861 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Count", renderer, "text", 1, 0 );
862 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
863 gtk_tree_view_column_set_sort_column_id( column, 1 );
866 gtk_widget_show( view );
868 gtk_container_add( GTK_CONTAINER( scr ), view );
870 EntityBreakdownWalker = store;
878 EntityBreakdown entitymap;
879 Scene_EntityBreakdown( entitymap );
881 for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i )
884 sprintf( tmp, "%u", Unsigned( ( *i ).second ) );
886 gtk_list_store_append( GTK_LIST_STORE( EntityBreakdownWalker ), &iter );
887 gtk_list_store_set( GTK_LIST_STORE( EntityBreakdownWalker ), &iter, 0, ( *i ).first.c_str(), 1, tmp, -1 );
891 g_object_unref( G_OBJECT( EntityBreakdownWalker ) );
894 sprintf( tmp, "%u", Unsigned( g_brushCount.get() ) );
895 gtk_entry_set_text( GTK_ENTRY( brushes_entry ), tmp );
896 sprintf( tmp, "%u", Unsigned( g_entityCount.get() ) );
897 gtk_entry_set_text( GTK_ENTRY( entities_entry ), tmp );
899 modal_dialog_show( window, dialog );
902 window_get_position( window, g_posMapInfoWnd );
904 gtk_widget_destroy( GTK_WIDGET( window ) );
912 const char* m_message;
914 ScopeTimer( const char* message )
915 : m_message( message ){
919 double elapsed_time = m_timer.elapsed_msec() / 1000.f;
920 globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
930 void Map_LoadFile( const char *filename ){
931 globalOutputStream() << "Loading map from " << filename << "\n";
932 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
935 ScopeTimer timer( "map load" );
937 const MapFormat* format = NULL;
938 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
939 if ( string_not_empty( moduleName ) ) {
940 format = ReferenceAPI_getMapModules().findModule( moduleName );
943 for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
948 Brush_toggleFormat( i );
949 g_map.m_name = filename;
950 Map_UpdateTitle( g_map );
951 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
953 format->wrongFormat = false;
955 g_map.m_resource->attach( g_map );
957 if ( !format->wrongFormat ) {
963 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
966 globalOutputStream() << "--- LoadMapFile ---\n";
967 globalOutputStream() << g_map.m_name.c_str() << "\n";
969 globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
970 globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
972 //GlobalEntityCreator().printStatistics();
975 // move the view to a start position
979 g_currentMap = &g_map;
985 virtual bool excluded( scene::Node& node ) const = 0;
988 class ExcludeWalker : public scene::Traversable::Walker
990 const scene::Traversable::Walker& m_walker;
991 const Excluder* m_exclude;
994 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
995 : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
997 bool pre( scene::Node& node ) const {
998 if ( m_exclude->excluded( node ) || node.isRoot() ) {
1004 m_walker.pre( node );
1008 void post( scene::Node& node ) const {
1014 m_walker.post( node );
1019 class AnyInstanceSelected : public scene::Instantiable::Visitor
1023 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1026 void visit( scene::Instance& instance ) const {
1027 Selectable* selectable = Instance_getSelectable( instance );
1028 if ( selectable != 0
1029 && selectable->isSelected() ) {
1035 bool Node_instanceSelected( scene::Node& node ){
1036 scene::Instantiable* instantiable = Node_getInstantiable( node );
1037 ASSERT_NOTNULL( instantiable );
1039 instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1043 class SelectedDescendantWalker : public scene::Traversable::Walker
1047 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1051 bool pre( scene::Node& node ) const {
1052 if ( node.isRoot() ) {
1056 if ( Node_instanceSelected( node ) ) {
1064 bool Node_selectedDescendant( scene::Node& node ){
1066 Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1070 class SelectionExcluder : public Excluder
1073 bool excluded( scene::Node& node ) const {
1074 return !Node_selectedDescendant( node );
1078 class IncludeSelectedWalker : public scene::Traversable::Walker
1080 const scene::Traversable::Walker& m_walker;
1081 mutable std::size_t m_selected;
1082 mutable bool m_skip;
1084 bool selectedParent() const {
1085 return m_selected != 0;
1088 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1089 : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1091 bool pre( scene::Node& node ) const {
1093 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1094 if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1095 if ( Node_instanceSelected( node ) ) {
1098 m_walker.pre( node );
1107 void post( scene::Node& node ) const {
1113 if ( Node_instanceSelected( node ) ) {
1116 m_walker.post( node );
1121 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1122 scene::Traversable* traversable = Node_getTraversable( root );
1123 if ( traversable != 0 ) {
1125 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1127 traversable->traverse( IncludeSelectedWalker( walker ) );
1132 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1133 format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1136 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1137 scene::Traversable* traversable = Node_getTraversable( root );
1138 if ( traversable != 0 ) {
1139 traversable->traverse( walker );
1143 class RegionExcluder : public Excluder
1146 bool excluded( scene::Node& node ) const {
1147 return node.excluded();
1151 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1152 scene::Traversable* traversable = Node_getTraversable( root );
1153 if ( traversable != 0 ) {
1154 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1158 bool Map_SaveRegion( const char *filename ){
1161 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1163 RemoveRegionBrushes();
1169 void Map_RenameAbsolute( const char* absolute ){
1170 Resource* resource = GlobalReferenceCache().capture( absolute );
1171 NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1172 resource->setNode( clone.get_pointer() );
1175 //ScopeTimer timer("clone subgraph");
1176 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1179 g_map.m_resource->detach( g_map );
1180 GlobalReferenceCache().release( g_map.m_name.c_str() );
1182 g_map.m_resource = resource;
1184 g_map.m_name = absolute;
1185 Map_UpdateTitle( g_map );
1187 g_map.m_resource->attach( g_map );
1190 void Map_Rename( const char* filename ){
1191 if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1192 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1194 Map_RenameAbsolute( filename );
1196 SceneChangeNotify();
1207 ScopeTimer timer( "map save" );
1209 return true; // assume success..
1219 //globalOutputStream() << "Map_New\n";
1221 g_map.m_name = "unnamed.map";
1222 Map_UpdateTitle( g_map );
1225 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1226 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1227 g_map.m_resource->attach( g_map );
1229 SceneChangeNotify();
1232 FocusViews( g_vector3_identity, 0 );
1234 g_currentMap = &g_map;
1237 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
1239 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1241 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1242 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1243 for now, let's just print an error
1246 Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1248 for ( int i = 0 ; i < 3 ; i++ )
1250 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1251 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1256 // write the info_playerstart
1258 sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1259 Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1260 sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1261 Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1265 ===========================================================
1269 ===========================================================
1272 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1273 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1275 scene::Node* region_sides[6];
1276 scene::Node* region_startpoint = 0;
1281 a regioned map will have temp walls put up at the region boundary
1282 \todo TODO TTimo old implementation of region brushes
1283 we still add them straight in the worldspawn and take them out after the map is saved
1284 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1287 void AddRegionBrushes( void ){
1290 for ( i = 0; i < 6; i++ )
1292 region_sides[i] = &GlobalBrushCreator().createBrush();
1293 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1296 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1298 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1299 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1301 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1304 void RemoveRegionBrushes( void ){
1305 for ( std::size_t i = 0; i < 6; i++ )
1307 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1309 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1312 inline void exclude_node( scene::Node& node, bool exclude ){
1314 ? node.enable( scene::Node::eExcluded )
1315 : node.disable( scene::Node::eExcluded );
1318 class ExcludeAllWalker : public scene::Graph::Walker
1322 ExcludeAllWalker( bool exclude )
1323 : m_exclude( exclude ){
1325 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1326 exclude_node( path.top(), m_exclude );
1332 void Scene_Exclude_All( bool exclude ){
1333 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1336 bool Instance_isSelected( const scene::Instance& instance ){
1337 const Selectable* selectable = Instance_getSelectable( instance );
1338 return selectable != 0 && selectable->isSelected();
1341 class ExcludeSelectedWalker : public scene::Graph::Walker
1345 ExcludeSelectedWalker( bool exclude )
1346 : m_exclude( exclude ){
1348 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1349 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1354 void Scene_Exclude_Selected( bool exclude ){
1355 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1358 class ExcludeRegionedWalker : public scene::Graph::Walker
1362 ExcludeRegionedWalker( bool exclude )
1363 : m_exclude( exclude ){
1365 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1370 aabb_intersects_aabb(
1371 instance.worldAABB(),
1372 aabb_for_minmax( region_mins, region_maxs )
1382 void Scene_Exclude_Region( bool exclude ){
1383 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1390 Other filtering options may still be on
1393 void Map_RegionOff(){
1394 region_active = false;
1396 region_maxs[0] = g_MaxWorldCoord - 64;
1397 region_mins[0] = g_MinWorldCoord + 64;
1398 region_maxs[1] = g_MaxWorldCoord - 64;
1399 region_mins[1] = g_MinWorldCoord + 64;
1400 region_maxs[2] = g_MaxWorldCoord - 64;
1401 region_mins[2] = g_MinWorldCoord + 64;
1403 Scene_Exclude_All( false );
1406 void Map_ApplyRegion( void ){
1407 region_active = true;
1409 Scene_Exclude_Region( false );
1414 ========================
1415 Map_RegionSelectedBrushes
1416 ========================
1418 void Map_RegionSelectedBrushes( void ){
1421 if ( GlobalSelectionSystem().countSelected() != 0
1422 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1423 region_active = true;
1424 Select_GetBounds( region_mins, region_maxs );
1426 Scene_Exclude_Selected( false );
1428 GlobalSelectionSystem().setSelectedAll( false );
1438 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1441 region_mins[0] = x_min;
1442 region_maxs[0] = x_max;
1443 region_mins[1] = y_min;
1444 region_maxs[1] = y_max;
1445 region_mins[2] = g_MinWorldCoord + 64;
1446 region_maxs[2] = g_MaxWorldCoord - 64;
1451 void Map_RegionBounds( const AABB& bounds ){
1454 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1455 region_maxs = vector3_added( bounds.origin, bounds.extents );
1467 void Map_RegionBrush( void ){
1468 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1469 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1470 Map_RegionBounds( instance.worldAABB() );
1479 bool Map_ImportFile( const char* filename ){
1480 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1482 bool success = false;
1484 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1489 const MapFormat* format = NULL;
1490 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1491 if ( string_not_empty( moduleName ) ) {
1492 format = ReferenceAPI_getMapModules().findModule( moduleName );
1496 format->wrongFormat = false;
1498 Resource* resource = GlobalReferenceCache().capture( filename );
1499 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1500 if ( !resource->load() ) {
1501 GlobalReferenceCache().release( filename );
1505 if ( format->wrongFormat ) {
1506 GlobalReferenceCache().release( filename );
1510 NodeSmartReference clone( NewMapRoot( "" ) );
1511 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1512 Map_gatherNamespaced( clone );
1513 Map_mergeClonedNames();
1516 GlobalReferenceCache().release( filename );
1519 SceneChangeNotify();
1525 const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
1526 int n = string_length( path_get_extension( filename ) );
1527 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1528 StringBuffer output;
1529 output.push_string( AppPath_get() );
1530 output.push_string( "q3map2." );
1531 output.push_string( RADIANT_EXECUTABLE );
1532 output.push_string( " -v -game " );
1533 output.push_string( ( type && *type ) ? type : "quake3" );
1534 output.push_string( " -fs_basepath \"" );
1535 output.push_string( EnginePath_get() );
1536 output.push_string( "\" -fs_homepath \"" );
1537 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1538 output.push_string( "\" -fs_game " );
1539 output.push_string( gamename_get() );
1540 output.push_string( " -convert -format " );
1541 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1542 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1543 output.push_string( " -readmap " );
1545 output.push_string( " \"" );
1546 output.push_string( filename );
1547 output.push_string( "\"" );
1550 Q_Exec( NULL, output.c_str(), NULL, false, true );
1552 // rebuild filename as "filenamewithoutext_converted.map"
1554 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1555 output.push_string( "_converted.map" );
1556 filename = output.c_str();
1559 Resource* resource = GlobalReferenceCache().capture( filename );
1560 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1561 if ( !resource->load() ) {
1562 GlobalReferenceCache().release( filename );
1565 NodeSmartReference clone( NewMapRoot( "" ) );
1566 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1567 Map_gatherNamespaced( clone );
1568 Map_mergeClonedNames();
1571 GlobalReferenceCache().release( filename );
1574 SceneChangeNotify();
1583 bool Map_SaveFile( const char* filename ){
1584 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1585 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1593 // Saves selected world brushes and whole entities with partial/full selections
1595 bool Map_SaveSelected( const char* filename ){
1596 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1600 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1602 scene::Node& m_parent;
1604 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
1606 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1607 if ( path.top().get_pointer() != &m_parent
1608 && Node_isPrimitive( path.top() ) ) {
1609 Selectable* selectable = Instance_getSelectable( instance );
1610 if ( selectable != 0
1611 && selectable->isSelected()
1612 && path.size() > 1 ) {
1618 void post( const scene::Path& path, scene::Instance& instance ) const {
1619 if ( path.top().get_pointer() != &m_parent
1620 && Node_isPrimitive( path.top() ) ) {
1621 Selectable* selectable = Instance_getSelectable( instance );
1622 if ( selectable != 0
1623 && selectable->isSelected()
1624 && path.size() > 1 ) {
1625 scene::Node& parent = path.parent();
1626 if ( &parent != &m_parent ) {
1627 NodeSmartReference node( path.top().get() );
1628 Node_getTraversable( parent )->erase( node );
1629 Node_getTraversable( m_parent )->insert( node );
1636 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1637 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1640 class CountSelectedBrushes : public scene::Graph::Walker
1642 std::size_t& m_count;
1643 mutable std::size_t m_depth;
1645 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1648 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1649 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1652 Selectable* selectable = Instance_getSelectable( instance );
1653 if ( selectable != 0
1654 && selectable->isSelected()
1655 && Node_isPrimitive( path.top() ) ) {
1660 void post( const scene::Path& path, scene::Instance& instance ) const {
1665 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1667 graph.traverse( CountSelectedBrushes( count ) );
1679 const char* nodetype_get_name( ENodeType type ){
1680 if ( type == eNodeMap ) {
1683 if ( type == eNodeEntity ) {
1686 if ( type == eNodePrimitive ) {
1692 ENodeType node_get_nodetype( scene::Node& node ){
1693 if ( Node_isEntity( node ) ) {
1696 if ( Node_isPrimitive( node ) ) {
1697 return eNodePrimitive;
1699 return eNodeUnknown;
1702 bool contains_entity( scene::Node& node ){
1703 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1706 bool contains_primitive( scene::Node& node ){
1707 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1710 ENodeType node_get_contains( scene::Node& node ){
1711 if ( contains_entity( node ) ) {
1714 if ( contains_primitive( node ) ) {
1715 return eNodePrimitive;
1717 return eNodeUnknown;
1720 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1721 ENodeType contains = node_get_contains( parent.top() );
1722 ENodeType type = node_get_nodetype( child.top() );
1724 if ( contains != eNodeUnknown && contains == type ) {
1725 NodeSmartReference node( child.top().get() );
1726 Path_deleteTop( child );
1727 Node_getTraversable( parent.top() )->insert( node );
1728 SceneChangeNotify();
1732 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1736 void Scene_parentSelected(){
1737 UndoableCommand undo( "parentSelected" );
1739 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1740 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1742 const scene::Path& m_parent;
1744 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1746 void visit( scene::Instance& instance ) const {
1747 if ( &m_parent != &instance.path() ) {
1748 Path_parent( m_parent, instance.path() );
1753 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1754 GlobalSelectionSystem().foreachSelected( visitor );
1758 globalOutputStream() << "failed - did not find two selected nodes.\n";
1765 if ( ConfirmModified( "New Map" ) ) {
1772 std::string g_mapsPath;
1774 const char* getMapsPath(){
1775 return g_mapsPath.c_str();
1778 const char* map_open( const char* title ){
1779 return MainFrame_getWindow().file_dialog( TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
1782 const char* map_import( const char* title ){
1783 return MainFrame_getWindow().file_dialog( TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
1786 const char* map_save( const char* title ){
1787 return MainFrame_getWindow().file_dialog( FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
1791 if ( !ConfirmModified( "Open Map" ) ) {
1795 const char* filename = map_open( "Open Map" );
1797 if ( filename != 0 ) {
1798 MRU_AddFile( filename );
1801 Map_LoadFile( filename );
1806 const char* filename = map_import( "Import Map" );
1808 if ( filename != 0 ) {
1809 UndoableCommand undo( "mapImport" );
1810 Map_ImportFile( filename );
1815 const char* filename = map_save( "Save Map" );
1817 if ( filename != 0 ) {
1818 MRU_AddFile( filename );
1819 Map_Rename( filename );
1830 if ( Map_Unnamed( g_map ) ) {
1833 else if ( Map_Modified( g_map ) ) {
1839 const char* filename = map_save( "Export Selection" );
1841 if ( filename != 0 ) {
1842 Map_SaveSelected( filename );
1847 const char* filename = map_save( "Export Region" );
1849 if ( filename != 0 ) {
1850 Map_SaveRegion( filename );
1857 SceneChangeNotify();
1862 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1863 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1864 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1865 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1867 SceneChangeNotify();
1872 SceneChangeNotify();
1875 void RegionSelected(){
1876 Map_RegionSelectedBrushes();
1877 SceneChangeNotify();
1884 class BrushFindByIndexWalker : public scene::Traversable::Walker
1886 mutable std::size_t m_index;
1887 scene::Path& m_path;
1889 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
1890 : m_index( index ), m_path( path ){
1892 bool pre( scene::Node& node ) const {
1893 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
1894 m_path.push( makeReference( node ) );
1900 class EntityFindByIndexWalker : public scene::Traversable::Walker
1902 mutable std::size_t m_index;
1903 scene::Path& m_path;
1905 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
1906 : m_index( index ), m_path( path ){
1908 bool pre( scene::Node& node ) const {
1909 if ( Node_isEntity( node ) && m_index-- == 0 ) {
1910 m_path.push( makeReference( node ) );
1916 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
1917 path.push( makeReference( GlobalSceneGraph().root() ) );
1919 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
1921 if ( path.size() == 2 ) {
1922 scene::Traversable* traversable = Node_getTraversable( path.top() );
1923 if ( traversable != 0 ) {
1924 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
1929 inline bool Node_hasChildren( scene::Node& node ){
1930 scene::Traversable* traversable = Node_getTraversable( node );
1931 return traversable != 0 && !traversable->empty();
1934 void SelectBrush( int entitynum, int brushnum ){
1936 Scene_FindEntityBrush( entitynum, brushnum, path );
1937 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
1938 scene::Instance* instance = GlobalSceneGraph().find( path );
1939 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
1940 Selectable* selectable = Instance_getSelectable( *instance );
1941 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
1942 selectable->setSelected( true );
1943 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
1948 class BrushFindIndexWalker : public scene::Graph::Walker
1950 mutable const scene::Node* m_node;
1951 std::size_t& m_count;
1953 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
1954 : m_node( &node ), m_count( count ){
1956 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1957 if ( Node_isPrimitive( path.top() ) ) {
1958 if ( m_node == path.top().get_pointer() ) {
1969 class EntityFindIndexWalker : public scene::Graph::Walker
1971 mutable const scene::Node* m_node;
1972 std::size_t& m_count;
1974 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
1975 : m_node( &node ), m_count( count ){
1977 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1978 if ( Node_isEntity( path.top() ) ) {
1979 if ( m_node == path.top().get_pointer() ) {
1990 static void GetSelectionIndex( int *ent, int *brush ){
1991 std::size_t count_brush = 0;
1992 std::size_t count_entity = 0;
1993 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1994 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
1996 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
1997 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
1999 *brush = int(count_brush);
2000 *ent = int(count_entity);
2008 ui::Window window = MainFrame_getWindow().create_dialog_window("Find Brush", G_CALLBACK(dialog_delete_callback ), &dialog );
2010 GtkAccelGroup* accel = gtk_accel_group_new();
2011 gtk_window_add_accel_group( window, accel );
2014 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2015 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2017 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2018 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2020 ui::Widget label = ui::Label( "Entity number" );
2021 gtk_widget_show( label );
2022 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2023 (GtkAttachOptions) ( 0 ),
2024 (GtkAttachOptions) ( 0 ), 0, 0 );
2027 ui::Widget label = ui::Label( "Brush number" );
2028 gtk_widget_show( label );
2029 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2030 (GtkAttachOptions) ( 0 ),
2031 (GtkAttachOptions) ( 0 ), 0, 0 );
2034 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2035 gtk_widget_show( GTK_WIDGET( entry ) );
2036 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2037 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2038 (GtkAttachOptions) ( 0 ), 0, 0 );
2039 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2043 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2044 gtk_widget_show( GTK_WIDGET( entry ) );
2045 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2046 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2047 (GtkAttachOptions) ( 0 ), 0, 0 );
2053 GtkHBox* hbox = create_dialog_hbox( 4 );
2054 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2056 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2057 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2058 widget_make_default( GTK_WIDGET( button ) );
2059 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2062 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2063 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2064 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2069 // Initialize dialog
2073 GetSelectionIndex( &ent, &br );
2074 sprintf( buf, "%i", ent );
2075 gtk_entry_set_text( entity, buf );
2076 sprintf( buf, "%i", br );
2077 gtk_entry_set_text( brush, buf );
2079 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2080 const char *entstr = gtk_entry_get_text( entity );
2081 const char *brushstr = gtk_entry_get_text( brush );
2082 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2085 gtk_widget_destroy( GTK_WIDGET( window ) );
2088 void Map_constructPreferences( PreferencesPage& page ){
2089 page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
2093 class MapEntityClasses : public ModuleObserver
2095 std::size_t m_unrealised;
2097 MapEntityClasses() : m_unrealised( 1 ){
2100 if ( --m_unrealised == 0 ) {
2101 if ( g_map.m_resource != 0 ) {
2102 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2103 g_map.m_resource->realise();
2108 if ( ++m_unrealised == 1 ) {
2109 if ( g_map.m_resource != 0 ) {
2110 g_map.m_resource->flush();
2111 g_map.m_resource->unrealise();
2117 MapEntityClasses g_MapEntityClasses;
2120 class MapModuleObserver : public ModuleObserver
2122 std::size_t m_unrealised;
2124 MapModuleObserver() : m_unrealised( 1 ){
2127 if ( --m_unrealised == 0 ) {
2128 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2129 StringOutputStream buffer( 256 );
2130 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2131 Q_mkdir( buffer.c_str() );
2132 g_mapsPath = buffer.c_str();
2136 if ( ++m_unrealised == 1 ) {
2142 MapModuleObserver g_MapModuleObserver;
2144 #include "preferencesystem.h"
2146 std::string g_strLastMap;
2147 bool g_bLoadLastMap = false;
2149 void Map_Construct(){
2150 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2151 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2152 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2153 GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2155 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2156 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2157 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2159 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2161 GlobalEntityClassManager().attach( g_MapEntityClasses );
2162 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2166 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2167 GlobalEntityClassManager().detach( g_MapEntityClasses );