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