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