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;
986 virtual bool excluded( scene::Node& node ) const = 0;
989 class ExcludeWalker : public scene::Traversable::Walker
991 const scene::Traversable::Walker& m_walker;
992 const Excluder* m_exclude;
995 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
996 : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
998 bool pre( scene::Node& node ) const {
999 if ( m_exclude->excluded( node ) || node.isRoot() ) {
1005 m_walker.pre( node );
1009 void post( scene::Node& node ) const {
1015 m_walker.post( node );
1020 class AnyInstanceSelected : public scene::Instantiable::Visitor
1024 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1027 void visit( scene::Instance& instance ) const {
1028 Selectable* selectable = Instance_getSelectable( instance );
1029 if ( selectable != 0
1030 && selectable->isSelected() ) {
1036 bool Node_instanceSelected( scene::Node& node ){
1037 scene::Instantiable* instantiable = Node_getInstantiable( node );
1038 ASSERT_NOTNULL( instantiable );
1040 instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1044 class SelectedDescendantWalker : public scene::Traversable::Walker
1048 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1052 bool pre( scene::Node& node ) const {
1053 if ( node.isRoot() ) {
1057 if ( Node_instanceSelected( node ) ) {
1065 bool Node_selectedDescendant( scene::Node& node ){
1067 Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1071 class SelectionExcluder : public Excluder
1074 bool excluded( scene::Node& node ) const {
1075 return !Node_selectedDescendant( node );
1079 class IncludeSelectedWalker : public scene::Traversable::Walker
1081 const scene::Traversable::Walker& m_walker;
1082 mutable std::size_t m_selected;
1083 mutable bool m_skip;
1085 bool selectedParent() const {
1086 return m_selected != 0;
1089 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1090 : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1092 bool pre( scene::Node& node ) const {
1094 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1095 if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1096 if ( Node_instanceSelected( node ) ) {
1099 m_walker.pre( node );
1108 void post( scene::Node& node ) const {
1114 if ( Node_instanceSelected( node ) ) {
1117 m_walker.post( node );
1122 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1123 scene::Traversable* traversable = Node_getTraversable( root );
1124 if ( traversable != 0 ) {
1126 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1128 traversable->traverse( IncludeSelectedWalker( walker ) );
1133 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1134 format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1137 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1138 scene::Traversable* traversable = Node_getTraversable( root );
1139 if ( traversable != 0 ) {
1140 traversable->traverse( walker );
1144 class RegionExcluder : public Excluder
1147 bool excluded( scene::Node& node ) const {
1148 return node.excluded();
1152 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1153 scene::Traversable* traversable = Node_getTraversable( root );
1154 if ( traversable != 0 ) {
1155 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1159 bool Map_SaveRegion( const char *filename ){
1162 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1164 RemoveRegionBrushes();
1170 void Map_RenameAbsolute( const char* absolute ){
1171 Resource* resource = GlobalReferenceCache().capture( absolute );
1172 NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1173 resource->setNode( clone.get_pointer() );
1176 //ScopeTimer timer("clone subgraph");
1177 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1180 g_map.m_resource->detach( g_map );
1181 GlobalReferenceCache().release( g_map.m_name.c_str() );
1183 g_map.m_resource = resource;
1185 g_map.m_name = absolute;
1186 Map_UpdateTitle( g_map );
1188 g_map.m_resource->attach( g_map );
1191 void Map_Rename( const char* filename ){
1192 if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1193 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1195 Map_RenameAbsolute( filename );
1197 SceneChangeNotify();
1208 ScopeTimer timer( "map save" );
1210 return true; // assume success..
1220 //globalOutputStream() << "Map_New\n";
1222 g_map.m_name = "unnamed.map";
1223 Map_UpdateTitle( g_map );
1226 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1227 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1228 g_map.m_resource->attach( g_map );
1230 SceneChangeNotify();
1233 FocusViews( g_vector3_identity, 0 );
1235 g_currentMap = &g_map;
1238 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
1240 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1242 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1243 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1244 for now, let's just print an error
1247 Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1249 for ( int i = 0 ; i < 3 ; i++ )
1251 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1252 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1257 // write the info_playerstart
1259 sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1260 Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1261 sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1262 Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1266 ===========================================================
1270 ===========================================================
1273 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1274 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1276 scene::Node* region_sides[6];
1277 scene::Node* region_startpoint = 0;
1282 a regioned map will have temp walls put up at the region boundary
1283 \todo TODO TTimo old implementation of region brushes
1284 we still add them straight in the worldspawn and take them out after the map is saved
1285 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1288 void AddRegionBrushes( void ){
1291 for ( i = 0; i < 6; i++ )
1293 region_sides[i] = &GlobalBrushCreator().createBrush();
1294 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1297 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1299 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1300 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1302 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1305 void RemoveRegionBrushes( void ){
1306 for ( std::size_t i = 0; i < 6; i++ )
1308 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1310 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1313 inline void exclude_node( scene::Node& node, bool exclude ){
1315 ? node.enable( scene::Node::eExcluded )
1316 : node.disable( scene::Node::eExcluded );
1319 class ExcludeAllWalker : public scene::Graph::Walker
1323 ExcludeAllWalker( bool exclude )
1324 : m_exclude( exclude ){
1326 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1327 exclude_node( path.top(), m_exclude );
1333 void Scene_Exclude_All( bool exclude ){
1334 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1337 bool Instance_isSelected( const scene::Instance& instance ){
1338 const Selectable* selectable = Instance_getSelectable( instance );
1339 return selectable != 0 && selectable->isSelected();
1342 class ExcludeSelectedWalker : public scene::Graph::Walker
1346 ExcludeSelectedWalker( bool exclude )
1347 : m_exclude( exclude ){
1349 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1350 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1355 void Scene_Exclude_Selected( bool exclude ){
1356 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1359 class ExcludeRegionedWalker : public scene::Graph::Walker
1363 ExcludeRegionedWalker( bool exclude )
1364 : m_exclude( exclude ){
1366 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1371 aabb_intersects_aabb(
1372 instance.worldAABB(),
1373 aabb_for_minmax( region_mins, region_maxs )
1383 void Scene_Exclude_Region( bool exclude ){
1384 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1391 Other filtering options may still be on
1394 void Map_RegionOff(){
1395 region_active = false;
1397 region_maxs[0] = g_MaxWorldCoord - 64;
1398 region_mins[0] = g_MinWorldCoord + 64;
1399 region_maxs[1] = g_MaxWorldCoord - 64;
1400 region_mins[1] = g_MinWorldCoord + 64;
1401 region_maxs[2] = g_MaxWorldCoord - 64;
1402 region_mins[2] = g_MinWorldCoord + 64;
1404 Scene_Exclude_All( false );
1407 void Map_ApplyRegion( void ){
1408 region_active = true;
1410 Scene_Exclude_Region( false );
1415 ========================
1416 Map_RegionSelectedBrushes
1417 ========================
1419 void Map_RegionSelectedBrushes( void ){
1422 if ( GlobalSelectionSystem().countSelected() != 0
1423 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1424 region_active = true;
1425 Select_GetBounds( region_mins, region_maxs );
1427 Scene_Exclude_Selected( false );
1429 GlobalSelectionSystem().setSelectedAll( false );
1439 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1442 region_mins[0] = x_min;
1443 region_maxs[0] = x_max;
1444 region_mins[1] = y_min;
1445 region_maxs[1] = y_max;
1446 region_mins[2] = g_MinWorldCoord + 64;
1447 region_maxs[2] = g_MaxWorldCoord - 64;
1452 void Map_RegionBounds( const AABB& bounds ){
1455 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1456 region_maxs = vector3_added( bounds.origin, bounds.extents );
1468 void Map_RegionBrush( void ){
1469 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1470 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1471 Map_RegionBounds( instance.worldAABB() );
1480 bool Map_ImportFile( const char* filename ){
1481 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1483 bool success = false;
1485 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1490 const MapFormat* format = NULL;
1491 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1492 if ( string_not_empty( moduleName ) ) {
1493 format = ReferenceAPI_getMapModules().findModule( moduleName );
1497 format->wrongFormat = false;
1499 Resource* resource = GlobalReferenceCache().capture( filename );
1500 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1501 if ( !resource->load() ) {
1502 GlobalReferenceCache().release( filename );
1506 if ( format->wrongFormat ) {
1507 GlobalReferenceCache().release( filename );
1511 NodeSmartReference clone( NewMapRoot( "" ) );
1512 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1513 Map_gatherNamespaced( clone );
1514 Map_mergeClonedNames();
1517 GlobalReferenceCache().release( filename );
1520 SceneChangeNotify();
1526 const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
1527 int n = string_length( path_get_extension( filename ) );
1528 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1529 StringBuffer output;
1530 output.push_string( AppPath_get() );
1531 output.push_string( "q3map2." );
1532 output.push_string( RADIANT_EXECUTABLE );
1533 output.push_string( " -v -game " );
1534 output.push_string( ( type && *type ) ? type : "quake3" );
1535 output.push_string( " -fs_basepath \"" );
1536 output.push_string( EnginePath_get() );
1537 output.push_string( "\" -fs_homepath \"" );
1538 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1539 output.push_string( "\" -fs_game " );
1540 output.push_string( gamename_get() );
1541 output.push_string( " -convert -format " );
1542 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1543 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1544 output.push_string( " -readmap " );
1546 output.push_string( " \"" );
1547 output.push_string( filename );
1548 output.push_string( "\"" );
1551 Q_Exec( NULL, output.c_str(), NULL, false, true );
1553 // rebuild filename as "filenamewithoutext_converted.map"
1555 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1556 output.push_string( "_converted.map" );
1557 filename = output.c_str();
1560 Resource* resource = GlobalReferenceCache().capture( filename );
1561 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1562 if ( !resource->load() ) {
1563 GlobalReferenceCache().release( filename );
1566 NodeSmartReference clone( NewMapRoot( "" ) );
1567 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1568 Map_gatherNamespaced( clone );
1569 Map_mergeClonedNames();
1572 GlobalReferenceCache().release( filename );
1575 SceneChangeNotify();
1584 bool Map_SaveFile( const char* filename ){
1585 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1586 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1594 // Saves selected world brushes and whole entities with partial/full selections
1596 bool Map_SaveSelected( const char* filename ){
1597 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1601 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1603 scene::Node& m_parent;
1605 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
1607 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1608 if ( path.top().get_pointer() != &m_parent
1609 && Node_isPrimitive( path.top() ) ) {
1610 Selectable* selectable = Instance_getSelectable( instance );
1611 if ( selectable != 0
1612 && selectable->isSelected()
1613 && path.size() > 1 ) {
1619 void post( const scene::Path& path, scene::Instance& instance ) const {
1620 if ( path.top().get_pointer() != &m_parent
1621 && Node_isPrimitive( path.top() ) ) {
1622 Selectable* selectable = Instance_getSelectable( instance );
1623 if ( selectable != 0
1624 && selectable->isSelected()
1625 && path.size() > 1 ) {
1626 scene::Node& parent = path.parent();
1627 if ( &parent != &m_parent ) {
1628 NodeSmartReference node( path.top().get() );
1629 Node_getTraversable( parent )->erase( node );
1630 Node_getTraversable( m_parent )->insert( node );
1637 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1638 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1641 class CountSelectedBrushes : public scene::Graph::Walker
1643 std::size_t& m_count;
1644 mutable std::size_t m_depth;
1646 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1649 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1650 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1653 Selectable* selectable = Instance_getSelectable( instance );
1654 if ( selectable != 0
1655 && selectable->isSelected()
1656 && Node_isPrimitive( path.top() ) ) {
1661 void post( const scene::Path& path, scene::Instance& instance ) const {
1666 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1668 graph.traverse( CountSelectedBrushes( count ) );
1680 const char* nodetype_get_name( ENodeType type ){
1681 if ( type == eNodeMap ) {
1684 if ( type == eNodeEntity ) {
1687 if ( type == eNodePrimitive ) {
1693 ENodeType node_get_nodetype( scene::Node& node ){
1694 if ( Node_isEntity( node ) ) {
1697 if ( Node_isPrimitive( node ) ) {
1698 return eNodePrimitive;
1700 return eNodeUnknown;
1703 bool contains_entity( scene::Node& node ){
1704 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1707 bool contains_primitive( scene::Node& node ){
1708 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1711 ENodeType node_get_contains( scene::Node& node ){
1712 if ( contains_entity( node ) ) {
1715 if ( contains_primitive( node ) ) {
1716 return eNodePrimitive;
1718 return eNodeUnknown;
1721 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1722 ENodeType contains = node_get_contains( parent.top() );
1723 ENodeType type = node_get_nodetype( child.top() );
1725 if ( contains != eNodeUnknown && contains == type ) {
1726 NodeSmartReference node( child.top().get() );
1727 Path_deleteTop( child );
1728 Node_getTraversable( parent.top() )->insert( node );
1729 SceneChangeNotify();
1733 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1737 void Scene_parentSelected(){
1738 UndoableCommand undo( "parentSelected" );
1740 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1741 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1743 const scene::Path& m_parent;
1745 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1747 void visit( scene::Instance& instance ) const {
1748 if ( &m_parent != &instance.path() ) {
1749 Path_parent( m_parent, instance.path() );
1754 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1755 GlobalSelectionSystem().foreachSelected( visitor );
1759 globalOutputStream() << "failed - did not find two selected nodes.\n";
1766 if ( ConfirmModified( "New Map" ) ) {
1773 CopiedString g_mapsPath;
1775 const char* getMapsPath(){
1776 return g_mapsPath.c_str();
1779 const char* map_open( const char* title ){
1780 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
1783 const char* map_import( const char* title ){
1784 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
1787 const char* map_save( const char* title ){
1788 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
1792 if ( !ConfirmModified( "Open Map" ) ) {
1796 const char* filename = map_open( "Open Map" );
1798 if ( filename != 0 ) {
1799 MRU_AddFile( filename );
1802 Map_LoadFile( filename );
1807 const char* filename = map_import( "Import Map" );
1809 if ( filename != 0 ) {
1810 UndoableCommand undo( "mapImport" );
1811 Map_ImportFile( filename );
1816 const char* filename = map_save( "Save Map" );
1818 if ( filename != 0 ) {
1819 MRU_AddFile( filename );
1820 Map_Rename( filename );
1831 if ( Map_Unnamed( g_map ) ) {
1834 else if ( Map_Modified( g_map ) ) {
1840 const char* filename = map_save( "Export Selection" );
1842 if ( filename != 0 ) {
1843 Map_SaveSelected( filename );
1848 const char* filename = map_save( "Export Region" );
1850 if ( filename != 0 ) {
1851 Map_SaveRegion( filename );
1858 SceneChangeNotify();
1863 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1864 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1865 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1866 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1868 SceneChangeNotify();
1873 SceneChangeNotify();
1876 void RegionSelected(){
1877 Map_RegionSelectedBrushes();
1878 SceneChangeNotify();
1885 class BrushFindByIndexWalker : public scene::Traversable::Walker
1887 mutable std::size_t m_index;
1888 scene::Path& m_path;
1890 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
1891 : m_index( index ), m_path( path ){
1893 bool pre( scene::Node& node ) const {
1894 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
1895 m_path.push( makeReference( node ) );
1901 class EntityFindByIndexWalker : public scene::Traversable::Walker
1903 mutable std::size_t m_index;
1904 scene::Path& m_path;
1906 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
1907 : m_index( index ), m_path( path ){
1909 bool pre( scene::Node& node ) const {
1910 if ( Node_isEntity( node ) && m_index-- == 0 ) {
1911 m_path.push( makeReference( node ) );
1917 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
1918 path.push( makeReference( GlobalSceneGraph().root() ) );
1920 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
1922 if ( path.size() == 2 ) {
1923 scene::Traversable* traversable = Node_getTraversable( path.top() );
1924 if ( traversable != 0 ) {
1925 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
1930 inline bool Node_hasChildren( scene::Node& node ){
1931 scene::Traversable* traversable = Node_getTraversable( node );
1932 return traversable != 0 && !traversable->empty();
1935 void SelectBrush( int entitynum, int brushnum ){
1937 Scene_FindEntityBrush( entitynum, brushnum, path );
1938 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
1939 scene::Instance* instance = GlobalSceneGraph().find( path );
1940 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
1941 Selectable* selectable = Instance_getSelectable( *instance );
1942 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
1943 selectable->setSelected( true );
1944 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
1949 class BrushFindIndexWalker : public scene::Graph::Walker
1951 mutable const scene::Node* m_node;
1952 std::size_t& m_count;
1954 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
1955 : m_node( &node ), m_count( count ){
1957 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1958 if ( Node_isPrimitive( path.top() ) ) {
1959 if ( m_node == path.top().get_pointer() ) {
1970 class EntityFindIndexWalker : public scene::Graph::Walker
1972 mutable const scene::Node* m_node;
1973 std::size_t& m_count;
1975 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
1976 : m_node( &node ), m_count( count ){
1978 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1979 if ( Node_isEntity( path.top() ) ) {
1980 if ( m_node == path.top().get_pointer() ) {
1991 static void GetSelectionIndex( int *ent, int *brush ){
1992 std::size_t count_brush = 0;
1993 std::size_t count_entity = 0;
1994 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1995 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
1997 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
1998 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
2000 *brush = int(count_brush);
2001 *ent = int(count_entity);
2009 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Find Brush", G_CALLBACK( dialog_delete_callback ), &dialog );
2011 GtkAccelGroup* accel = gtk_accel_group_new();
2012 gtk_window_add_accel_group( window, accel );
2015 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2016 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2018 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2019 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2021 GtkWidget* label = gtk_label_new( "Entity number" );
2022 gtk_widget_show( label );
2023 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2024 (GtkAttachOptions) ( 0 ),
2025 (GtkAttachOptions) ( 0 ), 0, 0 );
2028 GtkWidget* label = gtk_label_new( "Brush number" );
2029 gtk_widget_show( label );
2030 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2031 (GtkAttachOptions) ( 0 ),
2032 (GtkAttachOptions) ( 0 ), 0, 0 );
2035 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2036 gtk_widget_show( GTK_WIDGET( entry ) );
2037 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2038 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2039 (GtkAttachOptions) ( 0 ), 0, 0 );
2040 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2044 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2045 gtk_widget_show( GTK_WIDGET( entry ) );
2046 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2047 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2048 (GtkAttachOptions) ( 0 ), 0, 0 );
2054 GtkHBox* hbox = create_dialog_hbox( 4 );
2055 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2057 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2058 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2059 widget_make_default( GTK_WIDGET( button ) );
2060 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2063 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2064 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2065 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2070 // Initialize dialog
2074 GetSelectionIndex( &ent, &br );
2075 sprintf( buf, "%i", ent );
2076 gtk_entry_set_text( entity, buf );
2077 sprintf( buf, "%i", br );
2078 gtk_entry_set_text( brush, buf );
2080 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2081 const char *entstr = gtk_entry_get_text( entity );
2082 const char *brushstr = gtk_entry_get_text( brush );
2083 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2086 gtk_widget_destroy( GTK_WIDGET( window ) );
2089 void Map_constructPreferences( PreferencesPage& page ){
2090 page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
2094 class MapEntityClasses : public ModuleObserver
2096 std::size_t m_unrealised;
2098 MapEntityClasses() : m_unrealised( 1 ){
2101 if ( --m_unrealised == 0 ) {
2102 if ( g_map.m_resource != 0 ) {
2103 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2104 g_map.m_resource->realise();
2109 if ( ++m_unrealised == 1 ) {
2110 if ( g_map.m_resource != 0 ) {
2111 g_map.m_resource->flush();
2112 g_map.m_resource->unrealise();
2118 MapEntityClasses g_MapEntityClasses;
2121 class MapModuleObserver : public ModuleObserver
2123 std::size_t m_unrealised;
2125 MapModuleObserver() : m_unrealised( 1 ){
2128 if ( --m_unrealised == 0 ) {
2129 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2130 StringOutputStream buffer( 256 );
2131 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2132 Q_mkdir( buffer.c_str() );
2133 g_mapsPath = buffer.c_str();
2137 if ( ++m_unrealised == 1 ) {
2143 MapModuleObserver g_MapModuleObserver;
2145 #include "preferencesystem.h"
2147 CopiedString g_strLastMap;
2148 bool g_bLoadLastMap = false;
2150 void Map_Construct(){
2151 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2152 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2153 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2154 GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2156 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2157 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2158 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2160 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2162 GlobalEntityClassManager().attach( g_MapEntityClasses );
2163 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2167 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2168 GlobalEntityClassManager().detach( g_MapEntityClasses );