KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_edit_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2019 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <symbol_edit_frame.h>
22#include <tool/tool_manager.h>
24#include <kiway.h>
25#include <tool/action_manager.h>
26#include <tool/picker_tool.h>
27#include <tools/sch_edit_tool.h>
30#include <tools/sch_move_tool.h>
32#include <confirm.h>
33#include <connection_graph.h>
34#include <advanced_config.h>
36#include <sch_actions.h>
37#include <sch_tool_utils.h>
38#include <increment.h>
39#include <algorithm>
40#include <set>
41#include <string_utils.h>
42#include <sch_bitmap.h>
43#include <sch_bus_entry.h>
44#include <sch_commit.h>
45#include <sch_group.h>
46#include <sch_label.h>
47#include <sch_junction.h>
48#include <sch_marker.h>
49#include <sch_rule_area.h>
50#include <sch_pin.h>
51#include <sch_sheet_path.h>
52#include <sch_sheet_pin.h>
53#include <sch_textbox.h>
54#include <sch_table.h>
55#include <sch_no_connect.h>
57#include <eeschema_id.h>
58#include <widgets/wx_infobar.h>
73#include <pgm_base.h>
76#include <core/kicad_algo.h>
77#include <view/view_controls.h>
78#include <wx/textdlg.h>
79#include <wx/msgdlg.h>
81#include <project_sch.h>
86
87
89{
90public:
92 ACTION_MENU( true )
93 {
95 SetTitle( _( "Symbol Unit" ) );
96 }
97
98protected:
99 ACTION_MENU* create() const override { return new SYMBOL_UNIT_MENU(); }
100
101private:
102 void update() override
103 {
105 SCH_SELECTION& selection = selTool->GetSelection();
106 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
107
108 Clear();
109
110 wxCHECK( symbol, /* void */ );
111
112 const int unit = symbol->GetUnit();
113 const int nUnits = symbol->GetLibSymbolRef()->GetUnitCount();
114 const std::set<int> missingUnits = GetUnplacedUnitsForSymbol( *symbol );
115
116 for( int ii = 0; ii < nUnits; ii++ )
117 {
118 wxString unit_text = symbol->GetUnitDisplayName( ii + 1, false );
119
120 if( missingUnits.count( ii + 1 ) == 0 )
121 unit_text += _( " (already placed)" );
122
123 wxMenuItem* item = Append( ID_POPUP_SCH_SELECT_UNIT1 + ii, unit_text, wxEmptyString, wxITEM_CHECK );
124
125 if( unit == ii + 1 )
126 item->Check( true );
127
128 // The ID max for these submenus is ID_POPUP_SCH_SELECT_UNIT_END
129 // See eeschema_id to modify this value.
131 break; // We have used all IDs for these submenus
132 }
133
134 if( !missingUnits.empty() )
135 {
136 AppendSeparator();
137
138 for( int unitNumber : missingUnits )
139 {
140 wxString placeText =
141 wxString::Format( _( "Place unit %s" ), symbol->GetUnitDisplayName( unitNumber, false ) );
142 Append( ID_POPUP_SCH_PLACE_UNIT1 + unitNumber - 1, placeText );
143 }
144 }
145 }
146};
147
148
150{
151public:
153 ACTION_MENU( true )
154 {
156 SetTitle( _( "Body Style" ) );
157 }
158
159protected:
160 ACTION_MENU* create() const override { return new BODY_STYLE_MENU(); }
161
162private:
163 void update() override
164 {
166 SCH_SELECTION& selection = selTool->GetSelection();
167 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
168 wxMenuItem* item;
169
170 Clear();
171
172 wxCHECK( symbol, /* void */ );
173
174 if( symbol->HasDeMorganBodyStyles() )
175 {
176 item = Append( ID_POPUP_SCH_SELECT_BODY_STYLE, _( "Standard" ), wxEmptyString, wxITEM_CHECK );
177 item->Check( symbol->GetBodyStyle() == BODY_STYLE::BASE );
178
179 item = Append( ID_POPUP_SCH_SELECT_BODY_STYLE1, _( "Alternate" ), wxEmptyString, wxITEM_CHECK );
180 item->Check( symbol->GetBodyStyle() != BODY_STYLE::BASE );
181 }
182 else if( symbol->IsMultiBodyStyle() )
183 {
184 for( int i = 0; i < symbol->GetBodyStyleCount(); i++ )
185 {
186 item = Append( ID_POPUP_SCH_SELECT_BODY_STYLE + i, symbol->GetBodyStyleDescription( i + 1, true ),
187 wxEmptyString, wxITEM_CHECK );
188 item->Check( symbol->GetBodyStyle() == i + 1 );
189 }
190 }
191 }
192};
193
194
196{
197public:
199 ACTION_MENU( true )
200 {
202 SetTitle( _( "Pin Function" ) );
203 }
204
205protected:
206 ACTION_MENU* create() const override { return new ALT_PIN_FUNCTION_MENU(); }
207
208private:
209 void update() override
210 {
212 SCH_SELECTION& selection = selTool->GetSelection();
213 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( selection.Front() );
214 SCH_PIN* libPin = pin ? pin->GetLibPin() : nullptr;
215
216 Clear();
217
218 wxCHECK( libPin, /* void */ );
219
220 wxMenuItem* item = Append( ID_POPUP_SCH_ALT_PIN_FUNCTION, libPin->GetName(), wxEmptyString, wxITEM_CHECK );
221
222 if( pin->GetAlt().IsEmpty() || ( pin->GetAlt() == libPin->GetName() ) )
223 item->Check( true );
224
225 int ii = 1;
226
227 for( const auto& [name, definition] : libPin->GetAlternates() )
228 {
229 // The default pin name is set above, avoid setting it again.
230 if( name == libPin->GetName() )
231 continue;
232
233 item = Append( ID_POPUP_SCH_ALT_PIN_FUNCTION + ii, name, wxEmptyString, wxITEM_CHECK );
234
235 if( name == pin->GetAlt() )
236 item->Check( true );
237
238 // The ID max for these submenus is ID_POPUP_SCH_ALT_PIN_FUNCTION_END
239 // See eeschema_id to modify this value.
241 break; // We have used all IDs for these submenus
242 }
243 }
244};
245
246
248{
249public:
251 ACTION_MENU( true )
252 {
254 SetTitle( _( "Pin Helpers" ) );
255 }
256
257protected:
258 ACTION_MENU* create() const override { return new PIN_TRICKS_MENU(); }
259
260private:
261 void update() override
262 {
264 SCH_SELECTION& selection = selTool->GetSelection();
265 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( selection.Front() );
266 SCH_SHEET_PIN* sheetPin = dynamic_cast<SCH_SHEET_PIN*>( selection.Front() );
267
268 Clear();
269
270 if( !pin && !sheetPin )
271 return;
272
278 }
279};
280
281
301
302
304 SCH_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.InteractiveEdit" )
305{
306 m_pickerItem = nullptr;
307}
308
309
311
313{
315
316 SCH_DRAWING_TOOLS* drawingTools = m_toolMgr->GetTool<SCH_DRAWING_TOOLS>();
317 SCH_MOVE_TOOL* moveTool = m_toolMgr->GetTool<SCH_MOVE_TOOL>();
318
319 wxASSERT_MSG( drawingTools, "eeshema.InteractiveDrawing tool is not available" );
320
321 static const std::vector<KICAD_T> attribTypes = { SCH_SYMBOL_T, SCH_SHEET_T, SCH_RULE_AREA_T };
322 static const std::vector<KICAD_T> sheetTypes = { SCH_SHEET_T };
323
324 auto sheetSelection = S_C::Count( 1 ) && S_C::OnlyTypes( sheetTypes );
325
326 auto sheetHasUndefinedPins =
327 []( const SELECTION& aSel )
328 {
329 if( aSel.Size() == 1 && aSel.Front()->Type() == SCH_SHEET_T )
330 return static_cast<SCH_SHEET*>( aSel.Front() )->HasUndefinedPins();
331
332 return false;
333 };
334
335 auto attribDNPCond =
336 [this]( const SELECTION& aSel )
337 {
338 SCH_SHEET_PATH* sheet = &m_frame->GetCurrentSheet();
339 wxString variant = m_frame->Schematic().GetCurrentVariant();
340 int checked = 0;
341 int unchecked = 0;
342
343 for( EDA_ITEM* item : aSel )
344 {
345 switch( item->Type() )
346 {
347 case SCH_SYMBOL_T:
348 if( static_cast<const SCH_SYMBOL*>( item )->GetDNP( sheet, variant ) )
349 checked++;
350 else
351 unchecked++;
352
353 break;
354
355 case SCH_SHEET_T:
356 if( static_cast<const SCH_SHEET*>( item )->GetDNP( sheet, variant ) )
357 checked++;
358 else
359 unchecked++;
360
361 break;
362
363 case SCH_RULE_AREA_T:
364 if( static_cast<const SCH_RULE_AREA*>( item )->GetDNP() )
365 checked++;
366 else
367 unchecked++;
368
369 break;
370
371 default:
372 break;
373 }
374 }
375
376 return checked > 0 && unchecked == 0;
377 };
378
379 auto attribExcludeFromSimCond =
380 [this]( const SELECTION& aSel )
381 {
382 SCH_SHEET_PATH* sheet = &m_frame->GetCurrentSheet();
383 wxString variant = m_frame->Schematic().GetCurrentVariant();
384 int checked = 0;
385 int unchecked = 0;
386
387 for( EDA_ITEM* item : aSel )
388 {
389 switch( item->Type() )
390 {
391 case SCH_SYMBOL_T:
392 if( static_cast<const SCH_SYMBOL*>( item )->GetExcludedFromSim( sheet, variant ) )
393 checked++;
394 else
395 unchecked++;
396
397 break;
398
399 case SCH_SHEET_T:
400 if( static_cast<const SCH_SHEET*>( item )->GetExcludedFromSim( sheet, variant ) )
401 checked++;
402 else
403 unchecked++;
404
405 break;
406
407 case SCH_RULE_AREA_T:
408 if( static_cast<const SCH_RULE_AREA*>( item )->GetExcludedFromSim() )
409 checked++;
410 else
411 unchecked++;
412
413 break;
414
415 default:
416 break;
417 }
418 }
419
420 return checked > 0 && unchecked == 0;
421 };
422
423 auto attribExcludeFromBOMCond =
424 [this]( const SELECTION& aSel )
425 {
426 SCH_SHEET_PATH* sheet = &m_frame->GetCurrentSheet();
427 wxString variant = m_frame->Schematic().GetCurrentVariant();
428 int checked = 0;
429 int unchecked = 0;
430
431 for( EDA_ITEM* item : aSel )
432 {
433 switch( item->Type() )
434 {
435 case SCH_SYMBOL_T:
436 if( static_cast<const SCH_SYMBOL*>( item )->GetExcludedFromBOM( sheet, variant ) )
437 checked++;
438 else
439 unchecked++;
440
441 break;
442
443 case SCH_SHEET_T:
444 if( static_cast<const SCH_SHEET*>( item )->GetExcludedFromBOM( sheet, variant ) )
445 checked++;
446 else
447 unchecked++;
448
449 break;
450
451 case SCH_RULE_AREA_T:
452 if( static_cast<const SCH_RULE_AREA*>( item )->GetExcludedFromBOM() )
453 checked++;
454 else
455 unchecked++;
456
457 break;
458
459 default:
460 break;
461 }
462 }
463
464 return checked > 0 && unchecked == 0;
465 };
466
467
468 auto attribExcludeFromBoardCond =
469 [this]( const SELECTION& aSel )
470 {
471 SCH_SHEET_PATH* sheet = &m_frame->GetCurrentSheet();
472 wxString variant = m_frame->Schematic().GetCurrentVariant();
473 int checked = 0;
474 int unchecked = 0;
475
476 for( EDA_ITEM* item : aSel )
477 {
478 switch( item->Type() )
479 {
480 case SCH_SYMBOL_T:
481 if( static_cast<const SCH_SYMBOL*>( item )->GetExcludedFromBoard( sheet, variant ) )
482 checked++;
483 else
484 unchecked++;
485
486 break;
487
488 case SCH_SHEET_T:
489 if( static_cast<const SCH_SHEET*>( item )->GetExcludedFromBoard( sheet, variant ) )
490 checked++;
491 else
492 unchecked++;
493
494 break;
495
496 case SCH_RULE_AREA_T:
497 if( static_cast<const SCH_RULE_AREA*>( item )->GetExcludedFromBoard() )
498 checked++;
499 else
500 unchecked++;
501
502 break;
503
504 default:
505 break;
506 }
507 }
508
509 return checked > 0 && unchecked == 0;
510 };
511
512 auto attribExcludeFromPosFilesCond = [this]( const SELECTION& aSel )
513 {
514 SCH_SHEET_PATH* sheet = &m_frame->GetCurrentSheet();
515 wxString variant = m_frame->Schematic().GetCurrentVariant();
516 int checked = 0;
517 int unchecked = 0;
518
519 for( EDA_ITEM* item : aSel )
520 {
521 if( item->Type() == SCH_SYMBOL_T )
522 {
523 if( static_cast<const SCH_SYMBOL*>( item )->GetExcludedFromPosFiles( sheet, variant ) )
524 checked++;
525 else
526 unchecked++;
527 }
528 }
529
530 return checked > 0 && unchecked == 0;
531 };
532
533 auto haveHighlight =
534 [this]( const SELECTION& sel )
535 {
536 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
537
538 return editFrame && !editFrame->GetHighlightedConnection().IsEmpty();
539 };
540
541 auto anyTextTool =
542 [this]( const SELECTION& aSel )
543 {
544 return ( m_frame->IsCurrentTool( SCH_ACTIONS::placeLabel )
545 || m_frame->IsCurrentTool( SCH_ACTIONS::placeClassLabel )
546 || m_frame->IsCurrentTool( SCH_ACTIONS::placeGlobalLabel )
547 || m_frame->IsCurrentTool( SCH_ACTIONS::placeHierLabel )
548 || m_frame->IsCurrentTool( SCH_ACTIONS::placeSchematicText ) );
549 };
550
551 auto duplicateCondition =
552 []( const SELECTION& aSel )
553 {
555 return false;
556
557 return true;
558 };
559
560 auto orientCondition =
561 []( const SELECTION& aSel )
562 {
564 return false;
565
567 };
568
569 const auto swapSelectionCondition = S_C::OnlyTypes( SwappableItems ) && SELECTION_CONDITIONS::MoreThan( 1 );
570
571 auto propertiesCondition =
572 [this]( const SELECTION& aSel )
573 {
574 if( aSel.GetSize() == 0 )
575 {
576 if( getView()->IsLayerVisible( LAYER_SCHEMATIC_DRAWINGSHEET ) )
577 {
578 DS_PROXY_VIEW_ITEM* ds = m_frame->GetCanvas()->GetView()->GetDrawingSheet();
580
581 if( ds && ds->HitTestDrawingSheetItems( getView(), cursor ) )
582 return true;
583 }
584
585 return false;
586 }
587
588 SCH_ITEM* firstItem = dynamic_cast<SCH_ITEM*>( aSel.Front() );
589 const SCH_SELECTION* eeSelection = dynamic_cast<const SCH_SELECTION*>( &aSel );
590
591 if( !firstItem || !eeSelection )
592 return false;
593
594 switch( firstItem->Type() )
595 {
596 case SCH_SYMBOL_T:
597 case SCH_SHEET_T:
598 case SCH_SHEET_PIN_T:
599 case SCH_TEXT_T:
600 case SCH_TEXTBOX_T:
601 case SCH_TABLE_T:
602 case SCH_TABLECELL_T:
603 case SCH_LABEL_T:
605 case SCH_HIER_LABEL_T:
607 case SCH_RULE_AREA_T:
608 case SCH_FIELD_T:
609 case SCH_SHAPE_T:
610 case SCH_BITMAP_T:
611 case SCH_GROUP_T:
612 return aSel.GetSize() == 1;
613
614 case SCH_LINE_T:
616 case SCH_JUNCTION_T:
617 if( std::all_of( aSel.Items().begin(), aSel.Items().end(),
618 [&]( const EDA_ITEM* item )
619 {
620 return item->Type() == SCH_LINE_T
621 && static_cast<const SCH_LINE*>( item )->IsGraphicLine();
622 } ) )
623 {
624 return true;
625 }
626 else if( std::all_of( aSel.Items().begin(), aSel.Items().end(),
627 [&]( const EDA_ITEM* item )
628 {
629 return item->Type() == SCH_JUNCTION_T;
630 } ) )
631 {
632 return true;
633 }
634 else if( std::all_of( aSel.Items().begin(), aSel.Items().end(),
635 [&]( const EDA_ITEM* item )
636 {
637 const SCH_ITEM* schItem = dynamic_cast<const SCH_ITEM*>( item );
638
639 wxCHECK( schItem, false );
640
641 return ( schItem->HasLineStroke() && schItem->IsConnectable() )
642 || item->Type() == SCH_JUNCTION_T;
643 } ) )
644 {
645 return true;
646 }
647
648 return false;
649
650 default:
651 return false;
652 }
653 };
654
655 auto autoplaceCondition =
656 []( const SELECTION& aSel )
657 {
658 for( const EDA_ITEM* item : aSel )
659 {
660 if( item->IsType( SCH_COLLECTOR::FieldOwners ) )
661 return true;
662 }
663
664 return false;
665 };
666
667 // allTextTypes does not include SCH_SHEET_PIN_T because one cannot convert other
668 // types to/from this type, living only in a SHEET
669 static const std::vector<KICAD_T> allTextTypes = { SCH_LABEL_T,
675
676 auto toChangeCondition = ( S_C::OnlyTypes( allTextTypes ) );
677
678 static const std::vector<KICAD_T> toLabelTypes = { SCH_DIRECTIVE_LABEL_T,
683
684 auto toLabelCondition = ( S_C::Count( 1 ) && S_C::OnlyTypes( toLabelTypes ) )
685 || ( S_C::MoreThan( 1 ) && S_C::OnlyTypes( allTextTypes ) );
686
687 static const std::vector<KICAD_T> toCLabelTypes = { SCH_LABEL_T,
692
693 auto toCLabelCondition = ( S_C::Count( 1 ) && S_C::OnlyTypes( toCLabelTypes ) )
694 || ( S_C::MoreThan( 1 ) && S_C::OnlyTypes( allTextTypes ) );
695
696 static const std::vector<KICAD_T> toHLabelTypes = { SCH_LABEL_T,
701
702 auto toHLabelCondition = ( S_C::Count( 1 ) && S_C::OnlyTypes( toHLabelTypes ) )
703 || ( S_C::MoreThan( 1 ) && S_C::OnlyTypes( allTextTypes ) );
704
705 static const std::vector<KICAD_T> toGLabelTypes = { SCH_LABEL_T,
710
711 auto toGLabelCondition = ( S_C::Count( 1 ) && S_C::OnlyTypes( toGLabelTypes ) )
712 || ( S_C::MoreThan( 1 ) && S_C::OnlyTypes( allTextTypes ) );
713
714 static const std::vector<KICAD_T> toTextTypes = { SCH_LABEL_T,
719
720 auto toTextCondition = ( S_C::Count( 1 ) && S_C::OnlyTypes( toTextTypes ) )
721 || ( S_C::MoreThan( 1 ) && S_C::OnlyTypes( allTextTypes ) );
722
723 static const std::vector<KICAD_T> toTextBoxTypes = { SCH_LABEL_T,
727 SCH_TEXT_T };
728
729 auto toTextBoxCondition = ( S_C::Count( 1 ) && S_C::OnlyTypes( toTextBoxTypes ) )
730 || ( S_C::MoreThan( 1 ) && S_C::OnlyTypes( allTextTypes ) );
731
732 static const std::vector<KICAD_T> busEntryTypes = { SCH_BUS_WIRE_ENTRY_T, SCH_BUS_BUS_ENTRY_T };
733
734 auto entryCondition = S_C::MoreThan( 0 ) && S_C::OnlyTypes( busEntryTypes );
735
736 auto singleSheetCondition = S_C::Count( 1 ) && S_C::OnlyTypes( sheetTypes );
737
738 auto variantActiveCondition =
739 [this]( const SELECTION& aSel )
740 {
741 return !m_frame->Schematic().GetCurrentVariant().IsEmpty();
742 };
743
744 auto noVariantActiveCondition =
745 [this]( const SELECTION& aSel )
746 {
747 return m_frame->Schematic().GetCurrentVariant().IsEmpty();
748 };
749
750 auto symbolHasVariantSymbol =
751 [this]( const SELECTION& aSel )
752 {
753 if( m_frame->Schematic().GetCurrentVariant().IsEmpty() || aSel.GetSize() != 1 )
754 return false;
755
756 SCH_SYMBOL* sym = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
757
758 if( !sym )
759 return false;
760
761 SCH_SHEET_PATH& sheet = m_frame->GetCurrentSheet();
762
763 std::optional<SCH_SYMBOL_VARIANT> variant =
764 sym->GetVariant( sheet, m_frame->Schematic().GetCurrentVariant() );
765
766 return variant.has_value() && variant->m_SymbolOverride.has_value();
767 };
768
769 auto makeSymbolUnitMenu =
770 [&]( TOOL_INTERACTIVE* tool )
771 {
772 std::shared_ptr<SYMBOL_UNIT_MENU> menu = std::make_shared<SYMBOL_UNIT_MENU>();
773 menu->SetTool( tool );
774 tool->GetToolMenu().RegisterSubMenu( menu );
775 return menu.get();
776 };
777
778 auto makeBodyStyleMenu =
779 [&]( TOOL_INTERACTIVE* tool )
780 {
781 std::shared_ptr<BODY_STYLE_MENU> menu = std::make_shared<BODY_STYLE_MENU>();
782 menu->SetTool( tool );
783 tool->GetToolMenu().RegisterSubMenu( menu );
784 return menu.get();
785 };
786
787 auto makePinFunctionMenu =
788 [&]( TOOL_INTERACTIVE* tool )
789 {
790 std::shared_ptr<ALT_PIN_FUNCTION_MENU> menu = std::make_shared<ALT_PIN_FUNCTION_MENU>();
791 menu->SetTool( tool );
792 tool->GetToolMenu().RegisterSubMenu( menu );
793 return menu.get();
794 };
795
796 auto makePinTricksMenu =
797 [&]( TOOL_INTERACTIVE* tool )
798 {
799 std::shared_ptr<PIN_TRICKS_MENU> menu = std::make_shared<PIN_TRICKS_MENU>();
800 menu->SetTool( tool );
801 tool->GetToolMenu().RegisterSubMenu( menu );
802 return menu.get();
803 };
804
805 auto makeTransformMenu =
806 [&]()
807 {
808 CONDITIONAL_MENU* menu = new CONDITIONAL_MENU( moveTool );
809 menu->SetUntranslatedTitle( _HKI( "Transform Selection" ) );
810
811 menu->AddItem( SCH_ACTIONS::rotateCCW, orientCondition );
812 menu->AddItem( SCH_ACTIONS::rotateCW, orientCondition );
813 menu->AddItem( SCH_ACTIONS::mirrorV, orientCondition );
814 menu->AddItem( SCH_ACTIONS::mirrorH, orientCondition );
815
816 return menu;
817 };
818
819 auto makeAttributesMenu =
820 [&]()
821 {
822 CONDITIONAL_MENU* menu = new CONDITIONAL_MENU( moveTool );
823 menu->SetUntranslatedTitle( _HKI( "Attributes" ) );
824
830
831 return menu;
832 };
833
834 auto makeEditFieldsMenu =
835 [&]()
836 {
838 menu->SetUntranslatedTitle( _HKI( "Edit Main Fields" ) );
839
843
844 return menu;
845 };
846
847 auto makeConvertToMenu =
848 [&]()
849 {
851 menu->SetUntranslatedTitle( _HKI( "Change To" ) );
852 menu->SetIcon( BITMAPS::right );
853
854 menu->AddItem( SCH_ACTIONS::toLabel, toLabelCondition );
855 menu->AddItem(SCH_ACTIONS::toDLabel, toCLabelCondition );
856 menu->AddItem( SCH_ACTIONS::toHLabel, toHLabelCondition );
857 menu->AddItem( SCH_ACTIONS::toGLabel, toGLabelCondition );
858 menu->AddItem( SCH_ACTIONS::toText, toTextCondition );
859 menu->AddItem( SCH_ACTIONS::toTextBox, toTextBoxCondition );
860
861 return menu;
862 };
863
864 auto makeLockMenu = [&]( TOOL_INTERACTIVE* tool )
865 {
866 std::shared_ptr<LOCK_CONTEXT_MENU> menu = std::make_shared<LOCK_CONTEXT_MENU>( tool );
867 tool->GetToolMenu().RegisterSubMenu( menu );
868 return menu.get();
869 };
870
871 const auto canCopyText = SCH_CONDITIONS::OnlyTypes( {
880 SCH_PIN_T,
883 } );
884
885 //
886 // Add edit actions to the move tool menu
887 //
888 CONDITIONAL_MENU& moveMenu = moveTool->GetToolMenu().GetMenu();
889
890 moveMenu.AddSeparator();
891 moveMenu.AddMenu( makeSymbolUnitMenu( moveTool ), S_C::SingleMultiUnitSymbol, 1 );
892 moveMenu.AddMenu( makeBodyStyleMenu( moveTool ), S_C::SingleMultiBodyStyleSymbol, 1 );
893
894 moveMenu.AddMenu( makeTransformMenu(), orientCondition, 200 );
895 moveMenu.AddMenu( makeAttributesMenu(), S_C::HasTypes( attribTypes ), 200 );
896 moveMenu.AddItem( SCH_ACTIONS::swap, swapSelectionCondition, 200 );
897 moveMenu.AddItem( SCH_ACTIONS::properties, propertiesCondition, 200 );
898 moveMenu.AddMenu( makeEditFieldsMenu(), S_C::SingleSymbol, 200 );
899
900 moveMenu.AddSeparator();
901 moveMenu.AddItem( ACTIONS::cut, S_C::IdleSelection );
902 moveMenu.AddItem( ACTIONS::copy, S_C::IdleSelection );
903 moveMenu.AddItem( ACTIONS::copyAsText, canCopyText && S_C::IdleSelection );
904 moveMenu.AddItem( ACTIONS::doDelete, S_C::NotEmpty );
905 moveMenu.AddItem( ACTIONS::duplicate, duplicateCondition );
906
907 //
908 // Add editing actions to the drawing tool menu
909 //
910 CONDITIONAL_MENU& drawMenu = drawingTools->GetToolMenu().GetMenu();
911
912 drawMenu.AddItem( SCH_ACTIONS::clearHighlight, haveHighlight && S_C::Idle, 1 );
913 drawMenu.AddSeparator( haveHighlight && S_C::Idle, 1 );
914
915 drawMenu.AddItem( SCH_ACTIONS::enterSheet, sheetSelection && S_C::Idle, 1 );
916 drawMenu.AddSeparator( sheetSelection && S_C::Idle, 1 );
917
918 drawMenu.AddMenu( makeSymbolUnitMenu( drawingTools ), S_C::SingleMultiUnitSymbol, 1 );
919 drawMenu.AddMenu( makeBodyStyleMenu( drawingTools ), S_C::SingleMultiBodyStyleSymbol, 1 );
920
921 drawMenu.AddMenu( makeTransformMenu(), orientCondition, 200 );
922 drawMenu.AddMenu( makeAttributesMenu(), S_C::HasTypes( attribTypes ), 200 );
923 drawMenu.AddItem( SCH_ACTIONS::properties, propertiesCondition, 200 );
924 drawMenu.AddMenu( makeEditFieldsMenu(), S_C::SingleSymbol, 200 );
925 drawMenu.AddItem( SCH_ACTIONS::autoplaceFields, autoplaceCondition, 200 );
926
928
929 drawMenu.AddItem( SCH_ACTIONS::toLabel, anyTextTool && S_C::Idle, 200 );
930 drawMenu.AddItem( SCH_ACTIONS::toHLabel, anyTextTool && S_C::Idle, 200 );
931 drawMenu.AddItem( SCH_ACTIONS::toGLabel, anyTextTool && S_C::Idle, 200 );
932 drawMenu.AddItem( SCH_ACTIONS::toText, anyTextTool && S_C::Idle, 200 );
933 drawMenu.AddItem( SCH_ACTIONS::toTextBox, anyTextTool && S_C::Idle, 200 );
934
935 //
936 // Add editing actions to the selection tool menu
937 //
938 CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
939
940 selToolMenu.AddMenu( makeSymbolUnitMenu( m_selectionTool ), S_C::SingleMultiUnitSymbol, 1 );
941 selToolMenu.AddMenu( makeBodyStyleMenu( m_selectionTool ), S_C::SingleMultiBodyStyleSymbol, 1 );
942 selToolMenu.AddMenu( makePinFunctionMenu( m_selectionTool ), S_C::SingleMultiFunctionPin, 1 );
943 selToolMenu.AddMenu( makePinTricksMenu( m_selectionTool ), S_C::AllPinsOrSheetPins, 1 );
944
945 selToolMenu.AddMenu( makeTransformMenu(), orientCondition, 200 );
946 selToolMenu.AddMenu( makeAttributesMenu(), S_C::HasTypes( attribTypes ), 200 );
947 selToolMenu.AddItem( SCH_ACTIONS::swap, swapSelectionCondition, 200 );
948 selToolMenu.AddItem( SCH_ACTIONS::properties, propertiesCondition, 200 );
949 selToolMenu.AddMenu( makeEditFieldsMenu(), S_C::SingleSymbol, 200 );
950 selToolMenu.AddItem( SCH_ACTIONS::autoplaceFields, autoplaceCondition, 200 );
951
954 S_C::SingleSymbolOrPower && noVariantActiveCondition, 200 );
957 S_C::MultipleSymbolsOrPower && noVariantActiveCondition, 200 );
959
961 S_C::SingleSymbolOrPower && variantActiveCondition, 200 );
963 symbolHasVariantSymbol, 200 );
964 selToolMenu.AddMenu( makeConvertToMenu(), toChangeCondition, 200 );
965
966 selToolMenu.AddItem( SCH_ACTIONS::cleanupSheetPins, sheetHasUndefinedPins, 250 );
967 selToolMenu.AddMenu( makeLockMenu( m_selectionTool ), S_C::NotEmpty, 250 );
968
969 selToolMenu.AddSeparator( 300 );
970 selToolMenu.AddItem( ACTIONS::cut, S_C::IdleSelection, 300 );
971 selToolMenu.AddItem( ACTIONS::copy, S_C::IdleSelection, 300 );
972 selToolMenu.AddItem( ACTIONS::copyAsText, canCopyText && S_C::IdleSelection, 300 );
973 selToolMenu.AddItem( ACTIONS::paste, S_C::Idle, 300 );
974 selToolMenu.AddItem( ACTIONS::pasteSpecial, S_C::Idle, 300 );
975 selToolMenu.AddItem( ACTIONS::doDelete, S_C::NotEmpty, 300 );
976 selToolMenu.AddItem( ACTIONS::duplicate, duplicateCondition, 300 );
977
978 selToolMenu.AddSeparator( 400 );
979 selToolMenu.AddItem( ACTIONS::selectAll, S_C::ShowAlways, 400 );
980 selToolMenu.AddItem( ACTIONS::unselectAll, S_C::ShowAlways, 400 );
981
982 ACTION_MANAGER* mgr = m_toolMgr->GetActionManager();
983
984 mgr->SetConditions( SCH_ACTIONS::setDNP, ACTION_CONDITIONS().Check( attribDNPCond ) );
985 mgr->SetConditions( SCH_ACTIONS::setExcludeFromSim, ACTION_CONDITIONS().Check( attribExcludeFromSimCond ) );
986 mgr->SetConditions( SCH_ACTIONS::setExcludeFromBOM, ACTION_CONDITIONS().Check( attribExcludeFromBOMCond ) );
987 mgr->SetConditions( SCH_ACTIONS::setExcludeFromBoard, ACTION_CONDITIONS().Check( attribExcludeFromBoardCond ) );
989 ACTION_CONDITIONS().Check( attribExcludeFromPosFilesCond ) );
990
991 return true;
992}
993
994
995const std::vector<KICAD_T> SCH_EDIT_TOOL::RotatableItems = {
1001 SCH_TABLECELL_T, // will be promoted to parent table(s)
1014 SCH_LINE_T,
1017};
1018
1019
1020const std::vector<KICAD_T> SCH_EDIT_TOOL::SwappableItems = {
1023 SCH_TEXT_T,
1036};
1037
1038
1040{
1041 bool clockwise = ( aEvent.Matches( SCH_ACTIONS::rotateCW.MakeEvent() ) );
1042 SCH_SELECTION& selection = m_selectionTool->RequestSelection( RotatableItems, true, false );
1043
1044 m_selectionTool->FilterSelectionForLockedItems();
1045
1046 wxLogTrace( "KICAD_SCH_MOVE", "SCH_EDIT_TOOL::Rotate: start, clockwise=%d, selection size=%u", clockwise,
1047 selection.GetSize() );
1048
1049 if( selection.GetSize() == 0 )
1050 return 0;
1051
1052 SCH_ITEM* head = nullptr;
1053 int principalItemCount = 0; // User-selected items (as opposed to connected wires)
1054 VECTOR2I rotPoint;
1055 bool moving = false;
1056 SCH_COMMIT localCommit( m_toolMgr );
1057 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
1058 SCH_SCREEN* screen = m_frame->GetScreen();
1059
1060 std::map<SCH_SHEET_PIN*, SCH_NO_CONNECT*> noConnects;
1061
1062 if( !commit )
1063 commit = &localCommit;
1064
1065 for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
1066 {
1067 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
1068
1069 if( item->HasFlag( SELECTED_BY_DRAG ) )
1070 continue;
1071
1072 principalItemCount++;
1073
1074 if( !head )
1075 head = item;
1076 }
1077
1078 if( head && head->IsMoving() )
1079 moving = true;
1080
1081 if( principalItemCount == 1 )
1082 {
1083 if( moving && selection.HasReferencePoint() )
1084 rotPoint = selection.GetReferencePoint();
1085 else if( head->IsConnectable() )
1086 rotPoint = head->GetPosition();
1087 else
1088 rotPoint = m_frame->GetNearestHalfGridPosition( head->GetBoundingBox().GetCenter() );
1089
1090 if( !moving )
1091 commit->Modify( head, screen, RECURSE_MODE::RECURSE );
1092
1093 switch( head->Type() )
1094 {
1095 case SCH_SYMBOL_T:
1096 {
1097 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( head );
1098
1099 symbol->Rotate( rotPoint, !clockwise );
1100
1101 if( m_frame->eeconfig()->m_AutoplaceFields.enable )
1102 {
1103 AUTOPLACE_ALGO fieldsAutoplaced = symbol->GetFieldsAutoplaced();
1104
1105 if( fieldsAutoplaced == AUTOPLACE_AUTO || fieldsAutoplaced == AUTOPLACE_MANUAL )
1106 symbol->AutoplaceFields( screen, fieldsAutoplaced );
1107 }
1108
1109 break;
1110 }
1111
1112 case SCH_TEXT_T:
1113 case SCH_LABEL_T:
1114 case SCH_GLOBAL_LABEL_T:
1115 case SCH_HIER_LABEL_T:
1117 {
1118 SCH_TEXT* textItem = static_cast<SCH_TEXT*>( head );
1119 textItem->Rotate90( clockwise );
1120 break;
1121 }
1122
1123 case SCH_SHEET_PIN_T:
1124 {
1125 // Rotate pin within parent sheet
1126 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( head );
1127 SCH_SHEET* sheet = pin->GetParent();
1128
1129 for( SCH_ITEM* ncItem : screen->Items().Overlapping( SCH_NO_CONNECT_T, pin->GetTextPos() ) )
1130 noConnects[pin] = static_cast<SCH_NO_CONNECT*>( ncItem );
1131
1132 pin->Rotate( sheet->GetBoundingBox().GetCenter(), !clockwise );
1133
1134 break;
1135 }
1136
1137 case SCH_LINE_T:
1138 {
1139 SCH_LINE* line = static_cast<SCH_LINE*>( head );
1140
1141 // Equal checks for both and neither. We need this because on undo
1142 // the item will have both flags cleared, but will be selected, so it is possible
1143 // for the user to get a selected line with neither endpoint selected. We
1144 // set flags to make sure Rotate() works when we call it.
1145 if( line->HasFlag( STARTPOINT ) == line->HasFlag( ENDPOINT ) )
1146 {
1147 line->SetFlags( STARTPOINT | ENDPOINT );
1148
1149 // When we allow off grid items, the rotPoint should be set to the midpoint
1150 // of the line to allow rotation around the center, and the next if
1151 // should become an else-if
1152 }
1153
1154 if( line->HasFlag( STARTPOINT ) )
1155 rotPoint = line->GetEndPoint();
1156 else if( line->HasFlag( ENDPOINT ) )
1157 rotPoint = line->GetStartPoint();
1158 }
1159
1161 case SCH_JUNCTION_T:
1162 case SCH_NO_CONNECT_T:
1165 head->Rotate( rotPoint, !clockwise );
1166
1167 break;
1168
1169 case SCH_FIELD_T:
1170 {
1171 SCH_FIELD* field = static_cast<SCH_FIELD*>( head );
1172
1173 if( field->GetTextAngle().IsHorizontal() )
1174 field->SetTextAngle( ANGLE_VERTICAL );
1175 else
1177
1178 // Now that we're moving a field, they're no longer autoplaced.
1179 static_cast<SCH_ITEM*>( head->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
1180
1181 break;
1182 }
1183
1184 case SCH_RULE_AREA_T:
1185 case SCH_SHAPE_T:
1186 case SCH_TEXTBOX_T:
1187 head->Rotate( rotPoint, !clockwise );
1188
1189 break;
1190
1191 case SCH_GROUP_T:
1192 {
1193 // Rotate the group on itself. Groups do not have an anchor point.
1194 SCH_GROUP* group = static_cast<SCH_GROUP*>( head );
1195 rotPoint = m_frame->GetNearestHalfGridPosition( group->GetPosition() );
1196
1197 group->Rotate( rotPoint, !clockwise );
1198
1199 group->Move( rotPoint - m_frame->GetNearestHalfGridPosition( group->GetPosition() ) );
1200
1201 break;
1202 }
1203
1204 case SCH_TABLE_T:
1205 {
1206 // Rotate the table on itself. Tables do not have an anchor point.
1207 SCH_TABLE* table = static_cast<SCH_TABLE*>( head );
1208 rotPoint = m_frame->GetNearestHalfGridPosition( table->GetCenter() );
1209
1210 table->Rotate( rotPoint, !clockwise );
1211
1212 table->Move( rotPoint - m_frame->GetNearestHalfGridPosition( table->GetCenter() ) );
1213
1214 break;
1215 }
1216
1217 case SCH_BITMAP_T:
1218 head->Rotate( rotPoint, clockwise );
1219
1220 // The bitmap is cached in Opengl: clear the cache to redraw
1222 break;
1223
1224 case SCH_SHEET_T:
1225 {
1226 // Rotate the sheet on itself. Sheets do not have an anchor point.
1227 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( head );
1228
1229 noConnects = sheet->GetNoConnects();
1230
1231 rotPoint = m_frame->GetNearestHalfGridPosition( sheet->GetRotationCenter() );
1232 sheet->Rotate( rotPoint, !clockwise );
1233
1234 break;
1235 }
1236
1237 default:
1238 UNIMPLEMENTED_FOR( head->GetClass() );
1239 }
1240
1241 m_frame->UpdateItem( head, false, true );
1242 }
1243 else
1244 {
1245 if( moving && selection.HasReferencePoint() )
1246 rotPoint = selection.GetReferencePoint();
1247 else
1248 rotPoint = m_frame->GetNearestHalfGridPosition( selection.GetCenter() );
1249 }
1250
1251 for( EDA_ITEM* edaItem : selection )
1252 {
1253 SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
1254
1255 // We've already rotated the user selected item if there was only one. We're just
1256 // here to rotate the ends of wires that were attached to it.
1257 if( principalItemCount == 1 && !item->HasFlag( SELECTED_BY_DRAG ) )
1258 continue;
1259
1260 if( !moving )
1261 commit->Modify( item, screen, RECURSE_MODE::RECURSE );
1262
1263 if( item->Type() == SCH_LINE_T )
1264 {
1265 SCH_LINE* line = (SCH_LINE*) item;
1266
1267 line->Rotate( rotPoint, !clockwise );
1268 }
1269 else if( item->Type() == SCH_SHEET_PIN_T )
1270 {
1271 if( item->GetParent()->IsSelected() )
1272 {
1273 // parent will rotate us
1274 }
1275 else
1276 {
1277 // rotate within parent
1278 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
1279 SCH_SHEET* sheet = pin->GetParent();
1280
1281 for( SCH_ITEM* ncItem : screen->Items().Overlapping( SCH_NO_CONNECT_T, pin->GetTextPos() ) )
1282 noConnects[pin] = static_cast<SCH_NO_CONNECT*>( ncItem );
1283
1284 pin->Rotate( sheet->GetBodyBoundingBox().GetCenter(), !clockwise );
1285 }
1286 }
1287 else if( item->Type() == SCH_FIELD_T )
1288 {
1289 if( item->GetParent()->IsSelected() )
1290 {
1291 // parent will rotate us
1292 }
1293 else
1294 {
1295 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
1296
1297 field->Rotate( rotPoint, !clockwise );
1298
1299 // Now that we're moving a field, they're no longer autoplaced.
1300 static_cast<SCH_ITEM*>( field->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
1301 }
1302 }
1303 else if( item->Type() == SCH_TABLE_T )
1304 {
1305 SCH_TABLE* table = static_cast<SCH_TABLE*>( item );
1306 VECTOR2I beforeCenter = table->GetCenter();
1307
1308 table->Rotate( rotPoint, !clockwise );
1309 RotatePoint( beforeCenter, rotPoint, clockwise ? -ANGLE_90 : ANGLE_90 );
1310
1311 table->Move( beforeCenter - table->GetCenter() );
1312 }
1313 else if( item->Type() == SCH_SHEET_T )
1314 {
1315 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1316
1317 noConnects = sheet->GetNoConnects();
1318
1319 sheet->Rotate( rotPoint, !clockwise );
1320 }
1321 else
1322 {
1323 VECTOR2I posBefore = item->GetPosition();
1324 item->Rotate( rotPoint, !clockwise );
1325 VECTOR2I posAfter = item->GetPosition();
1326 wxLogTrace( "KICAD_SCH_MOVE", " SCH_EDIT_TOOL::Rotate: item type=%d rotated, pos (%d,%d) -> (%d,%d)",
1327 item->Type(), posBefore.x, posBefore.y, posAfter.x, posAfter.y );
1328 }
1329
1330 m_frame->UpdateItem( item, false, true );
1331 updateItem( item, true );
1332 }
1333
1334 wxLogTrace( "KICAD_SCH_MOVE", "SCH_EDIT_TOOL::Rotate: complete, moving=%d", moving );
1335
1336 if( moving )
1337 {
1338 wxLogTrace( "KICAD_SCH_MOVE", "SCH_EDIT_TOOL::Rotate: posting refreshPreview" );
1339 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1340 }
1341 else
1342 {
1343 for( auto& [sheetPin, noConnect] : noConnects )
1344 {
1345 if( noConnect->GetPosition() != sheetPin->GetTextPos() )
1346 {
1347 commit->Modify( noConnect, screen );
1348 noConnect->SetPosition( sheetPin->GetTextPos() );
1349 updateItem( noConnect, true );
1350 }
1351 }
1352
1353 SCH_SELECTION selectionCopy = selection;
1354
1355 if( selection.IsHover() )
1356 m_toolMgr->RunAction( ACTIONS::selectionClear );
1357
1359 lwbTool->TrimOverLappingWires( commit, &selectionCopy );
1360 lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy );
1361
1362 m_frame->Schematic().CleanUp( commit );
1363
1364 if( !localCommit.Empty() )
1365 localCommit.Push( _( "Rotate" ) );
1366 }
1367
1368 return 0;
1369}
1370
1371
1373{
1374 SCH_SELECTION& selection = m_selectionTool->RequestSelection( RotatableItems, false, false );
1375
1376 m_selectionTool->FilterSelectionForLockedItems();
1377
1378 if( selection.GetSize() == 0 )
1379 return 0;
1380
1381 bool vertical = ( aEvent.Matches( SCH_ACTIONS::mirrorV.MakeEvent() ) );
1382 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
1383 bool connections = false;
1384 bool moving = item->IsMoving();
1385 SCH_COMMIT localCommit( m_toolMgr );
1386 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
1387 SCH_SCREEN* screen = m_frame->GetScreen();
1388
1389 std::map<SCH_SHEET_PIN*, SCH_NO_CONNECT*> noConnects;
1390
1391 if( !commit )
1392 commit = &localCommit;
1393
1394 if( selection.GetSize() == 1 )
1395 {
1396 if( !moving )
1397 commit->Modify( item, screen, RECURSE_MODE::RECURSE );
1398
1399 switch( item->Type() )
1400 {
1401 case SCH_SYMBOL_T:
1402 {
1403 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1404
1405 if( vertical )
1406 symbol->SetOrientation( SYM_MIRROR_X );
1407 else
1408 symbol->SetOrientation( SYM_MIRROR_Y );
1409
1411 break;
1412 }
1413
1414 case SCH_TEXT_T:
1415 case SCH_LABEL_T:
1416 case SCH_GLOBAL_LABEL_T:
1417 case SCH_HIER_LABEL_T:
1419 {
1420 SCH_TEXT* textItem = static_cast<SCH_TEXT*>( item );
1421 textItem->MirrorSpinStyle( !vertical );
1422 break;
1423 }
1424
1425 case SCH_SHEET_PIN_T:
1426 {
1427 // mirror within parent sheet
1428 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
1429 SCH_SHEET* sheet = pin->GetParent();
1430
1431 for( SCH_ITEM* ncItem : screen->Items().Overlapping( SCH_NO_CONNECT_T, pin->GetTextPos() ) )
1432 noConnects[pin] = static_cast<SCH_NO_CONNECT*>( ncItem );
1433
1434 if( vertical )
1435 pin->MirrorVertically( sheet->GetBoundingBox().GetCenter().y );
1436 else
1437 pin->MirrorHorizontally( sheet->GetBoundingBox().GetCenter().x );
1438
1439 break;
1440 }
1441
1442 case SCH_FIELD_T:
1443 {
1444 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
1445
1446 if( vertical )
1448 else
1450
1451 // Now that we're re-justifying a field, they're no longer autoplaced.
1452 static_cast<SCH_ITEM*>( field->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
1453
1454 break;
1455 }
1456
1457 case SCH_BITMAP_T:
1458 if( vertical )
1459 item->MirrorVertically( item->GetPosition().y );
1460 else
1461 item->MirrorHorizontally( item->GetPosition().x );
1462
1463 // The bitmap is cached in Opengl: clear the cache to redraw
1465 break;
1466
1467 case SCH_SHEET_T:
1468 {
1469 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1470
1471 noConnects = sheet->GetNoConnects();
1472
1473 // Mirror the sheet on itself. Sheets do not have a anchor point.
1474 VECTOR2I mirrorPoint = m_frame->GetNearestHalfGridPosition( sheet->GetBoundingBox().Centre() );
1475
1476 if( vertical )
1477 sheet->MirrorVertically( mirrorPoint.y );
1478 else
1479 sheet->MirrorHorizontally( mirrorPoint.x );
1480
1482 break;
1483 }
1484
1485 default:
1486 if( vertical )
1487 item->MirrorVertically( item->GetPosition().y );
1488 else
1489 item->MirrorHorizontally( item->GetPosition().x );
1490
1492 break;
1493 }
1494
1495 connections = item->IsConnectable();
1496 m_frame->UpdateItem( item, false, true );
1497 }
1498 else if( selection.GetSize() > 1 )
1499 {
1500 VECTOR2I mirrorPoint = m_frame->GetNearestHalfGridPosition( selection.GetCenter() );
1501
1502 for( EDA_ITEM* edaItem : selection )
1503 {
1504 item = static_cast<SCH_ITEM*>( edaItem );
1505
1506 if( !moving )
1507 commit->Modify( item, screen, RECURSE_MODE::RECURSE );
1508
1509 if( item->Type() == SCH_SHEET_PIN_T )
1510 {
1511 if( item->GetParent()->IsSelected() )
1512 {
1513 // parent will mirror us
1514 }
1515 else
1516 {
1517 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
1518 SCH_SHEET* sheet = pin->GetParent();
1519
1520 for( SCH_ITEM* ncItem : screen->Items().Overlapping( SCH_NO_CONNECT_T, pin->GetTextPos() ) )
1521 noConnects[pin] = static_cast<SCH_NO_CONNECT*>( ncItem );
1522
1523 // mirror within parent sheet
1524 if( vertical )
1525 pin->MirrorVertically( sheet->GetBoundingBox().GetCenter().y );
1526 else
1527 pin->MirrorHorizontally( sheet->GetBoundingBox().GetCenter().x );
1528
1529 pin->SetFieldsAutoplaced( AUTOPLACE_NONE );
1530 }
1531 }
1532 else if( item->Type() == SCH_FIELD_T )
1533 {
1534 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
1535
1536 if( vertical )
1538 else
1540
1541 // Now that we're re-justifying a field, they're no longer autoplaced.
1542 static_cast<SCH_ITEM*>( field->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
1543 }
1544 else if( item->Type() == SCH_SHEET_T )
1545 {
1546 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1547
1548 noConnects = sheet->GetNoConnects();
1549
1550 if( vertical )
1551 sheet->MirrorVertically( mirrorPoint.y );
1552 else
1553 sheet->MirrorHorizontally( mirrorPoint.x );
1554
1556 }
1557 else
1558 {
1559 if( vertical )
1560 item->MirrorVertically( mirrorPoint.y );
1561 else
1562 item->MirrorHorizontally( mirrorPoint.x );
1563
1565 }
1566
1567 connections |= item->IsConnectable();
1568 m_frame->UpdateItem( item, false, true );
1569 }
1570 }
1571
1572 // Update R-Tree for modified items
1573 for( EDA_ITEM* selected : selection )
1574 updateItem( selected, true );
1575
1576 if( item->IsMoving() )
1577 {
1578 m_toolMgr->RunAction( ACTIONS::refreshPreview );
1579 }
1580 else
1581 {
1582 for( auto& [sheetPin, noConnect] : noConnects )
1583 {
1584 if( noConnect->GetPosition() != sheetPin->GetTextPos() )
1585 {
1586 commit->Modify( noConnect, screen );
1587 noConnect->SetPosition( sheetPin->GetTextPos() );
1588 updateItem( noConnect, true );
1589 }
1590 }
1591
1592 SCH_SELECTION selectionCopy = selection;
1593
1594 if( selection.IsHover() )
1595 m_toolMgr->RunAction( ACTIONS::selectionClear );
1596
1597 if( connections )
1598 {
1600 lwbTool->TrimOverLappingWires( commit, &selectionCopy );
1601 lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy );
1602
1603 m_frame->Schematic().CleanUp( commit );
1604 }
1605
1606 if( !localCommit.Empty() )
1607 localCommit.Push( _( "Mirror" ) );
1608 }
1609
1610 return 0;
1611}
1612
1622static void swapFieldPositionsWithMatching( std::vector<SCH_FIELD>& aAFields, std::vector<SCH_FIELD>& aBFields,
1623 unsigned aFallbackRotationsCCW )
1624{
1625 std::set<wxString> handledKeys;
1626
1627 const auto swapFieldTextProps = []( SCH_FIELD& aField, SCH_FIELD& bField )
1628 {
1629 const VECTOR2I aRelPos = aField.GetPosition() - aField.GetParentPosition();
1630 const GR_TEXT_H_ALIGN_T aTextJustifyH = aField.GetHorizJustify();
1631 const GR_TEXT_V_ALIGN_T aTextJustifyV = aField.GetVertJustify();
1632 const EDA_ANGLE aTextAngle = aField.GetTextAngle();
1633
1634 const VECTOR2I bRelPos = bField.GetPosition() - bField.GetParentPosition();
1635 const GR_TEXT_H_ALIGN_T bTextJustifyH = bField.GetHorizJustify();
1636 const GR_TEXT_V_ALIGN_T bTextJustifyV = bField.GetVertJustify();
1637 const EDA_ANGLE bTextAngle = bField.GetTextAngle();
1638
1639 aField.SetPosition( aField.GetParentPosition() + bRelPos );
1640 aField.SetHorizJustify( bTextJustifyH );
1641 aField.SetVertJustify( bTextJustifyV );
1642 aField.SetTextAngle( bTextAngle );
1643
1644 bField.SetPosition( bField.GetParentPosition() + aRelPos );
1645 bField.SetHorizJustify( aTextJustifyH );
1646 bField.SetVertJustify( aTextJustifyV );
1647 bField.SetTextAngle( aTextAngle );
1648 };
1649
1650 for( SCH_FIELD& aField : aAFields )
1651 {
1652 const wxString name = aField.GetUntranslatedName();
1653
1654 auto it = std::find_if( aBFields.begin(), aBFields.end(),
1655 [name]( const SCH_FIELD& bField )
1656 {
1657 return bField.GetUntranslatedName() == name;
1658 } );
1659
1660 if( it != aBFields.end() )
1661 {
1662 // We have a field with the same key in both labels
1663 SCH_FIELD& bField = *it;
1664 swapFieldTextProps( aField, bField );
1665 }
1666 else
1667 {
1668 // We only have this field in A, so just rotate it
1669 for( unsigned ii = 0; ii < aFallbackRotationsCCW; ii++ )
1670 {
1671 aField.Rotate( aField.GetParentPosition(), true );
1672 }
1673 }
1674
1675 // And keep track that we did this one
1676 handledKeys.insert( name );
1677 }
1678
1679 // Any fields in B that weren't in A weren't handled and need to be rotated
1680 // in reverse
1681 for( SCH_FIELD& bField : aBFields )
1682 {
1683 const wxString bName = bField.GetUntranslatedName();
1684 if( handledKeys.find( bName ) == handledKeys.end() )
1685 {
1686 for( unsigned ii = 0; ii < aFallbackRotationsCCW; ii++ )
1687 {
1688 bField.Rotate( bField.GetParentPosition(), false );
1689 }
1690 }
1691 }
1692}
1693
1694
1696{
1697 SCH_SELECTION& selection = m_selectionTool->RequestSelection( SwappableItems );
1698
1699 m_selectionTool->FilterSelectionForLockedItems();
1700
1701 std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
1702
1703 if( selection.Size() < 2 )
1704 return 0;
1705
1706 // Sheet pins are special, we need to make sure if we have any sheet pins,
1707 // that we only have sheet pins, and that they have the same parent
1708 if( selection.CountType( SCH_SHEET_PIN_T ) > 0 )
1709 {
1710 if( !selection.OnlyContains( { SCH_SHEET_PIN_T } ) )
1711 return 0;
1712
1713 EDA_ITEM* parent = selection.Front()->GetParent();
1714
1715 for( EDA_ITEM* item : selection )
1716 {
1717 if( item->GetParent() != parent )
1718 return 0;
1719 }
1720 }
1721
1722 bool moving = selection.Front()->IsMoving();
1723 bool connections = false;
1724
1725 SCH_COMMIT localCommit( m_toolMgr );
1726 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
1727
1728 if( !commit )
1729 commit = &localCommit;
1730
1731 SCH_SCREEN* screen = m_frame->GetScreen();
1732 std::map<SCH_SHEET_PIN*, SCH_NO_CONNECT*> noConnects;
1733
1734 if( !moving )
1735 {
1736 for( EDA_ITEM* item : sorted )
1737 {
1738 if( item->Type() == SCH_SHEET_T )
1739 {
1740 std::map<SCH_SHEET_PIN*, SCH_NO_CONNECT*> sheetNoConnects =
1741 static_cast<SCH_SHEET*>( item )->GetNoConnects();
1742
1743 noConnects.insert( sheetNoConnects.begin(), sheetNoConnects.end() );
1744 }
1745 else if( item->Type() == SCH_SHEET_PIN_T )
1746 {
1747 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
1748
1749 for( SCH_ITEM* ncItem : screen->Items().Overlapping( SCH_NO_CONNECT_T, pin->GetTextPos() ) )
1750 noConnects[pin] = static_cast<SCH_NO_CONNECT*>( ncItem );
1751 }
1752 }
1753 }
1754
1755 for( size_t i = 0; i < sorted.size() - 1; i++ )
1756 {
1757 SCH_ITEM* a = static_cast<SCH_ITEM*>( sorted[i] );
1758 SCH_ITEM* b = static_cast<SCH_ITEM*>( sorted[( i + 1 ) % sorted.size()] );
1759
1760 if( !moving )
1761 {
1762 commit->Modify( a, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1763 commit->Modify( b, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1764 }
1765
1766 VECTOR2I aPos = a->GetPosition(), bPos = b->GetPosition();
1767 std::swap( aPos, bPos );
1768
1769 // Sheet pins need to have their sides swapped before we change their
1770 // positions
1771 if( a->Type() == SCH_SHEET_PIN_T )
1772 {
1773 SCH_SHEET_PIN* aPin = static_cast<SCH_SHEET_PIN*>( a );
1774 SCH_SHEET_PIN* bPin = static_cast<SCH_SHEET_PIN*>( b );
1775 SHEET_SIDE aSide = aPin->GetSide(), bSide = bPin->GetSide();
1776 std::swap( aSide, bSide );
1777 aPin->SetSide( aSide );
1778 bPin->SetSide( bSide );
1779 }
1780
1781 a->SetPosition( aPos );
1782 b->SetPosition( bPos );
1783
1784 if( a->Type() == b->Type() )
1785 {
1786 switch( a->Type() )
1787 {
1788 case SCH_LABEL_T:
1789 case SCH_GLOBAL_LABEL_T:
1790 case SCH_HIER_LABEL_T:
1792 {
1793 SCH_LABEL_BASE& aLabelBase = static_cast<SCH_LABEL_BASE&>( *a );
1794 SCH_LABEL_BASE& bLabelBase = static_cast<SCH_LABEL_BASE&>( *b );
1795
1796 const SPIN_STYLE aSpinStyle = aLabelBase.GetSpinStyle();
1797 const SPIN_STYLE bSpinStyle = bLabelBase.GetSpinStyle();
1798 const GR_TEXT_V_ALIGN_T aVertJustify = aLabelBase.GetVertJustify();
1799 const GR_TEXT_V_ALIGN_T bVertJustify = bLabelBase.GetVertJustify();
1800
1801 // First, swap the label orientations
1802 aLabelBase.SetSpinStyle( bSpinStyle );
1803 bLabelBase.SetSpinStyle( aSpinStyle );
1804 aLabelBase.SetVertJustify( bVertJustify );
1805 bLabelBase.SetVertJustify( aVertJustify );
1806
1807 // And swap the fields as best we can
1808 std::vector<SCH_FIELD>& aFields = aLabelBase.GetFields();
1809 std::vector<SCH_FIELD>& bFields = bLabelBase.GetFields();
1810
1811 const unsigned rotationsAtoB = aSpinStyle.CCWRotationsTo( bSpinStyle );
1812
1813 swapFieldPositionsWithMatching( aFields, bFields, rotationsAtoB );
1814 break;
1815 }
1816 case SCH_TEXT_T:
1817 case SCH_TEXTBOX_T:
1818 {
1819 EDA_TEXT* aText = dynamic_cast<EDA_TEXT*>( a );
1820 EDA_TEXT* bText = dynamic_cast<EDA_TEXT*>( b );
1821
1822 if( !aText || !bText )
1823 break;
1824
1825 const GR_TEXT_H_ALIGN_T aHorizJustify = aText->GetHorizJustify();
1826 const GR_TEXT_V_ALIGN_T aVertJustify = aText->GetVertJustify();
1827 const GR_TEXT_H_ALIGN_T bHorizJustify = bText->GetHorizJustify();
1828 const GR_TEXT_V_ALIGN_T bVertJustify = bText->GetVertJustify();
1829
1830 aText->SetHorizJustify( bHorizJustify );
1831 aText->SetVertJustify( bVertJustify );
1832 bText->SetHorizJustify( aHorizJustify );
1833 bText->SetVertJustify( aVertJustify );
1834 break;
1835 }
1836 case SCH_SYMBOL_T:
1837 {
1838 SCH_SYMBOL* aSymbol = static_cast<SCH_SYMBOL*>( a );
1839 SCH_SYMBOL* bSymbol = static_cast<SCH_SYMBOL*>( b );
1840
1841 // Only swap orientations when both symbols are the same library symbol.
1842 // Different symbols (e.g. LED vs resistor) have different default orientations,
1843 // so swapping their orientations leads to unexpected visual results.
1844 if( aSymbol->GetLibId() == bSymbol->GetLibId() )
1845 {
1846 int aOrient = aSymbol->GetOrientation();
1847 int bOrient = bSymbol->GetOrientation();
1848 std::swap( aOrient, bOrient );
1849 aSymbol->SetOrientation( aOrient );
1850 bSymbol->SetOrientation( bOrient );
1851 }
1852
1853 break;
1854 }
1855 default: break;
1856 }
1857 }
1858
1859 connections |= a->IsConnectable();
1860 connections |= b->IsConnectable();
1861 m_frame->UpdateItem( a, false, true );
1862 m_frame->UpdateItem( b, false, true );
1863 }
1864
1865 if( moving )
1866 {
1867 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1868 }
1869 else
1870 {
1871 for( auto& [sheetPin, noConnect] : noConnects )
1872 {
1873 if( noConnect->GetPosition() != sheetPin->GetTextPos() )
1874 {
1875 commit->Modify( noConnect, screen );
1876 noConnect->SetPosition( sheetPin->GetTextPos() );
1877 updateItem( noConnect, true );
1878 }
1879 }
1880
1881 if( selection.IsHover() )
1882 m_toolMgr->RunAction( ACTIONS::selectionClear );
1883
1884 if( connections )
1885 m_frame->TestDanglingEnds();
1886 m_frame->OnModify();
1887
1888 if( !localCommit.Empty() )
1889 localCommit.Push( _( "Swap" ) );
1890 }
1891
1892 return 0;
1893}
1894
1895
1896/*
1897 * This command always works on the instance owned by the schematic, never directly on the
1898 * external library file. Pins that still reference their library definition are swapped by
1899 * touching that shared lib pin first; afterwards we call UpdatePins() so the schematic now owns a
1900 * cached copy with the new geometry. Pins that already have an instance-local copy simply swap in
1901 * place. In both cases the undo stack captures the modified pins (and the parent symbol) so the
1902 * user can revert the change. Saving the schematic writes the updated pin order into the sheet,
1903 * while the global symbol library remains untouched unless the user explicitly pushes it later.
1904 */
1906{
1907 wxCHECK( m_frame, 0 );
1908
1909 if( !m_frame->eeconfig()->m_Input.allow_unconstrained_pin_swaps )
1910 return 0;
1911
1912 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_PIN_T } );
1913 m_selectionTool->FilterSelectionForLockedItems();
1914 std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
1915
1916 if( selection.Size() < 2 )
1917 return 0;
1918
1919 EDA_ITEM* parent = selection.Front()->GetParent();
1920
1921 if( !parent || parent->Type() != SCH_SYMBOL_T )
1922 return 0;
1923
1924 SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( parent );
1925
1926 // All pins need to be on the same symbol
1927 for( EDA_ITEM* item : selection )
1928 {
1929 if( item->GetParent() != parent )
1930 return 0;
1931 }
1932
1933 std::set<wxString> sharedSheetPaths;
1934 std::set<wxString> sharedProjectNames;
1935
1936 if( SymbolHasSheetInstances( *parentSymbol, m_frame->Prj().GetProjectName(), &sharedSheetPaths,
1937 &sharedProjectNames ) )
1938 {
1939 // This will give us nice names for our project, but not when the sheet is shared across
1940 // multiple projects. But, in that case we bail early and just list the project names so it isn't an issue.
1941 std::set<wxString> friendlySheets;
1942
1943 if( !sharedSheetPaths.empty() )
1944 friendlySheets = GetSheetNamesFromPaths( sharedSheetPaths, m_frame->Schematic() );
1945
1946 if( !sharedProjectNames.empty() )
1947 {
1948 wxString projects = AccumulateDescriptions( sharedProjectNames );
1949
1950 if( projects.IsEmpty() )
1951 {
1952 m_frame->ShowInfoBarError( _( "Pin swaps are disabled for symbols shared across other projects. "
1953 "Duplicate the sheet to edit pins independently." ) );
1954 }
1955 else
1956 {
1957 m_frame->ShowInfoBarError(
1958 wxString::Format( _( "Pin swaps are disabled for symbols shared across other projects (%s). "
1959 "Duplicate the sheet to edit pins independently." ),
1960 projects ) );
1961 }
1962 }
1963 else if( !friendlySheets.empty() )
1964 {
1965 wxString sheets = AccumulateDescriptions( friendlySheets );
1966
1967 m_frame->ShowInfoBarError(
1968 wxString::Format( _( "Pin swaps are disabled for symbols used by multiple sheet instances (%s). "
1969 "Duplicate the sheet to edit pins independently." ),
1970 sheets ) );
1971 }
1972 else
1973 {
1974 m_frame->ShowInfoBarError(
1975 _( "Pin swaps are disabled for shared symbols. Duplicate the sheet to edit pins independently." ) );
1976 }
1977
1978 return 0;
1979 }
1980
1981 bool connections = false;
1982
1983 SCH_COMMIT localCommit( m_toolMgr );
1984 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
1985
1986 if( !commit )
1987 commit = &localCommit;
1988
1989 // Stage the parent symbol so undo/redo captures the cache copy that UpdatePins() may rebuild
1990 // after we touch any shared library pins.
1991 commit->Modify( parentSymbol, m_frame->GetScreen(), RECURSE_MODE::RECURSE ); // RECURSE is harmless here
1992
1993 bool swappedLibPins = false;
1994
1995 for( size_t i = 0; i < sorted.size() - 1; i++ )
1996 {
1997 SCH_PIN* aPin = static_cast<SCH_PIN*>( sorted[i] );
1998 SCH_PIN* bPin = static_cast<SCH_PIN*>( sorted[( i + 1 ) % sorted.size()] );
1999
2000 // Record both pins in the commit and swap their geometry. SwapPinGeometry returns true if
2001 // it had to operate on the shared library pins (meaning the schematic instance still
2002 // referenced them), in which case UpdatePins() below promotes the symbol to an instance
2003 // copy that reflects the new pin order.
2004 commit->Modify( aPin, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
2005 commit->Modify( bPin, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
2006
2007 swappedLibPins |= SwapPinGeometry( aPin, bPin );
2008
2009 connections |= aPin->IsConnectable();
2010 connections |= bPin->IsConnectable();
2011 m_frame->UpdateItem( aPin, false, true );
2012 m_frame->UpdateItem( bPin, false, true );
2013 }
2014
2015 if( swappedLibPins )
2016 parentSymbol->UpdatePins(); // clone the library data into the schematic cache with new geometry
2017
2018 // Refresh changed symbol in screen R-Tree / lib caches
2019 m_frame->UpdateItem( parentSymbol, false, true );
2020
2021 SCH_SELECTION selectionCopy = selection;
2022
2023 if( selection.IsHover() )
2024 m_toolMgr->RunAction( ACTIONS::selectionClear );
2025
2026 // Reconcile any wiring that was connected to the swapped pins so the schematic stays tidy and
2027 // the undo stack captures the resulting edits to wires and junctions.
2029 lwbTool->TrimOverLappingWires( commit, &selectionCopy );
2030 lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy );
2031
2032 m_frame->Schematic().CleanUp( commit );
2033
2034 if( connections )
2035 m_frame->TestDanglingEnds();
2036
2037 m_frame->OnModify();
2038
2039 if( !localCommit.Empty() )
2040 localCommit.Push( _( "Swap Pins" ) );
2041
2042 return 0;
2043}
2044
2045
2046// Used by SwapPinLabels() and SwapUnitLabels() to find the single net label connected to a pin
2048 const SCH_SHEET_PATH& aSheetPath )
2049{
2050 if( !aPin )
2051 return nullptr;
2052
2053 const bool usePublished = ADVANCED_CFG::GetCfg().m_ConnectivityEngine;
2054 std::vector<SCH_ITEM*> items;
2055
2056 if( usePublished )
2057 {
2058 // Walk physical neighbors so the result matches the legacy subgraph rather than the whole net
2059 std::set<SCH_ITEM*> seen{ aPin };
2060 std::vector<SCH_ITEM*> pending{ aPin };
2061
2062 while( !pending.empty() )
2063 {
2064 SCH_ITEM* item = pending.back();
2065 pending.pop_back();
2066 items.push_back( item );
2067
2068 if( const auto connection = aSchematic.Connectivity().Connection( item->m_Uuid, aSheetPath.PathRef() ) )
2069 {
2070 for( SCH_ITEM* neighbor : connection->ConnectedItems() )
2071 {
2072 if( seen.insert( neighbor ).second )
2073 pending.push_back( neighbor );
2074 }
2075 }
2076 }
2077 }
2078 else if( CONNECTION_GRAPH* graph = aSchematic.ConnectionGraph() )
2079 {
2080 if( CONNECTION_SUBGRAPH* sg = graph->GetSubgraphForItemOnSheet( aPin, aSheetPath ) )
2081 items.assign( sg->GetItems().begin(), sg->GetItems().end() );
2082 }
2083
2084 const auto isNet = [&]( SCH_ITEM* aItem )
2085 {
2086 if( usePublished )
2087 {
2088 const auto connection = aSchematic.Connectivity().Connection( aItem->m_Uuid, aSheetPath.PathRef() );
2089 return connection && connection->IsNet();
2090 }
2091
2092 const SCH_CONNECTION* connection = aItem->Connection( &aSheetPath );
2093 return connection && connection->IsNet();
2094 };
2095
2096 size_t pinCount = 0;
2097 SCH_LABEL_BASE* label = nullptr;
2098
2099 for( SCH_ITEM* item : items )
2100 {
2101 if( item->Type() == SCH_PIN_T )
2102 pinCount++;
2103
2104 if( !item->IsType( { SCH_LABEL_T, SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T } ) || !isNet( item ) )
2105 continue;
2106
2107 if( label )
2108 return nullptr; // more than one label
2109
2110 label = static_cast<SCH_LABEL_BASE*>( item );
2111 }
2112
2113 if( pinCount != 1 )
2114 return nullptr;
2115
2116 return label;
2117}
2118
2119
2121{
2122 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_PIN_T } );
2123 std::vector<EDA_ITEM*> orderedPins = selection.GetItemsSortedBySelectionOrder();
2124
2125 if( orderedPins.size() < 2 )
2126 return 0;
2127
2128 const SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
2129
2130 std::vector<SCH_LABEL_BASE*> labels;
2131
2132 for( EDA_ITEM* item : orderedPins )
2133 {
2134 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
2135 SCH_LABEL_BASE* label = findSingleNetLabelForPin( pin, m_frame->Schematic(), sheetPath );
2136
2137 if( !label )
2138 {
2139 m_frame->ShowInfoBarError(
2140 _( "Each selected pin must have exactly one attached net label and no other pin connections." ) );
2141 return 0;
2142 }
2143
2144 labels.push_back( label );
2145 }
2146
2147 if( labels.size() >= 2 )
2148 {
2149 SCH_COMMIT commit( m_frame );
2150
2151 for( SCH_LABEL_BASE* lb : labels )
2152 commit.Modify( lb, m_frame->GetScreen() );
2153
2154 for( size_t i = 0; i < labels.size() - 1; ++i )
2155 {
2156 SCH_LABEL_BASE* a = labels[i];
2157 SCH_LABEL_BASE* b = labels[( i + 1 ) % labels.size()];
2158 wxString aText = a->GetText();
2159 wxString bText = b->GetText();
2160 a->SetText( bText );
2161 b->SetText( aText );
2162 }
2163
2164 commit.Push( _( "Swap Pin Labels" ) );
2165 }
2166
2167 return 0;
2168}
2169
2170
2172{
2173 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
2174 std::vector<SCH_SYMBOL*> selectedUnits = GetSameSymbolMultiUnitSelection( selection );
2175
2176 if( selectedUnits.size() < 2 )
2177 return 0;
2178
2179 const SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
2180
2181 // Build ordered label vectors (sorted by pin X/Y) for each selected unit
2182 std::vector<std::vector<SCH_LABEL_BASE*>> symbolLabelVectors;
2183
2184 for( SCH_SYMBOL* symbol : selectedUnits )
2185 {
2186 std::vector<std::pair<VECTOR2I, SCH_LABEL_BASE*>> byPos;
2187
2188 for( SCH_PIN* pin : symbol->GetPins( &sheetPath ) )
2189 {
2190 SCH_LABEL_BASE* label = findSingleNetLabelForPin( pin, m_frame->Schematic(), sheetPath );
2191
2192 if( !label )
2193 {
2194 m_frame->ShowInfoBarError( _( "Each pin of selected units must have exactly one attached net label and "
2195 "no other pin connections." ) );
2196 return 0;
2197 }
2198
2199 byPos.emplace_back( pin->GetPosition(), label );
2200 }
2201
2202 // Sort labels by pin position (X, then Y)
2203 std::sort( byPos.begin(), byPos.end(),
2204 []( const auto& a, const auto& b )
2205 {
2206 if( a.first.x != b.first.x )
2207 return a.first.x < b.first.x;
2208
2209 return a.first.y < b.first.y;
2210 } );
2211
2212 // Discard position, just keep the order
2213 std::vector<SCH_LABEL_BASE*> labels;
2214
2215 for( const auto& pr : byPos )
2216 labels.push_back( pr.second );
2217
2218 symbolLabelVectors.push_back( labels );
2219 }
2220
2221 // All selected units are guaranteed to have identical pin counts by GetSameSymbolMultiUnitSelection()
2222 const size_t pinCount = symbolLabelVectors.front().size();
2223
2224 // Perform cyclic swap of labels across all selected symbols, per pin index
2225 SCH_COMMIT commit( m_frame );
2226
2227 for( size_t pin = 0; pin < pinCount; pin++ )
2228 {
2229 for( auto& vec : symbolLabelVectors )
2230 commit.Modify( vec[pin], m_frame->GetScreen() );
2231
2232 wxString carry = symbolLabelVectors.back()[pin]->GetText();
2233
2234 for( size_t i = 0; i < symbolLabelVectors.size(); i++ )
2235 {
2236 SCH_LABEL_BASE* lbl = symbolLabelVectors[i][pin];
2237 wxString next = lbl->GetText();
2238 lbl->SetText( carry );
2239 carry = next;
2240 }
2241 }
2242
2243 if( !commit.Empty() )
2244 commit.Push( _( "Swap Unit Labels" ) );
2245
2246 return 0;
2247}
2248
2249
2251{
2252 // This MUST be a copy as finalizing the SCH_ACTIONS::move lower down will reset m_frame's
2253 // m_items_to_repeat member variable.
2254 std::vector<std::unique_ptr<SCH_ITEM>> sourceItems( m_frame->GetRepeatItems().size() );
2255
2256 for( int ii = 0; ii < (int) sourceItems.size(); ++ii )
2257 sourceItems[ii].reset( m_frame->GetRepeatItems().at( ii )->Duplicate( IGNORE_PARENT_GROUP ) );
2258
2259 if( sourceItems.empty() )
2260 return 0;
2261
2262 m_toolMgr->RunAction( ACTIONS::selectionClear );
2263
2264 SCH_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
2265 SCH_COMMIT commit( m_toolMgr );
2266 SCH_SELECTION newItems;
2267
2268 for( const std::unique_ptr<SCH_ITEM>& item : sourceItems )
2269 {
2270 SCH_ITEM* newItem = item->Duplicate( IGNORE_PARENT_GROUP );
2271 int commitCheckpoint = commit.Checkpoint();
2272 bool restore_state = false;
2273
2274 // Ensure newItem has a suitable parent: the current screen, because an item from
2275 // a list of items to repeat must be attached to this current screen
2276 newItem->SetParent( m_frame->GetScreen() );
2277
2278 if( SCH_GROUP* enteredGroup = selectionTool->GetEnteredGroup() )
2279 {
2280 if( newItem->IsGroupableType() )
2281 {
2282 commit.Modify( enteredGroup, m_frame->GetScreen(), RECURSE_MODE::NO_RECURSE );
2283 enteredGroup->AddItem( newItem );
2284 }
2285 }
2286
2287 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( newItem ) )
2288 {
2289 // If incrementing tries to go below zero, tell user why the value is repeated
2290 if( EESCHEMA_SETTINGS* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ) )
2291 {
2292 if( !label->IncrementLabel( cfg->m_Drawing.repeat_label_increment ) )
2293 m_frame->ShowInfoBarWarning( _( "Label value cannot go below zero" ), true );
2294 }
2295 }
2296
2297 // If cloning a symbol then put into 'move' mode.
2298 if( newItem->Type() == SCH_SYMBOL_T )
2299 {
2300 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( true );
2301 newItem->Move( cursorPos - newItem->GetPosition() );
2302 }
2303 else if( EESCHEMA_SETTINGS* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ) )
2304 {
2305 newItem->Move( VECTOR2I( schIUScale.MilsToIU( cfg->m_Drawing.default_repeat_offset_x ),
2306 schIUScale.MilsToIU( cfg->m_Drawing.default_repeat_offset_y ) ) );
2307 }
2308
2309 // If cloning a sheet, check that we aren't going to create recursion
2310 if( newItem->Type() == SCH_SHEET_T )
2311 {
2312 SCH_SHEET_PATH* currentSheet = &m_frame->GetCurrentSheet();
2313 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( newItem );
2314
2315 if( m_frame->CheckSheetForRecursion( sheet, currentSheet ) )
2316 {
2317 // Clear out the filename so that the user can pick a new one
2318 const wxString originalFileName = sheet->GetFileName();
2319 const wxString originalScreenFileName = sheet->GetScreen()->GetFileName();
2320
2321 sheet->SetFileName( wxEmptyString );
2322 sheet->GetScreen()->SetFileName( wxEmptyString );
2323 restore_state = !m_frame->EditSheetProperties( sheet, currentSheet );
2324
2325 if( restore_state )
2326 {
2327 sheet->SetFileName( originalFileName );
2328 sheet->GetScreen()->SetFileName( originalScreenFileName );
2329 }
2330 }
2331 }
2332
2333 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, newItem );
2334 newItem->SetFlags( IS_NEW );
2335 m_frame->AddToScreen( newItem, m_frame->GetScreen() );
2336 commit.Added( newItem, m_frame->GetScreen() );
2337
2338 if( newItem->Type() == SCH_SYMBOL_T )
2339 {
2340 SCHEMATIC_SETTINGS& projSettings = m_frame->Schematic().Settings();
2341 int annotateStartNum = projSettings.m_AnnotateStartNum;
2342 ANNOTATE_ORDER_T annotateOrder = static_cast<ANNOTATE_ORDER_T>( projSettings.m_AnnotateSortOrder );
2343 ANNOTATE_ALGO_T annotateAlgo = static_cast<ANNOTATE_ALGO_T>( projSettings.m_AnnotateMethod );
2344
2345 if( m_frame->eeconfig()->m_AnnotatePanel.automatic )
2346 {
2347 static_cast<SCH_SYMBOL*>( newItem )->ClearAnnotation( nullptr, false );
2349 m_frame->AnnotateSymbols( &commit, ANNOTATE_SELECTION, annotateOrder, annotateAlgo,
2350 true /* recursive */, annotateStartNum, false, false, false, reporter );
2351 }
2352
2353 // Annotation clears the selection so re-add the item
2354 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, newItem );
2355
2356 restore_state = !m_toolMgr->RunSynchronousAction( SCH_ACTIONS::move, &commit );
2357 }
2358
2359 if( restore_state )
2360 commit.RevertToCheckpoint( commitCheckpoint );
2361 else
2362 newItems.Add( newItem );
2363 }
2364
2365 if( !newItems.Empty() )
2366 {
2368 lwbTool->TrimOverLappingWires( &commit, &newItems );
2369 lwbTool->AddJunctionsIfNeeded( &commit, &newItems );
2370
2371 m_frame->Schematic().CleanUp( &commit );
2372 commit.Push( _( "Repeat Item" ) );
2373
2374 m_frame->SaveCopyForRepeatItem( static_cast<SCH_ITEM*>( newItems[0] ) );
2375
2376 for( size_t ii = 1; ii < newItems.GetSize(); ++ii )
2377 m_frame->AddCopyForRepeatItem( static_cast<SCH_ITEM*>( newItems[ii] ) );
2378 }
2379
2380 return 0;
2381}
2382
2383
2385{
2386 SCH_SCREEN* screen = m_frame->GetScreen();
2387
2389 m_selectionTool->FilterSelectionForLockedItems();
2390
2391 std::deque<EDA_ITEM*> items = m_selectionTool->GetSelection().GetItems();
2392 SCH_COMMIT commit( m_toolMgr );
2393 std::vector<VECTOR2I> pts;
2394 bool updateHierarchy = false;
2395
2396 if( items.empty() )
2397 return 0;
2398
2399 // Don't leave a freed pointer in the selection
2400 m_toolMgr->RunAction( ACTIONS::selectionClear );
2401
2402 for( EDA_ITEM* item : items )
2403 item->ClearFlags( STRUCT_DELETED );
2404
2405 for( EDA_ITEM* item : items )
2406 {
2407 SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( item );
2408
2409 if( !sch_item )
2410 continue;
2411
2412 if( sch_item->IsConnectable() )
2413 {
2414 std::vector<VECTOR2I> tmp_pts = sch_item->GetConnectionPoints();
2415 pts.insert( pts.end(), tmp_pts.begin(), tmp_pts.end() );
2416 }
2417
2418 if( sch_item->Type() == SCH_JUNCTION_T )
2419 {
2420 sch_item->SetFlags( STRUCT_DELETED );
2421 // clean up junctions at the end
2422 }
2423 else if( sch_item->Type() == SCH_SHEET_PIN_T )
2424 {
2425 SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) sch_item;
2426 SCH_SHEET* sheet = pin->GetParent();
2427
2428 if( !alg::contains( items, sheet ) )
2429 {
2430 commit.Modify( sheet, m_frame->GetScreen() );
2431 sheet->RemovePin( pin );
2432 }
2433 }
2434 else if( sch_item->Type() == SCH_FIELD_T )
2435 {
2436 // Hide field
2437 commit.Modify( item, m_frame->GetScreen() );
2438 static_cast<SCH_FIELD*>( sch_item )->SetVisible( false );
2439 }
2440 else if( sch_item->Type() == SCH_TABLECELL_T )
2441 {
2442 // Clear contents of table cell
2443 commit.Modify( item, m_frame->GetScreen() );
2444 static_cast<SCH_TABLECELL*>( sch_item )->SetText( wxEmptyString );
2445 }
2446 else if( sch_item->Type() == SCH_RULE_AREA_T )
2447 {
2448 sch_item->SetFlags( STRUCT_DELETED );
2449 commit.Remove( item, m_frame->GetScreen() );
2450 }
2451 else if( sch_item->Type() == SCH_GROUP_T )
2452 {
2453 // Groups need to delete their children
2454 sch_item->RunOnChildren(
2455 [&]( SCH_ITEM* aChild )
2456 {
2457 aChild->SetFlags( STRUCT_DELETED );
2458 commit.Remove( aChild, m_frame->GetScreen() );
2459 },
2461
2462 sch_item->SetFlags( STRUCT_DELETED );
2463 commit.Remove( sch_item, m_frame->GetScreen() );
2464 }
2465 else
2466 {
2467 sch_item->SetFlags( STRUCT_DELETED );
2468 commit.Remove( item, m_frame->GetScreen() );
2469 updateHierarchy |= ( sch_item->Type() == SCH_SHEET_T );
2470 }
2471 }
2472
2473 for( const VECTOR2I& point : pts )
2474 {
2475 SCH_ITEM* junction = screen->GetItem( point, 0, SCH_JUNCTION_T );
2476
2477 if( !junction )
2478 continue;
2479
2480 if( junction->HasFlag( STRUCT_DELETED ) || !screen->IsExplicitJunction( point ) )
2481 m_frame->DeleteJunction( &commit, junction );
2482 }
2483
2484 commit.Push( _( "Delete" ) );
2485
2486 if( updateHierarchy )
2487 m_frame->UpdateHierarchyNavigator();
2488
2489 return 0;
2490}
2491
2492
2494{
2495 KICAD_T parentType = aField->GetParent() ? aField->GetParent()->Type() : SCHEMATIC_T;
2496 SCH_COMMIT commit( m_toolMgr );
2497 wxString caption;
2498
2499 // Use title caps for mandatory fields. "Edit Sheet name Field" looks dorky.
2500 if( aField->IsMandatory() )
2501 {
2502 wxString fieldName = GetDefaultFieldName( aField->GetId(), TRANSLATED );
2503 caption.Printf( _( "Edit %s Field" ), TitleCaps( fieldName ) );
2504 }
2505 else
2506 {
2507 caption.Printf( _( "Edit '%s' Field" ), aField->GetName() );
2508 }
2509
2510 DIALOG_FIELD_PROPERTIES dlg( m_frame, caption, aField );
2511
2512 // The footprint field dialog can invoke a KIWAY_PLAYER so we must use a quasi-modal
2513 if( dlg.ShowQuasiModal() != wxID_OK )
2514 return;
2515
2516 // The dialog changes nothing before OK, and staging bumps the connectivity revision
2517 if( aField->GetEditFlags() == 0 ) // i.e. not edited, or moved
2518 commit.Modify( aField, m_frame->GetScreen() );
2519
2520 dlg.UpdateField( &commit, aField, &m_frame->GetCurrentSheet() );
2521
2522 if( parentType == SCH_SYMBOL_T && aField->GetId() == FIELD_T::REFERENCE )
2523 static_cast<SCH_ITEM*>( aField->GetParent() )->SetConnectivityDirty();
2524
2525 if( m_frame->eeconfig()->m_AutoplaceFields.enable || parentType == SCH_SHEET_T )
2526 {
2527 SCH_ITEM* parent = static_cast<SCH_ITEM*>( aField->GetParent() );
2528 AUTOPLACE_ALGO fieldsAutoplaced = parent->GetFieldsAutoplaced();
2529
2530 if( fieldsAutoplaced == AUTOPLACE_AUTO || fieldsAutoplaced == AUTOPLACE_MANUAL )
2531 parent->AutoplaceFields( m_frame->GetScreen(), fieldsAutoplaced );
2532 }
2533
2534 if( !commit.Empty() )
2535 commit.Push( caption );
2536}
2537
2538
2540{
2541 SCH_SELECTION sel = m_selectionTool->RequestSelection( { SCH_FIELD_T, SCH_SYMBOL_T, SCH_PIN_T } );
2542
2543 if( sel.Size() != 1 )
2544 return 0;
2545
2546 bool clearSelection = sel.IsHover();
2547 EDA_ITEM* item = sel.Front();
2548
2549 if( item->Type() == SCH_FIELD_T )
2550 {
2551 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
2552
2553 if( ( aEvent.IsAction( &SCH_ACTIONS::editReference ) && field->GetId() != FIELD_T::REFERENCE )
2554 || ( aEvent.IsAction( &SCH_ACTIONS::editValue ) && field->GetId() != FIELD_T::VALUE )
2555 || ( aEvent.IsAction( &SCH_ACTIONS::editFootprint ) && field->GetId() != FIELD_T::FOOTPRINT ) )
2556 {
2557 item = field->GetParentSymbol();
2558
2559 m_selectionTool->ClearSelection( true );
2560
2561 // If the field to edit is not a symbol field, we cannot edit the ref, value or footprint
2562 if( item == nullptr )
2563 return 0;
2564
2565 m_selectionTool->AddItemToSel( item );
2566 }
2567 }
2568
2569 if( item->Type() == SCH_SYMBOL_T )
2570 {
2571 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2572
2573 if( aEvent.IsAction( &SCH_ACTIONS::editReference ) )
2574 {
2576 }
2577 else if( aEvent.IsAction( &SCH_ACTIONS::editValue ) )
2578 {
2580 }
2581 else if( aEvent.IsAction( &SCH_ACTIONS::editFootprint ) )
2582 {
2583 if( !symbol->IsPower() )
2585 }
2586 }
2587 else if( item->Type() == SCH_FIELD_T )
2588 {
2589 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
2590
2591 editFieldText( field );
2592
2593 if( !field->IsVisible() )
2594 clearSelection = true;
2595 }
2596 else if( item->Type() == SCH_PIN_T )
2597 {
2598 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item->GetParent() );
2599
2600 if( symbol )
2601 {
2602 if( aEvent.IsAction( &SCH_ACTIONS::editReference ) )
2603 {
2605 }
2606 else if( aEvent.IsAction( &SCH_ACTIONS::editValue ) )
2607 {
2609 }
2610 else if( aEvent.IsAction( &SCH_ACTIONS::editFootprint ) )
2611 {
2612 if( !symbol->IsPower() )
2614 }
2615 }
2616 }
2617
2618 if( clearSelection )
2619 m_toolMgr->RunAction( ACTIONS::selectionClear );
2620
2621 return 0;
2622}
2623
2624
2626{
2627 SCH_SELECTION& selection = m_selectionTool->RequestSelection( RotatableItems );
2628 SCH_COMMIT commit( m_toolMgr );
2629 SCH_ITEM* head = static_cast<SCH_ITEM*>( selection.Front() );
2630 bool moving = head && head->IsMoving();
2631
2632 if( selection.Empty() )
2633 return 0;
2634
2635 std::vector<SCH_ITEM*> autoplaceItems;
2636
2637 for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
2638 {
2639 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
2640
2641 if( item->IsType( SCH_COLLECTOR::FieldOwners ) )
2642 autoplaceItems.push_back( item );
2643 else if( item->GetParent() && item->GetParent()->IsType( SCH_COLLECTOR::FieldOwners ) )
2644 autoplaceItems.push_back( static_cast<SCH_ITEM*>( item->GetParent() ) );
2645 }
2646
2647 for( SCH_ITEM* sch_item : autoplaceItems )
2648 {
2649 if( sch_item->IsLocked() )
2650 continue;
2651
2652 if( !moving && !sch_item->IsNew() )
2653 commit.Modify( sch_item, m_frame->GetScreen() );
2654
2655 sch_item->AutoplaceFields( m_frame->GetScreen(), AUTOPLACE_MANUAL );
2656
2657 updateItem( sch_item, true );
2658 }
2659
2660 if( moving )
2661 {
2662 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2663 }
2664 else
2665 {
2666 if( !commit.Empty() )
2667 commit.Push( _( "Autoplace Fields" ) );
2668
2669 if( selection.IsHover() )
2670 m_toolMgr->RunAction( ACTIONS::selectionClear );
2671 }
2672
2673 return 0;
2674}
2675
2676
2678{
2679 SCH_SYMBOL* selectedSymbol = nullptr;
2680 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
2681
2682 if( !selection.Empty() )
2683 selectedSymbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
2684
2686
2688 {
2689 // Exchanging the base symbol under an active variant would rewrite all variants
2690 // while the canvas shows variant-resolved values, so require the default variant.
2691 if( !m_frame->Schematic().GetCurrentVariant().IsEmpty() )
2692 {
2694 _( "Change Symbol is not available when a design variant is active. "
2695 "Use Set Variant Symbol or switch to the default variant first." ) );
2696 return 0;
2697 }
2698
2700 }
2701
2702 DIALOG_CHANGE_SYMBOLS dlg( m_frame, selectedSymbol, mode );
2703
2704 // QuasiModal required to invoke symbol browser
2705 dlg.ShowQuasiModal();
2706
2707 if( selection.IsHover() )
2708 m_toolMgr->RunAction( ACTIONS::selectionClear );
2709
2710 return 0;
2711}
2712
2713
2715{
2716 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
2717
2718 if( selection.Empty() )
2719 return 0;
2720
2721 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
2722
2723 if( !symbol )
2724 return 0;
2725
2726 wxString variantName = m_frame->Schematic().GetCurrentVariant();
2727
2728 if( variantName.IsEmpty() )
2729 return 0;
2730
2732
2733 if( !adapter )
2734 return 0;
2735
2736 const LIB_SYMBOL* baseSymbol = symbol->GetLibSymbolRef().get();
2737 std::shared_ptr<LIB_SYMBOL> baseFlat;
2738 SYMBOL_COMPAT_FUNC compatFunc;
2739
2740 if( baseSymbol )
2741 {
2742 // The flattened copy is shared with the callback so its lifetime cannot be cut
2743 // short by schematic changes while the chooser is open.
2744 baseFlat = baseSymbol->Flatten();
2745
2746 compatFunc =
2747 [baseFlat, adapter]( const LIB_ID& aCandidate )
2748 -> std::vector<VARIANT_COMPAT_RESULT>
2749 {
2750 try
2751 {
2752 if( LIB_SYMBOL* raw = adapter->LoadSymbol( aCandidate ) )
2753 {
2754 std::unique_ptr<LIB_SYMBOL> flat = raw->Flatten();
2755 return ValidateVariantSymbolCompatibility( *baseFlat, *flat );
2756 }
2757 }
2758 catch( const IO_ERROR& )
2759 {
2760 }
2761
2762 return {};
2763 };
2764 }
2765
2767 std::vector<PICKED_SYMBOL> historyList;
2768 std::vector<PICKED_SYMBOL> alreadyPlaced;
2769
2770 PICKED_SYMBOL picked = m_frame->PickSymbolFromLibrary( &filter, historyList, alreadyPlaced, true,
2771 &symbol->GetLibId(), false, compatFunc );
2772
2773 if( !picked.LibId.IsValid() )
2774 return 0;
2775
2776 LIB_SYMBOL* candidateRaw = nullptr;
2777
2778 try
2779 {
2780 candidateRaw = adapter->LoadSymbol( picked.LibId );
2781 }
2782 catch( const IO_ERROR& )
2783 {
2784 wxString symLabel =
2785 picked.LibId.Format().empty() ? picked.LibId.GetLibItemName().wx_str() : picked.LibId.Format().wx_str();
2786
2787 DisplayErrorMessage( m_frame, wxString::Format(
2788 _( "Could not load symbol '%s' from library." ), symLabel ) );
2789 return 0;
2790 }
2791
2792 if( !candidateRaw )
2793 return 0;
2794
2795 std::unique_ptr<LIB_SYMBOL> candidateFlat = candidateRaw->Flatten();
2796
2797 if( baseFlat )
2798 {
2799 std::vector<VARIANT_COMPAT_RESULT> issues =
2800 ValidateVariantSymbolCompatibility( *baseFlat, *candidateFlat );
2801
2802 if( !issues.empty() )
2803 {
2804 wxString msg = wxString::Format(
2805 _( "The selected symbol '%s' is not pin-compatible with the base symbol "
2806 "'%s'.\n\n" ),
2807 picked.LibId.Format().wx_str(),
2808 symbol->GetLibId().Format().wx_str() );
2809
2810 for( const VARIANT_COMPAT_RESULT& issue : issues )
2811 msg += wxS( "\u2022 " ) + issue.detail + wxS( "\n" );
2812
2813 msg += wxS( "\n" ) + _( "Choose a symbol with equivalent pins and pin positions." );
2814 wxMessageBox( msg, _( "Variant Symbol Compatibility" ), wxOK | wxICON_ERROR, m_frame );
2815 return 0;
2816 }
2817 }
2818
2819 SCH_COMMIT commit( m_toolMgr );
2820 SCH_SHEET_PATH& currentSheet = m_frame->GetCurrentSheet();
2821
2822 commit.Modify( symbol, m_frame->GetScreen() );
2823
2824 symbol->SetVariantSymbolOverride( currentSheet, variantName, picked.LibId );
2825 symbol->ClearCaches();
2826
2827 commit.Push( _( "Set Variant Symbol" ) );
2828 m_frame->GetCanvas()->Refresh();
2829
2830 if( selection.IsHover() )
2831 m_toolMgr->RunAction( ACTIONS::selectionClear );
2832
2833 return 0;
2834}
2835
2836
2838{
2839 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
2840
2841 if( selection.Empty() )
2842 return 0;
2843
2844 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
2845
2846 if( !symbol )
2847 return 0;
2848
2849 wxString variantName = m_frame->Schematic().GetCurrentVariant();
2850
2851 if( variantName.IsEmpty() )
2852 return 0;
2853
2854 SCH_SHEET_PATH& currentSheet = m_frame->GetCurrentSheet();
2855
2856 std::optional<SCH_SYMBOL_VARIANT> existingVariant =
2857 symbol->GetVariant( currentSheet, variantName );
2858
2859 if( !existingVariant || !existingVariant->m_SymbolOverride.has_value() )
2860 return 0;
2861
2862 SCH_COMMIT commit( m_toolMgr );
2863 commit.Modify( symbol, m_frame->GetScreen() );
2864
2865 symbol->ClearVariantSymbolOverride( currentSheet, variantName );
2866 symbol->ClearCaches();
2867
2868 commit.Push( _( "Clear Variant Symbol" ) );
2869 m_frame->GetCanvas()->Refresh();
2870
2871 if( selection.IsHover() )
2872 m_toolMgr->RunAction( ACTIONS::selectionClear );
2873
2874 return 0;
2875}
2876
2877
2879{
2880 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
2881
2882 if( selection.Empty() )
2883 return 0;
2884
2885 SCH_SYMBOL* symbol = (SCH_SYMBOL*) selection.Front();
2886 int nextBodyStyle = symbol->GetBodyStyle() + 1;
2887
2888 if( nextBodyStyle > symbol->GetBodyStyleCount() )
2889 nextBodyStyle = 1;
2890
2891 m_frame->SelectBodyStyle( symbol, nextBodyStyle );
2892
2893 if( symbol->IsNew() )
2894 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2895
2896 if( selection.IsHover() )
2897 m_toolMgr->RunAction( ACTIONS::selectionClear );
2898
2899 return 0;
2900}
2901
2902
2904{
2905 SCH_SELECTION& selection = m_selectionTool->RequestSelection();
2906 bool clearSelection = selection.IsHover();
2907
2908 if( selection.Empty() )
2909 {
2910 if( getView()->IsLayerVisible( LAYER_SCHEMATIC_DRAWINGSHEET ) )
2911 {
2912 DS_PROXY_VIEW_ITEM* ds = m_frame->GetCanvas()->GetView()->GetDrawingSheet();
2913 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
2914
2915 if( ds && ds->HitTestDrawingSheetItems( getView(), cursorPos ) )
2916 m_toolMgr->PostAction( ACTIONS::pageSettings );
2917 }
2918
2919 return 0;
2920 }
2921
2922 EDA_ITEM* curr_item = selection.Front();
2923
2924 // If a single pin is selected, promote to its parent symbol
2925 if( ( selection.GetSize() == 1 ) && ( curr_item->Type() == SCH_PIN_T ) )
2926 {
2927 EDA_ITEM* parent = curr_item->GetParent();
2928
2929 if( parent->Type() == SCH_SYMBOL_T )
2930 curr_item = parent;
2931 }
2932
2933 switch( curr_item->Type() )
2934 {
2935 case SCH_LINE_T:
2937 case SCH_JUNCTION_T:
2939 {
2940 std::deque<SCH_LINE*> lines;
2941
2942 for( EDA_ITEM* selItem : selection.Items() )
2943 lines.push_back( static_cast<SCH_LINE*>( selItem ) );
2944
2945 DIALOG_LINE_PROPERTIES dlg( m_frame, lines );
2946
2947 dlg.ShowModal();
2948 }
2949 else if( SELECTION_CONDITIONS::OnlyTypes( { SCH_JUNCTION_T } )( selection ) )
2950 {
2951 std::deque<SCH_JUNCTION*> junctions;
2952
2953 for( EDA_ITEM* selItem : selection.Items() )
2954 junctions.push_back( static_cast<SCH_JUNCTION*>( selItem ) );
2955
2956 DIALOG_JUNCTION_PROPS dlg( m_frame, junctions );
2957
2958 dlg.ShowModal();
2959 }
2963 SCH_JUNCTION_T } )( selection ) )
2964 {
2965 std::deque<SCH_ITEM*> items;
2966
2967 for( EDA_ITEM* selItem : selection.Items() )
2968 items.push_back( static_cast<SCH_ITEM*>( selItem ) );
2969
2970 DIALOG_WIRE_BUS_PROPERTIES dlg( m_frame, items );
2971
2972 dlg.ShowModal();
2973 }
2974 else
2975 {
2976 frame()->ShowInfoBarMsg( _( "Use Properties panel to edit properties common to selected items." ) );
2977 return 0;
2978 }
2979
2980 break;
2981
2982 case SCH_MARKER_T:
2983 if( SELECTION_CONDITIONS::OnlyTypes( { SCH_MARKER_T } )( selection ) )
2984 {
2985 SCH_INSPECTION_TOOL* inspectionTool = m_toolMgr->GetTool<SCH_INSPECTION_TOOL>();
2986
2987 if( inspectionTool )
2988 inspectionTool->CrossProbe( static_cast<SCH_MARKER*>( selection.Front() ) );
2989 }
2990 break;
2991
2992 case SCH_TABLECELL_T:
2993 if( SELECTION_CONDITIONS::OnlyTypes( { SCH_TABLECELL_T } )( selection ) )
2994 {
2995 std::vector<SCH_TABLECELL*> cells;
2996
2997 for( EDA_ITEM* item : selection.Items() )
2998 cells.push_back( static_cast<SCH_TABLECELL*>( item ) );
2999
3001
3002 // QuasiModal required for syntax help and Scintilla auto-complete
3003 dlg.ShowQuasiModal();
3004
3006 {
3007 SCH_TABLE* table = static_cast<SCH_TABLE*>( cells[0]->GetParent() );
3009
3010 tableDlg.ShowModal();
3011 }
3012 }
3013
3014 break;
3015
3016 default:
3017 if( selection.Size() > 1 )
3018 {
3019 WX_INFOBAR* infobar = frame()->GetInfoBar();
3020
3021 infobar->RemoveAllButtons();
3022
3023 if( !frame()->GetPropertiesPanel()->IsShownOnScreen() )
3024 {
3025 infobar->AddLink( _( "Show Properties panel" ),
3026 [this]( wxHyperlinkEvent& )
3027 {
3028 frame()->ToggleProperties();
3029 } );
3030 }
3031
3032 infobar->AddCloseButton();
3033 infobar->ShowMessageFor( _( "Use Properties panel to edit properties common to selected items." ),
3034 8000, wxICON_INFORMATION );
3035
3036 return 0;
3037 }
3038
3039 EditProperties( curr_item );
3040 }
3041
3042 if( clearSelection )
3043 m_toolMgr->RunAction( ACTIONS::selectionClear );
3044
3045 return 0;
3046}
3047
3048
3050{
3051 switch( aItem->Type() )
3052 {
3053 case SCH_SYMBOL_T:
3054 {
3055 int retval;
3056 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( aItem );
3057
3058 // This needs to be scoped so the dialog destructor removes blocking status
3059 // before we launch the next dialog.
3060 {
3061 DIALOG_SYMBOL_PROPERTIES symbolPropsDialog( m_frame, symbol );
3062
3063 // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
3064 // frame. Therefore this dialog as a modal frame parent, MUST be run under
3065 // quasimodal mode for the quasimodal frame support to work. So don't use
3066 // the QUASIMODAL macros here.
3067 retval = symbolPropsDialog.ShowQuasiModal();
3068 }
3069
3070 if( retval == SYMBOL_PROPS_EDIT_OK )
3071 {
3072 if( m_frame->eeconfig()->m_AutoplaceFields.enable )
3073 {
3074 AUTOPLACE_ALGO fieldsAutoplaced = symbol->GetFieldsAutoplaced();
3075
3076 if( fieldsAutoplaced == AUTOPLACE_AUTO || fieldsAutoplaced == AUTOPLACE_MANUAL )
3077 symbol->AutoplaceFields( m_frame->GetScreen(), fieldsAutoplaced );
3078 }
3079
3080 m_frame->OnModify();
3081 }
3082 else if( retval == SYMBOL_PROPS_EDIT_SCHEMATIC_SYMBOL )
3083 {
3084 if( KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_SCH_SYMBOL_EDITOR, true ) )
3085 {
3086 SYMBOL_EDIT_FRAME* editor = static_cast<SYMBOL_EDIT_FRAME*>( frame );
3087
3088 if( wxWindow* blocking_win = editor->Kiway().GetBlockingDialog() )
3089 blocking_win->Close( true );
3090
3091 // The broken library symbol link indicator cannot be edited.
3092 if( symbol->IsMissingLibSymbol() )
3093 return;
3094
3095 editor->LoadSymbolFromSchematic( symbol );
3096 editor->Show( true );
3097 editor->Raise();
3098 }
3099 }
3100 else if( retval == SYMBOL_PROPS_EDIT_LIBRARY_SYMBOL )
3101 {
3102 if( KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_SCH_SYMBOL_EDITOR, true ) )
3103 {
3104 SYMBOL_EDIT_FRAME* editor = static_cast<SYMBOL_EDIT_FRAME*>( frame );
3105
3106 if( wxWindow* blocking_win = editor->Kiway().GetBlockingDialog() )
3107 blocking_win->Close( true );
3108
3109 editor->LoadSymbol( symbol->GetLibId(), symbol->GetUnit(), symbol->GetBodyStyle() );
3110 editor->Show( true );
3111 editor->Raise();
3112 }
3113 }
3114 else if( retval == SYMBOL_PROPS_WANT_UPDATE_SYMBOL )
3115 {
3117 dlg.ShowQuasiModal();
3118 }
3119 else if( retval == SYMBOL_PROPS_WANT_EXCHANGE_SYMBOL )
3120 {
3122 dlg.ShowQuasiModal();
3123 }
3124 else if( retval == SYMBOL_PROPS_WANT_SET_VARIANT_SYMBOL )
3125 {
3127 }
3128 else if( retval == SYMBOL_PROPS_WANT_CLEAR_VARIANT_SYMBOL )
3129 {
3131 }
3132
3133 break;
3134 }
3135
3136 case SCH_SHEET_T:
3137 {
3138 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
3139 bool isUndoable = false;
3140 bool doClearAnnotation = false;
3141 bool okPressed = false;
3142 bool updateHierarchyNavigator = false;
3143
3144 // Keep track of existing sheet paths. EditSheet() can modify this list.
3145 // Note that we use the validity checking/repairing version here just to make sure
3146 // we've got a valid hierarchy to begin with.
3147 SCH_SHEET_LIST originalHierarchy;
3148 originalHierarchy.BuildSheetList( &m_frame->Schematic().Root(), true );
3149
3150 SCH_COMMIT commit( m_toolMgr );
3151 commit.Modify( sheet, m_frame->GetScreen() );
3152 okPressed = m_frame->EditSheetProperties( sheet, &m_frame->GetCurrentSheet(), &isUndoable, &doClearAnnotation,
3153 &updateHierarchyNavigator );
3154
3155 if( okPressed )
3156 {
3157 if( isUndoable )
3158 {
3159 commit.Push( _( "Edit Sheet Properties" ) );
3160 }
3161 else
3162 {
3163 // The sheet file change invalidated the undo/redo list.
3164 m_frame->ClearUndoRedoList();
3165
3166 std::vector<SCH_ITEM*> items;
3167
3168 items.emplace_back( sheet );
3169 m_frame->Schematic().OnItemsRemoved( items );
3170 m_frame->Schematic().OnItemsAdded( items );
3171 m_frame->OnModify();
3172 m_frame->Schematic().RefreshHierarchy();
3173 m_frame->UpdateHierarchyNavigator();
3174 }
3175 }
3176
3177 // If the sheet file is changed and new sheet contents are loaded then we have to
3178 // clear the annotations on the new content (as it may have been set from some other
3179 // sheet path reference)
3180 if( doClearAnnotation )
3181 {
3182 SCH_SCREENS screensList( &m_frame->Schematic().Root() );
3183
3184 // We clear annotation of new sheet paths here:
3185 screensList.ClearAnnotationOfNewSheetPaths( originalHierarchy );
3186
3187 // Clear annotation of g_CurrentSheet itself, because its sheetpath is not a new
3188 // path, but symbols managed by its sheet path must have their annotation cleared
3189 // because they are new:
3190 sheet->GetScreen()->ClearAnnotation( &m_frame->GetCurrentSheet(), false );
3191 }
3192
3193 // Only a push republishes the staged sheet; a cancel, a file change and the annotation
3194 // reset each leave the screen revision ahead of the last recalculation
3195 if( !okPressed || !isUndoable || doClearAnnotation )
3196 m_frame->RecalculateConnections( nullptr, NO_CLEANUP );
3197
3198 if( okPressed )
3199 m_frame->GetCanvas()->Refresh();
3200
3201 if( updateHierarchyNavigator )
3202 m_frame->UpdateHierarchyNavigator();
3203
3204 break;
3205 }
3206
3207 case SCH_SHEET_PIN_T:
3208 {
3209 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( aItem );
3211
3212 // QuasiModal required for help dialog
3213 dlg.ShowQuasiModal();
3214 break;
3215 }
3216
3217 case SCH_TEXT_T:
3218 case SCH_TEXTBOX_T:
3219 {
3220 DIALOG_TEXT_PROPERTIES dlg( m_frame, static_cast<SCH_ITEM*>( aItem ) );
3221
3222 // QuasiModal required for syntax help and Scintilla auto-complete
3223 dlg.ShowQuasiModal();
3224 break;
3225 }
3226
3227 case SCH_TABLE_T:
3228 {
3229 DIALOG_TABLE_PROPERTIES dlg( m_frame, static_cast<SCH_TABLE*>( aItem ) );
3230
3231 // QuasiModal required for Scintilla auto-complete
3232 dlg.ShowQuasiModal();
3233 break;
3234 }
3235
3236 case SCH_LABEL_T:
3237 case SCH_GLOBAL_LABEL_T:
3238 case SCH_HIER_LABEL_T:
3240 {
3241 DIALOG_LABEL_PROPERTIES dlg( m_frame, static_cast<SCH_LABEL_BASE*>( aItem ), false );
3242
3243 // QuasiModal for syntax help and Scintilla auto-complete
3244 dlg.ShowQuasiModal();
3245 break;
3246 }
3247
3248 case SCH_FIELD_T:
3249 {
3250 SCH_FIELD* field = static_cast<SCH_FIELD*>( aItem );
3251
3252 editFieldText( field );
3253
3254 if( !field->IsVisible() )
3255 m_toolMgr->RunAction( ACTIONS::selectionClear );
3256
3257 break;
3258 }
3259
3260 case SCH_SHAPE_T:
3261 {
3262 DIALOG_SHAPE_PROPERTIES dlg( m_frame, static_cast<SCH_SHAPE*>( aItem ) );
3263
3264 dlg.ShowModal();
3265 break;
3266 }
3267
3268 case SCH_BITMAP_T:
3269 {
3270 SCH_BITMAP& bitmap = static_cast<SCH_BITMAP&>( *aItem );
3271 DIALOG_IMAGE_PROPERTIES dlg( m_frame, bitmap );
3272
3273 if( dlg.ShowModal() == wxID_OK )
3274 {
3275 // The bitmap is cached in Opengl: clear the cache in case it has become invalid
3277 }
3278
3279 break;
3280 }
3281
3282 case SCH_RULE_AREA_T:
3283 {
3284 DIALOG_SHAPE_PROPERTIES dlg( m_frame, static_cast<SCH_SHAPE*>( aItem ) );
3285 dlg.SetTitle( _( "Rule Area Properties" ) );
3286
3287 dlg.ShowModal();
3288 break;
3289 }
3290
3291 case SCH_NO_CONNECT_T:
3292 case SCH_PIN_T:
3293 break;
3294
3295 case SCH_GROUP_T:
3296 m_toolMgr->RunAction( ACTIONS::groupProperties, static_cast<EDA_GROUP*>( static_cast<SCH_GROUP*>( aItem ) ) );
3297
3298 break;
3299
3300 default: // Unexpected item
3301 wxFAIL_MSG( wxString( "Cannot edit schematic item type " ) + aItem->GetClass() );
3302 }
3303
3304 // A pushed commit already updated the R-tree, and a full screen update would bump the
3305 // connectivity revision after that recalculation
3306 updateItem( aItem, false );
3307
3308 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( aItem ) )
3309 m_frame->GetScreen()->UpdateDisplayBounds( schItem );
3310}
3311
3312
3314{
3315 KICAD_T convertTo = aEvent.Parameter<KICAD_T>();
3316 SCH_SELECTION selection = m_selectionTool->RequestSelection( { SCH_LABEL_LOCATE_ANY_T,
3317 SCH_TEXT_T,
3318 SCH_TEXTBOX_T } );
3319 SCH_COMMIT localCommit( m_toolMgr );
3320 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
3321
3322 if( !commit )
3323 commit = &localCommit;
3324
3325 for( unsigned int i = 0; i < selection.GetSize(); ++i )
3326 {
3327 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( selection.GetItem( i ) );
3328
3329 if( item && item->Type() != convertTo )
3330 {
3331 EDA_TEXT* sourceText = dynamic_cast<EDA_TEXT*>( item );
3332 bool selected = item->IsSelected();
3333 SCH_ITEM* newtext = nullptr;
3334 VECTOR2I position = item->GetPosition();
3335 wxString txt;
3336 wxString href;
3339
3340 wxCHECK2( sourceText, continue );
3341
3342 switch( item->Type() )
3343 {
3344 case SCH_LABEL_T:
3345 case SCH_GLOBAL_LABEL_T:
3346 case SCH_HIER_LABEL_T:
3347 {
3348 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( item );
3349
3350 txt = UnescapeString( label->GetText() );
3351 spinStyle = label->GetSpinStyle();
3352 shape = label->GetShape();
3353 href = label->GetHyperlink();
3354 break;
3355 }
3356
3358 {
3359 SCH_DIRECTIVE_LABEL* dirlabel = static_cast<SCH_DIRECTIVE_LABEL*>( item );
3360
3361 // a SCH_DIRECTIVE_LABEL has no text
3362 txt = _( "<empty>" );
3363
3364 spinStyle = dirlabel->GetSpinStyle();
3365 href = dirlabel->GetHyperlink();
3366 break;
3367 }
3368
3369 case SCH_TEXT_T:
3370 {
3371 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
3372
3373 txt = text->GetText();
3374 href = text->GetHyperlink();
3375 break;
3376 }
3377
3378 case SCH_TEXTBOX_T:
3379 {
3380 SCH_TEXTBOX* textbox = static_cast<SCH_TEXTBOX*>( item );
3381 BOX2I bbox = textbox->GetBoundingBox();
3382
3383 bbox.SetOrigin( bbox.GetLeft() + textbox->GetMarginLeft(), bbox.GetTop() + textbox->GetMarginTop() );
3384 bbox.SetEnd( bbox.GetRight() - textbox->GetMarginRight(),
3385 bbox.GetBottom() - textbox->GetMarginBottom() );
3386
3387 if( convertTo == SCH_LABEL_T || convertTo == SCH_HIER_LABEL_T || convertTo == SCH_GLOBAL_LABEL_T )
3388 {
3389 EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
3390 wxCHECK( text, 0 );
3391 int textSize = text->GetTextSize().y;
3392 bbox.Inflate( KiROUND( item->Schematic()->Settings().m_LabelSizeRatio * textSize ) );
3393 }
3394
3395 txt = textbox->GetText();
3396
3397 if( textbox->GetTextAngle().IsVertical() )
3398 {
3399 if( textbox->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
3400 {
3401 spinStyle = SPIN_STYLE::SPIN::BOTTOM;
3402 position = VECTOR2I( bbox.Centre().x, bbox.GetOrigin().y );
3403 }
3404 else
3405 {
3406 spinStyle = SPIN_STYLE::SPIN::UP;
3407 position = VECTOR2I( bbox.Centre().x, bbox.GetEnd().y );
3408 }
3409 }
3410 else
3411 {
3412 if( textbox->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
3413 {
3414 spinStyle = SPIN_STYLE::SPIN::LEFT;
3415 position = VECTOR2I( bbox.GetEnd().x, bbox.Centre().y );
3416 }
3417 else
3418 {
3419 spinStyle = SPIN_STYLE::SPIN::RIGHT;
3420 position = VECTOR2I( bbox.GetOrigin().x, bbox.Centre().y );
3421 }
3422 }
3423
3424 position = m_frame->GetNearestGridPosition( position );
3425 href = textbox->GetHyperlink();
3426 break;
3427 }
3428
3429 default:
3430 UNIMPLEMENTED_FOR( item->GetClass() );
3431 break;
3432 }
3433
3434 auto getValidNetname =
3435 []( const wxString& aText )
3436 {
3437 wxString local_txt = aText;
3438 local_txt.Replace( "\n", "_" );
3439 local_txt.Replace( "\r", "_" );
3440 local_txt.Replace( "\t", "_" );
3441
3442 // Bus groups can have spaces; bus vectors and signal names cannot
3443 if( !NET_SETTINGS::ParseBusGroup( aText, nullptr, nullptr ) )
3444 local_txt.Replace( " ", "_" );
3445
3446 // label strings are "escaped" i.e. a '/' is replaced by "{slash}"
3447 local_txt = EscapeString( local_txt, CTX_NETNAME );
3448
3449 if( local_txt.IsEmpty() )
3450 return _( "<empty>" );
3451 else
3452 return local_txt;
3453 };
3454
3455 switch( convertTo )
3456 {
3457 case SCH_LABEL_T:
3458 {
3459 SCH_LABEL_BASE* new_label = new SCH_LABEL( position, getValidNetname( txt ) );
3460
3461 new_label->SetShape( shape );
3462 new_label->SetAttributes( *sourceText, false );
3463 new_label->SetSpinStyle( spinStyle );
3464 new_label->SetHyperlink( href );
3465
3466 if( item->Type() == SCH_GLOBAL_LABEL_T || item->Type() == SCH_HIER_LABEL_T )
3467 {
3468 if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::UP )
3469 new_label->MirrorVertically( position.y );
3470 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::BOTTOM )
3471 new_label->MirrorVertically( position.y );
3472 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::LEFT )
3473 new_label->MirrorHorizontally( position.x );
3474 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::RIGHT )
3475 new_label->MirrorHorizontally( position.x );
3476 }
3477
3478 newtext = new_label;
3479 break;
3480 }
3481
3482 case SCH_GLOBAL_LABEL_T:
3483 {
3484 SCH_LABEL_BASE* new_label = new SCH_GLOBALLABEL( position, getValidNetname( txt ) );
3485
3486 new_label->SetShape( shape );
3487 new_label->SetAttributes( *sourceText, false );
3488 new_label->SetSpinStyle( spinStyle );
3489 new_label->SetHyperlink( href );
3490
3491 if( item->Type() == SCH_LABEL_T )
3492 {
3493 if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::UP )
3494 new_label->MirrorVertically( position.y );
3495 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::BOTTOM )
3496 new_label->MirrorVertically( position.y );
3497 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::LEFT )
3498 new_label->MirrorHorizontally( position.x );
3499 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::RIGHT )
3500 new_label->MirrorHorizontally( position.x );
3501 }
3502
3503 newtext = new_label;
3504 break;
3505 }
3506
3507 case SCH_HIER_LABEL_T:
3508 {
3509 SCH_LABEL_BASE* new_label = new SCH_HIERLABEL( position, getValidNetname( txt ) );
3510
3511 new_label->SetShape( shape );
3512 new_label->SetAttributes( *sourceText, false );
3513 new_label->SetSpinStyle( spinStyle );
3514 new_label->SetHyperlink( href );
3515
3516 if( item->Type() == SCH_LABEL_T )
3517 {
3518 if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::UP )
3519 new_label->MirrorVertically( position.y );
3520 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::BOTTOM )
3521 new_label->MirrorVertically( position.y );
3522 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::LEFT )
3523 new_label->MirrorHorizontally( position.x );
3524 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::RIGHT )
3525 new_label->MirrorHorizontally( position.x );
3526 }
3527
3528 newtext = new_label;
3529 break;
3530 }
3531
3533 {
3534 SCH_LABEL_BASE* new_label = new SCH_DIRECTIVE_LABEL( position );
3535
3536 // A SCH_DIRECTIVE_LABEL usually has at least one field containing the net class
3537 // name. If we're copying from a text object assume the text is the netclass
3538 // name. Otherwise, we'll just copy the fields which will either have a netclass
3539 // or not.
3540 if( !dynamic_cast<SCH_LABEL_BASE*>( item ) )
3541 {
3542 SCH_FIELD netclass( new_label, FIELD_T::USER, wxT( "Netclass" ) );
3543 netclass.SetText( txt );
3544 netclass.SetTextPos( position );
3545 new_label->GetFields().push_back( netclass );
3546 }
3547
3548 new_label->SetShape( LABEL_FLAG_SHAPE::F_ROUND );
3549 new_label->SetAttributes( *sourceText, false );
3550 new_label->SetSpinStyle( spinStyle );
3551 new_label->SetHyperlink( href );
3552 newtext = new_label;
3553 break;
3554 }
3555
3556 case SCH_TEXT_T:
3557 {
3558 SCH_TEXT* new_text = new SCH_TEXT( position, txt );
3559
3560 new_text->SetAttributes( *sourceText, false );
3561 new_text->SetHyperlink( href );
3562 newtext = new_text;
3563 break;
3564 }
3565
3566 case SCH_TEXTBOX_T:
3567 {
3568 SCH_TEXTBOX* new_textbox = new SCH_TEXTBOX( LAYER_NOTES, 0, FILL_T::NO_FILL, txt );
3569 BOX2I bbox = item->GetBoundingBox();
3570
3571 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item ) )
3572 bbox.Inflate( -label->GetLabelBoxExpansion() );
3573
3574 new_textbox->SetAttributes( *sourceText, false );
3575
3576 bbox.SetOrigin( bbox.GetLeft() - new_textbox->GetMarginLeft(),
3577 bbox.GetTop() - new_textbox->GetMarginTop() );
3578 bbox.SetEnd( bbox.GetRight() + new_textbox->GetMarginRight(),
3579 bbox.GetBottom() + new_textbox->GetMarginBottom() );
3580
3581 VECTOR2I topLeft = bbox.GetPosition();
3582 VECTOR2I botRight = bbox.GetEnd();
3583
3584 // Add 1/20 of the margin at the end to reduce line-breaking changes.
3585 int slop = new_textbox->GetLegacyTextMargin() / 20;
3586
3587 if( sourceText->GetTextAngle() == ANGLE_VERTICAL )
3588 {
3589 if( sourceText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
3590 botRight.y += slop;
3591 else
3592 topLeft.y -= slop;
3593 }
3594 else
3595 {
3596 if( sourceText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
3597 topLeft.x -= slop;
3598 else
3599 botRight.x += slop;
3600 }
3601
3602 new_textbox->SetPosition( topLeft );
3603 new_textbox->SetEnd( botRight );
3604
3605 new_textbox->SetHyperlink( href );
3606 newtext = new_textbox;
3607 break;
3608 }
3609
3610 default:
3611 UNIMPLEMENTED_FOR( wxString::Format( "%d.", convertTo ) );
3612 break;
3613 }
3614
3615 wxCHECK2( newtext, continue );
3616
3617 // Copy the old text item settings to the new one. Justifications are not copied
3618 // because they are not used in labels. Justifications will be set to default value
3619 // in the new text item type.
3620 //
3621 newtext->SetFlags( item->GetEditFlags() );
3622
3623 EDA_TEXT* eda_text = dynamic_cast<EDA_TEXT*>( item );
3624 EDA_TEXT* new_eda_text = dynamic_cast<EDA_TEXT*>( newtext );
3625
3626 wxCHECK2( eda_text && new_eda_text, continue );
3627
3628 new_eda_text->SetFont( eda_text->GetFont() );
3629 new_eda_text->SetTextSize( eda_text->GetTextSize() );
3630 new_eda_text->SetTextThickness( eda_text->GetTextThickness() );
3631
3632 // Must be after SetTextSize()
3633 new_eda_text->SetBold( eda_text->IsBold() );
3634 new_eda_text->SetItalic( eda_text->IsItalic() );
3635
3636 newtext->AutoplaceFields( m_frame->GetScreen(), AUTOPLACE_AUTO );
3637
3638 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item );
3639 SCH_LABEL_BASE* new_label = dynamic_cast<SCH_LABEL_BASE*>( newtext );
3640
3641 if( label && new_label )
3642 {
3643 new_label->AddFields( label->GetFields() );
3644
3645 // A SCH_GLOBALLABEL has a specific field for intersheet references that has
3646 // no meaning for other labels
3647 std::erase_if( new_label->GetFields(),
3648 [&]( SCH_FIELD& field )
3649 {
3650 return field.GetId() == FIELD_T::INTERSHEET_REFS
3651 && new_label->Type() != SCH_GLOBAL_LABEL_T;
3652 } );
3653 }
3654
3655 if( selected )
3656 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::unselectItem, item );
3657
3658 m_frame->RemoveFromScreen( item, m_frame->GetScreen() );
3659
3660 if( commit->GetStatus( item, m_frame->GetScreen() ) == CHT_ADD )
3661 commit->Unstage( item, m_frame->GetScreen() );
3662 else
3663 commit->Removed( item, m_frame->GetScreen() );
3664
3665 m_frame->AddToScreen( newtext, m_frame->GetScreen() );
3666 commit->Added( newtext, m_frame->GetScreen() );
3667
3668 if( selected )
3669 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, newtext );
3670 }
3671 }
3672
3673 if( !localCommit.Empty() )
3674 localCommit.Push( _( "Change To" ) );
3675
3676 if( selection.IsHover() )
3677 m_toolMgr->RunAction( ACTIONS::selectionClear );
3678
3679 return 0;
3680}
3681
3682
3684{
3685 static std::vector<KICAD_T> justifiableItems = { SCH_FIELD_T, SCH_TEXT_T, SCH_TEXTBOX_T, SCH_LABEL_T };
3686
3687 SCH_SELECTION& selection = m_selectionTool->RequestSelection( justifiableItems );
3688
3689 if( selection.GetSize() == 0 )
3690 return 0;
3691
3692 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
3693 bool moving = item->IsMoving();
3694 SCH_COMMIT localCommit( m_toolMgr );
3695 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
3696
3697 if( !commit )
3698 commit = &localCommit;
3699
3700 auto setJustify =
3701 [&]( EDA_TEXT* aTextItem )
3702 {
3703 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
3704 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
3705 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
3706 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
3707 else
3708 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
3709 };
3710
3711 for( EDA_ITEM* edaItem : selection )
3712 {
3713 item = static_cast<SCH_ITEM*>( edaItem );
3714
3715 if( !moving )
3716 commit->Modify( item, m_frame->GetScreen() );
3717
3718 if( item->Type() == SCH_FIELD_T )
3719 {
3720 setJustify( static_cast<SCH_FIELD*>( item ) );
3721
3722 // Now that we're re-justifying a field, they're no longer autoplaced.
3723 static_cast<SCH_ITEM*>( item->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
3724 }
3725 else if( item->Type() == SCH_TEXT_T )
3726 {
3727 setJustify( static_cast<SCH_TEXT*>( item ) );
3728 }
3729 else if( item->Type() == SCH_TEXTBOX_T )
3730 {
3731 setJustify( static_cast<SCH_TEXTBOX*>( item ) );
3732 }
3733 else if( item->Type() == SCH_LABEL_T )
3734 {
3735 SCH_LABEL* label = static_cast<SCH_LABEL*>( item );
3736
3737 if( label->GetTextAngle() == ANGLE_HORIZONTAL )
3738 setJustify( label );
3739 }
3740
3741 m_frame->UpdateItem( item, false, true );
3742 }
3743
3744 // Update R-Tree for modified items
3745 for( EDA_ITEM* selected : selection )
3746 updateItem( selected, true );
3747
3748 if( item->IsMoving() )
3749 {
3750 m_toolMgr->RunAction( ACTIONS::refreshPreview );
3751 }
3752 else
3753 {
3754 SCH_SELECTION selectionCopy = selection;
3755
3756 if( selection.IsHover() )
3757 m_toolMgr->RunAction( ACTIONS::selectionClear );
3758
3759 if( !localCommit.Empty() )
3760 {
3761 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
3762 localCommit.Push( _( "Left Justify" ) );
3763 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
3764 localCommit.Push( _( "Center Justify" ) );
3765 else
3766 localCommit.Push( _( "Right Justify" ) );
3767 }
3768 }
3769
3770 return 0;
3771}
3772
3773
3775{
3776 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SHEET_T } );
3777 SCH_SHEET* sheet = (SCH_SHEET*) selection.Front();
3778 SCH_COMMIT commit( m_toolMgr );
3779
3780 if( !sheet || !sheet->HasUndefinedPins() )
3781 return 0;
3782
3783 if( !IsOK( m_frame, _( "Do you wish to delete the unreferenced pins from this sheet?" ) ) )
3784 return 0;
3785
3786 commit.Modify( sheet, m_frame->GetScreen() );
3787
3788 sheet->CleanupSheet();
3789
3790 updateItem( sheet, true );
3791
3792 commit.Push( _( "Cleanup Sheet Pins" ) );
3793
3794 if( selection.IsHover() )
3795 m_toolMgr->RunAction( ACTIONS::selectionClear );
3796
3797 return 0;
3798}
3799
3800
3802{
3803 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SHEET_T } );
3804
3805 if( selection.GetSize() > 1 )
3806 return 0;
3807
3808 SCH_SHEET* sheet = (SCH_SHEET*) selection.Front();
3809
3810 SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
3811
3812 SCH_SCREEN* screen;
3813
3814 if( sheet )
3815 {
3816 // When changing the page number of a selected sheet, the current screen owns the sheet.
3817 screen = m_frame->GetScreen();
3818
3819 instance.push_back( sheet );
3820 }
3821 else
3822 {
3823 SCH_SHEET_PATH prevInstance = instance;
3824
3825 // When change the page number in the screen, the previous screen owns the sheet.
3826 if( prevInstance.size() )
3827 {
3828 prevInstance.pop_back();
3829 screen = prevInstance.LastScreen();
3830 }
3831 else
3832 {
3833 // The root sheet and root screen are effectively the same thing.
3834 screen = m_frame->GetScreen();
3835 }
3836
3837 sheet = m_frame->GetCurrentSheet().Last();
3838 }
3839
3840 wxString msg;
3841 wxString sheetPath = instance.PathHumanReadable( false );
3842 wxString pageNumber = instance.GetPageNumber();
3843
3844 msg.Printf( _( "Enter page number for sheet path%s" ),
3845 ( sheetPath.Length() > 20 ) ? "\n" + sheetPath : " " + sheetPath );
3846
3847 wxTextEntryDialog dlg( m_frame, msg, _( "Edit Sheet Page Number" ), pageNumber );
3848
3849 dlg.SetTextValidator( wxFILTER_ALPHANUMERIC ); // No white space.
3850
3851 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == instance.GetPageNumber() )
3852 return 0;
3853
3854 SCH_COMMIT commit( m_frame );
3855
3856 commit.Modify( sheet, screen );
3857
3858 instance.SetPageNumber( dlg.GetValue() );
3859
3860 if( instance == m_frame->GetCurrentSheet() )
3861 {
3862 m_frame->GetScreen()->SetPageNumber( dlg.GetValue() );
3863 m_frame->OnPageSettingsChange();
3864 }
3865
3866 commit.Push( wxS( "Change Sheet Page Number" ) );
3867
3868 if( selection.IsHover() )
3869 m_toolMgr->RunAction( ACTIONS::selectionClear );
3870
3871 return 0;
3872}
3873
3874
3876{
3877 return m_toolMgr->RunAction( SCH_ACTIONS::importSheet, aEvent.Parameter<wxString*>() );
3878}
3879
3880
3882{
3883 wxString* filename = aEvent.Parameter<wxString*>();
3884
3885 if( !filename )
3886 return 0;
3887
3888 SCH_BITMAP* image = new SCH_BITMAP( VECTOR2I( 0, 0 ) );
3889
3890 if( !image->GetReferenceImage().ReadImageFile( *filename ) )
3891 {
3892 wxMessageBox( wxString::Format( _( "Could not load image from '%s'." ), *filename ) );
3893 delete image;
3894 return 0;
3895 }
3896
3897 return m_toolMgr->RunAction( SCH_ACTIONS::placeImage, image );
3898}
3899
3900
3902{
3903 SCH_SELECTION& selection =
3905 ? m_selectionTool->RequestSelection( { SCH_SYMBOL_T } )
3906 : m_selectionTool->RequestSelection( { SCH_SYMBOL_T, SCH_SHEET_T, SCH_RULE_AREA_T } );
3907 std::set<std::pair<SCH_ITEM*, SCH_SCREEN*>> collectedItems;
3908
3909 for( EDA_ITEM* item : selection )
3910 {
3911 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item) )
3912 {
3913 collectedItems.insert( { symbol, m_frame->GetScreen() } );
3914
3915 // The attributes should be kept in sync in multi-unit parts.
3916 // Of course the symbol must be annotated to collect other units.
3917 if( symbol->IsAnnotated( &m_frame->GetCurrentSheet() ) )
3918 {
3919 wxString ref = symbol->GetRef( &m_frame->GetCurrentSheet() );
3920 int unit = symbol->GetUnit();
3921 LIB_ID libId = symbol->GetLibId();
3922
3923 for( SCH_SHEET_PATH& sheet : m_frame->Schematic().Hierarchy() )
3924 {
3925 SCH_SCREEN* screen = sheet.LastScreen();
3926 std::vector<SCH_SYMBOL*> otherUnits;
3927
3928 CollectOtherUnits( ref, unit, libId, sheet, &otherUnits );
3929
3930 for( SCH_SYMBOL* otherUnit : otherUnits )
3931 collectedItems.insert( { otherUnit, screen } );
3932 }
3933 }
3934 }
3935 else if( SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item ) )
3936 {
3937 collectedItems.insert( { sheet, m_frame->GetScreen() } );
3938 }
3939 else if( SCH_RULE_AREA* ruleArea = dynamic_cast<SCH_RULE_AREA*>( item ) )
3940 {
3941 collectedItems.insert( { ruleArea, m_frame->GetScreen() } );
3942 }
3943 }
3944
3945 SCH_COMMIT commit( m_toolMgr );
3946 SCH_SHEET_PATH* sheet = &m_frame->GetCurrentSheet();
3947 wxString variant = m_frame->Schematic().GetCurrentVariant();
3948 bool new_state = false;
3949
3950 for( const auto& [item, _] : collectedItems )
3951 {
3952 if( ( aEvent.IsAction( &SCH_ACTIONS::setDNP ) && !item->GetDNP( sheet, variant ) )
3953 || ( aEvent.IsAction( &SCH_ACTIONS::setExcludeFromSim ) && !item->GetExcludedFromSim( sheet, variant ) )
3954 || ( aEvent.IsAction( &SCH_ACTIONS::setExcludeFromBOM ) && !item->GetExcludedFromBOM( sheet, variant ) )
3955 || ( aEvent.IsAction( &SCH_ACTIONS::setExcludeFromBoard ) && !item->GetExcludedFromBoard( sheet, variant ) )
3957 && !item->GetExcludedFromPosFiles( sheet, variant ) ) )
3958 {
3959 new_state = true;
3960 break;
3961 }
3962 }
3963
3964 for( const auto& [item, screen] : collectedItems )
3965 {
3966 commit.Modify( item, screen );
3967
3968 if( aEvent.IsAction( &SCH_ACTIONS::setDNP ) )
3969 item->SetDNP( new_state, sheet, variant );
3970
3972 item->SetExcludedFromSim( new_state, sheet, variant );
3973
3975 item->SetExcludedFromBOM( new_state, sheet, variant );
3976
3978 item->SetExcludedFromBoard( new_state, sheet, variant );
3979
3981 item->SetExcludedFromPosFiles( new_state, sheet, variant );
3982 }
3983
3984 if( !commit.Empty() )
3985 commit.Push( _( "Toggle Attribute" ) );
3986
3987 if( selection.IsHover() )
3988 m_toolMgr->RunAction( ACTIONS::selectionClear );
3989
3990 return 0;
3991}
3992
3993
3994wxString SCH_EDIT_TOOL::FixERCErrorMenuText( const std::shared_ptr<RC_ITEM>& aERCItem )
3995{
3996 if( aERCItem->GetErrorCode() == ERCE_SIMULATION_MODEL
3997 || aERCItem->GetErrorCode() == ERCE_FOOTPRINT_FILTERS
3998 || aERCItem->GetErrorCode() == ERCE_FOOTPRINT_LINK_ISSUES )
3999 {
4000 return _( "Edit Symbol Properties..." );
4001 }
4002 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_ISSUES )
4003 {
4004 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::showSymbolLibTable );
4005 }
4006 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_MISMATCH )
4007 {
4008 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::updateSymbol );
4009 }
4010 else if( aERCItem->GetErrorCode() == ERCE_UNANNOTATED
4011 || aERCItem->GetErrorCode() == ERCE_DUPLICATE_REFERENCE )
4012 {
4013 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::annotate );
4014 }
4015 else if( aERCItem->GetErrorCode() == ERCE_UNDEFINED_NETCLASS )
4016 {
4017 return _( "Edit Netclasses..." );
4018 }
4019 else if( aERCItem->GetErrorCode() == ERCE_EMPTY_LABEL_NAME )
4020 {
4021 return _( "Remove Empty Label" );
4022 }
4023
4024 return wxEmptyString;
4025}
4026
4027
4028void SCH_EDIT_TOOL::FixERCError( const std::shared_ptr<RC_ITEM>& aERCItem )
4029{
4030 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
4031
4032 wxCHECK( frame, /* void */ );
4033
4034 if( aERCItem->GetErrorCode() == ERCE_SIMULATION_MODEL
4035 || aERCItem->GetErrorCode() == ERCE_FOOTPRINT_FILTERS
4036 || aERCItem->GetErrorCode() == ERCE_FOOTPRINT_LINK_ISSUES )
4037 {
4038 if( EDA_ITEM* item = frame->ResolveItem( aERCItem->GetMainItemID() ) )
4039 EditProperties( item );
4040 }
4041 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_ISSUES )
4042 {
4044 }
4045 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_MISMATCH )
4046 {
4047 EDA_ITEM* item = frame->ResolveItem( aERCItem->GetMainItemID() );
4048
4049 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
4050 {
4052 dlg.ShowQuasiModal();
4053 }
4054 }
4055 else if( aERCItem->GetErrorCode() == ERCE_UNANNOTATED
4056 || aERCItem->GetErrorCode() == ERCE_DUPLICATE_REFERENCE )
4057 {
4058 m_toolMgr->RunAction( SCH_ACTIONS::annotate );
4059 }
4060 else if( aERCItem->GetErrorCode() == ERCE_UNDEFINED_NETCLASS )
4061 {
4062 frame->ShowSchematicSetupDialog( _( "Net Classes" ) );
4063 }
4064 else if( aERCItem->GetErrorCode() == ERCE_EMPTY_LABEL_NAME )
4065 {
4066 SCH_SHEET_PATH sheetPath;
4067 SCH_ITEM* item = frame->Schematic().ResolveItem( aERCItem->GetMainItemID(), &sheetPath, true );
4068
4069 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item ) )
4070 {
4071 m_toolMgr->RunAction( ACTIONS::selectionClear );
4072
4073 SCH_COMMIT commit( m_toolMgr );
4074 commit.Remove( label, sheetPath.LastScreen() );
4075 commit.Push( _( "Remove Empty Label" ) );
4076 }
4077 }
4078}
4079
4080
4082{
4083 return modifyLockSelected( TOGGLE );
4084}
4085
4086
4088{
4089 return modifyLockSelected( ON );
4090}
4091
4092
4094{
4095 return modifyLockSelected( OFF );
4096}
4097
4098
4100{
4101 SCH_SELECTION& selection = m_selectionTool->RequestSelection();
4102
4103 if( selection.Empty() )
4104 m_toolMgr->RunAction( ACTIONS::cursorClick );
4105
4106 if( selection.Empty() )
4107 return 0;
4108
4109 if( aMode == TOGGLE )
4110 {
4111 aMode = ON;
4112
4113 for( EDA_ITEM* item : selection )
4114 {
4115 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
4116 {
4117 if( schItem->IsLocked() )
4118 {
4119 aMode = OFF;
4120 break;
4121 }
4122 }
4123 }
4124 }
4125
4126 wxString commitMsg = ( aMode == ON ) ? _( "Lock" ) : _( "Unlock" );
4127 SCH_COMMIT commit( m_toolMgr );
4128
4129 for( EDA_ITEM* item : selection )
4130 {
4131 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
4132 {
4133 // Don't lock/unlock pins, fields, or sheet pins - they inherit from parent
4134 if( schItem->Type() == SCH_PIN_T || schItem->Type() == SCH_FIELD_T
4135 || schItem->Type() == SCH_SHEET_PIN_T )
4136 continue;
4137
4138 commit.Modify( schItem, m_frame->GetScreen() );
4139 schItem->SetLocked( aMode == ON );
4140 }
4141 }
4142
4143 if( !commit.Empty() )
4144 {
4145 commit.Push( commitMsg );
4146 m_toolMgr->PostEvent( EVENTS::SelectedEvent );
4147 }
4148
4149 return 0;
4150}
4151
4152
4154{
4155 // clang-format off
4161 Go( &SCH_EDIT_TOOL::Swap, SCH_ACTIONS::swap.MakeEvent() );
4167
4173
4195
4196
4202
4204 Go( &SCH_EDIT_TOOL::Lock, SCH_ACTIONS::lock.MakeEvent() );
4206
4210
4213 // clang-format on
4214}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
@ add_hierarchical_label
@ component_select_unit
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
static TOOL_ACTION decrementPrimary
Definition actions.h:92
static TOOL_ACTION paste
Definition actions.h:76
static TOOL_ACTION unselectAll
Definition actions.h:79
static TOOL_ACTION decrementSecondary
Definition actions.h:94
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition actions.h:223
static TOOL_ACTION unselectItem
Definition actions.h:224
static TOOL_ACTION copy
Definition actions.h:74
static TOOL_ACTION showSymbolLibTable
Definition actions.h:281
static TOOL_ACTION pasteSpecial
Definition actions.h:77
static TOOL_ACTION groupProperties
Definition actions.h:243
static TOOL_ACTION rightJustify
Definition actions.h:85
static TOOL_ACTION pageSettings
Definition actions.h:59
static TOOL_ACTION incrementSecondary
Definition actions.h:93
static TOOL_ACTION duplicate
Definition actions.h:80
static TOOL_ACTION incrementPrimary
Definition actions.h:91
static TOOL_ACTION doDelete
Definition actions.h:81
static TOOL_ACTION cursorClick
Definition actions.h:176
static TOOL_ACTION deleteTool
Definition actions.h:82
static TOOL_ACTION increment
Definition actions.h:90
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION leftJustify
Definition actions.h:83
static TOOL_ACTION cut
Definition actions.h:73
static TOOL_ACTION copyAsText
Definition actions.h:75
static TOOL_ACTION refreshPreview
Definition actions.h:155
static TOOL_ACTION selectAll
Definition actions.h:78
static TOOL_ACTION centerJustify
Definition actions.h:84
Manage TOOL_ACTION objects.
void SetConditions(const TOOL_ACTION &aAction, const ACTION_CONDITIONS &aConditions)
Set the conditions the UI elements for activating a specific tool action should use for determining t...
ACTION_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool=nullptr)
Default constructor.
TOOL_MANAGER * getToolManager() const
Return an instance of TOOL_MANAGER class.
void SetUntranslatedTitle(const wxString &aTitle)
Definition action_menu.h:60
void Clear()
Remove all the entries from the menu (as well as its title).
void SetTitle(const wxString &aTitle) override
Set title for the menu.
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
friend class TOOL_INTERACTIVE
TOOL_INTERACTIVE * m_tool
Creator of the menu.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
void update() override
Update menu state stub.
void update() override
Update menu state stub.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
constexpr const Vec & GetPosition() const
Definition box2.h:208
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:234
constexpr Vec Centre() const
Definition box2.h:94
constexpr const Vec GetCenter() const
Definition box2.h:227
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr const Vec & GetOrigin() const
Definition box2.h:207
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:294
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr coord_type GetBottom() const
Definition box2.h:219
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:86
COMMIT & Added(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition commit.h:80
void Unstage(EDA_ITEM *aItem, BASE_SCREEN *aScreen)
Definition commit.cpp:177
bool Empty() const
Definition commit.h:145
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 & Removed(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been removed.
Definition commit.h:92
int GetStatus(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Returns status of an item.
Definition commit.cpp:235
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
void AddCheckItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a checked menu entry to run a TOOL_ACTION on selected items.
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
CONDITIONAL_MENU(TOOL_INTERACTIVE *aTool)
Calculate the connectivity of a schematic and generate netlists.
A subgraph is a set of items that are electrically connected on a single sheet.
Dialog to update or change schematic library symbols.
This class is setup in expectation of its children possibly using Kiway player so DIALOG_SHIM::ShowQu...
void UpdateField(SCH_FIELD *aField)
int ShowModal() override
Dialog used to edit SCH_SYMBOL objects in a schematic.
bool HitTestDrawingSheetItems(KIGFX::VIEW *aView, const VECTOR2I &aPosition)
bool IsHorizontal() const
Definition eda_angle.h:141
bool IsVertical() const
Definition eda_angle.h:147
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:43
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 void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:349
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:270
EDA_ITEM_FLAGS GetEditFlags() const
Definition eda_item.h:170
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
bool IsSelected() const
Definition eda_item.h:134
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:214
EDA_ITEM * GetParent() const
Definition eda_item.h:112
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:168
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:153
bool IsMoving() const
Definition eda_item.h:132
bool IsNew() const
Definition eda_item.h:131
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:329
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
virtual VECTOR2I GetTextSize() const
Definition eda_text.h:301
bool IsItalic() const
Definition eda_text.h:200
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:512
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
virtual bool IsVisible() const
Definition eda_text.h:226
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:556
KIFONT::FONT * GetFont() const
Definition eda_text.h:286
void SetAttributes(const EDA_TEXT &aSrc, bool aSetPosition=true)
Set the text attributes from another instance.
Definition eda_text.cpp:394
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:378
wxString GetHyperlink() const
Definition eda_text.h:449
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:239
virtual void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:250
virtual EDA_ANGLE GetTextAngle() const
Definition eda_text.h:178
void SetBold(bool aBold)
Set the text to be bold - this will also update the font if needed.
Definition eda_text.cpp:310
bool IsBold() const
Definition eda_text.h:215
void SetHyperlink(wxString aLink)
Definition eda_text.h:450
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:242
virtual int GetTextThickness() const
Definition eda_text.h:159
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:268
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:290
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:471
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:370
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition sch_rtree.h:253
static const TOOL_EVENT SelectedEvent
Definition actions.h:345
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual wxString GetClass() const =0
Return the class name.
void RecacheAllItems()
Rebuild GAL display lists.
Definition view.cpp:1597
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:168
UTF8 Format() const
Definition lib_id.cpp:132
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
Define a library symbol object.
Definition lib_symbol.h:114
int GetUnitCount() const override
std::unique_ptr< LIB_SYMBOL > Flatten() const
Return a flattened symbol inheritance to the caller.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
LOCK_CONTEXT_MENU(TOOL_INTERACTIVE *aTool)
static bool ParseBusGroup(const wxString &aGroup, wxString *name, std::vector< wxString > *aMemberList, size_t *aPrefixEnd=nullptr)
Parse a bus group label into the name and a list of components.
A singleton reporter that reports to nowhere.
Definition reporter.h:267
void update() override
Update menu state stub.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
These are loaded from Eeschema settings but then overwritten by the project settings.
Holds all the data relating to one schematic.
Definition schematic.h:149
SCHEMATIC_SETTINGS & Settings() const
SCH_CONNECTIVITY::FACADE & Connectivity() const
Definition schematic.h:316
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:319
static TOOL_ACTION rotateCCW
static TOOL_ACTION placeClassLabel
Definition sch_actions.h:85
static TOOL_ACTION swapPins
static TOOL_ACTION editValue
static TOOL_ACTION setExcludeFromBOM
static TOOL_ACTION mirrorV
static TOOL_ACTION clearHighlight
static TOOL_ACTION swap
static TOOL_ACTION placeGlobalLabel
Definition sch_actions.h:86
static TOOL_ACTION changeSymbols
static TOOL_ACTION updateSymbol
static TOOL_ACTION autoplaceFields
static TOOL_ACTION changeSymbol
static TOOL_ACTION ddAddImage
static TOOL_ACTION properties
static TOOL_ACTION editReference
static TOOL_ACTION placeHierLabel
Definition sch_actions.h:87
static TOOL_ACTION placeLabel
Definition sch_actions.h:84
static TOOL_ACTION toText
static TOOL_ACTION toHLabel
static TOOL_ACTION rotateCW
static TOOL_ACTION importSheet
Definition sch_actions.h:93
static TOOL_ACTION setExcludeFromSim
static TOOL_ACTION toLabel
static TOOL_ACTION setDNP
static TOOL_ACTION swapUnitLabels
static TOOL_ACTION placeImage
static TOOL_ACTION editWithLibEdit
static TOOL_ACTION cleanupSheetPins
static TOOL_ACTION toDLabel
static TOOL_ACTION clearVariantSymbol
static TOOL_ACTION setExcludeFromPosFiles
static TOOL_ACTION cycleBodyStyle
static TOOL_ACTION mirrorH
static TOOL_ACTION setVariantSymbol
static TOOL_ACTION ddAppendFile
static TOOL_ACTION placeSchematicText
Definition sch_actions.h:98
static TOOL_ACTION toTextBox
static TOOL_ACTION unlock
Definition sch_actions.h:68
static TOOL_ACTION lock
Definition sch_actions.h:67
static TOOL_ACTION annotate
static TOOL_ACTION updateSymbols
static TOOL_ACTION swapPinLabels
static TOOL_ACTION enterSheet
static TOOL_ACTION editFootprint
static TOOL_ACTION repeatDrawItem
static TOOL_ACTION editTextAndGraphics
static TOOL_ACTION editPageNumber
static TOOL_ACTION toGLabel
static TOOL_ACTION setExcludeFromBoard
static TOOL_ACTION move
static TOOL_ACTION toggleLock
Definition sch_actions.h:66
Object to handle a bitmap image that can be inserted in a schematic.
Definition sch_bitmap.h:36
static const std::vector< KICAD_T > FieldOwners
static const std::vector< KICAD_T > DeletableItems
Commit for the schematic and symbol editors.
Definition sch_commit.h:54
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
static SELECTION_CONDITION SingleMultiFunctionPin
static SELECTION_CONDITION SingleSymbol
static SELECTION_CONDITION MultipleSymbolsOrPower
static SELECTION_CONDITION HasLockedItems
static SELECTION_CONDITION AllPinsOrSheetPins
static SELECTION_CONDITION SingleSymbolOrPower
static SELECTION_CONDITION HasUnlockedItems
static SELECTION_CONDITION SingleMultiUnitSymbol
static SELECTION_CONDITION SingleMultiBodyStyleSymbol
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
bool IsNet() const
std::optional< ITEM_VIEW > Connection(const KIID &aItem, const KIID_PATH &aPath) const
Tool responsible for drawing/placing items (symbols, wires, buses, labels, etc.).
Schematic editor (Eeschema) main window.
const wxString & GetHighlightedConnection() const
wxString FixERCErrorMenuText(const std::shared_ptr< RC_ITEM > &aERCItem)
void setTransitions() override
Set up handlers for various events.
static const std::vector< KICAD_T > RotatableItems
int SwapUnitLabels(const TOOL_EVENT &aEvent)
int EditField(const TOOL_EVENT &aEvent)
int EditPageNumber(const TOOL_EVENT &aEvent)
int DoDelete(const TOOL_EVENT &aEvent)
Delete the selected items, or the item under the cursor.
bool Init() override
Init() is called once upon a registration of the tool.
int CleanupSheetPins(const TOOL_EVENT &aEvent)
int Unlock(const TOOL_EVENT &aEvent)
int ClearVariantSymbol(const TOOL_EVENT &aEvent)
void EditProperties(EDA_ITEM *aItem)
void editFieldText(SCH_FIELD *aField)
int SwapPins(const TOOL_EVENT &aEvent)
int Mirror(const TOOL_EVENT &aEvent)
MODIFY_MODE
How to modify a property for selected items.
int SetAttribute(const TOOL_EVENT &aEvent)
Modify Attributes (DNP, Exclude, etc.) All attributes are set to true unless all symbols already have...
int Properties(const TOOL_EVENT &aEvent)
int Rotate(const TOOL_EVENT &aEvent)
int Lock(const TOOL_EVENT &aEvent)
int DdAppendFile(const TOOL_EVENT &aEvent)
Drag and drop.
int JustifyText(const TOOL_EVENT &aEvent)
int modifyLockSelected(MODIFY_MODE aMode)
int SetVariantSymbol(const TOOL_EVENT &aEvent)
static const std::vector< KICAD_T > SwappableItems
void FixERCError(const std::shared_ptr< RC_ITEM > &aERCItem)
int SwapPinLabels(const TOOL_EVENT &aEvent)
int AutoplaceFields(const TOOL_EVENT &aEvent)
int CycleBodyStyle(const TOOL_EVENT &aEvent)
int Swap(const TOOL_EVENT &aEvent)
int ChangeSymbols(const TOOL_EVENT &aEvent)
int ChangeTextType(const TOOL_EVENT &aEvent)
Change a text type to another one.
int ToggleLock(const TOOL_EVENT &aEvent)
Lock/unlock selected items.
int DdAddImage(const TOOL_EVENT &aEvent)
int RepeatDrawItem(const TOOL_EVENT &aEvent)
bool IsMandatory() const
void Rotate(const VECTOR2I &aCenter, bool aRotateCCW) override
Rotate the item around aCenter 90 degrees in the clockwise direction.
VECTOR2I GetPosition() const override
FIELD_T GetId() const
Definition sch_field.h:143
wxString GetUntranslatedName() const
Get the untranslated field name for storage, variable look-up, etc.
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
VECTOR2I GetParentPosition() const
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:48
int CrossProbe(const TOOL_EVENT &aEvent)
Called when clicking on a item:
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
SCH_ITEM * Duplicate(bool addToParentGroup, SCH_COMMIT *aCommit=nullptr, bool aDoClone=false) const
Routine to create a new copy of given item.
Definition sch_item.cpp:191
virtual bool IsConnectable() const
Definition sch_item.h:545
virtual void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo)
Definition sch_item.h:676
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode)
Definition sch_item.h:678
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:350
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:302
int GetBodyStyle() const
Definition sch_item.h:253
virtual void MirrorHorizontally(int aCenter)
Mirror item horizontally about aCenter.
Definition sch_item.h:425
const std::vector< SCH_ITEM * > & ConnectedItems(const SCH_SHEET_PATH &aPath) const
Retrieve the set of items connected to this item on the given sheet.
Definition sch_item.cpp:766
virtual void Move(const VECTOR2I &aMoveVector)
Move the item by aMoveVector to a new position.
Definition sch_item.h:417
int GetUnit() const
Definition sch_item.h:243
void SetConnectivityDirty(bool aDirty=true)
Set the dirty flag.
Definition sch_item.cpp:314
void SetFieldsAutoplaced(AUTOPLACE_ALGO aAlgo)
Definition sch_item.h:674
virtual void Rotate(const VECTOR2I &aCenter, bool aRotateCCW)
Rotate the item around aCenter 90 degrees in the clockwise direction.
Definition sch_item.h:441
AUTOPLACE_ALGO GetFieldsAutoplaced() const
Return whether the fields have been automatically placed.
Definition sch_item.h:673
wxString GetClass() const override
Return the class name.
Definition sch_item.h:180
bool IsGroupableType() const
Definition sch_item.cpp:144
virtual std::vector< VECTOR2I > GetConnectionPoints() const
Add all the connection points for this item to aPoints.
Definition sch_item.h:558
bool IsType(const std::vector< KICAD_T > &aScanTypes) const override
Check whether the item is one of the listed types.
Definition sch_item.h:185
virtual void MirrorVertically(int aCenter)
Mirror item vertically about aCenter.
Definition sch_item.h:433
void AddFields(const std::vector< SCH_FIELD > &aFields)
void MirrorHorizontally(int aCenter) override
Mirror item horizontally about aCenter.
SPIN_STYLE GetSpinStyle() const
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:179
LABEL_FLAG_SHAPE GetShape() const
Definition sch_label.h:178
void MirrorVertically(int aCenter) override
Mirror item vertically about aCenter.
std::vector< SCH_FIELD > & GetFields()
Definition sch_label.h:210
virtual void SetSpinStyle(SPIN_STYLE aSpinStyle)
Tool responsible for drawing/placing items (symbols, wires, buses, labels, etc.)
int AddJunctionsIfNeeded(SCH_COMMIT *aCommit, SCH_SELECTION *aSelection)
Handle the addition of junctions to a selection of objects.
int TrimOverLappingWires(SCH_COMMIT *aCommit, SCH_SELECTION *aSelection)
Logic to remove wires when overlapping correct items.
static bool IsDrawingLineWireOrBus(const SELECTION &aSelection)
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:39
void Rotate(const VECTOR2I &aCenter, bool aRotateCCW) override
Rotate the item around aCenter 90 degrees in the clockwise direction.
Definition sch_line.cpp:498
VECTOR2I GetEndPoint() const
Definition sch_line.h:145
VECTOR2I GetStartPoint() const
Definition sch_line.h:136
const std::map< wxString, ALT > & GetAlternates() const
Definition sch_pin.h:219
bool IsConnectable() const override
Definition sch_pin.h:386
const wxString & GetName() const
Definition sch_pin.cpp:527
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:818
void ClearAnnotationOfNewSheetPaths(SCH_SHEET_LIST &aInitialSheetPathList)
Clear the annotation for the symbols inside new sheetpaths when a complex hierarchy is modified and n...
bool IsExplicitJunction(const VECTOR2I &aPosition) const
Indicate that a junction dot is necessary at the given location.
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
const wxString & GetFileName() const
Definition sch_screen.h:157
SCH_ITEM * GetItem(const VECTOR2I &aPosition, int aAccuracy=0, KICAD_T aType=SCH_LOCATE_ANY_T) const
Check aPosition within a distance of aAccuracy for items of type aFilter.
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
void ClearAnnotation(SCH_SHEET_PATH *aSheetPath, bool aResetPrefix)
Clear the annotation for the symbols in aSheetPath on the screen.
SCH_SELECTION & GetSelection()
SCH_GROUP * GetEnteredGroup()
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:87
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void BuildSheetList(SCH_SHEET *aSheet, bool aCheckIntegrity)
Build the list of sheets and their sheet path from aSheet.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
SCH_SCREEN * LastScreen()
wxString GetPageNumber() const
wxString PathHumanReadable(bool aUseShortRootName=true, bool aStripTrailingSeparator=false, bool aEscapeSheetNames=false) const
Return the sheet path in a human readable form made from the sheet names.
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
const KIID_PATH & PathRef() const
Borrow the cached path until this sheet path is modified or destroyed.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
size_t size() const
Forwarded method from std::vector.
void pop_back()
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
SHEET_SIDE GetSide() const
void SetSide(SHEET_SIDE aEdge)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:392
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:386
std::map< SCH_SHEET_PIN *, SCH_NO_CONNECT * > GetNoConnects() const
VECTOR2I GetRotationCenter() const
Rotating around the boundingBox's center can cause walking when the sheetname or filename is longer t...
void CleanupSheet()
Delete sheet label which do not have a corresponding hierarchical label.
void RemovePin(const SCH_SHEET_PIN *aSheetPin)
Remove aSheetPin from the sheet.
bool HasUndefinedPins() const
Check all sheet labels against schematic for undefined hierarchical labels.
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
const BOX2I GetBodyBoundingBox() const
Return a bounding box for the sheet body but not the fields.
void MirrorHorizontally(int aCenter) override
Mirror item horizontally about aCenter.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
void MirrorVertically(int aCenter) override
Mirror item vertically about aCenter.
void Rotate(const VECTOR2I &aCenter, bool aRotateCCW) override
Rotate the item around aCenter 90 degrees in the clockwise direction.
Schematic symbol object.
Definition sch_symbol.h:73
wxString GetUnitDisplayName(int aUnit, bool aLabel) const override
Return the display name for a given unit aUnit.
bool ClearVariantSymbolOverride(const SCH_SHEET_PATH &aPath, const wxString &aVariantName)
Remove the alternate-symbol override for a given variant and sheet instance.
void SetVariantSymbolOverride(const SCH_SHEET_PATH &aPath, const wxString &aVariantName, const LIB_ID &aLibId)
Set the alternate-symbol override for a given variant and sheet instance.
bool IsMultiBodyStyle() const override
Definition sch_symbol.h:264
void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo) override
Automatically orient all the fields in the symbol.
void ClearCaches() override
std::optional< SCH_SYMBOL_VARIANT > GetVariant(const SCH_SHEET_PATH &aInstance, const wxString &aVariantName) const
wxString GetBodyStyleDescription(int aBodyStyle, bool aLabel) const override
void UpdatePins()
Updates the cache of SCH_PIN objects for each pin.
bool HasDeMorganBodyStyles() const override
int GetBodyStyleCount() const override
Return the number of body styles of the symbol.
void SetOrientation(int aOrientation)
Compute the new transform matrix based on aOrientation for the symbol which is applied to the current...
bool IsMissingLibSymbol() const
Check to see if the library symbol is set to the dummy library symbol.
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:163
int GetOrientation() const override
Get the display symbol orientation.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:182
void Rotate(const VECTOR2I &aCenter, bool aRotateCCW) override
Rotate the item around aCenter 90 degrees in the clockwise direction.
bool IsPower() const override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
int GetMarginBottom() const
Definition sch_textbox.h:82
int GetLegacyTextMargin() const
int GetMarginLeft() const
Definition sch_textbox.h:79
int GetMarginRight() const
Definition sch_textbox.h:81
int GetMarginTop() const
Definition sch_textbox.h:80
virtual void Rotate90(bool aClockwise)
Definition sch_text.cpp:255
virtual void MirrorSpinStyle(bool aLeftRight)
Definition sch_text.cpp:266
void updateItem(EDA_ITEM *aItem, bool aUpdateRTree) const
int Increment(const TOOL_EVENT &aEvent)
int InteractiveDelete(const TOOL_EVENT &aEvent)
bool Init() override
Init() is called once upon a registration of the tool.
SCH_TOOL_BASE(const std::string &aName)
SCH_SELECTION_TOOL * m_selectionTool
static SELECTION_CONDITION HasTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if among the selected items there is at least one of a given types.
static SELECTION_CONDITION HasType(KICAD_T aType)
Create a functor that tests if among the selected items there is at least one of a given type.
static bool NotEmpty(const SELECTION &aSelection)
Test if there are any items selected.
static SELECTION_CONDITION MoreThan(int aNumber)
Create a functor that tests if the number of selected items is greater than the value given as parame...
static bool Idle(const SELECTION &aSelection)
Test if there no items selected or being edited.
static bool IdleSelection(const SELECTION &aSelection)
Test if all selected items are not being edited.
static SELECTION_CONDITION Count(int aNumber)
Create a functor that tests if the number of selected items is equal to the value given as parameter.
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
virtual void Add(EDA_ITEM *aItem)
A null aItem is ignored; the selection never holds null members.
Definition selection.cpp:38
virtual KIGFX::VIEW_ITEM * GetItem(unsigned int aIdx) const override
Definition selection.cpp:75
VECTOR2I GetReferencePoint() const
virtual VECTOR2I GetCenter() const
Returns the center point of the selection area bounding box.
Definition selection.cpp:92
bool IsHover() const
Definition selection.h:85
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition selection.h:104
EDA_ITEM * Front() const
Definition selection.h:176
int Size() const
Returns the number of selected parts.
Definition selection.h:120
std::deque< EDA_ITEM * > & Items()
Definition selection.h:181
std::vector< EDA_ITEM * > GetItemsSortedBySelectionOrder() const
bool OnlyContains(std::vector< KICAD_T > aList) const
Checks if all items in the selection have a type in aList.
bool Empty() const
Checks if there is anything selected.
Definition selection.h:114
bool HasReferencePoint() const
Definition selection.h:215
size_t CountType(KICAD_T aType) const
Spin style for labels of all kinds on schematics.
Definition sch_label.h:38
unsigned CCWRotationsTo(const SPIN_STYLE &aOther) const
Get CCW rotation needed to get to the given spin style.
The symbol library editor main window.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
LIB_SYMBOL * LoadSymbol(const wxString &aNickname, const wxString &aName)
Load a LIB_SYMBOL having aName from the library given by aNickname.
Helper object to filter a list of libraries.
void update() override
Update menu state stub.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
KIGFX::VIEW_CONTROLS * getViewControls() const
Definition tool_base.cpp:40
KIGFX::VIEW * getView() const
Definition tool_base.cpp:34
Generic, UI-independent tool event.
Definition tool_event.h:167
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition tool_event.h:388
COMMIT * Commit() const
Definition tool_event.h:279
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:484
void Go(int(SCH_EDIT_FRAME::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
TOOL_MENU & GetToolMenu()
TOOL_INTERACTIVE(TOOL_ID aId, const std::string &aName)
Create a tool with given id & name.
T * GetTool()
Return the tool of given type or nullptr if there is no such tool registered.
CONDITIONAL_MENU & GetMenu()
Definition tool_menu.cpp:40
bool empty() const
Definition utf8.h:105
wxString wx_str() const
Definition utf8.cpp:41
A modified version of the wxInfoBar class that allows us to:
Definition wx_infobar.h:76
void RemoveAllButtons()
Remove all the buttons that have been added by the user.
void ShowMessageFor(const wxString &aMessage, int aTime, int aFlags=wxICON_INFORMATION, MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the infobar with the provided message and icon for a specific period of time.
void AddLink(const wxString &aLinkText, const std::function< void(wxHyperlinkEvent &)> &aFn)
Add a hypertext link to the infobar.
void AddCloseButton(const wxString &aTooltip=_("Hide this message."))
Add the default close button to the infobar on the right side.
@ CHT_ADD
Definition commit.h:38
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:274
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition confirm.cpp:245
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
This file is part of the common library.
@ SYMBOL_PROPS_EDIT_SCHEMATIC_SYMBOL
@ SYMBOL_PROPS_WANT_EXCHANGE_SYMBOL
@ SYMBOL_PROPS_WANT_UPDATE_SYMBOL
@ SYMBOL_PROPS_WANT_SET_VARIANT_SYMBOL
@ SYMBOL_PROPS_EDIT_LIBRARY_SYMBOL
@ SYMBOL_PROPS_EDIT_OK
@ SYMBOL_PROPS_WANT_CLEAR_VARIANT_SYMBOL
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:445
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:444
@ NO_FILL
Definition eda_fill.h:30
@ RECURSE
Definition eda_item.h:51
@ NO_RECURSE
Definition eda_item.h:52
#define IGNORE_PARENT_GROUP
Definition eda_item.h:55
#define IS_NEW
New item, just created.
#define SELECTED_BY_DRAG
Item was algorithmically selected as a dragged item.
#define STRUCT_DELETED
flag indication structures to be erased
#define ENDPOINT
ends. (Used to support dragging.)
#define STARTPOINT
When a line is selected, these flags indicate which.
@ ID_POPUP_SCH_PIN_TRICKS_HIER_LABEL
Definition eeschema_id.h:99
@ ID_POPUP_SCH_PIN_TRICKS_WIRE
Definition eeschema_id.h:97
@ ID_POPUP_SCH_ALT_PIN_FUNCTION
@ ID_POPUP_SCH_SELECT_UNIT1
Definition eeschema_id.h:82
@ ID_POPUP_SCH_SELECT_UNIT
Definition eeschema_id.h:81
@ ID_POPUP_SCH_SELECT_BODY_STYLE
Definition eeschema_id.h:91
@ ID_POPUP_SCH_PLACE_UNIT1
Definition eeschema_id.h:88
@ ID_POPUP_SCH_SELECT_BODY_STYLE1
Definition eeschema_id.h:92
@ ID_POPUP_SCH_PIN_TRICKS_NET_LABEL
Definition eeschema_id.h:98
@ ID_POPUP_SCH_PIN_TRICKS_NO_CONNECT
Definition eeschema_id.h:96
@ ID_POPUP_SCH_SELECT_UNIT_END
Definition eeschema_id.h:85
@ ID_POPUP_SCH_ALT_PIN_FUNCTION_END
@ ID_POPUP_SCH_PIN_TRICKS_GLOBAL_LABEL
@ ERCE_UNANNOTATED
Symbol has not been annotated.
@ ERCE_DUPLICATE_REFERENCE
More than one symbol with the same reference.
@ ERCE_EMPTY_LABEL_NAME
Label has an empty or whitespace-only name.
@ ERCE_FOOTPRINT_LINK_ISSUES
The footprint link is invalid, or points to a missing (or inactive) footprint or library.
@ ERCE_UNDEFINED_NETCLASS
A netclass was referenced but not defined.
@ ERCE_SIMULATION_MODEL
An error was found in the simulation model.
@ ERCE_LIB_SYMBOL_MISMATCH
Symbol doesn't match copy in library.
@ ERCE_LIB_SYMBOL_ISSUES
Symbol not found in active libraries.
@ ERCE_FOOTPRINT_FILTERS
The assigned footprint doesn't match the footprint filters.
@ FRAME_SCH_SYMBOL_EDITOR
Definition frame_type.h:31
bool m_ConnectivityEngine
Use the schematic connectivity engine instead of CONNECTION_GRAPH.
@ LAYER_NOTES
Definition layer_ids.h:489
@ LAYER_SCHEMATIC_DRAWINGSHEET
Definition layer_ids.h:518
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:79
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:92
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
#define _HKI(x)
Definition page_info.cpp:40
see class PGM_BASE
CITER next(CITER it)
Definition ptree.cpp:120
void CollectOtherUnits(const wxString &aRef, int aUnit, const LIB_ID &aLibId, SCH_SHEET_PATH &aSheet, std::vector< SCH_SYMBOL * > *otherUnits)
static void swapFieldPositionsWithMatching(std::vector< SCH_FIELD > &aAFields, std::vector< SCH_FIELD > &aBFields, unsigned aFallbackRotationsCCW)
Swap the positions of the fields in the two lists, aAFields and aBFields, relative to their parent po...
static SCH_LABEL_BASE * findSingleNetLabelForPin(SCH_PIN *aPin, SCHEMATIC &aSchematic, const SCH_SHEET_PATH &aSheetPath)
SCH_CONDITIONS S_C
Class to handle a set of SCH_ITEMs.
AUTOPLACE_ALGO
Definition sch_item.h:73
@ AUTOPLACE_MANUAL
Definition sch_item.h:76
@ AUTOPLACE_NONE
Definition sch_item.h:74
@ AUTOPLACE_AUTO
Definition sch_item.h:75
@ BASE
Definition sch_item.h:64
LABEL_FLAG_SHAPE
Label and flag shapes used with text objects.
Definition sch_label.h:97
@ L_UNSPECIFIED
Definition sch_label.h:102
@ F_ROUND
Definition sch_label.h:106
ANNOTATE_ORDER_T
Schematic annotation order options.
@ ANNOTATE_SELECTION
Annotate the selection.
ANNOTATE_ALGO_T
Schematic annotation type options.
static std::vector< KICAD_T > sheetTypes
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
SHEET_SIDE
Define the edge of the sheet that the sheet pin is positioned.
std::set< wxString > GetSheetNamesFromPaths(const std::set< wxString > &aSheetPaths, const SCHEMATIC &aSchematic)
Get human-readable sheet names from a set of sheet paths, e.g.
bool SwapPinGeometry(SCH_PIN *aFirst, SCH_PIN *aSecond)
Swap the positions/lengths/etc.
std::set< int > GetUnplacedUnitsForSymbol(const SCH_SYMBOL &aSym)
Get a list of unplaced (i.e.
bool SymbolHasSheetInstances(const SCH_SYMBOL &aSymbol, const wxString &aCurrentProject, std::set< wxString > *aSheetPaths, std::set< wxString > *aProjectNames)
Returns true when the given symbol has instances, e.g.
std::vector< SCH_SYMBOL * > GetSameSymbolMultiUnitSelection(const SELECTION &aSel)
Validates and gathers a selection containing multiple symbol units that all belong to the same refere...
@ NO_CLEANUP
Definition schematic.h:93
T * GetAppSettings(const char *aFilename)
wxString UnescapeString(const wxString &aSource)
wxString TitleCaps(const wxString &aString)
Capitalize the first letter in each word.
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_NETNAME
void AccumulateDescriptions(wxString &aDesc, const T &aItemCollection)
Build a comma-separated list from a collection of wxStrings.
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
@ SYM_MIRROR_Y
Definition symbol.h:41
@ SYM_MIRROR_X
Definition symbol.h:40
wxString GetDefaultFieldName(FIELD_T aFieldId, TRANSLATION aTranslation)
Return a default symbol field name for a mandatory field type.
@ USER
The field ID hasn't been set yet; field is invalid.
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
@ TRANSLATED
IbisParser parser & reporter
KIBIS_PIN * pin
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
GR_TEXT_V_ALIGN_T
This is API surface mapped to common.types.VertialAlignment.
constexpr GR_TEXT_H_ALIGN_T GetFlippedAlignment(GR_TEXT_H_ALIGN_T aAlign)
Get the reverse alignment: left-right are swapped, others are unchanged.
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:227
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ SCH_GROUP_T
Definition typeinfo.h:169
@ SCH_TABLE_T
Definition typeinfo.h:161
@ SCH_LINE_T
Definition typeinfo.h:159
@ SCH_NO_CONNECT_T
Definition typeinfo.h:156
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_TABLECELL_T
Definition typeinfo.h:162
@ SCH_ITEM_LOCATE_WIRE_T
Definition typeinfo.h:182
@ SCH_FIELD_T
Definition typeinfo.h:146
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:167
@ SCH_LABEL_T
Definition typeinfo.h:163
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_ITEM_LOCATE_BUS_T
Definition typeinfo.h:183
@ SCH_MARKER_T
Definition typeinfo.h:154
@ SCH_SHAPE_T
Definition typeinfo.h:145
@ SCH_RULE_AREA_T
Definition typeinfo.h:166
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165
@ SCH_BUS_BUS_ENTRY_T
Definition typeinfo.h:158
@ SCH_LABEL_LOCATE_ANY_T
Definition typeinfo.h:187
@ SCH_ITEM_LOCATE_GRAPHIC_LINE_T
Definition typeinfo.h:184
@ SCHEMATIC_T
Definition typeinfo.h:200
@ SCH_SHEET_PIN_T
Definition typeinfo.h:170
@ SCH_TEXT_T
Definition typeinfo.h:147
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:157
@ SCH_BITMAP_T
Definition typeinfo.h:160
@ SCH_TEXTBOX_T
Definition typeinfo.h:148
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:164
@ SCH_JUNCTION_T
Definition typeinfo.h:155
@ SCH_PIN_T
Definition typeinfo.h:149
std::vector< VARIANT_COMPAT_RESULT > ValidateVariantSymbolCompatibility(const LIB_SYMBOL &aBase, const LIB_SYMBOL &aCandidate)
Check whether aCandidate can be used as a variant symbol override for aBase.
std::function< std::vector< VARIANT_COMPAT_RESULT >(const LIB_ID &)> SYMBOL_COMPAT_FUNC
Callback that evaluates variant symbol compatibility for a given candidate LIB_ID.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707