diff --git a/radiant/entityinspector.cpp b/radiant/entityinspector.cpp index fd36e685..8fe53330 100644 --- a/radiant/entityinspector.cpp +++ b/radiant/entityinspector.cpp @@ -1503,6 +1503,19 @@ void EntityInspector_focusSelected( GtkButton *button, gpointer user_data ){ FocusAllViews(); } +void EntityInspector_selectByKey( GtkButton *button, gpointer user_data ){ + Select_EntitiesByKeyValue( gtk_entry_get_text( g_entityKeyEntry ), nullptr ); +} + +void EntityInspector_selectByValue( GtkButton *button, gpointer user_data ){ + Select_EntitiesByKeyValue( nullptr, gtk_entry_get_text( g_entityValueEntry ) ); +} + +void EntityInspector_selectByKeyValue( GtkButton *button, gpointer user_data ){ + Select_EntitiesByKeyValue( gtk_entry_get_text( g_entityKeyEntry ), gtk_entry_get_text( g_entityValueEntry ) ); +} + + GtkWidget* EntityInspector_constructWindow( GtkWindow* toplevel ){ GtkWidget* vbox = gtk_vbox_new( FALSE, 2 ); gtk_widget_show( vbox ); @@ -1663,7 +1676,7 @@ GtkWidget* EntityInspector_constructWindow( GtkWindow* toplevel ){ { // key/value entry - GtkTable* table = GTK_TABLE( gtk_table_new( 2, 2, FALSE ) ); + GtkTable* table = GTK_TABLE( gtk_table_new( 2, 3, FALSE ) ); gtk_widget_show( GTK_WIDGET( table ) ); gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( table ), FALSE, TRUE, 0 ); gtk_table_set_row_spacings( table, 3 ); @@ -1708,6 +1721,49 @@ GtkWidget* EntityInspector_constructWindow( GtkWindow* toplevel ){ (GtkAttachOptions)( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); } + + /* select by key/value buttons */ + GtkTable* tab = GTK_TABLE( gtk_table_new( 2, 2, FALSE ) ); + gtk_table_attach( table, GTK_WIDGET( tab ), 2, 3, 0, 2, + (GtkAttachOptions)( GTK_FILL ), + (GtkAttachOptions)( 0 ), 0, 0 ); + table = tab; + gtk_widget_show( GTK_WIDGET( table ) ); + gtk_table_set_row_spacings( table, 0 ); + gtk_table_set_col_spacings( table, 0 ); + { + GtkWidget* button = gtk_button_new(); + gtk_button_set_image( GTK_BUTTON( button ), gtk_image_new_from_stock( GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU ) ); + gtk_widget_set_can_focus( button, FALSE ); + gtk_widget_set_tooltip_text( button, "Select by key" ); + gtk_widget_show( button ); + gtk_table_attach( table, button, 0, 1, 0, 1, + (GtkAttachOptions)( GTK_FILL ), + (GtkAttachOptions)( 0 ), 0, 0 ); + g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( EntityInspector_selectByKey ), 0 ); + } + { + GtkWidget* button = gtk_button_new(); + gtk_button_set_image( GTK_BUTTON( button ), gtk_image_new_from_stock( GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU ) ); + gtk_widget_set_can_focus( button, FALSE ); + gtk_widget_set_tooltip_text( button, "Select by value" ); + gtk_widget_show( button ); + gtk_table_attach( table, button, 0, 1, 1, 2, + (GtkAttachOptions)( GTK_FILL ), + (GtkAttachOptions)( 0 ), 0, 0 ); + g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( EntityInspector_selectByValue ), 0 ); + } + { + GtkWidget* button = gtk_button_new(); + gtk_button_set_image( GTK_BUTTON( button ), gtk_image_new_from_stock( GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU ) ); + gtk_widget_set_can_focus( button, FALSE ); + gtk_widget_set_tooltip_text( button, "Select by key + value" ); + gtk_widget_show( button ); + gtk_table_attach( table, button, 1, 2, 0, 2, + (GtkAttachOptions)( GTK_FILL ), + (GtkAttachOptions)( GTK_FILL ), 0, 0 ); + g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( EntityInspector_selectByKeyValue ), 0 ); + } } { @@ -1755,9 +1811,8 @@ GtkWidget* EntityInspector_constructWindow( GtkWindow* toplevel ){ } { GtkWidget* button = gtk_toggle_button_new(); - GtkImage* image = GTK_IMAGE( gtk_image_new_from_stock( GTK_STOCK_ZOOM_IN, GTK_ICON_SIZE_SMALL_TOOLBAR ) ); - gtk_widget_show( GTK_WIDGET( image ) ); - gtk_container_add( GTK_CONTAINER( button ), GTK_WIDGET( image ) ); + GtkWidget* image = gtk_image_new_from_stock( GTK_STOCK_ZOOM_IN, GTK_ICON_SIZE_SMALL_TOOLBAR ); + gtk_button_set_image( GTK_BUTTON( button ), image ); gtk_button_set_relief( GTK_BUTTON( button ), GTK_RELIEF_NONE ); gtk_widget_set_can_focus( button, FALSE ); gtk_box_pack_start( hbox, button, FALSE, FALSE, 0 ); @@ -1856,13 +1911,3 @@ void EntityInspector_construct(){ void EntityInspector_destroy(){ GlobalEntityClassManager().detach( g_EntityInspector ); } - -const char *EntityInspector_getCurrentKey(){ - if ( !GroupDialog_isShown() ) { - return 0; - } - if ( GroupDialog_getPage() != g_page_entity ) { - return 0; - } - return gtk_entry_get_text( g_entityKeyEntry ); -} diff --git a/radiant/entityinspector.h b/radiant/entityinspector.h index 1afa2584..e63cbde1 100644 --- a/radiant/entityinspector.h +++ b/radiant/entityinspector.h @@ -27,6 +27,5 @@ typedef struct _GtkWindow GtkWindow; GtkWidget* EntityInspector_constructWindow( GtkWindow* parent ); void EntityInspector_construct(); void EntityInspector_destroy(); -const char *EntityInspector_getCurrentKey(); #endif diff --git a/radiant/groupdialog.h b/radiant/groupdialog.h index 349802b1..6c259f70 100644 --- a/radiant/groupdialog.h +++ b/radiant/groupdialog.h @@ -43,7 +43,5 @@ GtkWidget* GroupDialog_addPage( const char* tabLabel, GtkWidget* widget, const S void GroupDialog_showPage( GtkWidget* page ); void GroupDialog_updatePageTitle( GtkWidget* page ); -bool GroupDialog_isShown(); -GtkWidget* GroupDialog_getPage(); #endif diff --git a/radiant/select.cpp b/radiant/select.cpp index ccf25cd8..93491278 100644 --- a/radiant/select.cpp +++ b/radiant/select.cpp @@ -725,14 +725,14 @@ bool propertyvalues_contain( const PropertyValues& propertyvalues, const char *s return false; } +template class EntityFindByPropertyValueWalker : public scene::Graph::Walker { -const PropertyValues& m_propertyvalues; -const char *m_prop; +const EntityMatcher& m_entityMatcher; const scene::Node* m_world; public: -EntityFindByPropertyValueWalker( const char *prop, const PropertyValues& propertyvalues ) - : m_propertyvalues( propertyvalues ), m_prop( prop ), m_world( Map_FindWorldspawn( g_map ) ){ +EntityFindByPropertyValueWalker( const EntityMatcher& entityMatcher ) + : m_entityMatcher( entityMatcher ), m_world( Map_FindWorldspawn( g_map ) ){ } bool pre( const scene::Path& path, scene::Instance& instance ) const { if( !path.top().get().visible() ){ @@ -745,7 +745,7 @@ bool pre( const scene::Path& path, scene::Instance& instance ) const { Entity* entity = Node_getEntity( path.top() ); if ( entity != 0 ){ - if( propertyvalues_contain( m_propertyvalues, entity->getKeyValue( m_prop ) ) ) { + if( m_entityMatcher( entity ) ) { Instance_getSelectable( instance )->setSelected( true ); return true; } @@ -760,8 +760,15 @@ bool pre( const scene::Path& path, scene::Instance& instance ) const { } }; +template +void Scene_EntitySelectByPropertyValues( scene::Graph& graph, const EntityMatcher& entityMatcher ){ + graph.traverse( EntityFindByPropertyValueWalker( entityMatcher ) ); +} + void Scene_EntitySelectByPropertyValues( scene::Graph& graph, const char *prop, const PropertyValues& propertyvalues ){ - graph.traverse( EntityFindByPropertyValueWalker( prop, propertyvalues ) ); + Scene_EntitySelectByPropertyValues( GlobalSceneGraph(), [prop, &propertyvalues]( const Entity* entity )->bool{ + return propertyvalues_contain( propertyvalues, entity->getKeyValue( prop ) ); + } ); } class EntityGetSelectedPropertyValuesWalker : public scene::Graph::Walker @@ -841,10 +848,7 @@ void Select_AllOfType(){ else { PropertyValues propertyvalues; - const char *prop = EntityInspector_getCurrentKey(); - if ( !prop || !*prop ) { - prop = "classname"; - } + const char *prop = "classname"; Scene_EntityGetPropertyValues( GlobalSceneGraph(), prop, propertyvalues ); GlobalSelectionSystem().setSelectedAll( false ); if ( !propertyvalues.empty() ) { @@ -858,6 +862,45 @@ void Select_AllOfType(){ } } +void Select_EntitiesByKeyValue( const char* key, const char* value ){ + GlobalSelectionSystem().setSelectedAll( false ); + if( key != nullptr && value != nullptr ){ + if( !string_empty( key ) && !string_empty( value ) ){ + Scene_EntitySelectByPropertyValues( GlobalSceneGraph(), [key, value]( const Entity* entity )->bool{ + return string_equal_nocase( entity->getKeyValue( key ), value ); + } ); + } + } + else if( key != nullptr ){ + if( !string_empty( key ) ){ + Scene_EntitySelectByPropertyValues( GlobalSceneGraph(), [key]( const Entity* entity )->bool{ + return !string_empty( entity->getKeyValue( key ) ); + } ); + } + } + else if( value != nullptr ){ + if( !string_empty( value ) ){ + Scene_EntitySelectByPropertyValues( GlobalSceneGraph(), [value]( const Entity* entity )->bool{ + class Visitor : public Entity::Visitor + { + const char* const m_value; + public: + bool m_found = false; + Visitor( const char* value ) : m_value( value ){ + } + void visit( const char* key, const char* value ){ + if ( string_equal_nocase( m_value, value ) ) { + m_found = true; + } + } + } visitor( value ); + entity->forEachKeyValue( visitor ); + return visitor.m_found; + } ); + } + } +} + void Select_FacesAndPatchesByShader(){ Scene_BrushFacesSelectByShader( GlobalSceneGraph(), TextureBrowser_GetSelectedShader() ); Scene_PatchSelectByShader( GlobalSceneGraph(), TextureBrowser_GetSelectedShader() ); diff --git a/radiant/select.h b/radiant/select.h index 0542446b..d49b84f9 100644 --- a/radiant/select.h +++ b/radiant/select.h @@ -38,6 +38,7 @@ void Selection_MoveDown(); void Selection_MoveUp(); void Select_AllOfType(); +void Select_EntitiesByKeyValue( const char* key, const char* value ); void Select_ConnectedEntities( bool targeting, bool targets, bool focus ); void SelectConnectedEntities();