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 along
19 * with this program. If not, see <http://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 && !( board->GetEnabledLayers() & item->GetLayerSet() ).any() )
353 {
354 status.set_code( ItemStatusCode::ISC_INVALID_DATA );
355 status.set_error_message(
356 "attempted to add item with no overlapping layers with the board" );
357 aItemHandler( status, anyItem );
358 continue;
359 }
360
361 status.set_code( ItemStatusCode::ISC_OK );
362 google::protobuf::Any newItem;
363
364 if( aCreate )
365 {
366 if( item->Type() == PCB_FOOTPRINT_T )
367 {
368 // Ensure children have unique identifiers; in case the API client created this new
369 // footprint by cloning an existing one and only changing the parent UUID.
370 item->RunOnChildren(
371 []( BOARD_ITEM* aChild )
372 {
373 aChild->ResetUuid();
374 },
375 RECURSE );
376 }
377
378 item->Serialize( newItem );
379 commit->Add( item.release() );
380 }
381 else
382 {
383 BOARD_ITEM* boardItem = *optItem;
384
385 // Footprints can't be modified by CopyFrom at the moment because the commit system
386 // doesn't currently know what to do with a footprint that has had its children
387 // replaced with other children; which results in things like the view not having its
388 // cached geometry for footprint children updated when you move a footprint around.
389 // And also, groups are special because they can contain any item type, so we
390 // can't use CopyFrom on them either.
391 if( boardItem->Type() == PCB_FOOTPRINT_T || boardItem->Type() == PCB_GROUP_T )
392 {
393 // Save group membership before removal, since Remove() severs the relationship
394 PCB_GROUP* parentGroup = dynamic_cast<PCB_GROUP*>( boardItem->GetParentGroup() );
395
396 commit->Remove( boardItem );
397 item->Serialize( newItem );
398
399 BOARD_ITEM* newBoardItem = item.release();
400 commit->Add( newBoardItem );
401
402 // Restore group membership for the newly added item
403 if( parentGroup )
404 parentGroup->AddItem( newBoardItem );
405 }
406 else
407 {
408 commit->Modify( boardItem );
409 boardItem->CopyFrom( item.get() );
410 boardItem->Serialize( newItem );
411 }
412 }
413
414 aItemHandler( status, newItem );
415 }
416
417 if( !m_activeClients.count( aClientName ) )
418 {
419 pushCurrentCommit( aClientName, aCreate ? _( "Created items via API" )
420 : _( "Modified items via API" ) );
421 }
422
423
424 return ItemRequestStatus::IRS_OK;
425}
426
427
429 const google::protobuf::RepeatedField<int>& aTypes )
430{
431 std::vector<KICAD_T> types;
432
433 for( int typeRaw : aTypes )
434 {
435 auto typeMessage = static_cast<common::types::KiCadObjectType>( typeRaw );
436 KICAD_T type = FromProtoEnum<KICAD_T>( typeMessage );
437
438 if( type != TYPE_NOT_INIT )
439 types.emplace_back( type );
440 }
441
442 return types;
443}
444
445
447 const HANDLER_CONTEXT<RunAction>& aCtx )
448{
449 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RunAction" ) )
450 return tl::unexpected( *headless );
451
452 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
453 return tl::unexpected( *busy );
454
455 RunActionResponse response;
456
457 if( toolManager()->RunAction( aCtx.Request.action(), true ) )
458 response.set_status( RunActionStatus::RAS_OK );
459 else
460 response.set_status( RunActionStatus::RAS_INVALID );
461
462 return response;
463}
464
465
468{
469 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
470 return tl::unexpected( *busy );
471
472 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
473 {
474 ApiResponseStatus e;
475 e.set_status( ApiStatusCode::AS_UNHANDLED );
476 return tl::unexpected( e );
477 }
478
479 GetItemsResponse response;
480
481 std::vector<BOARD_ITEM*> items;
482
483 for( const kiapi::common::types::KIID& id : aCtx.Request.items() )
484 {
485 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
486 items.emplace_back( *item );
487 }
488
489 if( items.empty() )
490 {
491 ApiResponseStatus e;
492 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
493 e.set_error_message( "none of the requested IDs were found or valid" );
494 return tl::unexpected( e );
495 }
496
497 for( const BOARD_ITEM* item : items )
498 {
499 google::protobuf::Any itemBuf;
500 item->Serialize( itemBuf );
501 response.mutable_items()->Add( std::move( itemBuf ) );
502 }
503
504 response.set_status( ItemRequestStatus::IRS_OK );
505 return response;
506}
507
508
511{
512 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetSelection" ) )
513 return tl::unexpected( *headless );
514
515 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
516 {
517 ApiResponseStatus e;
518 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
519 e.set_status( ApiStatusCode::AS_UNHANDLED );
520 return tl::unexpected( e );
521 }
522
523 std::set<KICAD_T> filter;
524
525 for( KICAD_T type : parseRequestedItemTypes( aCtx.Request.types() ) )
526 filter.insert( type );
527
528 TOOL_MANAGER* mgr = toolManager();
529 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
530
531 SelectionResponse response;
532
533 for( EDA_ITEM* item : selectionTool->GetSelection() )
534 {
535 if( filter.empty() || filter.contains( item->Type() ) )
536 item->Serialize( *response.add_items() );
537 }
538
539 return response;
540}
541
542
545{
546 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "ClearSelection" ) )
547 return tl::unexpected( *headless );
548
549 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
550 return tl::unexpected( *busy );
551
552 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
553 {
554 ApiResponseStatus e;
555 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
556 e.set_status( ApiStatusCode::AS_UNHANDLED );
557 return tl::unexpected( e );
558 }
559
560 TOOL_MANAGER* mgr = toolManager();
562 m_frame->Refresh();
563
564 return Empty();
565}
566
567
570{
571 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "AddToSelection" ) )
572 return tl::unexpected( *headless );
573
574 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
575 return tl::unexpected( *busy );
576
577 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
578 {
579 ApiResponseStatus e;
580 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
581 e.set_status( ApiStatusCode::AS_UNHANDLED );
582 return tl::unexpected( e );
583 }
584
585 TOOL_MANAGER* mgr = toolManager();
586 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
587
588 std::vector<EDA_ITEM*> toAdd;
589
590 for( const types::KIID& id : aCtx.Request.items() )
591 {
592 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
593 toAdd.emplace_back( *item );
594 }
595
596 selectionTool->AddItemsToSel( &toAdd );
597 m_frame->Refresh();
598
599 SelectionResponse response;
600
601 for( EDA_ITEM* item : selectionTool->GetSelection() )
602 item->Serialize( *response.add_items() );
603
604 return response;
605}
606
607
610{
611 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RemoveFromSelection" ) )
612 return tl::unexpected( *headless );
613
614 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
615 return tl::unexpected( *busy );
616
617 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
618 {
619 ApiResponseStatus e;
620 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
621 e.set_status( ApiStatusCode::AS_UNHANDLED );
622 return tl::unexpected( e );
623 }
624
625 TOOL_MANAGER* mgr = toolManager();
626 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
627
628 std::vector<EDA_ITEM*> toRemove;
629
630 for( const types::KIID& id : aCtx.Request.items() )
631 {
632 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
633 toRemove.emplace_back( *item );
634 }
635
636 selectionTool->RemoveItemsFromSel( &toRemove );
637 m_frame->Refresh();
638
639 SelectionResponse response;
640
641 for( EDA_ITEM* item : selectionTool->GetSelection() )
642 item->Serialize( *response.add_items() );
643
644 return response;
645}
646
647
650{
651 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
652
653 if( !documentValidation )
654 return tl::unexpected( documentValidation.error() );
655
656 BoardStackupResponse response;
657 google::protobuf::Any any;
658
660
661 any.UnpackTo( response.mutable_stackup() );
662
663 // User-settable layer names are not stored in BOARD_STACKUP at the moment
664 for( board::BoardStackupLayer& layer : *response.mutable_stackup()->mutable_layers() )
665 {
666 if( layer.type() == board::BoardStackupLayerType::BSLT_DIELECTRIC )
667 continue;
668
669 PCB_LAYER_ID id = FromProtoEnum<PCB_LAYER_ID>( layer.layer() );
670 wxCHECK2( id != UNDEFINED_LAYER, continue );
671
672 layer.set_user_name( board()->GetLayerName( id ) );
673 }
674
675 return response;
676}
677
678
681{
682 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
683
684 if( !documentValidation )
685 return tl::unexpected( documentValidation.error() );
686
687 BoardEnabledLayersResponse response;
688
689 BOARD* board = this->board();
690 int copperLayerCount = board->GetCopperLayerCount();
691
692 response.set_copper_layer_count( copperLayerCount );
693
694 LSET enabled = board->GetEnabledLayers();
695
696 // The Rescue layer is an internal detail and should be hidden from the API
697 enabled.reset( Rescue );
698
699 // Just in case this is out of sync; the API should always return the expected copper layers
700 enabled |= LSET::AllCuMask( copperLayerCount );
701
702 board::PackLayerSet( *response.mutable_layers(), enabled );
703
704 return response;
705}
706
707
710{
711 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
712
713 if( !documentValidation )
714 return tl::unexpected( documentValidation.error() );
715
717 GraphicsDefaultsResponse response;
718
719 // TODO: This should change to be an enum class
720 constexpr std::array<kiapi::board::BoardLayerClass, LAYER_CLASS_COUNT> classOrder = {
721 kiapi::board::BLC_SILKSCREEN,
722 kiapi::board::BLC_COPPER,
723 kiapi::board::BLC_EDGES,
724 kiapi::board::BLC_COURTYARD,
725 kiapi::board::BLC_FABRICATION,
726 kiapi::board::BLC_OTHER
727 };
728
729 for( int i = 0; i < LAYER_CLASS_COUNT; ++i )
730 {
731 kiapi::board::BoardLayerGraphicsDefaults* l = response.mutable_defaults()->add_layers();
732
733 l->set_layer( classOrder[i] );
734 l->mutable_line_thickness()->set_value_nm( bds.m_LineThickness[i] );
735
736 kiapi::common::types::TextAttributes* text = l->mutable_text();
737 text->mutable_size()->set_x_nm( bds.m_TextSize[i].x );
738 text->mutable_size()->set_y_nm( bds.m_TextSize[i].y );
739 text->mutable_stroke_width()->set_value_nm( bds.m_TextThickness[i] );
740 text->set_italic( bds.m_TextItalic[i] );
741 text->set_keep_upright( bds.m_TextUpright[i] );
742 }
743
744 return response;
745}
746
747
750{
751 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
752 return tl::unexpected( *busy );
753
754 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
755 {
756 ApiResponseStatus e;
757 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
758 e.set_status( ApiStatusCode::AS_UNHANDLED );
759 return tl::unexpected( e );
760 }
761
762 GetBoundingBoxResponse response;
763 bool includeText = aCtx.Request.mode() == BoundingBoxMode::BBM_ITEM_AND_CHILD_TEXT;
764
765 for( const types::KIID& idMsg : aCtx.Request.items() )
766 {
767 KIID id( idMsg.value() );
768 std::optional<BOARD_ITEM*> optItem = getItemById( id );
769
770 if( !optItem )
771 continue;
772
773 BOARD_ITEM* item = *optItem;
774 BOX2I bbox;
775
776 if( item->Type() == PCB_FOOTPRINT_T )
777 bbox = static_cast<FOOTPRINT*>( item )->GetBoundingBox( includeText );
778 else
779 bbox = item->GetBoundingBox();
780
781 response.add_items()->set_value( idMsg.value() );
782 PackBox2( *response.add_boxes(), bbox );
783 }
784
785 return response;
786}
787
788
791{
792 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
793 !documentValidation )
794 {
795 return tl::unexpected( documentValidation.error() );
796 }
797
798 PadShapeAsPolygonResponse response;
800
801 for( const types::KIID& padRequest : aCtx.Request.pads() )
802 {
803 KIID id( padRequest.value() );
804 std::optional<BOARD_ITEM*> optPad = getItemById( id );
805
806 if( !optPad || ( *optPad )->Type() != PCB_PAD_T )
807 continue;
808
809 response.add_pads()->set_value( padRequest.value() );
810
811 PAD* pad = static_cast<PAD*>( *optPad );
812 SHAPE_POLY_SET poly;
813 pad->TransformShapeToPolygon( poly, pad->Padstack().EffectiveLayerFor( layer ), 0,
814 pad->GetMaxError(), ERROR_INSIDE );
815
816 types::PolygonWithHoles* polyMsg = response.mutable_polygons()->Add();
817 PackPolyLine( *polyMsg->mutable_outline(), poly.COutline( 0 ) );
818 }
819
820 return response;
821}
822
823
826{
827 using board::types::BoardLayer;
828
829 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
830 !documentValidation )
831 {
832 return tl::unexpected( documentValidation.error() );
833 }
834
835 PadstackPresenceResponse response;
836
837 LSET layers;
838
839 for( const int layer : aCtx.Request.layers() )
840 layers.set( FromProtoEnum<PCB_LAYER_ID, BoardLayer>( static_cast<BoardLayer>( layer ) ) );
841
842 for( const types::KIID& padRequest : aCtx.Request.items() )
843 {
844 KIID id( padRequest.value() );
845 std::optional<BOARD_ITEM*> optItem = getItemById( id );
846
847 if( !optItem )
848 continue;
849
850 switch( ( *optItem )->Type() )
851 {
852 case PCB_PAD_T:
853 {
854 PAD* pad = static_cast<PAD*>( *optItem );
855
856 for( PCB_LAYER_ID layer : layers )
857 {
858 PadstackPresenceEntry* entry = response.add_entries();
859 entry->mutable_item()->set_value( pad->m_Uuid.AsStdString() );
860 entry->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( layer ) );
861 entry->set_presence( pad->FlashLayer( layer ) ? PSP_PRESENT : PSP_NOT_PRESENT );
862 }
863
864 break;
865 }
866
867 case PCB_VIA_T:
868 {
869 PCB_VIA* via = static_cast<PCB_VIA*>( *optItem );
870
871 for( PCB_LAYER_ID layer : layers )
872 {
873 PadstackPresenceEntry* entry = response.add_entries();
874 entry->mutable_item()->set_value( via->m_Uuid.AsStdString() );
875 entry->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( layer ) );
876 entry->set_presence( via->FlashLayer( layer ) ? PSP_PRESENT : PSP_NOT_PRESENT );
877 }
878
879 break;
880 }
881
882 default:
883 break;
884 }
885 }
886
887 return response;
888}
889
890
893{
894 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
895
896 if( !documentValidation )
897 return tl::unexpected( documentValidation.error() );
898
899 ExpandTextVariablesResponse reply;
900 BOARD* board = this->board();
901
902 std::function<bool( wxString* )> textResolver =
903 [&]( wxString* token ) -> bool
904 {
905 // Handles m_board->GetTitleBlock() *and* m_board->GetProject()
906 return board->ResolveTextVar( token, 0 );
907 };
908
909 for( const std::string& textMsg : aCtx.Request.text() )
910 {
911 wxString text = ExpandTextVars( wxString::FromUTF8( textMsg ), &textResolver );
912 reply.add_text( text.ToUTF8() );
913 }
914
915 return reply;
916}
917
918
921{
922 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "InteractiveMoveItems" ) )
923 return tl::unexpected( *headless );
924
925 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
926 return tl::unexpected( *busy );
927
928 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
929
930 if( !documentValidation )
931 return tl::unexpected( documentValidation.error() );
932
933 TOOL_MANAGER* mgr = toolManager();
934 std::vector<EDA_ITEM*> toSelect;
935
936 for( const kiapi::common::types::KIID& id : aCtx.Request.items() )
937 {
938 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
939 toSelect.emplace_back( static_cast<EDA_ITEM*>( *item ) );
940 }
941
942 if( toSelect.empty() )
943 {
944 ApiResponseStatus e;
945 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
946 e.set_error_message( fmt::format( "None of the given items exist on the board",
947 aCtx.Request.board().board_filename() ) );
948 return tl::unexpected( e );
949 }
950
951 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
952 selectionTool->GetSelection().SetReferencePoint( toSelect[0]->GetPosition() );
953
955 mgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &toSelect );
956
957 COMMIT* commit = getCurrentCommit( aCtx.ClientName );
958 mgr->PostAPIAction( PCB_ACTIONS::move, commit );
959
960 return Empty();
961}
962
963
966{
967 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
968
969 if( !documentValidation )
970 return tl::unexpected( documentValidation.error() );
971
972 SavedDocumentResponse response;
973 response.mutable_document()->CopyFrom( aCtx.Request.document() );
974
975 CLIPBOARD_IO io;
976 io.SetWriter(
977 [&]( const wxString& aData )
978 {
979 response.set_contents( aData.ToUTF8() );
980 } );
981
982 io.SaveBoard( wxEmptyString, board(), nullptr );
983
984 return response;
985}
986
987
990{
991 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SaveSelectionToString" ) )
992 return tl::unexpected( *headless );
993
994 SavedSelectionResponse response;
995
996 TOOL_MANAGER* mgr = toolManager();
997 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
998 PCB_SELECTION& selection = selectionTool->GetSelection();
999
1000 CLIPBOARD_IO io;
1001 io.SetWriter(
1002 [&]( const wxString& aData )
1003 {
1004 response.set_contents( aData.ToUTF8() );
1005 } );
1006
1007 io.SetBoard( board() );
1008 io.SaveSelection( selection, false );
1009
1010 return response;
1011}
1012
1013
1016{
1017 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1018 return tl::unexpected( *busy );
1019
1020 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
1021
1022 if( !documentValidation )
1023 return tl::unexpected( documentValidation.error() );
1024
1025 CreateItemsResponse response;
1026 return response;
1027}
1028
1029
1032{
1033 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetVisibleLayers" ) )
1034 return tl::unexpected( *headless );
1035
1036 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1037
1038 if( !documentValidation )
1039 return tl::unexpected( documentValidation.error() );
1040
1041 BoardLayers response;
1042
1043 for( PCB_LAYER_ID layer : board()->GetVisibleLayers() )
1044 response.add_layers( ToProtoEnum<PCB_LAYER_ID, board::types::BoardLayer>( layer ) );
1045
1046 return response;
1047}
1048
1049
1052{
1053 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetVisibleLayers" ) )
1054 return tl::unexpected( *headless );
1055
1056 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1057 return tl::unexpected( *busy );
1058
1059 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1060
1061 if( !documentValidation )
1062 return tl::unexpected( documentValidation.error() );
1063
1064 LSET visible;
1065 LSET enabled = board()->GetEnabledLayers();
1066
1067 for( int layerIdx : aCtx.Request.layers() )
1068 {
1069 PCB_LAYER_ID layer =
1070 FromProtoEnum<PCB_LAYER_ID>( static_cast<board::types::BoardLayer>( layerIdx ) );
1071
1072 if( enabled.Contains( layer ) )
1073 visible.set( layer );
1074 }
1075
1076 board()->SetVisibleLayers( visible );
1077
1078 PCB_BASE_EDIT_FRAME* editFrame = static_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
1079 editFrame->GetAppearancePanel()->OnBoardChanged();
1080 editFrame->GetCanvas()->SyncLayersVisibility( board() );
1081 editFrame->Refresh();
1082 return Empty();
1083}
1084
1085
1088{
1089 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetActiveLayer" ) )
1090 return tl::unexpected( *headless );
1091
1092 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1093
1094 if( !documentValidation )
1095 return tl::unexpected( documentValidation.error() );
1096
1097 PCB_BASE_EDIT_FRAME* editFrame = static_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
1098
1099 BoardLayerResponse response;
1100 response.set_layer(
1102
1103 return response;
1104}
1105
1106
1109{
1110 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetActiveLayer" ) )
1111 return tl::unexpected( *headless );
1112
1113 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1114 return tl::unexpected( *busy );
1115
1116 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1117
1118 if( !documentValidation )
1119 return tl::unexpected( documentValidation.error() );
1120
1121 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID>( aCtx.Request.layer() );
1122
1123 if( !board()->GetEnabledLayers().Contains( layer ) )
1124 {
1125 ApiResponseStatus err;
1126 err.set_status( ApiStatusCode::AS_BAD_REQUEST );
1127 err.set_error_message( fmt::format( "Layer {} is not a valid layer for the given board",
1128 magic_enum::enum_name( layer ) ) );
1129 return tl::unexpected( err );
1130 }
1131
1132 PCB_BASE_EDIT_FRAME* editFrame = static_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
1133 editFrame->SetActiveLayer( layer );
1134 return Empty();
1135}
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:922
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:224
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:232
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:84
virtual void CopyFrom(const BOARD_ITEM *aOther)
void ResetUuid()
Definition board_item.h:248
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:323
BOARD_STACKUP GetStackupOrDefault() const
Definition board.cpp:3179
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:1018
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1101
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:986
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:1798
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:72
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:90
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:106
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:78
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:62
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:100
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:139
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:118
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
virtual wxString GetFriendlyName() const
Definition eda_item.cpp:430
Definition kiid.h:48
std::string AsStdString() const
Definition kiid.cpp:250
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
Definition pad.h:65
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:53
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:93
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:63
The common library.
#define _(s)
@ RECURSE
Definition eda_item.h:53
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ Rescue
Definition layer_ids.h:121
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:75
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:103
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:100
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:94
@ TYPE_NOT_INIT
Definition typeinfo.h:78
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:101
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:108
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:83
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:99
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:84
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:97
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:102