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