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>
55 #include "transformlib.h"
56 #include "selectionlib.h"
57 #include "instancelib.h"
58 #include "traverselib.h"
60 #include "eclasslib.h"
62 #include "stream/textfilestream.h"
64 #include "uniquenames.h"
65 #include "modulesystem/singletonmodule.h"
66 #include "modulesystem/moduleregistry.h"
67 #include "stream/stringstream.h"
68 #include "signal/signal.h"
70 #include "gtkutil/filechooser.h"
74 #include "filetypes.h"
76 #include "entityinspector.h"
79 #include "camwindow.h"
81 #include "mainframe.h"
82 #include "preferences.h"
83 #include "referencecache.h"
87 #include "brushmodule.h"
97 //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
98 m_names.insert( name_read( c_str() ) );
103 //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
104 m_names.erase( name_read( c_str() ) );
108 NameObserver& operator=( const NameObserver& other );
110 NameObserver( UniqueNames& names ) : m_names( names ){
113 NameObserver( const NameObserver& other ) : m_names( other.m_names ), m_name( other.m_name ){
120 return string_empty( c_str() );
122 const char* c_str() const {
123 return m_name.c_str();
125 void nameChanged( const char* name ){
130 typedef MemberCaller1<NameObserver, const char*, &NameObserver::nameChanged> NameChangedCaller;
133 class BasicNamespace : public Namespace
135 typedef std::map<NameCallback, NameObserver> Names;
137 UniqueNames m_uniqueNames;
140 ASSERT_MESSAGE( m_names.empty(), "namespace: names still registered at shutdown" );
142 void attach( const NameCallback& setName, const NameCallbackCallback& attachObserver ){
143 std::pair<Names::iterator, bool> result = m_names.insert( Names::value_type( setName, m_uniqueNames ) );
144 ASSERT_MESSAGE( result.second, "cannot attach name" );
145 attachObserver( NameObserver::NameChangedCaller( ( *result.first ).second ) );
146 //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
148 void detach( const NameCallback& setName, const NameCallbackCallback& detachObserver ){
149 Names::iterator i = m_names.find( setName );
150 ASSERT_MESSAGE( i != m_names.end(), "cannot detach name" );
151 //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
152 detachObserver( NameObserver::NameChangedCaller( ( *i ).second ) );
156 void makeUnique( const char* name, const NameCallback& setName ) const {
158 name_write( buffer, m_uniqueNames.make_unique( name_read( name ) ) );
162 void mergeNames( const BasicNamespace& other ) const {
163 typedef std::list<NameCallback> SetNameCallbacks;
164 typedef std::map<CopiedString, SetNameCallbacks> NameGroups;
167 UniqueNames uniqueNames( other.m_uniqueNames );
169 for ( Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i )
171 groups[( *i ).second.c_str()].push_back( ( *i ).first );
174 for ( NameGroups::iterator i = groups.begin(); i != groups.end(); ++i )
176 name_t uniqueName( uniqueNames.make_unique( name_read( ( *i ).first.c_str() ) ) );
177 uniqueNames.insert( uniqueName );
180 name_write( buffer, uniqueName );
182 //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
184 SetNameCallbacks& setNameCallbacks = ( *i ).second;
186 for ( SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j )
194 BasicNamespace g_defaultNamespace;
195 BasicNamespace g_cloneNamespace;
199 Namespace* m_namespace;
201 typedef Namespace Type;
202 STRING_CONSTANT( Name, "*" );
205 m_namespace = &g_defaultNamespace;
207 Namespace* getTable(){
212 typedef SingletonModule<NamespaceAPI> NamespaceModule;
213 typedef Static<NamespaceModule> StaticNamespaceModule;
214 StaticRegisterModule staticRegisterDefaultNamespace( StaticNamespaceModule::instance() );
217 std::list<Namespaced*> g_cloned;
219 inline Namespaced* Node_getNamespaced( scene::Node& node ){
220 return NodeTypeCast<Namespaced>::cast( node );
223 void Node_gatherNamespaced( scene::Node& node ){
224 Namespaced* namespaced = Node_getNamespaced( node );
225 if ( namespaced != 0 ) {
226 g_cloned.push_back( namespaced );
230 class GatherNamespaced : public scene::Traversable::Walker
233 bool pre( scene::Node& node ) const {
234 Node_gatherNamespaced( node );
239 void Map_gatherNamespaced( scene::Node& root ){
240 Node_traverseSubgraph( root, GatherNamespaced() );
243 void Map_mergeClonedNames(){
244 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
246 ( *i )->setNamespace( g_cloneNamespace );
248 g_cloneNamespace.mergeNames( g_defaultNamespace );
249 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
251 ( *i )->setNamespace( g_defaultNamespace );
264 void set( scene::Node* node ){
273 scene::Node* get() const {
279 void Map_SetValid( Map& map, bool valid );
280 void Map_UpdateTitle( const Map& map );
281 void Map_SetWorldspawn( Map& map, scene::Node* node );
284 class Map : public ModuleObserver
288 Resource* m_resource;
292 void ( *m_modified_changed )( const Map& );
294 Signal0 m_mapValidCallbacks;
296 WorldNode m_world_node; // "classname" "worldspawn" !
298 Map() : m_resource( 0 ), m_valid( false ), m_modified_changed( Map_UpdateTitle ){
302 if ( m_resource != 0 ) {
303 if ( Map_Unnamed( *this ) ) {
304 g_map.m_resource->setNode( NewMapRoot( "" ).get_pointer() );
305 MapFile* map = Node_getMapFile( *g_map.m_resource->getNode() );
315 GlobalSceneGraph().insert_root( *m_resource->getNode() );
319 Map_SetValid( g_map, true );
323 if ( m_resource != 0 ) {
324 Map_SetValid( g_map, false );
325 Map_SetWorldspawn( g_map, 0 );
328 GlobalUndoSystem().clear();
330 GlobalSceneGraph().erase_root();
336 Map* g_currentMap = 0;
338 void Map_addValidCallback( Map& map, const SignalHandler& handler ){
339 map.m_mapValidCallbacks.connectLast( handler );
342 bool Map_Valid( const Map& map ){
346 void Map_SetValid( Map& map, bool valid ){
348 map.m_mapValidCallbacks();
352 const char* Map_Name( const Map& map ){
353 return map.m_name.c_str();
356 bool Map_Unnamed( const Map& map ){
357 return string_equal( Map_Name( map ), "unnamed.map" );
360 inline const MapFormat& MapFormat_forFile( const char* filename ){
361 const char* moduleName = findModuleName( GetFileTypeRegistry(), MapFormat::Name(), path_get_extension( filename ) );
362 MapFormat* format = Radiant_getMapModules().findModule( moduleName );
363 ASSERT_MESSAGE( format != 0, "map format not found for file " << makeQuoted( filename ) );
367 const MapFormat& Map_getFormat( const Map& map ){
368 return MapFormat_forFile( Map_Name( map ) );
372 bool Map_Modified( const Map& map ){
373 return map.m_modified;
376 void Map_SetModified( Map& map, bool modified ){
377 if ( map.m_modified ^ modified ) {
378 map.m_modified = modified;
380 map.m_modified_changed( map );
384 void Map_UpdateTitle( const Map& map ){
385 Sys_SetTitle( map.m_name.c_str(), Map_Modified( map ) );
390 scene::Node* Map_GetWorldspawn( const Map& map ){
391 return map.m_world_node.get();
394 void Map_SetWorldspawn( Map& map, scene::Node* node ){
395 map.m_world_node.set( node );
400 // need that in a variable, will have to tweak depending on the game
401 float g_MaxWorldCoord = 64 * 1024;
402 float g_MinWorldCoord = -64 * 1024;
404 void AddRegionBrushes( void );
405 void RemoveRegionBrushes( void );
411 free all map elements, reinitialize the structures that depend on them
417 g_map.m_resource->detach( g_map );
418 GlobalReferenceCache().release( g_map.m_name.c_str() );
419 g_map.m_resource = 0;
424 Brush_unlatchPreferences();
427 class EntityFindByClassname : public scene::Graph::Walker
432 EntityFindByClassname( const char* name, Entity*& entity ) : m_name( name ), m_entity( entity ){
435 bool pre( const scene::Path& path, scene::Instance& instance ) const {
436 if ( m_entity == 0 ) {
437 Entity* entity = Node_getEntity( path.top() );
439 && string_equal( m_name, entity->getKeyValue( "classname" ) ) ) {
447 Entity* Scene_FindEntityByClass( const char* name ){
449 GlobalSceneGraph().traverse( EntityFindByClassname( name, entity ) );
453 Entity *Scene_FindPlayerStart(){
454 typedef const char* StaticString;
455 StaticString strings[] = {
457 "info_player_deathmatch",
458 "team_CTF_redplayer",
459 "team_CTF_blueplayer",
461 "team_CTF_bluespawn",
463 typedef const StaticString* StaticStringIterator;
464 for ( StaticStringIterator i = strings, end = strings + ( sizeof( strings ) / sizeof( StaticString ) ); i != end; ++i )
466 Entity* entity = Scene_FindEntityByClass( *i );
475 // move the view to a start position
479 void FocusViews( const Vector3& point, float angle ){
480 CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
481 Camera_setOrigin( camwnd, point );
482 Vector3 angles( Camera_getAngles( camwnd ) );
483 angles[CAMERA_PITCH] = 0;
484 angles[CAMERA_YAW] = angle;
485 Camera_setAngles( camwnd, angles );
487 XYWnd* xywnd = g_pParentWnd->GetXYWnd();
488 xywnd->SetOrigin( point );
491 #include "stringio.h"
493 void Map_StartPosition(){
494 Entity* entity = Scene_FindPlayerStart();
498 string_parse_vector3( entity->getKeyValue( "origin" ), origin );
499 FocusViews( origin, string_read_float( entity->getKeyValue( "angle" ) ) );
503 FocusViews( g_vector3_identity, 0 );
508 inline bool node_is_worldspawn( scene::Node& node ){
509 Entity* entity = Node_getEntity( node );
510 return entity != 0 && string_equal( entity->getKeyValue( "classname" ), "worldspawn" );
514 // use first worldspawn
515 class entity_updateworldspawn : public scene::Traversable::Walker
518 bool pre( scene::Node& node ) const {
519 if ( node_is_worldspawn( node ) ) {
520 if ( Map_GetWorldspawn( g_map ) == 0 ) {
521 Map_SetWorldspawn( g_map, &node );
528 scene::Node* Map_FindWorldspawn( Map& map ){
529 Map_SetWorldspawn( map, 0 );
531 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
533 return Map_GetWorldspawn( map );
537 class CollectAllWalker : public scene::Traversable::Walker
540 UnsortedNodeSet& m_nodes;
542 CollectAllWalker( scene::Node& root, UnsortedNodeSet& nodes ) : m_root( root ), m_nodes( nodes ){
544 bool pre( scene::Node& node ) const {
545 m_nodes.insert( NodeSmartReference( node ) );
546 Node_getTraversable( m_root )->erase( node );
551 void Node_insertChildFirst( scene::Node& parent, scene::Node& child ){
552 UnsortedNodeSet nodes;
553 Node_getTraversable( parent )->traverse( CollectAllWalker( parent, nodes ) );
554 Node_getTraversable( parent )->insert( child );
556 for ( UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i )
558 Node_getTraversable( parent )->insert( ( *i ) );
562 scene::Node& createWorldspawn(){
563 NodeSmartReference worldspawn( GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "worldspawn", true ) ) );
564 Node_insertChildFirst( GlobalSceneGraph().root(), worldspawn );
568 void Map_UpdateWorldspawn( Map& map ){
569 if ( Map_FindWorldspawn( map ) == 0 ) {
570 Map_SetWorldspawn( map, &createWorldspawn() );
574 scene::Node& Map_FindOrInsertWorldspawn( Map& map ){
575 Map_UpdateWorldspawn( map );
576 return *Map_GetWorldspawn( map );
580 class MapMergeAll : public scene::Traversable::Walker
582 mutable scene::Path m_path;
584 MapMergeAll( const scene::Path& root )
587 bool pre( scene::Node& node ) const {
588 Node_getTraversable( m_path.top() )->insert( node );
589 m_path.push( makeReference( node ) );
590 selectPath( m_path, true );
593 void post( scene::Node& node ) const {
598 class MapMergeEntities : public scene::Traversable::Walker
600 mutable scene::Path m_path;
602 MapMergeEntities( const scene::Path& root )
605 bool pre( scene::Node& node ) const {
606 if ( node_is_worldspawn( node ) ) {
607 scene::Node* world_node = Map_FindWorldspawn( g_map );
608 if ( world_node == 0 ) {
609 Map_SetWorldspawn( g_map, &node );
610 Node_getTraversable( m_path.top().get() )->insert( node );
611 m_path.push( makeReference( node ) );
612 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
616 m_path.push( makeReference( *world_node ) );
617 Node_getTraversable( node )->traverse( MapMergeAll( m_path ) );
622 Node_getTraversable( m_path.top() )->insert( node );
623 m_path.push( makeReference( node ) );
624 if ( node_is_group( node ) ) {
625 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
629 selectPath( m_path, true );
634 void post( scene::Node& node ) const {
639 class BasicContainer : public scene::Node::Symbiot
643 NodeTypeCastTable m_casts;
646 NodeContainedCast<BasicContainer, scene::Traversable>::install( m_casts );
648 NodeTypeCastTable& get(){
654 TraversableNodeSet m_traverse;
657 typedef LazyStatic<TypeCasts> StaticTypeCasts;
659 scene::Traversable& get( NullType<scene::Traversable>){
663 BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){
673 /// Merges the map graph rooted at \p node into the global scene-graph.
674 void MergeMap( scene::Node& node ){
675 Node_getTraversable( node )->traverse( MapMergeEntities( scene::Path( makeReference( GlobalSceneGraph().root() ) ) ) );
677 void Map_ImportSelected( TextInputStream& in, const MapFormat& format ){
678 NodeSmartReference node( ( new BasicContainer )->node() );
679 format.readGraph( node, in, GlobalEntityCreator() );
680 Map_gatherNamespaced( node );
681 Map_mergeClonedNames();
685 inline scene::Cloneable* Node_getCloneable( scene::Node& node ){
686 return NodeTypeCast<scene::Cloneable>::cast( node );
689 inline scene::Node& node_clone( scene::Node& node ){
690 scene::Cloneable* cloneable = Node_getCloneable( node );
691 if ( cloneable != 0 ) {
692 return cloneable->clone();
695 return ( new scene::NullNode )->node();
698 class CloneAll : public scene::Traversable::Walker
700 mutable scene::Path m_path;
702 CloneAll( scene::Node& root )
703 : m_path( makeReference( root ) ){
705 bool pre( scene::Node& node ) const {
706 if ( node.isRoot() ) {
710 m_path.push( makeReference( node_clone( node ) ) );
711 m_path.top().get().IncRef();
715 void post( scene::Node& node ) const {
716 if ( node.isRoot() ) {
720 Node_getTraversable( m_path.parent() )->insert( m_path.top() );
722 m_path.top().get().DecRef();
727 scene::Node& Node_Clone( scene::Node& node ){
728 scene::Node& clone = node_clone( node );
729 scene::Traversable* traversable = Node_getTraversable( node );
730 if ( traversable != 0 ) {
731 traversable->traverse( CloneAll( clone ) );
737 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
739 class EntityBreakdownWalker : public scene::Graph::Walker
741 EntityBreakdown& m_entitymap;
743 EntityBreakdownWalker( EntityBreakdown& entitymap )
744 : m_entitymap( entitymap ){
746 bool pre( const scene::Path& path, scene::Instance& instance ) const {
747 Entity* entity = Node_getEntity( path.top() );
749 const EntityClass& eclass = entity->getEntityClass();
750 if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
751 m_entitymap[eclass.name()] = 1;
753 else{ ++m_entitymap[eclass.name()]; }
759 void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
760 GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
764 WindowPosition g_posMapInfoWnd( c_default_window_pos );
768 GtkEntry* brushes_entry;
769 GtkEntry* entities_entry;
770 GtkListStore* EntityBreakdownWalker;
772 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Map Info", G_CALLBACK( dialog_delete_callback ), &dialog );
774 window_set_position( window, g_posMapInfoWnd );
777 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
778 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
781 GtkHBox* hbox = create_dialog_hbox( 4 );
782 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
785 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
786 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
789 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
790 gtk_widget_show( GTK_WIDGET( entry ) );
791 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
792 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
793 (GtkAttachOptions) ( 0 ), 0, 0 );
794 gtk_entry_set_editable( entry, FALSE );
796 brushes_entry = entry;
799 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
800 gtk_widget_show( GTK_WIDGET( entry ) );
801 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
802 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
803 (GtkAttachOptions) ( 0 ), 0, 0 );
804 gtk_entry_set_editable( entry, FALSE );
806 entities_entry = entry;
809 GtkWidget* label = gtk_label_new( "Total Brushes" );
810 gtk_widget_show( label );
811 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
812 (GtkAttachOptions) ( GTK_FILL ),
813 (GtkAttachOptions) ( 0 ), 0, 0 );
814 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
817 GtkWidget* label = gtk_label_new( "Total Entities" );
818 gtk_widget_show( label );
819 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
820 (GtkAttachOptions) ( GTK_FILL ),
821 (GtkAttachOptions) ( 0 ), 0, 0 );
822 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
826 GtkVBox* vbox2 = create_dialog_vbox( 4 );
827 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, FALSE, 0 );
830 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
831 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
836 GtkWidget* label = gtk_label_new( "Entity breakdown" );
837 gtk_widget_show( label );
838 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, TRUE, 0 );
839 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
842 GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 );
843 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( scr ), TRUE, TRUE, 0 );
846 GtkListStore* store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING );
848 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
849 gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE );
852 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
853 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Entity", renderer, "text", 0, 0 );
854 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
855 gtk_tree_view_column_set_sort_column_id( column, 0 );
859 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
860 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Count", renderer, "text", 1, 0 );
861 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
862 gtk_tree_view_column_set_sort_column_id( column, 1 );
865 gtk_widget_show( view );
867 gtk_container_add( GTK_CONTAINER( scr ), view );
869 EntityBreakdownWalker = store;
877 EntityBreakdown entitymap;
878 Scene_EntityBreakdown( entitymap );
880 for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i )
883 sprintf( tmp, "%u", Unsigned( ( *i ).second ) );
885 gtk_list_store_append( GTK_LIST_STORE( EntityBreakdownWalker ), &iter );
886 gtk_list_store_set( GTK_LIST_STORE( EntityBreakdownWalker ), &iter, 0, ( *i ).first.c_str(), 1, tmp, -1 );
890 g_object_unref( G_OBJECT( EntityBreakdownWalker ) );
893 sprintf( tmp, "%u", Unsigned( g_brushCount.get() ) );
894 gtk_entry_set_text( GTK_ENTRY( brushes_entry ), tmp );
895 sprintf( tmp, "%u", Unsigned( g_entityCount.get() ) );
896 gtk_entry_set_text( GTK_ENTRY( entities_entry ), tmp );
898 modal_dialog_show( window, dialog );
901 window_get_position( window, g_posMapInfoWnd );
903 gtk_widget_destroy( GTK_WIDGET( window ) );
911 const char* m_message;
913 ScopeTimer( const char* message )
914 : m_message( message ){
918 double elapsed_time = m_timer.elapsed_msec() / 1000.f;
919 globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
929 void Map_LoadFile( const char *filename ){
930 globalOutputStream() << "Loading map from " << filename << "\n";
931 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
934 ScopeTimer timer( "map load" );
936 const MapFormat* format = NULL;
937 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
938 if ( string_not_empty( moduleName ) ) {
939 format = ReferenceAPI_getMapModules().findModule( moduleName );
942 for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
947 Brush_toggleFormat( i );
948 g_map.m_name = filename;
949 Map_UpdateTitle( g_map );
950 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
952 format->wrongFormat = false;
954 g_map.m_resource->attach( g_map );
956 if ( !format->wrongFormat ) {
962 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
965 globalOutputStream() << "--- LoadMapFile ---\n";
966 globalOutputStream() << g_map.m_name.c_str() << "\n";
968 globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
969 globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
971 //GlobalEntityCreator().printStatistics();
974 // move the view to a start position
978 g_currentMap = &g_map;
984 virtual bool excluded( scene::Node& node ) const = 0;
987 class ExcludeWalker : public scene::Traversable::Walker
989 const scene::Traversable::Walker& m_walker;
990 const Excluder* m_exclude;
993 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
994 : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
996 bool pre( scene::Node& node ) const {
997 if ( m_exclude->excluded( node ) || node.isRoot() ) {
1003 m_walker.pre( node );
1007 void post( scene::Node& node ) const {
1013 m_walker.post( node );
1018 class AnyInstanceSelected : public scene::Instantiable::Visitor
1022 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1025 void visit( scene::Instance& instance ) const {
1026 Selectable* selectable = Instance_getSelectable( instance );
1027 if ( selectable != 0
1028 && selectable->isSelected() ) {
1034 bool Node_instanceSelected( scene::Node& node ){
1035 scene::Instantiable* instantiable = Node_getInstantiable( node );
1036 ASSERT_NOTNULL( instantiable );
1038 instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1042 class SelectedDescendantWalker : public scene::Traversable::Walker
1046 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1050 bool pre( scene::Node& node ) const {
1051 if ( node.isRoot() ) {
1055 if ( Node_instanceSelected( node ) ) {
1063 bool Node_selectedDescendant( scene::Node& node ){
1065 Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1069 class SelectionExcluder : public Excluder
1072 bool excluded( scene::Node& node ) const {
1073 return !Node_selectedDescendant( node );
1077 class IncludeSelectedWalker : public scene::Traversable::Walker
1079 const scene::Traversable::Walker& m_walker;
1080 mutable std::size_t m_selected;
1081 mutable bool m_skip;
1083 bool selectedParent() const {
1084 return m_selected != 0;
1087 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1088 : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1090 bool pre( scene::Node& node ) const {
1092 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1093 if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1094 if ( Node_instanceSelected( node ) ) {
1097 m_walker.pre( node );
1106 void post( scene::Node& node ) const {
1112 if ( Node_instanceSelected( node ) ) {
1115 m_walker.post( node );
1120 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1121 scene::Traversable* traversable = Node_getTraversable( root );
1122 if ( traversable != 0 ) {
1124 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1126 traversable->traverse( IncludeSelectedWalker( walker ) );
1131 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1132 format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1135 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1136 scene::Traversable* traversable = Node_getTraversable( root );
1137 if ( traversable != 0 ) {
1138 traversable->traverse( walker );
1142 class RegionExcluder : public Excluder
1145 bool excluded( scene::Node& node ) const {
1146 return node.excluded();
1150 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1151 scene::Traversable* traversable = Node_getTraversable( root );
1152 if ( traversable != 0 ) {
1153 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1157 bool Map_SaveRegion( const char *filename ){
1160 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1162 RemoveRegionBrushes();
1168 void Map_RenameAbsolute( const char* absolute ){
1169 Resource* resource = GlobalReferenceCache().capture( absolute );
1170 NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1171 resource->setNode( clone.get_pointer() );
1174 //ScopeTimer timer("clone subgraph");
1175 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1178 g_map.m_resource->detach( g_map );
1179 GlobalReferenceCache().release( g_map.m_name.c_str() );
1181 g_map.m_resource = resource;
1183 g_map.m_name = absolute;
1184 Map_UpdateTitle( g_map );
1186 g_map.m_resource->attach( g_map );
1189 void Map_Rename( const char* filename ){
1190 if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1191 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1193 Map_RenameAbsolute( filename );
1195 SceneChangeNotify();
1206 ScopeTimer timer( "map save" );
1208 return true; // assume success..
1218 //globalOutputStream() << "Map_New\n";
1220 g_map.m_name = "unnamed.map";
1221 Map_UpdateTitle( g_map );
1224 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1225 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1226 g_map.m_resource->attach( g_map );
1228 SceneChangeNotify();
1231 FocusViews( g_vector3_identity, 0 );
1233 g_currentMap = &g_map;
1236 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
1238 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1240 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1241 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1242 for now, let's just print an error
1245 Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1247 for ( int i = 0 ; i < 3 ; i++ )
1249 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1250 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1255 // write the info_playerstart
1257 sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1258 Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1259 sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1260 Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1264 ===========================================================
1268 ===========================================================
1271 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1272 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1274 scene::Node* region_sides[6];
1275 scene::Node* region_startpoint = 0;
1280 a regioned map will have temp walls put up at the region boundary
1281 \todo TODO TTimo old implementation of region brushes
1282 we still add them straight in the worldspawn and take them out after the map is saved
1283 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1286 void AddRegionBrushes( void ){
1289 for ( i = 0; i < 6; i++ )
1291 region_sides[i] = &GlobalBrushCreator().createBrush();
1292 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1295 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1297 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1298 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1300 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1303 void RemoveRegionBrushes( void ){
1304 for ( std::size_t i = 0; i < 6; i++ )
1306 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1308 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1311 inline void exclude_node( scene::Node& node, bool exclude ){
1313 ? node.enable( scene::Node::eExcluded )
1314 : node.disable( scene::Node::eExcluded );
1317 class ExcludeAllWalker : public scene::Graph::Walker
1321 ExcludeAllWalker( bool exclude )
1322 : m_exclude( exclude ){
1324 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1325 exclude_node( path.top(), m_exclude );
1331 void Scene_Exclude_All( bool exclude ){
1332 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1335 bool Instance_isSelected( const scene::Instance& instance ){
1336 const Selectable* selectable = Instance_getSelectable( instance );
1337 return selectable != 0 && selectable->isSelected();
1340 class ExcludeSelectedWalker : public scene::Graph::Walker
1344 ExcludeSelectedWalker( bool exclude )
1345 : m_exclude( exclude ){
1347 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1348 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1353 void Scene_Exclude_Selected( bool exclude ){
1354 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1357 class ExcludeRegionedWalker : public scene::Graph::Walker
1361 ExcludeRegionedWalker( bool exclude )
1362 : m_exclude( exclude ){
1364 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1369 aabb_intersects_aabb(
1370 instance.worldAABB(),
1371 aabb_for_minmax( region_mins, region_maxs )
1381 void Scene_Exclude_Region( bool exclude ){
1382 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1389 Other filtering options may still be on
1392 void Map_RegionOff(){
1393 region_active = false;
1395 region_maxs[0] = g_MaxWorldCoord - 64;
1396 region_mins[0] = g_MinWorldCoord + 64;
1397 region_maxs[1] = g_MaxWorldCoord - 64;
1398 region_mins[1] = g_MinWorldCoord + 64;
1399 region_maxs[2] = g_MaxWorldCoord - 64;
1400 region_mins[2] = g_MinWorldCoord + 64;
1402 Scene_Exclude_All( false );
1405 void Map_ApplyRegion( void ){
1406 region_active = true;
1408 Scene_Exclude_Region( false );
1413 ========================
1414 Map_RegionSelectedBrushes
1415 ========================
1417 void Map_RegionSelectedBrushes( void ){
1420 if ( GlobalSelectionSystem().countSelected() != 0
1421 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1422 region_active = true;
1423 Select_GetBounds( region_mins, region_maxs );
1425 Scene_Exclude_Selected( false );
1427 GlobalSelectionSystem().setSelectedAll( false );
1437 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1440 region_mins[0] = x_min;
1441 region_maxs[0] = x_max;
1442 region_mins[1] = y_min;
1443 region_maxs[1] = y_max;
1444 region_mins[2] = g_MinWorldCoord + 64;
1445 region_maxs[2] = g_MaxWorldCoord - 64;
1450 void Map_RegionBounds( const AABB& bounds ){
1453 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1454 region_maxs = vector3_added( bounds.origin, bounds.extents );
1466 void Map_RegionBrush( void ){
1467 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1468 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1469 Map_RegionBounds( instance.worldAABB() );
1478 bool Map_ImportFile( const char* filename ){
1479 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1481 bool success = false;
1483 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1488 const MapFormat* format = NULL;
1489 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1490 if ( string_not_empty( moduleName ) ) {
1491 format = ReferenceAPI_getMapModules().findModule( moduleName );
1495 format->wrongFormat = false;
1497 Resource* resource = GlobalReferenceCache().capture( filename );
1498 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1499 if ( !resource->load() ) {
1500 GlobalReferenceCache().release( filename );
1504 if ( format->wrongFormat ) {
1505 GlobalReferenceCache().release( filename );
1509 NodeSmartReference clone( NewMapRoot( "" ) );
1510 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1511 Map_gatherNamespaced( clone );
1512 Map_mergeClonedNames();
1515 GlobalReferenceCache().release( filename );
1518 SceneChangeNotify();
1524 const char *type = GlobalRadiant().getGameDescriptionKeyValue( "q3map2_type" );
1525 int n = string_length( path_get_extension( filename ) );
1526 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1527 StringBuffer output;
1528 output.push_string( AppPath_get() );
1529 output.push_string( "q3map2." );
1530 output.push_string( RADIANT_EXECUTABLE );
1531 output.push_string( " -v -game " );
1532 output.push_string( ( type && *type ) ? type : "quake3" );
1533 output.push_string( " -fs_basepath \"" );
1534 output.push_string( EnginePath_get() );
1535 output.push_string( "\" -fs_homepath \"" );
1536 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1537 output.push_string( "\" -fs_game " );
1538 output.push_string( gamename_get() );
1539 output.push_string( " -convert -format " );
1540 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1541 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1542 output.push_string( " -readmap " );
1544 output.push_string( " \"" );
1545 output.push_string( filename );
1546 output.push_string( "\"" );
1549 Q_Exec( NULL, output.c_str(), NULL, false, true );
1551 // rebuild filename as "filenamewithoutext_converted.map"
1553 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1554 output.push_string( "_converted.map" );
1555 filename = output.c_str();
1558 Resource* resource = GlobalReferenceCache().capture( filename );
1559 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1560 if ( !resource->load() ) {
1561 GlobalReferenceCache().release( filename );
1564 NodeSmartReference clone( NewMapRoot( "" ) );
1565 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1566 Map_gatherNamespaced( clone );
1567 Map_mergeClonedNames();
1570 GlobalReferenceCache().release( filename );
1573 SceneChangeNotify();
1582 bool Map_SaveFile( const char* filename ){
1583 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1584 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1592 // Saves selected world brushes and whole entities with partial/full selections
1594 bool Map_SaveSelected( const char* filename ){
1595 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1598 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1600 scene::Node& m_parent;
1601 mutable bool m_emptyOldParent;
1604 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ), m_emptyOldParent( false ){
1606 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1607 if ( path.top().get_pointer() != &m_parent && ( Node_isPrimitive( path.top() ) || m_emptyOldParent ) ) {
1608 Selectable* selectable = Instance_getSelectable( instance );
1609 if ( selectable && selectable->isSelected() && path.size() > 1 ) {
1615 void post( const scene::Path& path, scene::Instance& instance ) const {
1616 if ( path.top().get_pointer() == &m_parent )
1619 if ( Node_isPrimitive( path.top() ) ){
1620 m_emptyOldParent = false;
1621 Selectable* selectable = Instance_getSelectable( instance );
1623 if ( selectable && selectable->isSelected() && path.size() > 1 ){
1624 scene::Node& parent = path.parent();
1625 if ( &parent != &m_parent ){
1626 NodeSmartReference node( path.top().get() );
1627 scene::Traversable* traversable_parent = Node_getTraversable( parent );
1628 traversable_parent->erase( node );
1629 Node_getTraversable( m_parent )->insert( node );
1630 if ( traversable_parent->empty() )
1631 m_emptyOldParent = true;
1635 else if ( m_emptyOldParent ){
1636 m_emptyOldParent = false;
1637 // delete empty entities
1638 Entity* entity = Node_getEntity( path.top() );
1639 if ( entity != 0 && path.top().get_pointer() != Map_FindWorldspawn( g_map ) && Node_getTraversable( path.top() )->empty() ) {
1640 Path_deleteTop( path );
1646 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1647 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1650 class CountSelectedBrushes : public scene::Graph::Walker
1652 std::size_t& m_count;
1653 mutable std::size_t m_depth;
1655 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1658 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1659 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1662 Selectable* selectable = Instance_getSelectable( instance );
1663 if ( selectable != 0
1664 && selectable->isSelected()
1665 && Node_isPrimitive( path.top() ) ) {
1670 void post( const scene::Path& path, scene::Instance& instance ) const {
1675 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1677 graph.traverse( CountSelectedBrushes( count ) );
1689 const char* nodetype_get_name( ENodeType type ){
1690 if ( type == eNodeMap ) {
1693 if ( type == eNodeEntity ) {
1696 if ( type == eNodePrimitive ) {
1702 ENodeType node_get_nodetype( scene::Node& node ){
1703 if ( Node_isEntity( node ) ) {
1706 if ( Node_isPrimitive( node ) ) {
1707 return eNodePrimitive;
1709 return eNodeUnknown;
1712 bool contains_entity( scene::Node& node ){
1713 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1716 bool contains_primitive( scene::Node& node ){
1717 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1720 ENodeType node_get_contains( scene::Node& node ){
1721 if ( contains_entity( node ) ) {
1724 if ( contains_primitive( node ) ) {
1725 return eNodePrimitive;
1727 return eNodeUnknown;
1730 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1731 ENodeType contains = node_get_contains( parent.top() );
1732 ENodeType type = node_get_nodetype( child.top() );
1734 if ( contains != eNodeUnknown && contains == type ) {
1735 NodeSmartReference node( child.top().get() );
1736 Path_deleteTop( child );
1737 Node_getTraversable( parent.top() )->insert( node );
1738 SceneChangeNotify();
1742 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1746 void Scene_parentSelected(){
1747 UndoableCommand undo( "parentSelected" );
1749 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1750 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1752 const scene::Path& m_parent;
1754 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1756 void visit( scene::Instance& instance ) const {
1757 if ( &m_parent != &instance.path() ) {
1758 Path_parent( m_parent, instance.path() );
1763 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1764 GlobalSelectionSystem().foreachSelected( visitor );
1768 globalOutputStream() << "failed - did not find two selected nodes.\n";
1775 if ( ConfirmModified( "New Map" ) ) {
1782 CopiedString g_mapsPath;
1784 const char* getMapsPath(){
1785 return g_mapsPath.c_str();
1788 const char* map_open( const char* title ){
1789 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
1792 const char* map_import( const char* title ){
1793 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
1796 const char* map_save( const char* title ){
1797 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
1801 if ( !ConfirmModified( "Open Map" ) ) {
1805 const char* filename = map_open( "Open Map" );
1807 if ( filename != 0 ) {
1808 MRU_AddFile( filename );
1811 Map_LoadFile( filename );
1816 const char* filename = map_import( "Import Map" );
1818 if ( filename != 0 ) {
1819 UndoableCommand undo( "mapImport" );
1820 Map_ImportFile( filename );
1825 const char* filename = map_save( "Save Map" );
1827 if ( filename != 0 ) {
1828 MRU_AddFile( filename );
1829 Map_Rename( filename );
1840 if ( Map_Unnamed( g_map ) ) {
1843 else if ( Map_Modified( g_map ) ) {
1845 MRU_AddFile( g_map.m_name.c_str() ); //add on saving, but not opening via cmd line: spoils the list
1850 const char* filename = map_save( "Export Selection" );
1852 if ( filename != 0 ) {
1853 Map_SaveSelected( filename );
1858 const char* filename = map_save( "Export Region" );
1860 if ( filename != 0 ) {
1861 Map_SaveRegion( filename );
1868 SceneChangeNotify();
1873 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1874 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1875 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1876 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1878 SceneChangeNotify();
1883 SceneChangeNotify();
1886 void RegionSelected(){
1887 Map_RegionSelectedBrushes();
1888 SceneChangeNotify();
1895 class BrushFindByIndexWalker : public scene::Traversable::Walker
1897 mutable std::size_t m_index;
1898 scene::Path& m_path;
1900 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
1901 : m_index( index ), m_path( path ){
1903 bool pre( scene::Node& node ) const {
1904 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
1905 m_path.push( makeReference( node ) );
1911 class EntityFindByIndexWalker : public scene::Traversable::Walker
1913 mutable std::size_t m_index;
1914 scene::Path& m_path;
1916 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
1917 : m_index( index ), m_path( path ){
1919 bool pre( scene::Node& node ) const {
1920 if ( Node_isEntity( node ) && m_index-- == 0 ) {
1921 m_path.push( makeReference( node ) );
1927 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
1928 path.push( makeReference( GlobalSceneGraph().root() ) );
1930 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
1932 if ( path.size() == 2 ) {
1933 scene::Traversable* traversable = Node_getTraversable( path.top() );
1934 if ( traversable != 0 ) {
1935 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
1940 inline bool Node_hasChildren( scene::Node& node ){
1941 scene::Traversable* traversable = Node_getTraversable( node );
1942 return traversable != 0 && !traversable->empty();
1945 void SelectBrush( int entitynum, int brushnum ){
1947 Scene_FindEntityBrush( entitynum, brushnum, path );
1948 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
1949 scene::Instance* instance = GlobalSceneGraph().find( path );
1950 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
1951 Selectable* selectable = Instance_getSelectable( *instance );
1952 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
1953 selectable->setSelected( true );
1954 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
1959 class BrushFindIndexWalker : public scene::Graph::Walker
1961 mutable const scene::Node* m_node;
1962 std::size_t& m_count;
1964 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
1965 : m_node( &node ), m_count( count ){
1967 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1968 if ( Node_isPrimitive( path.top() ) ) {
1969 if ( m_node == path.top().get_pointer() ) {
1980 class EntityFindIndexWalker : public scene::Graph::Walker
1982 mutable const scene::Node* m_node;
1983 std::size_t& m_count;
1985 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
1986 : m_node( &node ), m_count( count ){
1988 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1989 if ( Node_isEntity( path.top() ) ) {
1990 if ( m_node == path.top().get_pointer() ) {
2001 static void GetSelectionIndex( int *ent, int *brush ){
2002 std::size_t count_brush = 0;
2003 std::size_t count_entity = 0;
2004 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2005 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2007 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
2008 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
2010 *brush = int(count_brush);
2011 *ent = int(count_entity);
2019 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Find Brush", G_CALLBACK( dialog_delete_callback ), &dialog );
2021 GtkAccelGroup* accel = gtk_accel_group_new();
2022 gtk_window_add_accel_group( window, accel );
2025 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2026 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2028 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2029 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2031 GtkWidget* label = gtk_label_new( "Entity number" );
2032 gtk_widget_show( label );
2033 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2034 (GtkAttachOptions) ( 0 ),
2035 (GtkAttachOptions) ( 0 ), 0, 0 );
2038 GtkWidget* label = gtk_label_new( "Brush number" );
2039 gtk_widget_show( label );
2040 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2041 (GtkAttachOptions) ( 0 ),
2042 (GtkAttachOptions) ( 0 ), 0, 0 );
2045 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2046 gtk_widget_show( GTK_WIDGET( entry ) );
2047 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2048 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2049 (GtkAttachOptions) ( 0 ), 0, 0 );
2050 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2054 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2055 gtk_widget_show( GTK_WIDGET( entry ) );
2056 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2057 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2058 (GtkAttachOptions) ( 0 ), 0, 0 );
2064 GtkHBox* hbox = create_dialog_hbox( 4 );
2065 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2067 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2068 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2069 widget_make_default( GTK_WIDGET( button ) );
2070 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2073 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2074 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2075 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2080 // Initialize dialog
2084 GetSelectionIndex( &ent, &br );
2085 sprintf( buf, "%i", ent );
2086 gtk_entry_set_text( entity, buf );
2087 sprintf( buf, "%i", br );
2088 gtk_entry_set_text( brush, buf );
2090 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2091 const char *entstr = gtk_entry_get_text( entity );
2092 const char *brushstr = gtk_entry_get_text( brush );
2093 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2096 gtk_widget_destroy( GTK_WIDGET( window ) );
2099 void Map_constructPreferences( PreferencesPage& page ){
2100 page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
2104 class MapEntityClasses : public ModuleObserver
2106 std::size_t m_unrealised;
2108 MapEntityClasses() : m_unrealised( 1 ){
2111 if ( --m_unrealised == 0 ) {
2112 if ( g_map.m_resource != 0 ) {
2113 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2114 g_map.m_resource->realise();
2119 if ( ++m_unrealised == 1 ) {
2120 if ( g_map.m_resource != 0 ) {
2121 g_map.m_resource->flush();
2122 g_map.m_resource->unrealise();
2128 MapEntityClasses g_MapEntityClasses;
2131 class MapModuleObserver : public ModuleObserver
2133 std::size_t m_unrealised;
2135 MapModuleObserver() : m_unrealised( 1 ){
2138 if ( --m_unrealised == 0 ) {
2139 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2140 StringOutputStream buffer( 256 );
2141 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2142 Q_mkdir( buffer.c_str() );
2143 g_mapsPath = buffer.c_str();
2147 if ( ++m_unrealised == 1 ) {
2153 MapModuleObserver g_MapModuleObserver;
2155 #include "preferencesystem.h"
2157 CopiedString g_strLastMap;
2158 bool g_bLoadLastMap = false;
2160 void Map_Construct(){
2161 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2162 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2163 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2164 GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2166 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2167 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2168 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2170 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2172 GlobalEntityClassManager().attach( g_MapEntityClasses );
2173 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2177 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2178 GlobalEntityClassManager().detach( g_MapEntityClasses );