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<GetItems, GetItemsResponse>( &API_HANDLER_PCB::handleGetItems ); + registerHandler<GetSelection, SelectionResponse>( &API_HANDLER_PCB::handleGetSelection ); + registerHandler<ClearSelection, Empty>( &API_HANDLER_PCB::handleClearSelection ); + registerHandler<AddToSelection, SelectionResponse>( &API_HANDLER_PCB::handleAddToSelection ); + registerHandler<RemoveFromSelection, SelectionResponse>( + &API_HANDLER_PCB::handleRemoveFromSelection ); + registerHandler<GetBoardStackup, BoardStackupResponse>( &API_HANDLER_PCB::handleGetStackup ); registerHandler<GetGraphicsDefaults, GraphicsDefaultsResponse>( &API_HANDLER_PCB::handleGetGraphicsDefaults ); @@ -657,6 +663,141 @@ std::optional<EDA_ITEM*> API_HANDLER_PCB::getItemFromDocument( const DocumentSpe } +HANDLER_RESULT<SelectionResponse> API_HANDLER_PCB::handleGetSelection( + const HANDLER_CONTEXT<GetSelection>& aCtx ) +{ + if( std::optional<ApiResponseStatus> 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<KICAD_T> filter; + + for( int typeRaw : aCtx.Request.types() ) + { + auto typeMessage = static_cast<types::KiCadObjectType>( typeRaw ); + KICAD_T type = FromProtoEnum<KICAD_T>( typeMessage ); + + if( type == TYPE_NOT_INIT ) + continue; + + filter.insert( type ); + } + + TOOL_MANAGER* mgr = frame()->GetToolManager(); + PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>(); + + SelectionResponse response; + + for( EDA_ITEM* item : selectionTool->GetSelection() ) + { + if( filter.empty() || filter.contains( item->Type() ) ) + item->Serialize( *response.add_items() ); + } + + return response; +} + + +HANDLER_RESULT<Empty> API_HANDLER_PCB::handleClearSelection( + const HANDLER_CONTEXT<ClearSelection>& aCtx ) +{ + if( std::optional<ApiResponseStatus> 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<SelectionResponse> API_HANDLER_PCB::handleAddToSelection( + const HANDLER_CONTEXT<AddToSelection>& aCtx ) +{ + if( std::optional<ApiResponseStatus> 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<PCB_SELECTION_TOOL>(); + + std::vector<EDA_ITEM*> toAdd; + + for( const types::KIID& id : aCtx.Request.items() ) + { + if( std::optional<BOARD_ITEM*> 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<SelectionResponse> API_HANDLER_PCB::handleRemoveFromSelection( + const HANDLER_CONTEXT<RemoveFromSelection>& aCtx ) +{ + if( std::optional<ApiResponseStatus> 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<PCB_SELECTION_TOOL>(); + + std::vector<EDA_ITEM*> toRemove; + + for( const types::KIID& id : aCtx.Request.items() ) + { + if( std::optional<BOARD_ITEM*> 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<BoardStackupResponse> API_HANDLER_PCB::handleGetStackup( const HANDLER_CONTEXT<GetBoardStackup>& 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<commands::GetItemsResponse> handleGetItems( const HANDLER_CONTEXT<commands::GetItems>& aCtx ); - HANDLER_RESULT<BoardStackupResponse> handleGetStackup( const HANDLER_CONTEXT<GetBoardStackup>& aCtx ); + HANDLER_RESULT<commands::SelectionResponse> handleGetSelection( + const HANDLER_CONTEXT<commands::GetSelection>& aCtx ); + + HANDLER_RESULT<Empty> handleClearSelection( + const HANDLER_CONTEXT<commands::ClearSelection>& aCtx ); + + HANDLER_RESULT<commands::SelectionResponse> handleAddToSelection( + const HANDLER_CONTEXT<commands::AddToSelection>& aCtx ); + + HANDLER_RESULT<commands::SelectionResponse> handleRemoveFromSelection( + const HANDLER_CONTEXT<commands::RemoveFromSelection>& aCtx ); + + HANDLER_RESULT<BoardStackupResponse> handleGetStackup( + const HANDLER_CONTEXT<GetBoardStackup>& aCtx ); HANDLER_RESULT<GraphicsDefaultsResponse> handleGetGraphicsDefaults( const HANDLER_CONTEXT<GetGraphicsDefaults>& aCtx );