diff --git a/api/proto/common/commands/editor_commands.proto b/api/proto/common/commands/editor_commands.proto index 7718256f53..9551e01b7d 100644 --- a/api/proto/common/commands/editor_commands.proto +++ b/api/proto/common/commands/editor_commands.proto @@ -326,6 +326,47 @@ message GetBoundingBoxResponse repeated kiapi.common.types.Box2 boxes = 2; } +// Retrieves a list of items. Returns SelectionResponse +message GetSelection +{ + // Specifies which document to query for selected items. + kiapi.common.types.ItemHeader header = 1; + + // An optional list of types to filter on. + // If none are provided, all selected items will be returned. + repeated kiapi.common.types.KiCadObjectType types = 2; +} + +// The set of currently selected items +message SelectionResponse +{ + repeated google.protobuf.Any items = 1; +} + +// Adds the given items to the selection. Returns SelectionResponse +message AddToSelection +{ + kiapi.common.types.ItemHeader header = 1; + + // The items to select + repeated kiapi.common.types.KIID items = 2; +} + +// Removes the given items to the selection. Returns SelectionResponse +message RemoveFromSelection +{ + kiapi.common.types.ItemHeader header = 1; + + // The items to deselect + repeated kiapi.common.types.KIID items = 2; +} + +// Removes all items from selection +message ClearSelection +{ + kiapi.common.types.ItemHeader header = 1; +} + // Tests if a certain point falls within tolerance of an item's geometry message HitTest { diff --git a/pcbnew/api/api_handler_pcb.cpp b/pcbnew/api/api_handler_pcb.cpp index 67734701a1..c859b541b2 100644 --- a/pcbnew/api/api_handler_pcb.cpp +++ b/pcbnew/api/api_handler_pcb.cpp @@ -65,6 +65,12 @@ API_HANDLER_PCB::API_HANDLER_PCB( PCB_EDIT_FRAME* aFrame ) : registerHandler( &API_HANDLER_PCB::handleGetItems ); + registerHandler( &API_HANDLER_PCB::handleGetSelection ); + registerHandler( &API_HANDLER_PCB::handleClearSelection ); + registerHandler( &API_HANDLER_PCB::handleAddToSelection ); + registerHandler( + &API_HANDLER_PCB::handleRemoveFromSelection ); + registerHandler( &API_HANDLER_PCB::handleGetStackup ); registerHandler( &API_HANDLER_PCB::handleGetGraphicsDefaults ); @@ -657,6 +663,141 @@ std::optional API_HANDLER_PCB::getItemFromDocument( const DocumentSpe } +HANDLER_RESULT API_HANDLER_PCB::handleGetSelection( + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + if( !validateItemHeaderDocument( aCtx.Request.header() ) ) + { + ApiResponseStatus e; + // No message needed for AS_UNHANDLED; this is an internal flag for the API server + e.set_status( ApiStatusCode::AS_UNHANDLED ); + return tl::unexpected( e ); + } + + std::set filter; + + for( int typeRaw : aCtx.Request.types() ) + { + auto typeMessage = static_cast( typeRaw ); + KICAD_T type = FromProtoEnum( typeMessage ); + + if( type == TYPE_NOT_INIT ) + continue; + + filter.insert( type ); + } + + TOOL_MANAGER* mgr = frame()->GetToolManager(); + PCB_SELECTION_TOOL* selectionTool = mgr->GetTool(); + + SelectionResponse response; + + for( EDA_ITEM* item : selectionTool->GetSelection() ) + { + if( filter.empty() || filter.contains( item->Type() ) ) + item->Serialize( *response.add_items() ); + } + + return response; +} + + +HANDLER_RESULT API_HANDLER_PCB::handleClearSelection( + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + if( !validateItemHeaderDocument( aCtx.Request.header() ) ) + { + ApiResponseStatus e; + // No message needed for AS_UNHANDLED; this is an internal flag for the API server + e.set_status( ApiStatusCode::AS_UNHANDLED ); + return tl::unexpected( e ); + } + + TOOL_MANAGER* mgr = frame()->GetToolManager(); + mgr->RunAction( PCB_ACTIONS::selectionClear ); + + return Empty(); +} + + +HANDLER_RESULT API_HANDLER_PCB::handleAddToSelection( + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + if( !validateItemHeaderDocument( aCtx.Request.header() ) ) + { + ApiResponseStatus e; + // No message needed for AS_UNHANDLED; this is an internal flag for the API server + e.set_status( ApiStatusCode::AS_UNHANDLED ); + return tl::unexpected( e ); + } + + TOOL_MANAGER* mgr = frame()->GetToolManager(); + PCB_SELECTION_TOOL* selectionTool = mgr->GetTool(); + + std::vector toAdd; + + for( const types::KIID& id : aCtx.Request.items() ) + { + if( std::optional item = getItemById( KIID( id.value() ) ) ) + toAdd.emplace_back( *item ); + } + + selectionTool->AddItemsToSel( &toAdd ); + + SelectionResponse response; + + for( EDA_ITEM* item : selectionTool->GetSelection() ) + item->Serialize( *response.add_items() ); + + return response; +} + + +HANDLER_RESULT API_HANDLER_PCB::handleRemoveFromSelection( + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + if( !validateItemHeaderDocument( aCtx.Request.header() ) ) + { + ApiResponseStatus e; + // No message needed for AS_UNHANDLED; this is an internal flag for the API server + e.set_status( ApiStatusCode::AS_UNHANDLED ); + return tl::unexpected( e ); + } + + TOOL_MANAGER* mgr = frame()->GetToolManager(); + PCB_SELECTION_TOOL* selectionTool = mgr->GetTool(); + + std::vector toRemove; + + for( const types::KIID& id : aCtx.Request.items() ) + { + if( std::optional item = getItemById( KIID( id.value() ) ) ) + toRemove.emplace_back( *item ); + } + + selectionTool->RemoveItemsFromSel( &toRemove ); + + SelectionResponse response; + + for( EDA_ITEM* item : selectionTool->GetSelection() ) + item->Serialize( *response.add_items() ); + + return response; +} + + HANDLER_RESULT API_HANDLER_PCB::handleGetStackup( const HANDLER_CONTEXT& aCtx ) { diff --git a/pcbnew/api/api_handler_pcb.h b/pcbnew/api/api_handler_pcb.h index f4d1a43824..ac437854cb 100644 --- a/pcbnew/api/api_handler_pcb.h +++ b/pcbnew/api/api_handler_pcb.h @@ -74,7 +74,20 @@ private: HANDLER_RESULT handleGetItems( const HANDLER_CONTEXT& aCtx ); - HANDLER_RESULT handleGetStackup( const HANDLER_CONTEXT& aCtx ); + HANDLER_RESULT handleGetSelection( + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleClearSelection( + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleAddToSelection( + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleRemoveFromSelection( + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleGetStackup( + const HANDLER_CONTEXT& aCtx ); HANDLER_RESULT handleGetGraphicsDefaults( const HANDLER_CONTEXT& aCtx );