KiCad PCB EDA Suite
Loading...
Searching...
No Matches
api_handler_pcb.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2023 Jon Evans <[email protected]>
5 * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <magic_enum.hpp>
22
23#include <api/api_handler_pcb.h>
24#include <api/api_pcb_utils.h>
25#include <api/api_enums.h>
26#include <api/api_utils.h>
27#include <board_commit.h>
29#include <footprint.h>
30#include <netinfo.h>
31#include <pad.h>
32#include <pcb_edit_frame.h>
33#include <pcb_group.h>
34#include <pcb_reference_image.h>
35#include <pcb_shape.h>
36#include <pcb_text.h>
37#include <pcb_textbox.h>
38#include <pcb_track.h>
39#include <project.h>
40#include <tool/tool_manager.h>
41#include <tools/pcb_actions.h>
43#include <zone.h>
44
45#include <api/common/types/base_types.pb.h>
46
47using namespace kiapi::common::commands;
48using kiapi::common::types::CommandStatus;
49using kiapi::common::types::DocumentType;
50using kiapi::common::types::ItemRequestStatus;
51
52
54 API_HANDLER_EDITOR( aFrame )
55{
56 registerHandler<RunAction, RunActionResponse>( &API_HANDLER_PCB::handleRunAction );
57 registerHandler<GetOpenDocuments, GetOpenDocumentsResponse>(
59
60
61 registerHandler<GetItems, GetItemsResponse>( &API_HANDLER_PCB::handleGetItems );
62
63 registerHandler<GetBoardStackup, BoardStackupResponse>( &API_HANDLER_PCB::handleGetStackup );
64 registerHandler<GetGraphicsDefaults, GraphicsDefaultsResponse>(
66 registerHandler<GetBoundingBox, GetBoundingBoxResponse>(
68 registerHandler<GetTextExtents, types::Box2>(
70 registerHandler<GetPadShapeAsPolygon, PadShapeAsPolygonResponse>(
72 registerHandler<GetTitleBlockInfo, types::TitleBlockInfo>(
74
75 registerHandler<InteractiveMoveItems, Empty>( &API_HANDLER_PCB::handleInteractiveMoveItems );
76 registerHandler<GetNets, NetsResponse>( &API_HANDLER_PCB::handleGetNets );
77 registerHandler<RefillZones, Empty>( &API_HANDLER_PCB::handleRefillZones );
78}
79
80
82{
83 return static_cast<PCB_EDIT_FRAME*>( m_frame );
84}
85
86
88 const HANDLER_CONTEXT& )
89{
90 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
91 return tl::unexpected( *busy );
92
93 RunActionResponse response;
94
95 if( frame()->GetToolManager()->RunAction( aRequest.action(), true ) )
96 response.set_status( RunActionStatus::RAS_OK );
97 else
98 response.set_status( RunActionStatus::RAS_INVALID );
99
100 return response;
101}
102
103
105 GetOpenDocuments& aMsg, const HANDLER_CONTEXT& )
106{
107 if( aMsg.type() != DocumentType::DOCTYPE_PCB )
108 {
109 ApiResponseStatus e;
110 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
111 e.set_status( ApiStatusCode::AS_UNHANDLED );
112 return tl::unexpected( e );
113 }
114
115 GetOpenDocumentsResponse response;
116 common::types::DocumentSpecifier doc;
117
118 wxFileName fn( frame()->GetCurrentFileName() );
119
120 doc.set_type( DocumentType::DOCTYPE_PCB );
121 doc.set_board_filename( fn.GetFullName() );
122
123 doc.mutable_project()->set_name( frame()->Prj().GetProjectName().ToStdString() );
124 doc.mutable_project()->set_path( frame()->Prj().GetProjectDirectory().ToStdString() );
125
126 response.mutable_documents()->Add( std::move( doc ) );
127 return response;
128}
129
130
131void API_HANDLER_PCB::pushCurrentCommit( const HANDLER_CONTEXT& aCtx, const wxString& aMessage )
132{
134 frame()->Refresh();
135}
136
137
138std::unique_ptr<COMMIT> API_HANDLER_PCB::createCommit()
139{
140 return std::make_unique<BOARD_COMMIT>( frame() );
141}
142
143
144std::optional<BOARD_ITEM*> API_HANDLER_PCB::getItemById( const KIID& aId ) const
145{
146 BOARD_ITEM* item = frame()->GetBoard()->GetItem( aId );
147
148 if( item == DELETED_BOARD_ITEM::GetInstance() )
149 return std::nullopt;
150
151 return item;
152}
153
154
155bool API_HANDLER_PCB::validateDocumentInternal( const DocumentSpecifier& aDocument ) const
156{
157 if( aDocument.type() != DocumentType::DOCTYPE_PCB )
158 return false;
159
160 wxFileName fn( frame()->GetCurrentFileName() );
161 return 0 == aDocument.board_filename().compare( fn.GetFullName() );
162}
163
164
166 BOARD_ITEM_CONTAINER* aContainer )
167{
168 if( !aContainer )
169 {
170 ApiResponseStatus e;
171 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
172 e.set_error_message( "Tried to create an item in a null container" );
173 return tl::unexpected( e );
174 }
175
176 if( aType == PCB_PAD_T && !dynamic_cast<FOOTPRINT*>( aContainer ) )
177 {
178 ApiResponseStatus e;
179 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
180 e.set_error_message( fmt::format( "Tried to create a pad in {}, which is not a footprint",
181 aContainer->GetFriendlyName().ToStdString() ) );
182 return tl::unexpected( e );
183 }
184 else if( aType == PCB_FOOTPRINT_T && !dynamic_cast<BOARD*>( aContainer ) )
185 {
186 ApiResponseStatus e;
187 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
188 e.set_error_message( fmt::format( "Tried to create a footprint in {}, which is not a board",
189 aContainer->GetFriendlyName().ToStdString() ) );
190 return tl::unexpected( e );
191 }
192
193 std::unique_ptr<BOARD_ITEM> created = CreateItemForType( aType, aContainer );
194
195 if( !created )
196 {
197 ApiResponseStatus e;
198 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
199 e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled",
200 magic_enum::enum_name( aType ) ) );
201 return tl::unexpected( e );
202 }
203
204 return created;
205}
206
207
209 const HANDLER_CONTEXT& aCtx,
210 const types::ItemHeader &aHeader,
211 const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
212 std::function<void( ItemStatus, google::protobuf::Any )> aItemHandler )
213{
214 ApiResponseStatus e;
215
216 auto containerResult = validateItemHeaderDocument( aHeader );
217
218 if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
219 {
220 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
221 e.set_status( ApiStatusCode::AS_UNHANDLED );
222 return tl::unexpected( e );
223 }
224 else if( !containerResult )
225 {
226 e.CopyFrom( containerResult.error() );
227 return tl::unexpected( e );
228 }
229
230 BOARD* board = frame()->GetBoard();
231 BOARD_ITEM_CONTAINER* container = board;
232
233 if( containerResult->has_value() )
234 {
235 const KIID& containerId = **containerResult;
236 std::optional<BOARD_ITEM*> optItem = getItemById( containerId );
237
238 if( optItem )
239 {
240 container = dynamic_cast<BOARD_ITEM_CONTAINER*>( *optItem );
241
242 if( !container )
243 {
244 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
245 e.set_error_message( fmt::format(
246 "The requested container {} is not a valid board item container",
247 containerId.AsStdString() ) );
248 return tl::unexpected( e );
249 }
250 }
251 else
252 {
253 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
254 e.set_error_message( fmt::format(
255 "The requested container {} does not exist in this document",
256 containerId.AsStdString() ) );
257 return tl::unexpected( e );
258 }
259 }
260
261 BOARD_COMMIT* commit = static_cast<BOARD_COMMIT*>( getCurrentCommit( aCtx ) );
262
263 for( const google::protobuf::Any& anyItem : aItems )
264 {
265 ItemStatus status;
266 std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
267
268 if( !type )
269 {
270 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
271 status.set_error_message( fmt::format( "Could not decode a valid type from {}",
272 anyItem.type_url() ) );
273 aItemHandler( status, anyItem );
274 continue;
275 }
276
278 createItemForType( *type, container );
279
280 if( !creationResult )
281 {
282 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
283 status.set_error_message( creationResult.error().error_message() );
284 aItemHandler( status, anyItem );
285 continue;
286 }
287
288 std::unique_ptr<BOARD_ITEM> item( std::move( *creationResult ) );
289
290 if( !item->Deserialize( anyItem ) )
291 {
292 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
293 e.set_error_message( fmt::format( "could not unpack {} from request",
294 item->GetClass().ToStdString() ) );
295 return tl::unexpected( e );
296 }
297
298 std::optional<BOARD_ITEM*> optItem = getItemById( item->m_Uuid );
299
300 if( aCreate && optItem )
301 {
302 status.set_code( ItemStatusCode::ISC_EXISTING );
303 status.set_error_message( fmt::format( "an item with UUID {} already exists",
304 item->m_Uuid.AsStdString() ) );
305 aItemHandler( status, anyItem );
306 continue;
307 }
308 else if( !aCreate && !optItem )
309 {
310 status.set_code( ItemStatusCode::ISC_NONEXISTENT );
311 status.set_error_message( fmt::format( "an item with UUID {} does not exist",
312 item->m_Uuid.AsStdString() ) );
313 aItemHandler( status, anyItem );
314 continue;
315 }
316
317 if( aCreate && !board->GetEnabledLayers().Contains( item->GetLayer() ) )
318 {
319 status.set_code( ItemStatusCode::ISC_INVALID_DATA );
320 status.set_error_message( fmt::format( "attempted to add item on disabled layer {}",
321 LayerName( item->GetLayer() ).ToStdString() ) );
322 aItemHandler( status, anyItem );
323 continue;
324 }
325
326 status.set_code( ItemStatusCode::ISC_OK );
327 google::protobuf::Any newItem;
328
329 if( aCreate )
330 {
331 item->Serialize( newItem );
332 commit->Add( item.release() );
333 }
334 else
335 {
336 BOARD_ITEM* boardItem = *optItem;
337 commit->Modify( boardItem );
338 boardItem->SwapItemData( item.get() );
339 boardItem->Serialize( newItem );
340 }
341
342 aItemHandler( status, newItem );
343 }
344
345 if( !m_activeClients.count( aCtx.ClientName ) )
346 {
347 pushCurrentCommit( aCtx, aCreate ? _( "Created items via API" )
348 : _( "Added items via API" ) );
349 }
350
351
352 return ItemRequestStatus::IRS_OK;
353}
354
355
357 const HANDLER_CONTEXT& )
358{
359 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
360 return tl::unexpected( *busy );
361
362 if( !validateItemHeaderDocument( aMsg.header() ) )
363 {
364 ApiResponseStatus e;
365 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
366 e.set_status( ApiStatusCode::AS_UNHANDLED );
367 return tl::unexpected( e );
368 }
369
370 GetItemsResponse response;
371
372 BOARD* board = frame()->GetBoard();
373 std::vector<BOARD_ITEM*> items;
374 std::set<KICAD_T> typesRequested, typesInserted;
375 bool handledAnything = false;
376
377 for( int typeRaw : aMsg.types() )
378 {
379 auto typeMessage = static_cast<common::types::KiCadObjectType>( typeRaw );
380 KICAD_T type = FromProtoEnum<KICAD_T>( typeMessage );
381
382 if( type == TYPE_NOT_INIT )
383 continue;
384
385 typesRequested.emplace( type );
386
387 if( typesInserted.count( type ) )
388 continue;
389
390 switch( type )
391 {
392 case PCB_TRACE_T:
393 case PCB_ARC_T:
394 case PCB_VIA_T:
395 handledAnything = true;
396 std::copy( board->Tracks().begin(), board->Tracks().end(),
397 std::back_inserter( items ) );
398 typesInserted.insert( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } );
399 break;
400
401 case PCB_PAD_T:
402 {
403 handledAnything = true;
404
405 for( FOOTPRINT* fp : board->Footprints() )
406 {
407 std::copy( fp->Pads().begin(), fp->Pads().end(),
408 std::back_inserter( items ) );
409 }
410
411 typesInserted.insert( PCB_PAD_T );
412 break;
413 }
414
415 case PCB_FOOTPRINT_T:
416 {
417 handledAnything = true;
418
419 std::copy( board->Footprints().begin(), board->Footprints().end(),
420 std::back_inserter( items ) );
421
422 typesInserted.insert( PCB_FOOTPRINT_T );
423 break;
424 }
425
426 case PCB_SHAPE_T:
427 {
428 handledAnything = true;
429 bool inserted = false;
430
431 for( BOARD_ITEM* item : board->Drawings() )
432 {
433 if( item->Type() == PCB_SHAPE_T )
434 {
435 items.emplace_back( item );
436 inserted = true;
437 }
438 }
439
440 if( inserted )
441 typesInserted.insert( PCB_SHAPE_T );
442
443 break;
444 }
445
446 default:
447 break;
448 }
449 }
450
451 if( !handledAnything )
452 {
453 ApiResponseStatus e;
454 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
455 e.set_error_message( "none of the requested types are valid for a Board object" );
456 return tl::unexpected( e );
457 }
458
459 for( const BOARD_ITEM* item : items )
460 {
461 if( !typesRequested.count( item->Type() ) )
462 continue;
463
464 google::protobuf::Any itemBuf;
465 item->Serialize( itemBuf );
466 response.mutable_items()->Add( std::move( itemBuf ) );
467 }
468
469 response.set_status( ItemRequestStatus::IRS_OK );
470 return response;
471}
472
473
474void API_HANDLER_PCB::deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
475 const HANDLER_CONTEXT& aCtx )
476{
477 BOARD* board = frame()->GetBoard();
478 std::vector<BOARD_ITEM*> validatedItems;
479
480 for( std::pair<const KIID, ItemDeletionStatus> pair : aItemsToDelete )
481 {
482 if( BOARD_ITEM* item = board->GetItem( pair.first ) )
483 {
484 validatedItems.push_back( item );
485 aItemsToDelete[pair.first] = ItemDeletionStatus::IDS_OK;
486 }
487
488 // Note: we don't currently support locking items from API modification, but here is where
489 // to add it in the future (and return IDS_IMMUTABLE)
490 }
491
492 COMMIT* commit = getCurrentCommit( aCtx );
493
494 for( BOARD_ITEM* item : validatedItems )
495 commit->Remove( item );
496
497 if( !m_activeClients.count( aCtx.ClientName ) )
498 pushCurrentCommit( aCtx, _( "Deleted items via API" ) );
499}
500
501
502std::optional<EDA_ITEM*> API_HANDLER_PCB::getItemFromDocument( const DocumentSpecifier& aDocument,
503 const KIID& aId )
504{
505 if( !validateDocument( aDocument ) )
506 return std::nullopt;
507
508 return getItemById( aId );
509}
510
511
513 const HANDLER_CONTEXT& aCtx )
514{
515 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
516 return tl::unexpected( *busy );
517
518 HANDLER_RESULT<bool> documentValidation = validateDocument( aMsg.board() );
519
520 if( !documentValidation )
521 return tl::unexpected( documentValidation.error() );
522
523 BoardStackupResponse response;
524 google::protobuf::Any any;
525
527
528 any.UnpackTo( response.mutable_stackup() );
529
530 return response;
531}
532
533
535 GetGraphicsDefaults& aMsg,
536 const HANDLER_CONTEXT& aCtx )
537{
538 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
539 return tl::unexpected( *busy );
540
541 HANDLER_RESULT<bool> documentValidation = validateDocument( aMsg.board() );
542
543 if( !documentValidation )
544 return tl::unexpected( documentValidation.error() );
545
547 GraphicsDefaultsResponse response;
548
549 // TODO: This should change to be an enum class
550 constexpr std::array<kiapi::board::BoardLayerClass, LAYER_CLASS_COUNT> classOrder = {
551 kiapi::board::BLC_SILKSCREEN,
552 kiapi::board::BLC_COPPER,
553 kiapi::board::BLC_EDGES,
554 kiapi::board::BLC_COURTYARD,
555 kiapi::board::BLC_FABRICATION,
556 kiapi::board::BLC_OTHER
557 };
558
559 for( int i = 0; i < LAYER_CLASS_COUNT; ++i )
560 {
561 kiapi::board::BoardLayerGraphicsDefaults* l = response.mutable_defaults()->add_layers();
562
563 l->set_layer( classOrder[i] );
564 l->mutable_line_thickness()->set_value_nm( bds.m_LineThickness[i] );
565
566 kiapi::common::types::TextAttributes* text = l->mutable_text();
567 text->mutable_size()->set_x_nm( bds.m_TextSize[i].x );
568 text->mutable_size()->set_y_nm( bds.m_TextSize[i].y );
569 text->mutable_stroke_width()->set_value_nm( bds.m_TextThickness[i] );
570 text->set_italic( bds.m_TextItalic[i] );
571 text->set_keep_upright( bds.m_TextUpright[i] );
572 }
573
574 return response;
575}
576
577
579 const HANDLER_CONTEXT& aCtx )
580{
581 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
582 return tl::unexpected( *busy );
583
584 if( !validateItemHeaderDocument( aMsg.header() ) )
585 {
586 ApiResponseStatus e;
587 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
588 e.set_status( ApiStatusCode::AS_UNHANDLED );
589 return tl::unexpected( e );
590 }
591
592 GetBoundingBoxResponse response;
593 bool includeText = aMsg.mode() == BoundingBoxMode::BBM_ITEM_AND_CHILD_TEXT;
594
595 for( const types::KIID& idMsg : aMsg.items() )
596 {
597 KIID id( idMsg.value() );
598 std::optional<BOARD_ITEM*> optItem = getItemById( id );
599
600 if( !optItem )
601 continue;
602
603 BOARD_ITEM* item = *optItem;
604 BOX2I bbox;
605
606 if( item->Type() == PCB_FOOTPRINT_T )
607 bbox = static_cast<FOOTPRINT*>( item )->GetBoundingBox( includeText );
608 else
609 bbox = item->GetBoundingBox();
610
611 response.add_items()->set_value( idMsg.value() );
612 PackBox2( *response.add_boxes(), bbox );
613 }
614
615 return response;
616}
617
618
620 GetTextExtents& aMsg,
621 const HANDLER_CONTEXT& aCtx )
622{
623 PCB_TEXT text( frame()->GetBoard() );
624
625 google::protobuf::Any any;
626 any.PackFrom( aMsg.text() );
627
628 if( !text.Deserialize( any ) )
629 {
630 ApiResponseStatus e;
631 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
632 e.set_error_message( "Could not decode text in GetTextExtents message" );
633 return tl::unexpected( e );
634 }
635
636 types::Box2 response;
637
638 BOX2I bbox = text.GetTextBox();
639 EDA_ANGLE angle = text.GetTextAngle();
640
641 if( !angle.IsZero() )
642 bbox = bbox.GetBoundingBoxRotated( text.GetTextPos(), text.GetTextAngle() );
643
644 response.mutable_position()->set_x_nm( bbox.GetPosition().x );
645 response.mutable_position()->set_y_nm( bbox.GetPosition().y );
646 response.mutable_size()->set_x_nm( bbox.GetSize().x );
647 response.mutable_size()->set_y_nm( bbox.GetSize().y );
648
649 return response;
650}
651
652
654 GetPadShapeAsPolygon& aMsg,
655 const HANDLER_CONTEXT& aCtx )
656{
657 HANDLER_RESULT<bool> documentValidation = validateDocument( aMsg.board() );
658
659 if( !documentValidation )
660 return tl::unexpected( documentValidation.error() );
661
662 SHAPE_POLY_SET poly;
663 PadShapeAsPolygonResponse response;
664 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID, board::types::BoardLayer>( aMsg.layer() );
665
666 for( const types::KIID& padRequest : aMsg.pads() )
667 {
668 KIID id( padRequest.value() );
669 std::optional<BOARD_ITEM*> optPad = getItemById( id );
670
671 if( !optPad || ( *optPad )->Type() != PCB_PAD_T )
672 continue;
673
674 response.add_pads()->set_value( padRequest.value() );
675
676 PAD* pad = static_cast<PAD*>( *optPad );
677 pad->TransformShapeToPolygon( poly, pad->Padstack().EffectiveLayerFor( layer ), 0,
679
680 types::PolygonWithHoles* polyMsg = response.mutable_polygons()->Add();
681 PackPolyLine( *polyMsg->mutable_outline(), poly.COutline( 0 ) );
682 }
683
684 return response;
685}
686
687
689 GetTitleBlockInfo& aMsg,
690 const HANDLER_CONTEXT& aCtx )
691{
692 HANDLER_RESULT<bool> documentValidation = validateDocument( aMsg.document() );
693
694 if( !documentValidation )
695 return tl::unexpected( documentValidation.error() );
696
697 BOARD* board = frame()->GetBoard();
698 const TITLE_BLOCK& block = board->GetTitleBlock();
699
700 types::TitleBlockInfo response;
701
702 response.set_title( block.GetTitle().ToUTF8() );
703 response.set_date( block.GetDate().ToUTF8() );
704 response.set_revision( block.GetRevision().ToUTF8() );
705 response.set_company( block.GetCompany().ToUTF8() );
706 response.set_comment1( block.GetComment( 0 ).ToUTF8() );
707 response.set_comment2( block.GetComment( 1 ).ToUTF8() );
708 response.set_comment3( block.GetComment( 2 ).ToUTF8() );
709 response.set_comment4( block.GetComment( 3 ).ToUTF8() );
710 response.set_comment5( block.GetComment( 4 ).ToUTF8() );
711 response.set_comment6( block.GetComment( 5 ).ToUTF8() );
712 response.set_comment7( block.GetComment( 6 ).ToUTF8() );
713 response.set_comment8( block.GetComment( 7 ).ToUTF8() );
714 response.set_comment9( block.GetComment( 8 ).ToUTF8() );
715
716 return response;
717}
718
719
721 const HANDLER_CONTEXT& aCtx )
722{
723 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
724 return tl::unexpected( *busy );
725
726 HANDLER_RESULT<bool> documentValidation = validateDocument( aMsg.board() );
727
728 if( !documentValidation )
729 return tl::unexpected( documentValidation.error() );
730
732 std::vector<EDA_ITEM*> toSelect;
733
734 for( const kiapi::common::types::KIID& id : aMsg.items() )
735 {
736 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
737 toSelect.emplace_back( static_cast<EDA_ITEM*>( *item ) );
738 }
739
740 if( toSelect.empty() )
741 {
742 ApiResponseStatus e;
743 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
744 e.set_error_message( fmt::format( "None of the given items exist on the board",
745 aMsg.board().board_filename() ) );
746 return tl::unexpected( e );
747 }
748
749 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
750 selectionTool->GetSelection().SetReferencePoint( toSelect[0]->GetPosition() );
751
753 mgr->RunAction<EDA_ITEMS*>( PCB_ACTIONS::selectItems, &toSelect );
754
755 COMMIT* commit = getCurrentCommit( aCtx );
756 mgr->PostAction( PCB_ACTIONS::move, commit );
757
758 return Empty();
759}
760
761
763 const HANDLER_CONTEXT& aCtx )
764{
765 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
766 return tl::unexpected( *busy );
767
768 HANDLER_RESULT<bool> documentValidation = validateDocument( aMsg.board() );
769
770 if( !documentValidation )
771 return tl::unexpected( documentValidation.error() );
772
773 NetsResponse response;
774 BOARD* board = frame()->GetBoard();
775
776 std::set<wxString> netclassFilter;
777
778 for( const std::string& nc : aMsg.netclass_filter() )
779 netclassFilter.insert( wxString( nc.c_str(), wxConvUTF8 ) );
780
781 for( NETINFO_ITEM* net : board->GetNetInfo() )
782 {
783 NETCLASS* nc = net->GetNetClass();
784
785 if( !netclassFilter.empty() && nc && !netclassFilter.count( nc->GetName() ) )
786 continue;
787
788 board::types::Net* netProto = response.add_nets();
789 netProto->set_name( net->GetNetname() );
790 netProto->mutable_code()->set_value( net->GetNetCode() );
791 }
792
793 return response;
794}
795
796
798 const HANDLER_CONTEXT& aCtx )
799{
800 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
801 return tl::unexpected( *busy );
802
803 HANDLER_RESULT<bool> documentValidation = validateDocument( aMsg.board() );
804
805 if( !documentValidation )
806 return tl::unexpected( documentValidation.error() );
807
808 if( aMsg.zones().empty() )
809 {
811 frame()->CallAfter( [mgr]()
812 {
814 } );
815 }
816 else
817 {
818 // TODO
819 ApiResponseStatus e;
820 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
821 return tl::unexpected( e );
822 }
823
824 return Empty();
825}
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
Definition: api_handler.h:45
std::unique_ptr< EDA_ITEM > CreateItemForType(KICAD_T aType, EDA_ITEM *aContainer)
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
@ LAYER_CLASS_COUNT
Base class for API handlers related to editor frames.
virtual void pushCurrentCommit(const HANDLER_CONTEXT &aCtx, const wxString &aMessage)
HANDLER_RESULT< bool > validateDocument(const DocumentSpecifier &aDocument)
HANDLER_RESULT< std::optional< KIID > > validateItemHeaderDocument(const kiapi::common::types::ItemHeader &aHeader)
If the header is valid, returns the item container.
COMMIT * getCurrentCommit(const HANDLER_CONTEXT &aCtx)
std::set< std::string > m_activeClients
virtual std::optional< ApiResponseStatus > checkForBusy()
Checks if the editor can accept commands.
EDA_BASE_FRAME * m_frame
HANDLER_RESULT< commands::GetItemsResponse > handleGetItems(commands::GetItems &aMsg, const HANDLER_CONTEXT &aCtx)
bool validateDocumentInternal(const DocumentSpecifier &aDocument) const override
HANDLER_RESULT< types::TitleBlockInfo > handleGetTitleBlockInfo(commands::GetTitleBlockInfo &aMsg, const HANDLER_CONTEXT &aCtx)
API_HANDLER_PCB(PCB_EDIT_FRAME *aFrame)
static HANDLER_RESULT< std::unique_ptr< BOARD_ITEM > > createItemForType(KICAD_T aType, BOARD_ITEM_CONTAINER *aContainer)
std::optional< BOARD_ITEM * > getItemById(const KIID &aId) const
std::unique_ptr< COMMIT > createCommit() override
Override this to create an appropriate COMMIT subclass for the frame in question.
HANDLER_RESULT< Empty > handleInteractiveMoveItems(InteractiveMoveItems &aMsg, const HANDLER_CONTEXT &aCtx)
HANDLER_RESULT< BoardStackupResponse > handleGetStackup(GetBoardStackup &aMsg, const HANDLER_CONTEXT &aCtx)
void deleteItemsInternal(std::map< KIID, ItemDeletionStatus > &aItemsToDelete, const HANDLER_CONTEXT &aCtx) override
std::optional< EDA_ITEM * > getItemFromDocument(const DocumentSpecifier &aDocument, const KIID &aId) override
HANDLER_RESULT< commands::RunActionResponse > handleRunAction(commands::RunAction &aMsg, const HANDLER_CONTEXT &aCtx)
HANDLER_RESULT< commands::GetBoundingBoxResponse > handleGetBoundingBox(commands::GetBoundingBox &aMsg, const HANDLER_CONTEXT &aCtx)
HANDLER_RESULT< types::ItemRequestStatus > handleCreateUpdateItemsInternal(bool aCreate, const HANDLER_CONTEXT &aCtx, const types::ItemHeader &aHeader, const google::protobuf::RepeatedPtrField< google::protobuf::Any > &aItems, std::function< void(commands::ItemStatus, google::protobuf::Any)> aItemHandler) override
HANDLER_RESULT< Empty > handleRefillZones(RefillZones &aMsg, const HANDLER_CONTEXT &aCtx)
HANDLER_RESULT< GraphicsDefaultsResponse > handleGetGraphicsDefaults(GetGraphicsDefaults &aMsg, const HANDLER_CONTEXT &aCtx)
HANDLER_RESULT< types::Box2 > handleGetTextExtents(GetTextExtents &aMsg, const HANDLER_CONTEXT &aCtx)
void pushCurrentCommit(const HANDLER_CONTEXT &aCtx, const wxString &aMessage) override
HANDLER_RESULT< commands::GetOpenDocumentsResponse > handleGetOpenDocuments(commands::GetOpenDocuments &aMsg, const HANDLER_CONTEXT &aCtx)
HANDLER_RESULT< PadShapeAsPolygonResponse > handleGetPadShapeAsPolygon(GetPadShapeAsPolygon &aMsg, const HANDLER_CONTEXT &aCtx)
PCB_EDIT_FRAME * frame() const
HANDLER_RESULT< NetsResponse > handleGetNets(GetNets &aMsg, const HANDLER_CONTEXT &aCtx)
Container for design settings for a BOARD object.
bool m_TextUpright[LAYER_CLASS_COUNT]
int m_TextThickness[LAYER_CLASS_COUNT]
int m_LineThickness[LAYER_CLASS_COUNT]
VECTOR2I m_TextSize[LAYER_CLASS_COUNT]
bool m_TextItalic[LAYER_CLASS_COUNT]
Abstract interface for BOARD_ITEMs capable of storing other items inside.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
void SwapItemData(BOARD_ITEM *aImage)
Swap data between aItem and aImage.
Definition: board_item.cpp:226
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:290
BOARD_STACKUP GetStackupOrDefault() const
Definition: board.cpp:2287
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:871
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:775
BOARD_ITEM * GetItem(const KIID &aID) const
Definition: board.cpp:1408
TITLE_BLOCK & GetTitleBlock()
Definition: board.h:695
const FOOTPRINTS & Footprints() const
Definition: board.h:331
const TRACKS & Tracks() const
Definition: board.h:329
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:892
const DRAWINGS & Drawings() const
Definition: board.h:333
constexpr const Vec & GetPosition() const
Definition: box2.h:211
const BOX2< Vec > GetBoundingBoxRotated(const VECTOR2I &aRotCenter, const EDA_ANGLE &aAngle) const
Useful to calculate bounding box of rotated items, when rotation is not cardinal.
Definition: box2.h:720
constexpr const SizeVec & GetSize() const
Definition: box2.h:206
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition: commit.h:74
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been removed.
Definition: commit.h:92
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition: commit.h:80
static DELETED_BOARD_ITEM * GetInstance()
Definition: board_item.h:475
bool IsZero() const
Definition: eda_angle.h:133
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:77
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
virtual wxString GetFriendlyName() const
Definition: eda_item.cpp:332
Definition: kiid.h:49
std::string AsStdString() const
Definition: kiid.cpp:244
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition: lset.h:60
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:44
const wxString GetName() const
Gets the consolidated name of this netclass (which may be an aggregate)
Definition: netclass.cpp:151
Handle the data for a net.
Definition: netinfo.h:56
Definition: pad.h:54
static TOOL_ACTION zoneFillAll
Definition: pcb_actions.h:408
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:68
static TOOL_ACTION move
move or drag an item
Definition: pcb_actions.h:120
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: pcb_actions.h:76
BOARD * GetBoard() const
The main frame for Pcbnew.
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.cpp:180
virtual void Serialize(google::protobuf::Any &aContainer) const
Serializes this object to the given Any message.
Definition: serializable.h:43
Represent a set of closed polygons.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition: title_block.h:41
const wxString & GetCompany() const
Definition: title_block.h:96
const wxString & GetRevision() const
Definition: title_block.h:86
const wxString & GetDate() const
Definition: title_block.h:76
const wxString & GetComment(int aIdx) const
Definition: title_block.h:107
const wxString & GetTitle() const
Definition: title_block.h:63
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:55
Master controller class:
Definition: tool_manager.h:62
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:150
bool PostAction(const std::string &aActionName, T aParam)
Run the specified action after the current action (coroutine) ends.
Definition: tool_manager.h:235
#define _(s)
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition: eda_item.h:536
PROJECT & Prj()
Definition: kicad.cpp:595
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition: layer_id.cpp:31
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
std::optional< KICAD_T > TypeNameFromAny(const google::protobuf::Any &aMessage)
Definition: api_utils.cpp:27
void PackPolyLine(kiapi::common::types::PolyLine &aOutput, const SHAPE_LINE_CHAIN &aSlc)
Definition: api_utils.cpp:95
void PackBox2(types::Box2 &aOutput, const BOX2I &aInput)
Definition: api_utils.cpp:82
Class to handle a set of BOARD_ITEMs.
BOARD * GetBoard()
std::string ClientName
Definition: api_handler.h:50
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:78
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ TYPE_NOT_INIT
Definition: typeinfo.h:81
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96