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 <set>
23#include <fmt/ranges.h>
24#include <magic_enum.hpp>
25
26#include <common.h>
28#include <api/api_pcb_utils.h>
29#include <api/api_enums.h>
30#include <api/api_utils.h>
31#include <board_commit.h>
33#include <footprint.h>
34#include <kicad_clipboard.h>
35#include <pad.h>
36#include <pcb_base_edit_frame.h>
37#include <pcb_field.h>
38#include <pcb_group.h>
39#include <pcb_track.h>
40#include <pcb_table.h>
41#include <pcb_tablecell.h>
42
43#include <layer_ids.h>
44#include <project.h>
45#include <tool/tool_manager.h>
46#include <tools/pcb_actions.h>
49
50#include <api/common/types/base_types.pb.h>
51
52using namespace kiapi::common::commands;
53using types::CommandStatus;
54
55using types::DocumentType;
56using types::ItemRequestStatus;
57
58
59API_HANDLER_BOARD::API_HANDLER_BOARD( std::shared_ptr<BOARD_CONTEXT> aContext,
60 EDA_BASE_FRAME* aFrame ) :
61 API_HANDLER_EDITOR( aFrame ),
62 m_context( std::move( aContext ) )
63{
64 wxCHECK( m_context, /* void */ );
65
67
69
75
88
91
102}
103
104
105std::optional<ApiResponseStatus> API_HANDLER_BOARD::checkForHeadless(
106 const std::string& aCommandName ) const
107{
108 if( m_frame )
109 return std::nullopt;
110
111 ApiResponseStatus e;
112 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
113 e.set_error_message( fmt::format( "{} is not available in headless mode", aCommandName ) );
114 return e;
115}
116
117
122
123
124void API_HANDLER_BOARD::pushCurrentCommit( const std::string& aClientName,
125 const wxString& aMessage )
126{
127 API_HANDLER_EDITOR::pushCurrentCommit( aClientName, aMessage );
128 onModified();
129}
130
131
132std::unique_ptr<COMMIT> API_HANDLER_BOARD::createCommit()
133{
134 if( m_frame )
135 return std::make_unique<BOARD_COMMIT>( static_cast<EDA_DRAW_FRAME*>( m_frame ) );
136
137 return std::make_unique<BOARD_COMMIT>( toolManager(), true, false );
138}
139
140
141std::optional<BOARD_ITEM*> API_HANDLER_BOARD::getItemById( const KIID& aId ) const
142{
143 BOARD_ITEM* item = board()->ResolveItem( aId, true );
144
145 if( !item )
146 return std::nullopt;
147
148 return item;
149}
150
151
153 BOARD_ITEM_CONTAINER* aContainer )
154{
155 if( !aContainer )
156 {
157 ApiResponseStatus e;
158 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
159 e.set_error_message( "Tried to create an item in a null container" );
160 return tl::unexpected( e );
161 }
162
163 if( aType == PCB_PAD_T && !dynamic_cast<FOOTPRINT*>( aContainer ) )
164 {
165 ApiResponseStatus e;
166 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
167 e.set_error_message( fmt::format( "Tried to create a pad in {}, which is not a footprint",
168 aContainer->GetFriendlyName().ToStdString() ) );
169 return tl::unexpected( e );
170 }
171 else if( aType == PCB_FOOTPRINT_T && !dynamic_cast<BOARD*>( aContainer ) )
172 {
173 ApiResponseStatus e;
174 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
175 e.set_error_message( fmt::format( "Tried to create a footprint in {}, which is not a board",
176 aContainer->GetFriendlyName().ToStdString() ) );
177 return tl::unexpected( e );
178 }
179
180 std::unique_ptr<BOARD_ITEM> created = CreateItemForType( aType, aContainer );
181
182 if( !created )
183 {
184 ApiResponseStatus e;
185 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
186 e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled",
187 magic_enum::enum_name( aType ) ) );
188 return tl::unexpected( e );
189 }
190
191 return created;
192}
193
194
195void API_HANDLER_BOARD::deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
196 const std::string& aClientName )
197{
198 BOARD* board = this->board();
199 std::vector<BOARD_ITEM*> validatedItems;
200
201 for( std::pair<const KIID, ItemDeletionStatus> pair : aItemsToDelete )
202 {
203 if( BOARD_ITEM* item = board->ResolveItem( pair.first, true ) )
204 {
205 // A footprint without its mandatory fields is not a state the editor can load or
206 // render; the const field accessors return nullptr and callers dereference them
207 if( item->Type() == PCB_FIELD_T && static_cast<PCB_FIELD*>( item )->IsMandatory() )
208 {
209 aItemsToDelete[pair.first] = ItemDeletionStatus::IDS_IMMUTABLE;
210 continue;
211 }
212
213 validatedItems.push_back( item );
214 aItemsToDelete[pair.first] = ItemDeletionStatus::IDS_OK;
215 }
216
217 // Note: we don't currently support locking items from API modification, but here is where
218 // to add it in the future (and return IDS_IMMUTABLE)
219 }
220
221 COMMIT* commit = getCurrentCommit( aClientName );
222
223 for( BOARD_ITEM* item : validatedItems )
224 commit->Remove( item );
225
226 if( !m_activeClients.count( aClientName ) )
227 pushCurrentCommit( aClientName, _( "Deleted items via API" ) );
228}
229
230
232 const DocumentSpecifier& aDocument, const KIID& aId )
233{
234 if( !validateDocument( aDocument ) )
235 return std::nullopt;
236
237 return getItemById( aId );
238}
239
240
242 const std::string& aClientName,
243 const types::ItemHeader &aHeader,
244 const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
245 std::function<void( ItemStatus, google::protobuf::Any )> aItemHandler )
246{
247 ApiResponseStatus e;
248
249 auto containerResult = validateItemHeaderDocument( aHeader );
250
251 if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
252 {
253 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
254 e.set_status( ApiStatusCode::AS_UNHANDLED );
255 return tl::unexpected( e );
256 }
257 else if( !containerResult )
258 {
259 e.CopyFrom( containerResult.error() );
260 return tl::unexpected( e );
261 }
262
263 BOARD* board = this->board();
265
266 if( containerResult->has_value() )
267 {
268 const KIID& containerId = **containerResult;
269 std::optional<BOARD_ITEM*> optItem = getItemById( containerId );
270
271 if( optItem )
272 {
273 container = dynamic_cast<BOARD_ITEM_CONTAINER*>( *optItem );
274
275 if( !container )
276 {
277 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
278 e.set_error_message( fmt::format(
279 "The requested container {} is not a valid board item container",
280 containerId.AsStdString() ) );
281 return tl::unexpected( e );
282 }
283 }
284 else
285 {
286 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
287 e.set_error_message( fmt::format(
288 "The requested container {} does not exist in this document",
289 containerId.AsStdString() ) );
290 return tl::unexpected( e );
291 }
292 }
293
294 BOARD_COMMIT* commit = static_cast<BOARD_COMMIT*>( getCurrentCommit( aClientName ) );
295
296 for( const google::protobuf::Any& anyItem : aItems )
297 {
298 ItemStatus status;
299 std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
300
301 if( !type )
302 {
303 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
304 status.set_error_message( fmt::format( "Could not decode a valid type from {}",
305 anyItem.type_url() ) );
306 aItemHandler( status, anyItem );
307 continue;
308 }
309
310 if( type == PCB_DIMENSION_T )
311 {
312 board::types::Dimension dimension;
313 anyItem.UnpackTo( &dimension );
314
315 switch( dimension.dimension_style_case() )
316 {
317 case board::types::Dimension::kAligned: type = PCB_DIM_ALIGNED_T; break;
318 case board::types::Dimension::kOrthogonal: type = PCB_DIM_ORTHOGONAL_T; break;
319 case board::types::Dimension::kRadial: type = PCB_DIM_RADIAL_T; break;
320 case board::types::Dimension::kLeader: type = PCB_DIM_LEADER_T; break;
321 case board::types::Dimension::kCenter: type = PCB_DIM_CENTER_T; break;
322 case board::types::Dimension::DIMENSION_STYLE_NOT_SET: break;
323 }
324 }
325
327 createItemForType( *type, container );
328
329 if( !creationResult )
330 {
331 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
332 status.set_error_message( creationResult.error().error_message() );
333 aItemHandler( status, anyItem );
334 continue;
335 }
336
337 std::unique_ptr<BOARD_ITEM> item( std::move( *creationResult ) );
338
339 bool unpacked = false;
340
341 if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( item.get() ) )
342 unpacked = group->DeserializeGroup( anyItem, commit );
343 else
344 unpacked = item->Deserialize( anyItem );
345
346 if( !unpacked )
347 {
348 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
349 e.set_error_message( fmt::format( "could not unpack {} from request",
350 item->GetClass().ToStdString() ) );
351 return tl::unexpected( e );
352 }
353
354 if( std::vector<wxString> removed = item->RemoveConflictingCustomProperties(); !removed.empty() )
355 {
356 auto as_str =
357 []( const wxString& aIn )
358 {
359 return std::string( aIn.ToUTF8() );
360 };
361
362 status.set_code( ItemStatusCode::ISC_INVALID_DATA );
363 status.set_error_message( fmt::format(
364 "Invalid custom properties for item {}: property name(s) '{}' already in use",
365 item->m_Uuid.AsStdString(), fmt::join( std::views::transform( removed, as_str ), ", " ) ) );
366
367 aItemHandler( status, anyItem );
368 continue;
369 }
370
371 std::optional<BOARD_ITEM*> optItem = getItemById( item->m_Uuid );
372
373 if( aCreate && optItem )
374 {
375 status.set_code( ItemStatusCode::ISC_EXISTING );
376 status.set_error_message( fmt::format( "an item with UUID {} already exists",
377 item->m_Uuid.AsStdString() ) );
378 aItemHandler( status, anyItem );
379 continue;
380 }
381 else if( !aCreate && !optItem )
382 {
383 status.set_code( ItemStatusCode::ISC_NONEXISTENT );
384 status.set_error_message( fmt::format( "an item with UUID {} does not exist",
385 item->m_Uuid.AsStdString() ) );
386 aItemHandler( status, anyItem );
387 continue;
388 }
389
390 if( aCreate
391 && !item->FitsEnabledLayers( board->GetEnabledLayers(), board->GetCopperLayerCount() ) )
392 {
393 status.set_code( ItemStatusCode::ISC_INVALID_DATA );
394 status.set_error_message(
395 "attempted to add item with no overlapping layers with the board" );
396 aItemHandler( status, anyItem );
397 continue;
398 }
399
400 status.set_code( ItemStatusCode::ISC_OK );
401 google::protobuf::Any newItem;
402
403 if( aCreate )
404 {
405 if( item->Type() == PCB_TABLECELL_T )
406 {
407 PCB_TABLE* table = dynamic_cast<PCB_TABLE*>( container );
408
409 if( !table )
410 {
411 status.set_code( ItemStatusCode::ISC_INVALID_DATA );
412 status.set_error_message( "a table cell must target a table container" );
413 aItemHandler( status, anyItem );
414 continue;
415 }
416
417 PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( item.release() );
418 commit->Modify( table );
419 table->AddCell( cell );
420 cell->Serialize( newItem );
421 }
422 else
423 {
424 if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_TABLE_T )
425 {
426 // Ensure children have unique identifiers; in case the API client created
427 // this new item by cloning an existing one and only changing the parent UUID.
428 item->RunOnChildren(
429 []( BOARD_ITEM* aChild )
430 {
431 aChild->ResetUuid();
432 },
433 RECURSE );
434 }
435
436 item->Serialize( newItem );
437 commit->Add( item.release() );
438 }
439 }
440 else
441 {
442 BOARD_ITEM* boardItem = *optItem;
443
444 // Footprints can't be modified by CopyFrom at the moment because the commit system
445 // doesn't currently know what to do with a footprint that has had its children
446 // replaced with other children; which results in things like the view not having its
447 // cached geometry for footprint children updated when you move a footprint around.
448 // And also, groups are special because they can contain any item type, so we
449 // can't use CopyFrom on them either.
450 if( boardItem->Type() == PCB_FOOTPRINT_T || boardItem->Type() == PCB_GROUP_T )
451 {
452 // Save group membership before removal, since Remove() severs the relationship
453 PCB_GROUP* parentGroup = dynamic_cast<PCB_GROUP*>( boardItem->GetParentGroup() );
454
455 commit->Remove( boardItem );
456 item->Serialize( newItem );
457
458 BOARD_ITEM* newBoardItem = item.release();
459 commit->Add( newBoardItem );
460
461 // Restore group membership for the newly added item
462 if( parentGroup )
463 parentGroup->AddItem( newBoardItem );
464 }
465 else
466 {
467 commit->Modify( boardItem );
468 boardItem->CopyFrom( item.get() );
469 boardItem->Serialize( newItem );
470 }
471 }
472
473 aItemHandler( status, newItem );
474 }
475
476 if( !m_activeClients.count( aClientName ) )
477 {
478 pushCurrentCommit( aClientName, aCreate ? _( "Created items via API" )
479 : _( "Modified items via API" ) );
480 }
481
482
483 return ItemRequestStatus::IRS_OK;
484}
485
486
488 const google::protobuf::RepeatedField<int>& aTypes )
489{
490 std::vector<KICAD_T> types;
491
492 for( int typeRaw : aTypes )
493 {
494 auto typeMessage = static_cast<common::types::KiCadObjectType>( typeRaw );
495 KICAD_T type = FromProtoEnum<KICAD_T>( typeMessage );
496
497 if( type != TYPE_NOT_INIT )
498 types.emplace_back( type );
499 }
500
501 return types;
502}
503
504
506 const HANDLER_CONTEXT<RunAction>& aCtx )
507{
508 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RunAction" ) )
509 return tl::unexpected( *headless );
510
511 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
512 return tl::unexpected( *busy );
513
514 RunActionResponse response;
515
516 if( toolManager()->RunAction( aCtx.Request.action(), true ) )
517 response.set_status( RunActionStatus::RAS_OK );
518 else
519 response.set_status( RunActionStatus::RAS_INVALID );
520
521 return response;
522}
523
524
527{
528 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
529 return tl::unexpected( *busy );
530
531 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
532 {
533 ApiResponseStatus e;
534 e.set_status( ApiStatusCode::AS_UNHANDLED );
535 return tl::unexpected( e );
536 }
537
538 GetItemsResponse response;
539
540 std::vector<BOARD_ITEM*> items;
541
542 for( const kiapi::common::types::KIID& id : aCtx.Request.items() )
543 {
544 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
545 items.emplace_back( *item );
546 }
547
548 if( items.empty() )
549 {
550 ApiResponseStatus e;
551 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
552 e.set_error_message( "none of the requested IDs were found or valid" );
553 return tl::unexpected( e );
554 }
555
556 for( const BOARD_ITEM* item : items )
557 {
558 google::protobuf::Any itemBuf;
559 item->Serialize( itemBuf );
560 response.mutable_items()->Add( std::move( itemBuf ) );
561 }
562
563 response.set_status( ItemRequestStatus::IRS_OK );
564 return response;
565}
566
567
570{
571 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetSelection" ) )
572 return tl::unexpected( *headless );
573
574 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
575 {
576 ApiResponseStatus e;
577 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
578 e.set_status( ApiStatusCode::AS_UNHANDLED );
579 return tl::unexpected( e );
580 }
581
582 std::set<KICAD_T> filter;
583
584 for( KICAD_T type : parseRequestedItemTypes( aCtx.Request.types() ) )
585 filter.insert( type );
586
587 TOOL_MANAGER* mgr = toolManager();
588 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
589
590 SelectionResponse response;
591
592 for( EDA_ITEM* item : selectionTool->GetSelection() )
593 {
594 if( filter.empty() || filter.contains( item->Type() ) )
595 item->Serialize( *response.add_items() );
596 }
597
598 return response;
599}
600
601
604{
605 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "ClearSelection" ) )
606 return tl::unexpected( *headless );
607
608 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
609 return tl::unexpected( *busy );
610
611 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
612 {
613 ApiResponseStatus e;
614 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
615 e.set_status( ApiStatusCode::AS_UNHANDLED );
616 return tl::unexpected( e );
617 }
618
619 TOOL_MANAGER* mgr = toolManager();
621 m_frame->Refresh();
622
623 return Empty();
624}
625
626
629{
630 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "AddToSelection" ) )
631 return tl::unexpected( *headless );
632
633 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
634 return tl::unexpected( *busy );
635
636 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
637 {
638 ApiResponseStatus e;
639 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
640 e.set_status( ApiStatusCode::AS_UNHANDLED );
641 return tl::unexpected( e );
642 }
643
644 TOOL_MANAGER* mgr = toolManager();
645 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
646
647 std::vector<EDA_ITEM*> toAdd;
648
649 for( const types::KIID& id : aCtx.Request.items() )
650 {
651 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
652 toAdd.emplace_back( *item );
653 }
654
655 selectionTool->AddItemsToSel( &toAdd );
656 m_frame->Refresh();
657
658 SelectionResponse response;
659
660 for( EDA_ITEM* item : selectionTool->GetSelection() )
661 item->Serialize( *response.add_items() );
662
663 return response;
664}
665
666
669{
670 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RemoveFromSelection" ) )
671 return tl::unexpected( *headless );
672
673 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
674 return tl::unexpected( *busy );
675
676 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
677 {
678 ApiResponseStatus e;
679 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
680 e.set_status( ApiStatusCode::AS_UNHANDLED );
681 return tl::unexpected( e );
682 }
683
684 TOOL_MANAGER* mgr = toolManager();
685 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
686
687 std::vector<EDA_ITEM*> toRemove;
688
689 for( const types::KIID& id : aCtx.Request.items() )
690 {
691 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
692 toRemove.emplace_back( *item );
693 }
694
695 selectionTool->RemoveItemsFromSel( &toRemove );
696 m_frame->Refresh();
697
698 SelectionResponse response;
699
700 for( EDA_ITEM* item : selectionTool->GetSelection() )
701 item->Serialize( *response.add_items() );
702
703 return response;
704}
705
706
709{
710 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
711
712 if( !documentValidation )
713 return tl::unexpected( documentValidation.error() );
714
715 BoardStackupResponse response;
716
717 board::PackBoardStackup( *board(), *response.mutable_stackup() );
718
719 return response;
720}
721
722
725{
726 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
727
728 if( !documentValidation )
729 return tl::unexpected( documentValidation.error() );
730
731 BoardEnabledLayersResponse response;
732
733 BOARD* board = this->board();
734 int copperLayerCount = board->GetCopperLayerCount();
735
736 response.set_copper_layer_count( copperLayerCount );
737
738 LSET enabled = board->GetEnabledLayers();
739
740 // The Rescue layer is an internal detail and should be hidden from the API
741 enabled.reset( Rescue );
742
743 // Just in case this is out of sync; the API should always return the expected copper layers
744 enabled |= LSET::AllCuMask( copperLayerCount );
745
746 board::PackLayerSet( *response.mutable_layers(), enabled );
747
748 return response;
749}
750
751
754{
755 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
756
757 if( !documentValidation )
758 return tl::unexpected( documentValidation.error() );
759
761 GraphicsDefaultsResponse response;
762
763 // TODO: This should change to be an enum class
764 constexpr std::array<kiapi::board::BoardLayerClass, LAYER_CLASS_COUNT> classOrder = {
765 kiapi::board::BLC_SILKSCREEN,
766 kiapi::board::BLC_COPPER,
767 kiapi::board::BLC_EDGES,
768 kiapi::board::BLC_COURTYARD,
769 kiapi::board::BLC_FABRICATION,
770 kiapi::board::BLC_OTHER
771 };
772
773 for( int i = 0; i < LAYER_CLASS_COUNT; ++i )
774 {
775 kiapi::board::BoardLayerGraphicsDefaults* l = response.mutable_defaults()->add_layers();
776
777 l->set_layer( classOrder[i] );
778 l->mutable_line_thickness()->set_value_nm( bds.m_LineThickness[i] );
779
780 kiapi::common::types::TextAttributes* text = l->mutable_text();
781 text->mutable_size()->set_x_nm( bds.m_TextSize[i].x );
782 text->mutable_size()->set_y_nm( bds.m_TextSize[i].y );
783 text->mutable_stroke_width()->set_value_nm( bds.m_TextThickness[i] );
784 text->set_italic( bds.m_TextItalic[i] );
785 text->set_keep_upright( bds.m_TextUpright[i] );
786 }
787
788 return response;
789}
790
791
794{
795 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
796 return tl::unexpected( *busy );
797
798 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
799 {
800 ApiResponseStatus e;
801 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
802 e.set_status( ApiStatusCode::AS_UNHANDLED );
803 return tl::unexpected( e );
804 }
805
806 GetBoundingBoxResponse response;
807 bool includeText = aCtx.Request.mode() == BoundingBoxMode::BBM_ITEM_AND_CHILD_TEXT;
808
809 for( const types::KIID& idMsg : aCtx.Request.items() )
810 {
811 KIID id( idMsg.value() );
812 std::optional<BOARD_ITEM*> optItem = getItemById( id );
813
814 if( !optItem )
815 continue;
816
817 BOARD_ITEM* item = *optItem;
818 BOX2I bbox;
819
820 if( item->Type() == PCB_FOOTPRINT_T )
821 bbox = static_cast<FOOTPRINT*>( item )->GetBoundingBox( includeText );
822 else
823 bbox = item->GetBoundingBox();
824
825 response.add_items()->set_value( idMsg.value() );
826 PackBox2( *response.add_boxes(), bbox );
827 }
828
829 return response;
830}
831
832
835{
836 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
837 !documentValidation )
838 {
839 return tl::unexpected( documentValidation.error() );
840 }
841
842 PadShapeAsPolygonResponse response;
844
845 for( const types::KIID& padRequest : aCtx.Request.pads() )
846 {
847 KIID id( padRequest.value() );
848 std::optional<BOARD_ITEM*> optPad = getItemById( id );
849
850 if( !optPad || ( *optPad )->Type() != PCB_PAD_T )
851 continue;
852
853 response.add_pads()->set_value( padRequest.value() );
854
855 PAD* pad = static_cast<PAD*>( *optPad );
856 SHAPE_POLY_SET poly;
857 pad->TransformShapeToPolygon( poly, pad->Padstack().EffectiveLayerFor( layer ), 0,
858 pad->GetMaxError(), ERROR_INSIDE );
859
860 types::PolygonWithHoles* polyMsg = response.mutable_polygons()->Add();
861 PackPolyLine( *polyMsg->mutable_outline(), poly.COutline( 0 ) );
862 }
863
864 return response;
865}
866
867
870{
871 using board::types::BoardLayer;
872
873 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
874 !documentValidation )
875 {
876 return tl::unexpected( documentValidation.error() );
877 }
878
879 PadstackPresenceResponse response;
880
881 LSET layers;
882
883 for( const int layer : aCtx.Request.layers() )
884 layers.set( FromProtoEnum<PCB_LAYER_ID, BoardLayer>( static_cast<BoardLayer>( layer ) ) );
885
886 for( const types::KIID& padRequest : aCtx.Request.items() )
887 {
888 KIID id( padRequest.value() );
889 std::optional<BOARD_ITEM*> optItem = getItemById( id );
890
891 if( !optItem )
892 continue;
893
894 switch( ( *optItem )->Type() )
895 {
896 case PCB_PAD_T:
897 {
898 PAD* pad = static_cast<PAD*>( *optItem );
899
900 for( PCB_LAYER_ID layer : layers )
901 {
902 PadstackPresenceEntry* entry = response.add_entries();
903 entry->mutable_item()->set_value( pad->m_Uuid.AsStdString() );
904 entry->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( layer ) );
905 entry->set_presence( pad->FlashLayer( layer ) ? PSP_PRESENT : PSP_NOT_PRESENT );
906 }
907
908 break;
909 }
910
911 case PCB_VIA_T:
912 {
913 PCB_VIA* via = static_cast<PCB_VIA*>( *optItem );
914
915 for( PCB_LAYER_ID layer : layers )
916 {
917 PadstackPresenceEntry* entry = response.add_entries();
918 entry->mutable_item()->set_value( via->m_Uuid.AsStdString() );
919 entry->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( layer ) );
920 entry->set_presence( via->FlashLayer( layer ) ? PSP_PRESENT : PSP_NOT_PRESENT );
921 }
922
923 break;
924 }
925
926 default:
927 break;
928 }
929 }
930
931 return response;
932}
933
934
937{
938 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
939
940 if( !documentValidation )
941 return tl::unexpected( documentValidation.error() );
942
943 ExpandTextVariablesResponse reply;
944 BOARD* board = this->board();
945
946 std::function<bool( wxString* )> textResolver =
947 [&]( wxString* token ) -> bool
948 {
949 // Handles m_board->GetTitleBlock() *and* m_board->GetProject()
950 return board->ResolveTextVar( token, 0 );
951 };
952
953 for( const std::string& textMsg : aCtx.Request.text() )
954 {
955 wxString text = ExpandTextVars( wxString::FromUTF8( textMsg ), &textResolver, INTERNAL );
956
957 if( aCtx.Request.expand_env_vars() )
958 text = ExpandEnvVarSubstitutions( text, board->GetProject() );
959
960 reply.add_text( text.ToUTF8() );
961 }
962
963 return reply;
964}
965
966
969{
970 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "InteractiveMoveItems" ) )
971 return tl::unexpected( *headless );
972
973 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
974 return tl::unexpected( *busy );
975
976 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
977
978 if( !documentValidation )
979 return tl::unexpected( documentValidation.error() );
980
981 TOOL_MANAGER* mgr = toolManager();
982 std::vector<EDA_ITEM*> toSelect;
983
984 for( const kiapi::common::types::KIID& id : aCtx.Request.items() )
985 {
986 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
987 toSelect.emplace_back( static_cast<EDA_ITEM*>( *item ) );
988 }
989
990 if( toSelect.empty() )
991 {
992 ApiResponseStatus e;
993 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
994 e.set_error_message( fmt::format( "None of the given items exist on the board",
995 aCtx.Request.board().board_filename() ) );
996 return tl::unexpected( e );
997 }
998
999 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
1000 selectionTool->GetSelection().SetReferencePoint( toSelect[0]->GetPosition() );
1001
1003 mgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &toSelect );
1004
1005 COMMIT* commit = getCurrentCommit( aCtx.ClientName );
1006 mgr->PostAPIAction( PCB_ACTIONS::move, commit );
1007
1008 return Empty();
1009}
1010
1011
1013 const HANDLER_CONTEXT<FlipItems>& aCtx )
1014{
1015 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1016 return tl::unexpected( *busy );
1017
1018 auto containerResult = validateItemHeaderDocument( aCtx.Request.header() );
1019
1020 if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
1021 {
1022 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
1023 ApiResponseStatus e;
1024 e.set_status( ApiStatusCode::AS_UNHANDLED );
1025 return tl::unexpected( e );
1026 }
1027 else if( !containerResult )
1028 {
1029 return tl::unexpected( containerResult.error() );
1030 }
1031
1033 aCtx.Request.direction() );
1034
1035 FlipItemsResponse response;
1036 response.mutable_header()->CopyFrom( aCtx.Request.header() );
1037
1038 BOARD_COMMIT* commit = static_cast<BOARD_COMMIT*>( getCurrentCommit( aCtx.ClientName ) );
1039
1040 bool anyModified = false;
1041
1042 for( const types::KIID& id : aCtx.Request.items() )
1043 {
1044 ItemFlipResult* result = response.add_flipped_items();
1045
1046 std::optional<BOARD_ITEM*> optItem = getItemById( KIID( id.value() ) );
1047
1048 if( !optItem )
1049 {
1050 result->mutable_status()->set_code( ItemStatusCode::ISC_NONEXISTENT );
1051 result->mutable_status()->set_error_message(
1052 fmt::format( "an item with UUID {} does not exist", id.value() ) );
1053 continue;
1054 }
1055
1056 BOARD_ITEM* boardItem = *optItem;
1057
1058 static const std::set<KICAD_T> flippableTypes = {
1060 PCB_PAD_T,
1065 PCB_TEXT_T,
1069 PCB_VIA_T,
1070 PCB_ARC_T,
1071 PCB_ZONE_T,
1083 };
1084
1085 KICAD_T itemType = boardItem->Type();
1086
1087 if( !flippableTypes.contains( itemType ) )
1088 {
1089 result->mutable_status()->set_code( ItemStatusCode::ISC_INVALID_TYPE );
1090 result->mutable_status()->set_error_message(
1091 fmt::format( "items of type {} cannot be flipped",
1092 magic_enum::enum_name( itemType ) ) );
1093 continue;
1094 }
1095
1096 commit->Modify( boardItem, nullptr, RECURSE_MODE::RECURSE );
1097 boardItem->Flip( boardItem->GetPosition(), flipDirection );
1098 boardItem->Normalize();
1099
1100 // Maybe this should be in FOOTPRINT::Normalize?
1101 if( boardItem->Type() == PCB_FOOTPRINT_T )
1102 static_cast<FOOTPRINT*>( boardItem )->InvalidateComponentClassCache();
1103
1104 anyModified = true;
1105
1106 google::protobuf::Any itemBuf;
1107 boardItem->Serialize( itemBuf );
1108 *result->mutable_item() = std::move( itemBuf );
1109
1110 result->mutable_status()->set_code( ItemStatusCode::ISC_OK );
1111 }
1112
1113 response.set_status( ItemRequestStatus::IRS_OK );
1114
1115 if( anyModified && !m_activeClients.count( aCtx.ClientName ) )
1116 pushCurrentCommit( aCtx.ClientName, _( "Flipped items via API" ) );
1117
1118 return response;
1119}
1120
1121
1124{
1125 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
1126
1127 if( !documentValidation )
1128 return tl::unexpected( documentValidation.error() );
1129
1130 SavedDocumentResponse response;
1131 response.mutable_document()->CopyFrom( aCtx.Request.document() );
1132
1133 CLIPBOARD_IO io;
1134 io.SetWriter(
1135 [&]( const wxString& aData )
1136 {
1137 response.set_contents( aData.ToUTF8() );
1138 } );
1139
1140 io.SaveBoard( wxEmptyString, *board(), nullptr );
1141
1142 return response;
1143}
1144
1145
1148{
1149 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SaveSelectionToString" ) )
1150 return tl::unexpected( *headless );
1151
1152 SavedSelectionResponse response;
1153
1154 TOOL_MANAGER* mgr = toolManager();
1155 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
1156 PCB_SELECTION& selection = selectionTool->GetSelection();
1157
1158 CLIPBOARD_IO io;
1159 io.SetWriter(
1160 [&]( const wxString& aData )
1161 {
1162 response.set_contents( aData.ToUTF8() );
1163 } );
1164
1165 io.SetBoard( board() );
1166 io.SaveSelection( selection, false );
1167
1168 return response;
1169}
1170
1171
1174{
1175 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1176 return tl::unexpected( *busy );
1177
1178 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
1179
1180 if( !documentValidation )
1181 return tl::unexpected( documentValidation.error() );
1182
1183 CreateItemsResponse response;
1184 return response;
1185}
1186
1187
1190{
1191 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetVisibleLayers" ) )
1192 return tl::unexpected( *headless );
1193
1194 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1195
1196 if( !documentValidation )
1197 return tl::unexpected( documentValidation.error() );
1198
1199 BoardLayers response;
1200
1201 for( PCB_LAYER_ID layer : board()->GetVisibleLayers() )
1202 response.add_layers( ToProtoEnum<PCB_LAYER_ID, board::types::BoardLayer>( layer ) );
1203
1204 return response;
1205}
1206
1207
1210{
1211 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetVisibleLayers" ) )
1212 return tl::unexpected( *headless );
1213
1214 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1215 return tl::unexpected( *busy );
1216
1217 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1218
1219 if( !documentValidation )
1220 return tl::unexpected( documentValidation.error() );
1221
1222 LSET visible;
1223 LSET enabled = board()->GetEnabledLayers();
1224
1225 for( int layerIdx : aCtx.Request.layers() )
1226 {
1227 PCB_LAYER_ID layer =
1228 FromProtoEnum<PCB_LAYER_ID>( static_cast<board::types::BoardLayer>( layerIdx ) );
1229
1230 if( enabled.Contains( layer ) )
1231 visible.set( layer );
1232 }
1233
1234 board()->SetVisibleLayers( visible );
1235
1236 PCB_BASE_EDIT_FRAME* editFrame = static_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
1237 editFrame->GetAppearancePanel()->OnBoardChanged();
1238 editFrame->GetCanvas()->SyncLayersVisibility( board() );
1239 editFrame->Refresh();
1240 return Empty();
1241}
1242
1243
1246{
1247 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetActiveLayer" ) )
1248 return tl::unexpected( *headless );
1249
1250 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1251
1252 if( !documentValidation )
1253 return tl::unexpected( documentValidation.error() );
1254
1255 PCB_BASE_EDIT_FRAME* editFrame = static_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
1256
1257 BoardLayerResponse response;
1258 response.set_layer(
1260
1261 return response;
1262}
1263
1264
1267{
1268 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetActiveLayer" ) )
1269 return tl::unexpected( *headless );
1270
1271 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1272 return tl::unexpected( *busy );
1273
1274 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1275
1276 if( !documentValidation )
1277 return tl::unexpected( documentValidation.error() );
1278
1279 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID>( aCtx.Request.layer() );
1280
1281 if( !board()->GetEnabledLayers().Contains( layer ) )
1282 {
1283 ApiResponseStatus err;
1284 err.set_status( ApiStatusCode::AS_BAD_REQUEST );
1285 err.set_error_message( fmt::format( "Layer {} is not a valid layer for the given board",
1286 magic_enum::enum_name( layer ) ) );
1287 return tl::unexpected( err );
1288 }
1289
1290 PCB_BASE_EDIT_FRAME* editFrame = static_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
1291 editFrame->SetActiveLayer( layer );
1292 return Empty();
1293}
KICOMMON_API types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICOMMON_API KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:55
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:927
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< FlipItemsResponse > handleFlipItems(const HANDLER_CONTEXT< FlipItems > &aCtx)
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.
virtual void onModified()
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:153
BASE_SET & set(size_t pos)
Definition base_set.h:126
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:280
virtual void Normalize()
Perform any normalization required after a user rotate and/or flip.
Definition board_item.h:487
virtual void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection)
Flip this object, i.e.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
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:1216
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1299
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1183
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:2116
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 SaveSelection(const PCB_SELECTION &selected, bool isFootprintEditor)
void SetWriter(std::function< void(const wxString &)> aWriter)
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:98
virtual VECTOR2I GetPosition() const
Definition eda_item.h:348
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:270
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:116
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual wxString GetFriendlyName() const
Definition eda_item.cpp:565
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.
bool IsMandatory() const
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
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)
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:776
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, RESOLUTION_CONTEXT aContext)
Definition common.cpp:60
@ INTERNAL
Definition common.h:92
#define _(s)
@ RECURSE
Definition eda_item.h:51
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ Rescue
Definition layer_ids.h:117
FLIP_DIRECTION
Definition mirror.h:23
void PackLayerSet(google::protobuf::RepeatedField< int > &aOutput, const LSET &aLayerSet)
void PackBoardStackup(const BOARD &aBoard, BoardStackup &aOut)
KICOMMON_API std::optional< KICAD_T > TypeNameFromAny(const google::protobuf::Any &aMessage)
Definition api_utils.cpp:50
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
wxString result
Test unit parsing edge cases and error handling.
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:98
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:95
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:83
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ TYPE_NOT_INIT
Definition typeinfo.h:73
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:96
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:103
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:85
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:81
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition typeinfo.h:91
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:93
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:99
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:87
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
@ PCB_GRID_ITEM_T
a subgrid placed on a board
Definition typeinfo.h:238
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:94
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:92
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:86
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:105
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:97