KiCad PCB EDA Suite
Loading...
Searching...
No Matches
api_handler_board.cpp
Go to the documentation of this file.
1
2/*
3 * This program source code file is part of KiCad, a free EDA CAD application.
4 *
5 * Copyright (C) 2023 Jon Evans <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <magic_enum.hpp>
23
24#include <common.h>
26#include <api/api_pcb_utils.h>
27#include <api/api_enums.h>
28#include <api/api_utils.h>
29#include <board_commit.h>
31#include <footprint.h>
32#include <kicad_clipboard.h>
33#include <pad.h>
34#include <pcb_base_edit_frame.h>
35#include <pcb_group.h>
36#include <pcb_track.h>
37#include <layer_ids.h>
38#include <project.h>
39#include <tool/tool_manager.h>
40#include <tools/pcb_actions.h>
43
44#include <api/common/types/base_types.pb.h>
45
46using namespace kiapi::common::commands;
47using types::CommandStatus;
48using types::DocumentType;
49using types::ItemRequestStatus;
50
51
52API_HANDLER_BOARD::API_HANDLER_BOARD( std::shared_ptr<BOARD_CONTEXT> aContext,
53 EDA_BASE_FRAME* aFrame ) :
54 API_HANDLER_EDITOR( aFrame ),
55 m_context( std::move( aContext ) )
56{
57 wxCHECK( m_context, /* void */ );
58
60
62
68
81
83
94}
95
96
97std::optional<ApiResponseStatus> API_HANDLER_BOARD::checkForHeadless(
98 const std::string& aCommandName ) const
99{
100 if( m_frame )
101 return std::nullopt;
102
103 ApiResponseStatus e;
104 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
105 e.set_error_message( fmt::format( "{} is not available in headless mode", aCommandName ) );
106 return e;
107}
108
109
114
115
116void API_HANDLER_BOARD::pushCurrentCommit( const std::string& aClientName,
117 const wxString& aMessage )
118{
119 API_HANDLER_EDITOR::pushCurrentCommit( aClientName, aMessage );
120
121 if( m_frame )
122 m_frame->Refresh();
123}
124
125
126std::unique_ptr<COMMIT> API_HANDLER_BOARD::createCommit()
127{
128 if( m_frame )
129 return std::make_unique<BOARD_COMMIT>( static_cast<EDA_DRAW_FRAME*>( m_frame ) );
130
131 return std::make_unique<BOARD_COMMIT>( toolManager(), true, false );
132}
133
134
135std::optional<BOARD_ITEM*> API_HANDLER_BOARD::getItemById( const KIID& aId ) const
136{
137 BOARD_ITEM* item = board()->ResolveItem( aId, true );
138
139 if( !item )
140 return std::nullopt;
141
142 return item;
143}
144
145
147 BOARD_ITEM_CONTAINER* aContainer )
148{
149 if( !aContainer )
150 {
151 ApiResponseStatus e;
152 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
153 e.set_error_message( "Tried to create an item in a null container" );
154 return tl::unexpected( e );
155 }
156
157 if( aType == PCB_PAD_T && !dynamic_cast<FOOTPRINT*>( aContainer ) )
158 {
159 ApiResponseStatus e;
160 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
161 e.set_error_message( fmt::format( "Tried to create a pad in {}, which is not a footprint",
162 aContainer->GetFriendlyName().ToStdString() ) );
163 return tl::unexpected( e );
164 }
165 else if( aType == PCB_FOOTPRINT_T && !dynamic_cast<BOARD*>( aContainer ) )
166 {
167 ApiResponseStatus e;
168 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
169 e.set_error_message( fmt::format( "Tried to create a footprint in {}, which is not a board",
170 aContainer->GetFriendlyName().ToStdString() ) );
171 return tl::unexpected( e );
172 }
173
174 std::unique_ptr<BOARD_ITEM> created = CreateItemForType( aType, aContainer );
175
176 if( !created )
177 {
178 ApiResponseStatus e;
179 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
180 e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled",
181 magic_enum::enum_name( aType ) ) );
182 return tl::unexpected( e );
183 }
184
185 return created;
186}
187
188
189void API_HANDLER_BOARD::deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
190 const std::string& aClientName )
191{
192 BOARD* board = this->board();
193 std::vector<BOARD_ITEM*> validatedItems;
194
195 for( std::pair<const KIID, ItemDeletionStatus> pair : aItemsToDelete )
196 {
197 if( BOARD_ITEM* item = board->ResolveItem( pair.first, true ) )
198 {
199 validatedItems.push_back( item );
200 aItemsToDelete[pair.first] = ItemDeletionStatus::IDS_OK;
201 }
202
203 // Note: we don't currently support locking items from API modification, but here is where
204 // to add it in the future (and return IDS_IMMUTABLE)
205 }
206
207 COMMIT* commit = getCurrentCommit( aClientName );
208
209 for( BOARD_ITEM* item : validatedItems )
210 commit->Remove( item );
211
212 if( !m_activeClients.count( aClientName ) )
213 pushCurrentCommit( aClientName, _( "Deleted items via API" ) );
214}
215
216
218 const DocumentSpecifier& aDocument, const KIID& aId )
219{
220 if( !validateDocument( aDocument ) )
221 return std::nullopt;
222
223 return getItemById( aId );
224}
225
226
228 const std::string& aClientName,
229 const types::ItemHeader &aHeader,
230 const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
231 std::function<void( ItemStatus, google::protobuf::Any )> aItemHandler )
232{
233 ApiResponseStatus e;
234
235 auto containerResult = validateItemHeaderDocument( aHeader );
236
237 if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
238 {
239 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
240 e.set_status( ApiStatusCode::AS_UNHANDLED );
241 return tl::unexpected( e );
242 }
243 else if( !containerResult )
244 {
245 e.CopyFrom( containerResult.error() );
246 return tl::unexpected( e );
247 }
248
249 BOARD* board = this->board();
251
252 if( containerResult->has_value() )
253 {
254 const KIID& containerId = **containerResult;
255 std::optional<BOARD_ITEM*> optItem = getItemById( containerId );
256
257 if( optItem )
258 {
259 container = dynamic_cast<BOARD_ITEM_CONTAINER*>( *optItem );
260
261 if( !container )
262 {
263 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
264 e.set_error_message( fmt::format(
265 "The requested container {} is not a valid board item container",
266 containerId.AsStdString() ) );
267 return tl::unexpected( e );
268 }
269 }
270 else
271 {
272 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
273 e.set_error_message( fmt::format(
274 "The requested container {} does not exist in this document",
275 containerId.AsStdString() ) );
276 return tl::unexpected( e );
277 }
278 }
279
280 BOARD_COMMIT* commit = static_cast<BOARD_COMMIT*>( getCurrentCommit( aClientName ) );
281
282 for( const google::protobuf::Any& anyItem : aItems )
283 {
284 ItemStatus status;
285 std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
286
287 if( !type )
288 {
289 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
290 status.set_error_message( fmt::format( "Could not decode a valid type from {}",
291 anyItem.type_url() ) );
292 aItemHandler( status, anyItem );
293 continue;
294 }
295
296 if( type == PCB_DIMENSION_T )
297 {
298 board::types::Dimension dimension;
299 anyItem.UnpackTo( &dimension );
300
301 switch( dimension.dimension_style_case() )
302 {
303 case board::types::Dimension::kAligned: type = PCB_DIM_ALIGNED_T; break;
304 case board::types::Dimension::kOrthogonal: type = PCB_DIM_ORTHOGONAL_T; break;
305 case board::types::Dimension::kRadial: type = PCB_DIM_RADIAL_T; break;
306 case board::types::Dimension::kLeader: type = PCB_DIM_LEADER_T; break;
307 case board::types::Dimension::kCenter: type = PCB_DIM_CENTER_T; break;
308 case board::types::Dimension::DIMENSION_STYLE_NOT_SET: break;
309 }
310 }
311
313 createItemForType( *type, container );
314
315 if( !creationResult )
316 {
317 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
318 status.set_error_message( creationResult.error().error_message() );
319 aItemHandler( status, anyItem );
320 continue;
321 }
322
323 std::unique_ptr<BOARD_ITEM> item( std::move( *creationResult ) );
324
325 if( !item->Deserialize( anyItem ) )
326 {
327 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
328 e.set_error_message( fmt::format( "could not unpack {} from request",
329 item->GetClass().ToStdString() ) );
330 return tl::unexpected( e );
331 }
332
333 std::optional<BOARD_ITEM*> optItem = getItemById( item->m_Uuid );
334
335 if( aCreate && optItem )
336 {
337 status.set_code( ItemStatusCode::ISC_EXISTING );
338 status.set_error_message( fmt::format( "an item with UUID {} already exists",
339 item->m_Uuid.AsStdString() ) );
340 aItemHandler( status, anyItem );
341 continue;
342 }
343 else if( !aCreate && !optItem )
344 {
345 status.set_code( ItemStatusCode::ISC_NONEXISTENT );
346 status.set_error_message( fmt::format( "an item with UUID {} does not exist",
347 item->m_Uuid.AsStdString() ) );
348 aItemHandler( status, anyItem );
349 continue;
350 }
351
352 if( aCreate
353 && !item->FitsEnabledLayers( board->GetEnabledLayers(), board->GetCopperLayerCount() ) )
354 {
355 status.set_code( ItemStatusCode::ISC_INVALID_DATA );
356 status.set_error_message(
357 "attempted to add item with no overlapping layers with the board" );
358 aItemHandler( status, anyItem );
359 continue;
360 }
361
362 status.set_code( ItemStatusCode::ISC_OK );
363 google::protobuf::Any newItem;
364
365 if( aCreate )
366 {
367 if( item->Type() == PCB_FOOTPRINT_T )
368 {
369 // Ensure children have unique identifiers; in case the API client created this new
370 // footprint by cloning an existing one and only changing the parent UUID.
371 item->RunOnChildren(
372 []( BOARD_ITEM* aChild )
373 {
374 aChild->ResetUuid();
375 },
376 RECURSE );
377 }
378
379 item->Serialize( newItem );
380 commit->Add( item.release() );
381 }
382 else
383 {
384 BOARD_ITEM* boardItem = *optItem;
385
386 // Footprints can't be modified by CopyFrom at the moment because the commit system
387 // doesn't currently know what to do with a footprint that has had its children
388 // replaced with other children; which results in things like the view not having its
389 // cached geometry for footprint children updated when you move a footprint around.
390 // And also, groups are special because they can contain any item type, so we
391 // can't use CopyFrom on them either.
392 if( boardItem->Type() == PCB_FOOTPRINT_T || boardItem->Type() == PCB_GROUP_T )
393 {
394 // Save group membership before removal, since Remove() severs the relationship
395 PCB_GROUP* parentGroup = dynamic_cast<PCB_GROUP*>( boardItem->GetParentGroup() );
396
397 commit->Remove( boardItem );
398 item->Serialize( newItem );
399
400 BOARD_ITEM* newBoardItem = item.release();
401 commit->Add( newBoardItem );
402
403 // Restore group membership for the newly added item
404 if( parentGroup )
405 parentGroup->AddItem( newBoardItem );
406 }
407 else
408 {
409 commit->Modify( boardItem );
410 boardItem->CopyFrom( item.get() );
411 boardItem->Serialize( newItem );
412 }
413 }
414
415 aItemHandler( status, newItem );
416 }
417
418 if( !m_activeClients.count( aClientName ) )
419 {
420 pushCurrentCommit( aClientName, aCreate ? _( "Created items via API" )
421 : _( "Modified items via API" ) );
422 }
423
424
425 return ItemRequestStatus::IRS_OK;
426}
427
428
430 const google::protobuf::RepeatedField<int>& aTypes )
431{
432 std::vector<KICAD_T> types;
433
434 for( int typeRaw : aTypes )
435 {
436 auto typeMessage = static_cast<common::types::KiCadObjectType>( typeRaw );
437 KICAD_T type = FromProtoEnum<KICAD_T>( typeMessage );
438
439 if( type != TYPE_NOT_INIT )
440 types.emplace_back( type );
441 }
442
443 return types;
444}
445
446
448 const HANDLER_CONTEXT<RunAction>& aCtx )
449{
450 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RunAction" ) )
451 return tl::unexpected( *headless );
452
453 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
454 return tl::unexpected( *busy );
455
456 RunActionResponse response;
457
458 if( toolManager()->RunAction( aCtx.Request.action(), true ) )
459 response.set_status( RunActionStatus::RAS_OK );
460 else
461 response.set_status( RunActionStatus::RAS_INVALID );
462
463 return response;
464}
465
466
469{
470 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
471 return tl::unexpected( *busy );
472
473 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
474 {
475 ApiResponseStatus e;
476 e.set_status( ApiStatusCode::AS_UNHANDLED );
477 return tl::unexpected( e );
478 }
479
480 GetItemsResponse response;
481
482 std::vector<BOARD_ITEM*> items;
483
484 for( const kiapi::common::types::KIID& id : aCtx.Request.items() )
485 {
486 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
487 items.emplace_back( *item );
488 }
489
490 if( items.empty() )
491 {
492 ApiResponseStatus e;
493 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
494 e.set_error_message( "none of the requested IDs were found or valid" );
495 return tl::unexpected( e );
496 }
497
498 for( const BOARD_ITEM* item : items )
499 {
500 google::protobuf::Any itemBuf;
501 item->Serialize( itemBuf );
502 response.mutable_items()->Add( std::move( itemBuf ) );
503 }
504
505 response.set_status( ItemRequestStatus::IRS_OK );
506 return response;
507}
508
509
512{
513 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetSelection" ) )
514 return tl::unexpected( *headless );
515
516 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
517 {
518 ApiResponseStatus e;
519 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
520 e.set_status( ApiStatusCode::AS_UNHANDLED );
521 return tl::unexpected( e );
522 }
523
524 std::set<KICAD_T> filter;
525
526 for( KICAD_T type : parseRequestedItemTypes( aCtx.Request.types() ) )
527 filter.insert( type );
528
529 TOOL_MANAGER* mgr = toolManager();
530 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
531
532 SelectionResponse response;
533
534 for( EDA_ITEM* item : selectionTool->GetSelection() )
535 {
536 if( filter.empty() || filter.contains( item->Type() ) )
537 item->Serialize( *response.add_items() );
538 }
539
540 return response;
541}
542
543
546{
547 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "ClearSelection" ) )
548 return tl::unexpected( *headless );
549
550 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
551 return tl::unexpected( *busy );
552
553 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
554 {
555 ApiResponseStatus e;
556 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
557 e.set_status( ApiStatusCode::AS_UNHANDLED );
558 return tl::unexpected( e );
559 }
560
561 TOOL_MANAGER* mgr = toolManager();
563 m_frame->Refresh();
564
565 return Empty();
566}
567
568
571{
572 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "AddToSelection" ) )
573 return tl::unexpected( *headless );
574
575 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
576 return tl::unexpected( *busy );
577
578 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
579 {
580 ApiResponseStatus e;
581 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
582 e.set_status( ApiStatusCode::AS_UNHANDLED );
583 return tl::unexpected( e );
584 }
585
586 TOOL_MANAGER* mgr = toolManager();
587 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
588
589 std::vector<EDA_ITEM*> toAdd;
590
591 for( const types::KIID& id : aCtx.Request.items() )
592 {
593 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
594 toAdd.emplace_back( *item );
595 }
596
597 selectionTool->AddItemsToSel( &toAdd );
598 m_frame->Refresh();
599
600 SelectionResponse response;
601
602 for( EDA_ITEM* item : selectionTool->GetSelection() )
603 item->Serialize( *response.add_items() );
604
605 return response;
606}
607
608
611{
612 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RemoveFromSelection" ) )
613 return tl::unexpected( *headless );
614
615 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
616 return tl::unexpected( *busy );
617
618 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
619 {
620 ApiResponseStatus e;
621 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
622 e.set_status( ApiStatusCode::AS_UNHANDLED );
623 return tl::unexpected( e );
624 }
625
626 TOOL_MANAGER* mgr = toolManager();
627 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
628
629 std::vector<EDA_ITEM*> toRemove;
630
631 for( const types::KIID& id : aCtx.Request.items() )
632 {
633 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
634 toRemove.emplace_back( *item );
635 }
636
637 selectionTool->RemoveItemsFromSel( &toRemove );
638 m_frame->Refresh();
639
640 SelectionResponse response;
641
642 for( EDA_ITEM* item : selectionTool->GetSelection() )
643 item->Serialize( *response.add_items() );
644
645 return response;
646}
647
648
651{
652 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
653
654 if( !documentValidation )
655 return tl::unexpected( documentValidation.error() );
656
657 BoardStackupResponse response;
658 google::protobuf::Any any;
659
661
662 any.UnpackTo( response.mutable_stackup() );
663
664 // User-settable layer names are not stored in BOARD_STACKUP at the moment
665 for( board::BoardStackupLayer& layer : *response.mutable_stackup()->mutable_layers() )
666 {
667 if( layer.type() == board::BoardStackupLayerType::BSLT_DIELECTRIC )
668 continue;
669
670 PCB_LAYER_ID id = FromProtoEnum<PCB_LAYER_ID>( layer.layer() );
671 wxCHECK2( id != UNDEFINED_LAYER, continue );
672
673 layer.set_user_name( board()->GetLayerName( id ) );
674 }
675
676 return response;
677}
678
679
682{
683 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
684
685 if( !documentValidation )
686 return tl::unexpected( documentValidation.error() );
687
688 BoardEnabledLayersResponse response;
689
690 BOARD* board = this->board();
691 int copperLayerCount = board->GetCopperLayerCount();
692
693 response.set_copper_layer_count( copperLayerCount );
694
695 LSET enabled = board->GetEnabledLayers();
696
697 // The Rescue layer is an internal detail and should be hidden from the API
698 enabled.reset( Rescue );
699
700 // Just in case this is out of sync; the API should always return the expected copper layers
701 enabled |= LSET::AllCuMask( copperLayerCount );
702
703 board::PackLayerSet( *response.mutable_layers(), enabled );
704
705 return response;
706}
707
708
711{
712 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
713
714 if( !documentValidation )
715 return tl::unexpected( documentValidation.error() );
716
718 GraphicsDefaultsResponse response;
719
720 // TODO: This should change to be an enum class
721 constexpr std::array<kiapi::board::BoardLayerClass, LAYER_CLASS_COUNT> classOrder = {
722 kiapi::board::BLC_SILKSCREEN,
723 kiapi::board::BLC_COPPER,
724 kiapi::board::BLC_EDGES,
725 kiapi::board::BLC_COURTYARD,
726 kiapi::board::BLC_FABRICATION,
727 kiapi::board::BLC_OTHER
728 };
729
730 for( int i = 0; i < LAYER_CLASS_COUNT; ++i )
731 {
732 kiapi::board::BoardLayerGraphicsDefaults* l = response.mutable_defaults()->add_layers();
733
734 l->set_layer( classOrder[i] );
735 l->mutable_line_thickness()->set_value_nm( bds.m_LineThickness[i] );
736
737 kiapi::common::types::TextAttributes* text = l->mutable_text();
738 text->mutable_size()->set_x_nm( bds.m_TextSize[i].x );
739 text->mutable_size()->set_y_nm( bds.m_TextSize[i].y );
740 text->mutable_stroke_width()->set_value_nm( bds.m_TextThickness[i] );
741 text->set_italic( bds.m_TextItalic[i] );
742 text->set_keep_upright( bds.m_TextUpright[i] );
743 }
744
745 return response;
746}
747
748
751{
752 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
753 return tl::unexpected( *busy );
754
755 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
756 {
757 ApiResponseStatus e;
758 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
759 e.set_status( ApiStatusCode::AS_UNHANDLED );
760 return tl::unexpected( e );
761 }
762
763 GetBoundingBoxResponse response;
764 bool includeText = aCtx.Request.mode() == BoundingBoxMode::BBM_ITEM_AND_CHILD_TEXT;
765
766 for( const types::KIID& idMsg : aCtx.Request.items() )
767 {
768 KIID id( idMsg.value() );
769 std::optional<BOARD_ITEM*> optItem = getItemById( id );
770
771 if( !optItem )
772 continue;
773
774 BOARD_ITEM* item = *optItem;
775 BOX2I bbox;
776
777 if( item->Type() == PCB_FOOTPRINT_T )
778 bbox = static_cast<FOOTPRINT*>( item )->GetBoundingBox( includeText );
779 else
780 bbox = item->GetBoundingBox();
781
782 response.add_items()->set_value( idMsg.value() );
783 PackBox2( *response.add_boxes(), bbox );
784 }
785
786 return response;
787}
788
789
792{
793 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
794 !documentValidation )
795 {
796 return tl::unexpected( documentValidation.error() );
797 }
798
799 PadShapeAsPolygonResponse response;
801
802 for( const types::KIID& padRequest : aCtx.Request.pads() )
803 {
804 KIID id( padRequest.value() );
805 std::optional<BOARD_ITEM*> optPad = getItemById( id );
806
807 if( !optPad || ( *optPad )->Type() != PCB_PAD_T )
808 continue;
809
810 response.add_pads()->set_value( padRequest.value() );
811
812 PAD* pad = static_cast<PAD*>( *optPad );
813 SHAPE_POLY_SET poly;
814 pad->TransformShapeToPolygon( poly, pad->Padstack().EffectiveLayerFor( layer ), 0,
815 pad->GetMaxError(), ERROR_INSIDE );
816
817 types::PolygonWithHoles* polyMsg = response.mutable_polygons()->Add();
818 PackPolyLine( *polyMsg->mutable_outline(), poly.COutline( 0 ) );
819 }
820
821 return response;
822}
823
824
827{
828 using board::types::BoardLayer;
829
830 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
831 !documentValidation )
832 {
833 return tl::unexpected( documentValidation.error() );
834 }
835
836 PadstackPresenceResponse response;
837
838 LSET layers;
839
840 for( const int layer : aCtx.Request.layers() )
841 layers.set( FromProtoEnum<PCB_LAYER_ID, BoardLayer>( static_cast<BoardLayer>( layer ) ) );
842
843 for( const types::KIID& padRequest : aCtx.Request.items() )
844 {
845 KIID id( padRequest.value() );
846 std::optional<BOARD_ITEM*> optItem = getItemById( id );
847
848 if( !optItem )
849 continue;
850
851 switch( ( *optItem )->Type() )
852 {
853 case PCB_PAD_T:
854 {
855 PAD* pad = static_cast<PAD*>( *optItem );
856
857 for( PCB_LAYER_ID layer : layers )
858 {
859 PadstackPresenceEntry* entry = response.add_entries();
860 entry->mutable_item()->set_value( pad->m_Uuid.AsStdString() );
861 entry->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( layer ) );
862 entry->set_presence( pad->FlashLayer( layer ) ? PSP_PRESENT : PSP_NOT_PRESENT );
863 }
864
865 break;
866 }
867
868 case PCB_VIA_T:
869 {
870 PCB_VIA* via = static_cast<PCB_VIA*>( *optItem );
871
872 for( PCB_LAYER_ID layer : layers )
873 {
874 PadstackPresenceEntry* entry = response.add_entries();
875 entry->mutable_item()->set_value( via->m_Uuid.AsStdString() );
876 entry->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( layer ) );
877 entry->set_presence( via->FlashLayer( layer ) ? PSP_PRESENT : PSP_NOT_PRESENT );
878 }
879
880 break;
881 }
882
883 default:
884 break;
885 }
886 }
887
888 return response;
889}
890
891
894{
895 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
896
897 if( !documentValidation )
898 return tl::unexpected( documentValidation.error() );
899
900 ExpandTextVariablesResponse reply;
901 BOARD* board = this->board();
902
903 std::function<bool( wxString* )> textResolver =
904 [&]( wxString* token ) -> bool
905 {
906 // Handles m_board->GetTitleBlock() *and* m_board->GetProject()
907 return board->ResolveTextVar( token, 0 );
908 };
909
910 for( const std::string& textMsg : aCtx.Request.text() )
911 {
912 wxString text = ExpandTextVars( wxString::FromUTF8( textMsg ), &textResolver );
913 reply.add_text( text.ToUTF8() );
914 }
915
916 return reply;
917}
918
919
922{
923 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "InteractiveMoveItems" ) )
924 return tl::unexpected( *headless );
925
926 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
927 return tl::unexpected( *busy );
928
929 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
930
931 if( !documentValidation )
932 return tl::unexpected( documentValidation.error() );
933
934 TOOL_MANAGER* mgr = toolManager();
935 std::vector<EDA_ITEM*> toSelect;
936
937 for( const kiapi::common::types::KIID& id : aCtx.Request.items() )
938 {
939 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
940 toSelect.emplace_back( static_cast<EDA_ITEM*>( *item ) );
941 }
942
943 if( toSelect.empty() )
944 {
945 ApiResponseStatus e;
946 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
947 e.set_error_message( fmt::format( "None of the given items exist on the board",
948 aCtx.Request.board().board_filename() ) );
949 return tl::unexpected( e );
950 }
951
952 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
953 selectionTool->GetSelection().SetReferencePoint( toSelect[0]->GetPosition() );
954
956 mgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &toSelect );
957
958 COMMIT* commit = getCurrentCommit( aCtx.ClientName );
959 mgr->PostAPIAction( PCB_ACTIONS::move, commit );
960
961 return Empty();
962}
963
964
967{
968 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
969
970 if( !documentValidation )
971 return tl::unexpected( documentValidation.error() );
972
973 SavedDocumentResponse response;
974 response.mutable_document()->CopyFrom( aCtx.Request.document() );
975
976 CLIPBOARD_IO io;
977 io.SetWriter(
978 [&]( const wxString& aData )
979 {
980 response.set_contents( aData.ToUTF8() );
981 } );
982
983 io.SaveBoard( wxEmptyString, board(), nullptr );
984
985 return response;
986}
987
988
991{
992 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SaveSelectionToString" ) )
993 return tl::unexpected( *headless );
994
995 SavedSelectionResponse response;
996
997 TOOL_MANAGER* mgr = toolManager();
998 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
999 PCB_SELECTION& selection = selectionTool->GetSelection();
1000
1001 CLIPBOARD_IO io;
1002 io.SetWriter(
1003 [&]( const wxString& aData )
1004 {
1005 response.set_contents( aData.ToUTF8() );
1006 } );
1007
1008 io.SetBoard( board() );
1009 io.SaveSelection( selection, false );
1010
1011 return response;
1012}
1013
1014
1017{
1018 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1019 return tl::unexpected( *busy );
1020
1021 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
1022
1023 if( !documentValidation )
1024 return tl::unexpected( documentValidation.error() );
1025
1026 CreateItemsResponse response;
1027 return response;
1028}
1029
1030
1033{
1034 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetVisibleLayers" ) )
1035 return tl::unexpected( *headless );
1036
1037 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1038
1039 if( !documentValidation )
1040 return tl::unexpected( documentValidation.error() );
1041
1042 BoardLayers response;
1043
1044 for( PCB_LAYER_ID layer : board()->GetVisibleLayers() )
1045 response.add_layers( ToProtoEnum<PCB_LAYER_ID, board::types::BoardLayer>( layer ) );
1046
1047 return response;
1048}
1049
1050
1053{
1054 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetVisibleLayers" ) )
1055 return tl::unexpected( *headless );
1056
1057 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1058 return tl::unexpected( *busy );
1059
1060 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1061
1062 if( !documentValidation )
1063 return tl::unexpected( documentValidation.error() );
1064
1065 LSET visible;
1066 LSET enabled = board()->GetEnabledLayers();
1067
1068 for( int layerIdx : aCtx.Request.layers() )
1069 {
1070 PCB_LAYER_ID layer =
1071 FromProtoEnum<PCB_LAYER_ID>( static_cast<board::types::BoardLayer>( layerIdx ) );
1072
1073 if( enabled.Contains( layer ) )
1074 visible.set( layer );
1075 }
1076
1077 board()->SetVisibleLayers( visible );
1078
1079 PCB_BASE_EDIT_FRAME* editFrame = static_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
1080 editFrame->GetAppearancePanel()->OnBoardChanged();
1081 editFrame->GetCanvas()->SyncLayersVisibility( board() );
1082 editFrame->Refresh();
1083 return Empty();
1084}
1085
1086
1089{
1090 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetActiveLayer" ) )
1091 return tl::unexpected( *headless );
1092
1093 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1094
1095 if( !documentValidation )
1096 return tl::unexpected( documentValidation.error() );
1097
1098 PCB_BASE_EDIT_FRAME* editFrame = static_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
1099
1100 BoardLayerResponse response;
1101 response.set_layer(
1103
1104 return response;
1105}
1106
1107
1110{
1111 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetActiveLayer" ) )
1112 return tl::unexpected( *headless );
1113
1114 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1115 return tl::unexpected( *busy );
1116
1117 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1118
1119 if( !documentValidation )
1120 return tl::unexpected( documentValidation.error() );
1121
1122 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID>( aCtx.Request.layer() );
1123
1124 if( !board()->GetEnabledLayers().Contains( layer ) )
1125 {
1126 ApiResponseStatus err;
1127 err.set_status( ApiStatusCode::AS_BAD_REQUEST );
1128 err.set_error_message( fmt::format( "Layer {} is not a valid layer for the given board",
1129 magic_enum::enum_name( layer ) ) );
1130 return tl::unexpected( err );
1131 }
1132
1133 PCB_BASE_EDIT_FRAME* editFrame = static_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
1134 editFrame->SetActiveLayer( layer );
1135 return Empty();
1136}
types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:47
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
@ LAYER_CLASS_COUNT
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:228
API_HANDLER_BOARD(std::shared_ptr< BOARD_CONTEXT > aContext, EDA_BASE_FRAME *aFrame=nullptr)
HANDLER_RESULT< commands::ExpandTextVariablesResponse > handleExpandTextVariables(const HANDLER_CONTEXT< commands::ExpandTextVariables > &aCtx)
HANDLER_RESULT< BoardEnabledLayersResponse > handleGetBoardEnabledLayers(const HANDLER_CONTEXT< GetBoardEnabledLayers > &aCtx)
std::unique_ptr< COMMIT > createCommit() override
Override this to create an appropriate COMMIT subclass for the frame in question.
HANDLER_RESULT< commands::SelectionResponse > handleGetSelection(const HANDLER_CONTEXT< commands::GetSelection > &aCtx)
HANDLER_RESULT< commands::SavedSelectionResponse > handleSaveSelectionToString(const HANDLER_CONTEXT< commands::SaveSelectionToString > &aCtx)
HANDLER_RESULT< Empty > handleSetActiveLayer(const HANDLER_CONTEXT< SetActiveLayer > &aCtx)
HANDLER_RESULT< BoardLayers > handleGetVisibleLayers(const HANDLER_CONTEXT< GetVisibleLayers > &aCtx)
TOOL_MANAGER * toolManager() const
HANDLER_RESULT< commands::SelectionResponse > handleAddToSelection(const HANDLER_CONTEXT< commands::AddToSelection > &aCtx)
HANDLER_RESULT< commands::CreateItemsResponse > handleParseAndCreateItemsFromString(const HANDLER_CONTEXT< commands::ParseAndCreateItemsFromString > &aCtx)
HANDLER_RESULT< Empty > handleClearSelection(const HANDLER_CONTEXT< commands::ClearSelection > &aCtx)
std::vector< KICAD_T > parseRequestedItemTypes(const google::protobuf::RepeatedField< int > &aTypes)
HANDLER_RESULT< Empty > handleInteractiveMoveItems(const HANDLER_CONTEXT< InteractiveMoveItems > &aCtx)
HANDLER_RESULT< types::ItemRequestStatus > handleCreateUpdateItemsInternal(bool aCreate, const std::string &aClientName, const types::ItemHeader &aHeader, const google::protobuf::RepeatedPtrField< google::protobuf::Any > &aItems, std::function< void(commands::ItemStatus, google::protobuf::Any)> aItemHandler) override
HANDLER_RESULT< BoardStackupResponse > handleGetStackup(const HANDLER_CONTEXT< GetBoardStackup > &aCtx)
void pushCurrentCommit(const std::string &aClientName, const wxString &aMessage) override
HANDLER_RESULT< commands::GetItemsResponse > handleGetItemsById(const HANDLER_CONTEXT< commands::GetItemsById > &aCtx)
BOARD * board() const
std::optional< EDA_ITEM * > getItemFromDocument(const DocumentSpecifier &aDocument, const KIID &aId) override
void deleteItemsInternal(std::map< KIID, ItemDeletionStatus > &aItemsToDelete, const std::string &aClientName) override
HANDLER_RESULT< commands::SelectionResponse > handleRemoveFromSelection(const HANDLER_CONTEXT< commands::RemoveFromSelection > &aCtx)
HANDLER_RESULT< GraphicsDefaultsResponse > handleGetGraphicsDefaults(const HANDLER_CONTEXT< GetGraphicsDefaults > &aCtx)
HANDLER_RESULT< BoardLayerResponse > handleGetActiveLayer(const HANDLER_CONTEXT< GetActiveLayer > &aCtx)
HANDLER_RESULT< commands::GetBoundingBoxResponse > handleGetBoundingBox(const HANDLER_CONTEXT< commands::GetBoundingBox > &aCtx)
static HANDLER_RESULT< std::unique_ptr< BOARD_ITEM > > createItemForType(KICAD_T aType, BOARD_ITEM_CONTAINER *aContainer)
HANDLER_RESULT< commands::RunActionResponse > handleRunAction(const HANDLER_CONTEXT< commands::RunAction > &aCtx)
std::optional< ApiResponseStatus > checkForHeadless(const std::string &aCommandName) const
HANDLER_RESULT< commands::SavedDocumentResponse > handleSaveDocumentToString(const HANDLER_CONTEXT< commands::SaveDocumentToString > &aCtx)
HANDLER_RESULT< PadShapeAsPolygonResponse > handleGetPadShapeAsPolygon(const HANDLER_CONTEXT< GetPadShapeAsPolygon > &aCtx)
std::shared_ptr< BOARD_CONTEXT > m_context
HANDLER_RESULT< PadstackPresenceResponse > handleCheckPadstackPresenceOnLayers(const HANDLER_CONTEXT< CheckPadstackPresenceOnLayers > &aCtx)
virtual BOARD_ITEM_CONTAINER * getDefaultContainer()
HANDLER_RESULT< Empty > handleSetVisibleLayers(const HANDLER_CONTEXT< SetVisibleLayers > &aCtx)
std::optional< BOARD_ITEM * > getItemById(const KIID &aId) const
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.
API_HANDLER_EDITOR(EDA_BASE_FRAME *aFrame=nullptr)
COMMIT * getCurrentCommit(const std::string &aClientName)
virtual void pushCurrentCommit(const std::string &aClientName, const wxString &aMessage)
std::set< std::string > m_activeClients
virtual std::optional< ApiResponseStatus > checkForBusy()
Checks if the editor can accept commands.
EDA_BASE_FRAME * m_frame
void registerHandler(HANDLER_RESULT< ResponseType >(HandlerType::*aHandler)(const HANDLER_CONTEXT< RequestType > &))
Registers an API command handler for the given message types.
Definition api_handler.h:93
BASE_SET & reset(size_t pos)
Definition base_set.h:143
BASE_SET & set(size_t pos)
Definition base_set.h:116
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:83
virtual void CopyFrom(const BOARD_ITEM *aOther)
void ResetUuid()
Definition board_item.h:249
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:373
BOARD_STACKUP GetStackupOrDefault() const
Definition board.cpp:3389
void SetVisibleLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings changes the bit-mask of vis...
Definition board.cpp:1075
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1158
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1043
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:1928
void SaveSelection(const PCB_SELECTION &selected, bool isFootprintEditor)
void SetWriter(std::function< void(const wxString &)> aWriter)
void SaveBoard(const wxString &aFileName, BOARD *aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PCB_IO implementation knows about or it can be u...
void SetBoard(BOARD *aBoard)
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition commit.h:68
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:86
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
The base frame for deriving all KiCad main window classes.
The base class for create windows for drawing purpose.
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:58
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:135
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:114
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
virtual wxString GetFriendlyName() const
Definition eda_item.cpp:426
Definition kiid.h:46
std::string AsStdString() const
Definition kiid.cpp:270
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
Definition pad.h:61
static TOOL_ACTION move
move or drag an item
Common, abstract interface for edit frames.
APPEARANCE_CONTROLS * GetAppearancePanel()
virtual void SetActiveLayer(PCB_LAYER_ID aLayer)
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
virtual PCB_LAYER_ID GetActiveLayer() const
void SyncLayersVisibility(const BOARD *aBoard)
Update "visibility" property of each layer of a given BOARD.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
int AddItemsToSel(const TOOL_EVENT &aEvent)
int RemoveItemsFromSel(const TOOL_EVENT &aEvent)
void SetReferencePoint(const VECTOR2I &aP)
virtual void Serialize(google::protobuf::Any &aContainer) const
Serializes this object to the given Any message.
Represent a set of closed polygons.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Master controller class:
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
bool PostAPIAction(const TOOL_ACTION &aAction, COMMIT *aCommit)
A type-safe container of any type.
Definition ki_any.h:92
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:59
The common library.
#define _(s)
@ RECURSE
Definition eda_item.h:49
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ Rescue
Definition layer_ids.h:117
void PackLayerSet(google::protobuf::RepeatedField< int > &aOutput, const LSET &aLayerSet)
KICOMMON_API std::optional< KICAD_T > TypeNameFromAny(const google::protobuf::Any &aMessage)
Definition api_utils.cpp:35
KICOMMON_API void PackBox2(types::Box2 &aOutput, const BOX2I &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackPolyLine(types::PolyLine &aOutput, const SHAPE_LINE_CHAIN &aSlc, const EDA_IU_SCALE &aScale)
STL namespace.
Class to handle a set of BOARD_ITEMs.
std::vector< EDA_ITEM * > EDA_ITEMS
std::string ClientName
Definition api_handler.h:51
RequestMessageType Request
Definition api_handler.h:52
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:71
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:99
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:96
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ TYPE_NOT_INIT
Definition typeinfo.h:74
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:104
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:95
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:93
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:98