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 ===========================================================
1270 bool region_active = false;
1272 BoolExportCaller g_region_caller( region_active );
1273 ToggleItem g_region_item( g_region_caller );
1275 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1276 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1278 scene::Node* region_sides[6];
1279 scene::Node* region_startpoint = 0;
1284 a regioned map will have temp walls put up at the region boundary
1285 \todo TODO TTimo old implementation of region brushes
1286 we still add them straight in the worldspawn and take them out after the map is saved
1287 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1290 void AddRegionBrushes( void ){
1293 for ( i = 0; i < 6; i++ )
1295 region_sides[i] = &GlobalBrushCreator().createBrush();
1296 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1299 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1301 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1302 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1304 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1307 void RemoveRegionBrushes( void ){
1308 for ( std::size_t i = 0; i < 6; i++ )
1310 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1312 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1315 inline void exclude_node( scene::Node& node, bool exclude ){
1317 ? node.enable( scene::Node::eExcluded )
1318 : node.disable( scene::Node::eExcluded );
1321 class ExcludeAllWalker : public scene::Graph::Walker
1325 ExcludeAllWalker( bool exclude )
1326 : m_exclude( exclude ){
1328 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1329 exclude_node( path.top(), m_exclude );
1335 void Scene_Exclude_All( bool exclude ){
1336 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1339 bool Instance_isSelected( const scene::Instance& instance ){
1340 const Selectable* selectable = Instance_getSelectable( instance );
1341 return selectable != 0 && selectable->isSelected();
1344 class ExcludeSelectedWalker : public scene::Graph::Walker
1348 ExcludeSelectedWalker( bool exclude )
1349 : m_exclude( exclude ){
1351 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1352 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1357 void Scene_Exclude_Selected( bool exclude ){
1358 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1361 class ExcludeRegionedWalker : public scene::Graph::Walker
1365 ExcludeRegionedWalker( bool exclude )
1366 : m_exclude( exclude ){
1368 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1373 aabb_intersects_aabb(
1374 instance.worldAABB(),
1375 aabb_for_minmax( region_mins, region_maxs )
1385 void Scene_Exclude_Region( bool exclude ){
1386 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1393 Other filtering options may still be on
1396 void Map_RegionOff(){
1397 region_active = false;
1398 g_region_item.update();
1400 region_maxs[0] = g_MaxWorldCoord - 64;
1401 region_mins[0] = g_MinWorldCoord + 64;
1402 region_maxs[1] = g_MaxWorldCoord - 64;
1403 region_mins[1] = g_MinWorldCoord + 64;
1404 region_maxs[2] = g_MaxWorldCoord - 64;
1405 region_mins[2] = g_MinWorldCoord + 64;
1407 Scene_Exclude_All( false );
1410 void Map_ApplyRegion( void ){
1411 region_active = true;
1412 g_region_item.update();
1414 Scene_Exclude_Region( false );
1419 ========================
1420 Map_RegionSelectedBrushes
1421 ========================
1423 void Map_RegionSelectedBrushes( void ){
1426 if ( GlobalSelectionSystem().countSelected() != 0
1427 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1428 region_active = true;
1429 g_region_item.update();
1430 Select_GetBounds( region_mins, region_maxs );
1432 Scene_Exclude_Selected( false );
1434 GlobalSelectionSystem().setSelectedAll( false );
1444 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1447 region_mins[0] = x_min;
1448 region_maxs[0] = x_max;
1449 region_mins[1] = y_min;
1450 region_maxs[1] = y_max;
1451 region_mins[2] = g_MinWorldCoord + 64;
1452 region_maxs[2] = g_MaxWorldCoord - 64;
1457 void Map_RegionBounds( const AABB& bounds ){
1460 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1461 region_maxs = vector3_added( bounds.origin, bounds.extents );
1473 void Map_RegionBrush( void ){
1474 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1475 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1476 Map_RegionBounds( instance.worldAABB() );
1485 bool Map_ImportFile( const char* filename ){
1486 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1488 bool success = false;
1490 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1495 const MapFormat* format = NULL;
1496 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1497 if ( string_not_empty( moduleName ) ) {
1498 format = ReferenceAPI_getMapModules().findModule( moduleName );
1502 format->wrongFormat = false;
1504 Resource* resource = GlobalReferenceCache().capture( filename );
1505 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1506 if ( !resource->load() ) {
1507 GlobalReferenceCache().release( filename );
1511 if ( format->wrongFormat ) {
1512 GlobalReferenceCache().release( filename );
1516 NodeSmartReference clone( NewMapRoot( "" ) );
1517 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1518 Map_gatherNamespaced( clone );
1519 Map_mergeClonedNames();
1522 GlobalReferenceCache().release( filename );
1525 SceneChangeNotify();
1531 const char *type = GlobalRadiant().getGameDescriptionKeyValue( "q3map2_type" );
1532 int n = string_length( path_get_extension( filename ) );
1533 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1534 StringBuffer output;
1535 output.push_string( AppPath_get() );
1536 output.push_string( "q3map2." );
1537 output.push_string( RADIANT_EXECUTABLE );
1538 output.push_string( " -v -game " );
1539 output.push_string( ( type && *type ) ? type : "quake3" );
1540 output.push_string( " -fs_basepath \"" );
1541 output.push_string( EnginePath_get() );
1542 output.push_string( "\" -fs_homepath \"" );
1543 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1544 output.push_string( "\" -fs_game " );
1545 output.push_string( gamename_get() );
1546 output.push_string( " -convert -format " );
1547 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1548 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1549 output.push_string( " -readmap " );
1551 output.push_string( " \"" );
1552 output.push_string( filename );
1553 output.push_string( "\"" );
1556 Q_Exec( NULL, output.c_str(), NULL, false, true );
1558 // rebuild filename as "filenamewithoutext_converted.map"
1560 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1561 output.push_string( "_converted.map" );
1562 filename = output.c_str();
1565 Resource* resource = GlobalReferenceCache().capture( filename );
1566 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1567 if ( !resource->load() ) {
1568 GlobalReferenceCache().release( filename );
1571 NodeSmartReference clone( NewMapRoot( "" ) );
1572 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1573 Map_gatherNamespaced( clone );
1574 Map_mergeClonedNames();
1577 GlobalReferenceCache().release( filename );
1580 SceneChangeNotify();
1589 bool Map_SaveFile( const char* filename ){
1590 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1591 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1599 // Saves selected world brushes and whole entities with partial/full selections
1601 bool Map_SaveSelected( const char* filename ){
1602 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1605 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1607 scene::Node& m_parent;
1608 mutable bool m_emptyOldParent;
1611 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ), m_emptyOldParent( false ){
1613 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1614 if ( path.top().get_pointer() != &m_parent && ( Node_isPrimitive( path.top() ) || m_emptyOldParent ) ) {
1615 Selectable* selectable = Instance_getSelectable( instance );
1616 if ( selectable && selectable->isSelected() && path.size() > 1 ) {
1622 void post( const scene::Path& path, scene::Instance& instance ) const {
1623 if ( path.top().get_pointer() == &m_parent )
1626 if ( Node_isPrimitive( path.top() ) ){
1627 m_emptyOldParent = false;
1628 Selectable* selectable = Instance_getSelectable( instance );
1630 if ( selectable && selectable->isSelected() && path.size() > 1 ){
1631 scene::Node& parent = path.parent();
1632 if ( &parent != &m_parent ){
1633 NodeSmartReference node( path.top().get() );
1634 scene::Traversable* traversable_parent = Node_getTraversable( parent );
1635 traversable_parent->erase( node );
1636 Node_getTraversable( m_parent )->insert( node );
1637 if ( traversable_parent->empty() )
1638 m_emptyOldParent = true;
1642 else if ( m_emptyOldParent ){
1643 m_emptyOldParent = false;
1644 // delete empty entities
1645 Entity* entity = Node_getEntity( path.top() );
1646 if ( entity != 0 && path.top().get_pointer() != Map_FindWorldspawn( g_map ) && Node_getTraversable( path.top() )->empty() ) {
1647 Path_deleteTop( path );
1653 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1654 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1657 class CountSelectedBrushes : public scene::Graph::Walker
1659 std::size_t& m_count;
1660 mutable std::size_t m_depth;
1662 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1665 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1666 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1669 Selectable* selectable = Instance_getSelectable( instance );
1670 if ( selectable != 0
1671 && selectable->isSelected()
1672 && Node_isPrimitive( path.top() ) ) {
1677 void post( const scene::Path& path, scene::Instance& instance ) const {
1682 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1684 graph.traverse( CountSelectedBrushes( count ) );
1696 const char* nodetype_get_name( ENodeType type ){
1697 if ( type == eNodeMap ) {
1700 if ( type == eNodeEntity ) {
1703 if ( type == eNodePrimitive ) {
1709 ENodeType node_get_nodetype( scene::Node& node ){
1710 if ( Node_isEntity( node ) ) {
1713 if ( Node_isPrimitive( node ) ) {
1714 return eNodePrimitive;
1716 return eNodeUnknown;
1719 bool contains_entity( scene::Node& node ){
1720 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1723 bool contains_primitive( scene::Node& node ){
1724 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1727 ENodeType node_get_contains( scene::Node& node ){
1728 if ( contains_entity( node ) ) {
1731 if ( contains_primitive( node ) ) {
1732 return eNodePrimitive;
1734 return eNodeUnknown;
1737 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1738 ENodeType contains = node_get_contains( parent.top() );
1739 ENodeType type = node_get_nodetype( child.top() );
1741 if ( contains != eNodeUnknown && contains == type ) {
1742 NodeSmartReference node( child.top().get() );
1743 Path_deleteTop( child );
1744 Node_getTraversable( parent.top() )->insert( node );
1745 SceneChangeNotify();
1749 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1753 void Scene_parentSelected(){
1754 UndoableCommand undo( "parentSelected" );
1756 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1757 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1759 const scene::Path& m_parent;
1761 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1763 void visit( scene::Instance& instance ) const {
1764 if ( &m_parent != &instance.path() ) {
1765 Path_parent( m_parent, instance.path() );
1770 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1771 GlobalSelectionSystem().foreachSelected( visitor );
1775 globalOutputStream() << "failed - did not find two selected nodes.\n";
1782 if ( ConfirmModified( "New Map" ) ) {
1789 CopiedString g_mapsPath;
1791 const char* getMapsPath(){
1792 return g_mapsPath.c_str();
1795 const char* map_open( const char* title ){
1796 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
1799 const char* map_import( const char* title ){
1800 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
1803 const char* map_save( const char* title ){
1804 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
1808 if ( !ConfirmModified( "Open Map" ) ) {
1812 const char* filename = map_open( "Open Map" );
1814 if ( filename != 0 ) {
1815 MRU_AddFile( filename );
1818 Map_LoadFile( filename );
1823 const char* filename = map_import( "Import Map" );
1825 if ( filename != 0 ) {
1826 UndoableCommand undo( "mapImport" );
1827 Map_ImportFile( filename );
1832 const char* filename = map_save( "Save Map" );
1834 if ( filename != 0 ) {
1835 MRU_AddFile( filename );
1836 Map_Rename( filename );
1847 if ( Map_Unnamed( g_map ) ) {
1850 else if ( Map_Modified( g_map ) ) {
1852 MRU_AddFile( g_map.m_name.c_str() ); //add on saving, but not opening via cmd line: spoils the list
1857 const char* filename = map_save( "Export Selection" );
1859 if ( filename != 0 ) {
1860 Map_SaveSelected( filename );
1865 const char* filename = map_save( "Export Region" );
1867 if ( filename != 0 ) {
1868 Map_SaveRegion( filename );
1875 SceneChangeNotify();
1880 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1881 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1882 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1883 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1885 SceneChangeNotify();
1890 SceneChangeNotify();
1893 void RegionSelected(){
1894 Map_RegionSelectedBrushes();
1895 SceneChangeNotify();
1902 class BrushFindByIndexWalker : public scene::Traversable::Walker
1904 mutable std::size_t m_index;
1905 scene::Path& m_path;
1907 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
1908 : m_index( index ), m_path( path ){
1910 bool pre( scene::Node& node ) const {
1911 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
1912 m_path.push( makeReference( node ) );
1918 class EntityFindByIndexWalker : public scene::Traversable::Walker
1920 mutable std::size_t m_index;
1921 scene::Path& m_path;
1923 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
1924 : m_index( index ), m_path( path ){
1926 bool pre( scene::Node& node ) const {
1927 if ( Node_isEntity( node ) && m_index-- == 0 ) {
1928 m_path.push( makeReference( node ) );
1934 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
1935 path.push( makeReference( GlobalSceneGraph().root() ) );
1937 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
1939 if ( path.size() == 2 ) {
1940 scene::Traversable* traversable = Node_getTraversable( path.top() );
1941 if ( traversable != 0 ) {
1942 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
1947 inline bool Node_hasChildren( scene::Node& node ){
1948 scene::Traversable* traversable = Node_getTraversable( node );
1949 return traversable != 0 && !traversable->empty();
1952 void SelectBrush( int entitynum, int brushnum ){
1954 Scene_FindEntityBrush( entitynum, brushnum, path );
1955 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
1956 scene::Instance* instance = GlobalSceneGraph().find( path );
1957 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
1958 Selectable* selectable = Instance_getSelectable( *instance );
1959 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
1960 selectable->setSelected( true );
1961 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
1966 class BrushFindIndexWalker : public scene::Graph::Walker
1968 mutable const scene::Node* m_node;
1969 std::size_t& m_count;
1971 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
1972 : m_node( &node ), m_count( count ){
1974 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1975 if ( Node_isPrimitive( path.top() ) ) {
1976 if ( m_node == path.top().get_pointer() ) {
1987 class EntityFindIndexWalker : public scene::Graph::Walker
1989 mutable const scene::Node* m_node;
1990 std::size_t& m_count;
1992 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
1993 : m_node( &node ), m_count( count ){
1995 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1996 if ( Node_isEntity( path.top() ) ) {
1997 if ( m_node == path.top().get_pointer() ) {
2008 static void GetSelectionIndex( int *ent, int *brush ){
2009 std::size_t count_brush = 0;
2010 std::size_t count_entity = 0;
2011 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2012 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2014 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
2015 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
2017 *brush = int(count_brush);
2018 *ent = int(count_entity);
2026 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Find Brush", G_CALLBACK( dialog_delete_callback ), &dialog );
2028 GtkAccelGroup* accel = gtk_accel_group_new();
2029 gtk_window_add_accel_group( window, accel );
2032 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2033 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2035 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2036 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2038 GtkWidget* label = gtk_label_new( "Entity number" );
2039 gtk_widget_show( label );
2040 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2041 (GtkAttachOptions) ( 0 ),
2042 (GtkAttachOptions) ( 0 ), 0, 0 );
2045 GtkWidget* label = gtk_label_new( "Brush number" );
2046 gtk_widget_show( label );
2047 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2048 (GtkAttachOptions) ( 0 ),
2049 (GtkAttachOptions) ( 0 ), 0, 0 );
2052 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2053 gtk_widget_show( GTK_WIDGET( entry ) );
2054 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2055 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2056 (GtkAttachOptions) ( 0 ), 0, 0 );
2057 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2061 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2062 gtk_widget_show( GTK_WIDGET( entry ) );
2063 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2064 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2065 (GtkAttachOptions) ( 0 ), 0, 0 );
2071 GtkHBox* hbox = create_dialog_hbox( 4 );
2072 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2074 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2075 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2076 widget_make_default( GTK_WIDGET( button ) );
2077 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2080 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2081 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2082 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2087 // Initialize dialog
2091 GetSelectionIndex( &ent, &br );
2092 sprintf( buf, "%i", ent );
2093 gtk_entry_set_text( entity, buf );
2094 sprintf( buf, "%i", br );
2095 gtk_entry_set_text( brush, buf );
2097 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2098 const char *entstr = gtk_entry_get_text( entity );
2099 const char *brushstr = gtk_entry_get_text( brush );
2100 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2103 gtk_widget_destroy( GTK_WIDGET( window ) );
2106 void Map_constructPreferences( PreferencesPage& page ){
2107 page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
2111 class MapEntityClasses : public ModuleObserver
2113 std::size_t m_unrealised;
2115 MapEntityClasses() : m_unrealised( 1 ){
2118 if ( --m_unrealised == 0 ) {
2119 if ( g_map.m_resource != 0 ) {
2120 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2121 g_map.m_resource->realise();
2126 if ( ++m_unrealised == 1 ) {
2127 if ( g_map.m_resource != 0 ) {
2128 g_map.m_resource->flush();
2129 g_map.m_resource->unrealise();
2135 MapEntityClasses g_MapEntityClasses;
2138 class MapModuleObserver : public ModuleObserver
2140 std::size_t m_unrealised;
2142 MapModuleObserver() : m_unrealised( 1 ){
2145 if ( --m_unrealised == 0 ) {
2146 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2147 StringOutputStream buffer( 256 );
2148 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2149 Q_mkdir( buffer.c_str() );
2150 g_mapsPath = buffer.c_str();
2154 if ( ++m_unrealised == 1 ) {
2160 MapModuleObserver g_MapModuleObserver;
2162 #include "preferencesystem.h"
2164 CopiedString g_strLastMap;
2165 bool g_bLoadLastMap = false;
2167 void Map_Construct(){
2168 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2169 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2170 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2171 //GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2172 GlobalToggles_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), ToggleItem::AddCallbackCaller( g_region_item ), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2174 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2175 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2176 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2178 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2180 GlobalEntityClassManager().attach( g_MapEntityClasses );
2181 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2185 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2186 GlobalEntityClassManager().detach( g_MapEntityClasses );