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().getRequiredGameDescriptionKeyValue( "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 );
1599 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1601 scene::Node& m_parent;
1603 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
1605 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1606 if ( path.top().get_pointer() != &m_parent
1607 && Node_isPrimitive( path.top() ) ) {
1608 Selectable* selectable = Instance_getSelectable( instance );
1609 if ( selectable != 0
1610 && selectable->isSelected()
1611 && path.size() > 1 ) {
1617 void post( const scene::Path& path, scene::Instance& instance ) const {
1618 if ( path.top().get_pointer() != &m_parent
1619 && Node_isPrimitive( path.top() ) ) {
1620 Selectable* selectable = Instance_getSelectable( instance );
1621 if ( selectable != 0
1622 && selectable->isSelected()
1623 && path.size() > 1 ) {
1624 scene::Node& parent = path.parent();
1625 if ( &parent != &m_parent ) {
1626 NodeSmartReference node( path.top().get() );
1627 Node_getTraversable( parent )->erase( node );
1628 Node_getTraversable( m_parent )->insert( node );
1635 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1636 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1639 class CountSelectedBrushes : public scene::Graph::Walker
1641 std::size_t& m_count;
1642 mutable std::size_t m_depth;
1644 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1647 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1648 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1651 Selectable* selectable = Instance_getSelectable( instance );
1652 if ( selectable != 0
1653 && selectable->isSelected()
1654 && Node_isPrimitive( path.top() ) ) {
1659 void post( const scene::Path& path, scene::Instance& instance ) const {
1664 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1666 graph.traverse( CountSelectedBrushes( count ) );
1678 const char* nodetype_get_name( ENodeType type ){
1679 if ( type == eNodeMap ) {
1682 if ( type == eNodeEntity ) {
1685 if ( type == eNodePrimitive ) {
1691 ENodeType node_get_nodetype( scene::Node& node ){
1692 if ( Node_isEntity( node ) ) {
1695 if ( Node_isPrimitive( node ) ) {
1696 return eNodePrimitive;
1698 return eNodeUnknown;
1701 bool contains_entity( scene::Node& node ){
1702 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1705 bool contains_primitive( scene::Node& node ){
1706 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1709 ENodeType node_get_contains( scene::Node& node ){
1710 if ( contains_entity( node ) ) {
1713 if ( contains_primitive( node ) ) {
1714 return eNodePrimitive;
1716 return eNodeUnknown;
1719 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1720 ENodeType contains = node_get_contains( parent.top() );
1721 ENodeType type = node_get_nodetype( child.top() );
1723 if ( contains != eNodeUnknown && contains == type ) {
1724 NodeSmartReference node( child.top().get() );
1725 Path_deleteTop( child );
1726 Node_getTraversable( parent.top() )->insert( node );
1727 SceneChangeNotify();
1731 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1735 void Scene_parentSelected(){
1736 UndoableCommand undo( "parentSelected" );
1738 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1739 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1741 const scene::Path& m_parent;
1743 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1745 void visit( scene::Instance& instance ) const {
1746 if ( &m_parent != &instance.path() ) {
1747 Path_parent( m_parent, instance.path() );
1752 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1753 GlobalSelectionSystem().foreachSelected( visitor );
1757 globalOutputStream() << "failed - did not find two selected nodes.\n";
1764 if ( ConfirmModified( "New Map" ) ) {
1771 CopiedString g_mapsPath;
1773 const char* getMapsPath(){
1774 return g_mapsPath.c_str();
1777 const char* map_open( const char* title ){
1778 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
1781 const char* map_import( const char* title ){
1782 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
1785 const char* map_save( const char* title ){
1786 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
1790 if ( !ConfirmModified( "Open Map" ) ) {
1794 const char* filename = map_open( "Open Map" );
1796 if ( filename != 0 ) {
1797 MRU_AddFile( filename );
1800 Map_LoadFile( filename );
1805 const char* filename = map_import( "Import Map" );
1807 if ( filename != 0 ) {
1808 UndoableCommand undo( "mapImport" );
1809 Map_ImportFile( filename );
1814 const char* filename = map_save( "Save Map" );
1816 if ( filename != 0 ) {
1817 MRU_AddFile( filename );
1818 Map_Rename( filename );
1829 if ( Map_Unnamed( g_map ) ) {
1832 else if ( Map_Modified( g_map ) ) {
1838 const char* filename = map_save( "Export Selection" );
1840 if ( filename != 0 ) {
1841 Map_SaveSelected( filename );
1846 const char* filename = map_save( "Export Region" );
1848 if ( filename != 0 ) {
1849 Map_SaveRegion( filename );
1856 SceneChangeNotify();
1861 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1862 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1863 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1864 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1866 SceneChangeNotify();
1871 SceneChangeNotify();
1874 void RegionSelected(){
1875 Map_RegionSelectedBrushes();
1876 SceneChangeNotify();
1883 class BrushFindByIndexWalker : public scene::Traversable::Walker
1885 mutable std::size_t m_index;
1886 scene::Path& m_path;
1888 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
1889 : m_index( index ), m_path( path ){
1891 bool pre( scene::Node& node ) const {
1892 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
1893 m_path.push( makeReference( node ) );
1899 class EntityFindByIndexWalker : public scene::Traversable::Walker
1901 mutable std::size_t m_index;
1902 scene::Path& m_path;
1904 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
1905 : m_index( index ), m_path( path ){
1907 bool pre( scene::Node& node ) const {
1908 if ( Node_isEntity( node ) && m_index-- == 0 ) {
1909 m_path.push( makeReference( node ) );
1915 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
1916 path.push( makeReference( GlobalSceneGraph().root() ) );
1918 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
1920 if ( path.size() == 2 ) {
1921 scene::Traversable* traversable = Node_getTraversable( path.top() );
1922 if ( traversable != 0 ) {
1923 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
1928 inline bool Node_hasChildren( scene::Node& node ){
1929 scene::Traversable* traversable = Node_getTraversable( node );
1930 return traversable != 0 && !traversable->empty();
1933 void SelectBrush( int entitynum, int brushnum ){
1935 Scene_FindEntityBrush( entitynum, brushnum, path );
1936 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
1937 scene::Instance* instance = GlobalSceneGraph().find( path );
1938 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
1939 Selectable* selectable = Instance_getSelectable( *instance );
1940 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
1941 selectable->setSelected( true );
1942 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
1947 class BrushFindIndexWalker : public scene::Graph::Walker
1949 mutable const scene::Node* m_node;
1950 std::size_t& m_count;
1952 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
1953 : m_node( &node ), m_count( count ){
1955 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1956 if ( Node_isPrimitive( path.top() ) ) {
1957 if ( m_node == path.top().get_pointer() ) {
1968 class EntityFindIndexWalker : public scene::Graph::Walker
1970 mutable const scene::Node* m_node;
1971 std::size_t& m_count;
1973 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
1974 : m_node( &node ), m_count( count ){
1976 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1977 if ( Node_isEntity( path.top() ) ) {
1978 if ( m_node == path.top().get_pointer() ) {
1989 static void GetSelectionIndex( int *ent, int *brush ){
1990 std::size_t count_brush = 0;
1991 std::size_t count_entity = 0;
1992 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1993 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
1995 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
1996 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
1998 *brush = int(count_brush);
1999 *ent = int(count_entity);
2007 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Find Brush", G_CALLBACK( dialog_delete_callback ), &dialog );
2009 GtkAccelGroup* accel = gtk_accel_group_new();
2010 gtk_window_add_accel_group( window, accel );
2013 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2014 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2016 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2017 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2019 GtkWidget* label = gtk_label_new( "Entity number" );
2020 gtk_widget_show( label );
2021 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2022 (GtkAttachOptions) ( 0 ),
2023 (GtkAttachOptions) ( 0 ), 0, 0 );
2026 GtkWidget* label = gtk_label_new( "Brush number" );
2027 gtk_widget_show( label );
2028 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2029 (GtkAttachOptions) ( 0 ),
2030 (GtkAttachOptions) ( 0 ), 0, 0 );
2033 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2034 gtk_widget_show( GTK_WIDGET( entry ) );
2035 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2036 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2037 (GtkAttachOptions) ( 0 ), 0, 0 );
2038 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2042 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2043 gtk_widget_show( GTK_WIDGET( entry ) );
2044 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2045 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2046 (GtkAttachOptions) ( 0 ), 0, 0 );
2052 GtkHBox* hbox = create_dialog_hbox( 4 );
2053 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2055 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2056 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2057 widget_make_default( GTK_WIDGET( button ) );
2058 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2061 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2062 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2063 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2068 // Initialize dialog
2072 GetSelectionIndex( &ent, &br );
2073 sprintf( buf, "%i", ent );
2074 gtk_entry_set_text( entity, buf );
2075 sprintf( buf, "%i", br );
2076 gtk_entry_set_text( brush, buf );
2078 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2079 const char *entstr = gtk_entry_get_text( entity );
2080 const char *brushstr = gtk_entry_get_text( brush );
2081 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2084 gtk_widget_destroy( GTK_WIDGET( window ) );
2087 void Map_constructPreferences( PreferencesPage& page ){
2088 page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
2092 class MapEntityClasses : public ModuleObserver
2094 std::size_t m_unrealised;
2096 MapEntityClasses() : m_unrealised( 1 ){
2099 if ( --m_unrealised == 0 ) {
2100 if ( g_map.m_resource != 0 ) {
2101 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2102 g_map.m_resource->realise();
2107 if ( ++m_unrealised == 1 ) {
2108 if ( g_map.m_resource != 0 ) {
2109 g_map.m_resource->flush();
2110 g_map.m_resource->unrealise();
2116 MapEntityClasses g_MapEntityClasses;
2119 class MapModuleObserver : public ModuleObserver
2121 std::size_t m_unrealised;
2123 MapModuleObserver() : m_unrealised( 1 ){
2126 if ( --m_unrealised == 0 ) {
2127 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2128 StringOutputStream buffer( 256 );
2129 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2130 Q_mkdir( buffer.c_str() );
2131 g_mapsPath = buffer.c_str();
2135 if ( ++m_unrealised == 1 ) {
2141 MapModuleObserver g_MapModuleObserver;
2143 #include "preferencesystem.h"
2145 CopiedString g_strLastMap;
2146 bool g_bLoadLastMap = false;
2148 void Map_Construct(){
2149 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2150 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2151 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2152 GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2154 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2155 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2156 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2158 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2160 GlobalEntityClassManager().attach( g_MapEntityClasses );
2161 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2165 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2166 GlobalEntityClassManager().detach( g_MapEntityClasses );