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, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <kiway.h>
26#include <tool/action_manager.h>
27#include <tool/picker_tool.h>
28#include <tools/sch_edit_tool.h>
31#include <tools/sch_move_tool.h>
33#include <confirm.h>
34#include <connection_graph.h>
35#include <sch_actions.h>
36#include <sch_tool_utils.h>
37#include <increment.h>
38#include <algorithm>
39#include <set>
40#include <string_utils.h>
41#include <sch_bitmap.h>
42#include <sch_bus_entry.h>
43#include <sch_commit.h>
44#include <sch_group.h>
45#include <sch_label.h>
46#include <sch_junction.h>
47#include <sch_marker.h>
48#include <sch_rule_area.h>
49#include <sch_pin.h>
50#include <sch_sheet_pin.h>
51#include <sch_textbox.h>
52#include <sch_table.h>
54#include <eeschema_id.h>
65#include <dialogs/dialog_text_properties.h>
66#include <dialogs/dialog_tablecell_properties.h>
67#include <dialogs/dialog_table_properties.h>
68#include <pgm_base.h>
71#include <core/kicad_algo.h>
72#include <view/view_controls.h>
73#include <wx/textdlg.h>
74#include <wx/msgdlg.h>
77
78
80{
81public:
83 ACTION_MENU( true )
84 {
86 SetTitle( _( "Symbol Unit" ) );
87 }
88
89protected:
90 ACTION_MENU* create() const override
91 {
92 return new SYMBOL_UNIT_MENU();
93 }
94
95private:
96 void update() override
97 {
99 SCH_SELECTION& selection = selTool->GetSelection();
100 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
101
102 Clear();
103
104 wxCHECK( symbol, /* void */ );
105
106 const int unit = symbol->GetUnit();
107 const int nUnits = symbol->GetLibSymbolRef()->GetUnitCount();
108 const std::set<int> missingUnits = GetUnplacedUnitsForSymbol( *symbol );
109
110 for( int ii = 0; ii < nUnits; ii++ )
111 {
112 wxString unit_text = symbol->GetUnitDisplayName( ii + 1, false );
113
114 if( missingUnits.count( ii + 1 ) == 0 )
115 unit_text += _( " (already placed)" );
116
117 wxMenuItem* item = Append( ID_POPUP_SCH_SELECT_UNIT1 + ii, unit_text, wxEmptyString,
118 wxITEM_CHECK );
119
120 if( unit == ii + 1 )
121 item->Check( true );
122
123 // The ID max for these submenus is ID_POPUP_SCH_SELECT_UNIT_END
124 // See eeschema_id to modify this value.
126 break; // We have used all IDs for these submenus
127 }
128
129 if( !missingUnits.empty() )
130 {
131 AppendSeparator();
132
133 for( int unitNumber : missingUnits )
134 {
135 wxString placeText = wxString::Format( _( "Place unit %s" ),
136 symbol->GetUnitDisplayName( unitNumber, false ) );
137 Append( ID_POPUP_SCH_PLACE_UNIT1 + unitNumber - 1, placeText );
138 }
139 }
140 }
141};
142
143
145{
146public:
148 ACTION_MENU( true )
149 {
151 SetTitle( _( "Body Style" ) );
152 }
153
154protected:
155 ACTION_MENU* create() const override
156 {
157 return new BODY_STYLE_MENU();
158 }
159
160private:
161 void update() override
162 {
164 SCH_SELECTION& selection = selTool->GetSelection();
165 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
166 wxMenuItem* item;
167
168 Clear();
169
170 wxCHECK( symbol, /* void */ );
171
172 if( symbol->HasDeMorganBodyStyles() )
173 {
174 item = Append( ID_POPUP_SCH_SELECT_BODY_STYLE, _( "Standard" ), wxEmptyString, wxITEM_CHECK );
175 item->Check( symbol->GetBodyStyle() == BODY_STYLE::BASE );
176
177 item = Append( ID_POPUP_SCH_SELECT_BODY_STYLE1, _( "Alternate" ), wxEmptyString, wxITEM_CHECK );
178 item->Check( symbol->GetBodyStyle() != BODY_STYLE::BASE );
179 }
180 else if( symbol->IsMultiBodyStyle() )
181 {
182 for( int i = 0; i < symbol->GetBodyStyleCount(); i++ )
183 {
184 item = Append( ID_POPUP_SCH_SELECT_BODY_STYLE + i, symbol->GetBodyStyleDescription( i + 1, true ),
185 wxEmptyString, wxITEM_CHECK );
186 item->Check( symbol->GetBodyStyle() == i + 1 );
187 }
188 }
189 }
190};
191
192
194{
195public:
197 ACTION_MENU( true )
198 {
200 SetTitle( _( "Pin Function" ) );
201 }
202
203protected:
204 ACTION_MENU* create() const override
205 {
206 return new ALT_PIN_FUNCTION_MENU();
207 }
208
209private:
210 void update() override
211 {
213 SCH_SELECTION& selection = selTool->GetSelection();
214 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( selection.Front() );
215 SCH_PIN* libPin = pin ? pin->GetLibPin() : nullptr;
216
217 Clear();
218
219 wxCHECK( libPin, /* void */ );
220
221 wxMenuItem* item = Append( ID_POPUP_SCH_ALT_PIN_FUNCTION, libPin->GetName(), wxEmptyString,
222 wxITEM_CHECK );
223
224 if( pin->GetAlt().IsEmpty() || ( pin->GetAlt() == libPin->GetName() ) )
225 item->Check( true );
226
227 int ii = 1;
228
229 for( const auto& [ name, definition ] : libPin->GetAlternates() )
230 {
231 // The default pin name is set above, avoid setting it again.
232 if( name == libPin->GetName() )
233 continue;
234
235 item = Append( ID_POPUP_SCH_ALT_PIN_FUNCTION + ii, name, wxEmptyString, wxITEM_CHECK );
236
237 if( name == pin->GetAlt() )
238 item->Check( true );
239
240 // The ID max for these submenus is ID_POPUP_SCH_ALT_PIN_FUNCTION_END
241 // See eeschema_id to modify this value.
243 break; // We have used all IDs for these submenus
244 }
245 }
246};
247
248
250{
251public:
253 {
255 SetTitle( _( "Pin Helpers" ) );
256 }
257
258protected:
259 ACTION_MENU* create() const override { return new PIN_TRICKS_MENU(); }
260
261private:
262 void update() override
263 {
265 SCH_SELECTION& selection = selTool->GetSelection();
266 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( selection.Front() );
267 SCH_SHEET_PIN* sheetPin = dynamic_cast<SCH_SHEET_PIN*>( selection.Front() );
268
269 Clear();
270
271 if( !pin && !sheetPin )
272 return;
273
279 }
280};
281
282
284 SCH_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.InteractiveEdit" )
285{
286 m_pickerItem = nullptr;
287}
288
289
291
293{
295
296 SCH_DRAWING_TOOLS* drawingTools = m_toolMgr->GetTool<SCH_DRAWING_TOOLS>();
297 SCH_MOVE_TOOL* moveTool = m_toolMgr->GetTool<SCH_MOVE_TOOL>();
298
299 wxASSERT_MSG( drawingTools, "eeshema.InteractiveDrawing tool is not available" );
300
301 auto hasElements =
302 [this]( const SELECTION& aSel )
303 {
304 return !m_frame->GetScreen()->Items().empty();
305 };
306
307 auto sheetHasUndefinedPins =
308 []( const SELECTION& aSel )
309 {
310 if( aSel.Size() == 1 && aSel.Front()->Type() == SCH_SHEET_T )
311 return static_cast<SCH_SHEET*>( aSel.Front() )->HasUndefinedPins();
312
313 return false;
314 };
315
316 auto attribDNPCond =
317 [] ( const SELECTION& aSel )
318 {
319 return std::all_of( aSel.Items().begin(), aSel.Items().end(),
320 []( const EDA_ITEM* item )
321 {
322 return !item->IsType( { SCH_SYMBOL_T } )
323 || static_cast<const SCH_SYMBOL*>( item )->GetDNP();
324 } );
325 };
326
327 auto attribExcludeFromSimCond =
328 [] ( const SELECTION& aSel )
329 {
330 return std::all_of( aSel.Items().begin(), aSel.Items().end(),
331 []( const EDA_ITEM* item )
332 {
333 return !item->IsType( { SCH_SYMBOL_T } )
334 || static_cast<const SCH_SYMBOL*>( item )->GetExcludedFromSim();
335 } );
336 };
337
338 auto attribExcludeFromBOMCond =
339 [] ( const SELECTION& aSel )
340 {
341 return std::all_of( aSel.Items().begin(), aSel.Items().end(),
342 []( const EDA_ITEM* item )
343 {
344 return !item->IsType( { SCH_SYMBOL_T } )
345 || static_cast<const SCH_SYMBOL*>( item )->GetExcludedFromBOM();
346 } );
347 };
348
349
350 auto attribExcludeFromBoardCond =
351 [] ( const SELECTION& aSel )
352 {
353 return std::all_of( aSel.Items().begin(), aSel.Items().end(),
354 []( const EDA_ITEM* item )
355 {
356 return !item->IsType( { SCH_SYMBOL_T } )
357 || static_cast<const SCH_SYMBOL*>( item )->GetExcludedFromBoard();
358 } );
359 };
360
361 static const std::vector<KICAD_T> sheetTypes = { SCH_SHEET_T };
362
363 auto sheetSelection = S_C::Count( 1 ) && S_C::OnlyTypes( sheetTypes );
364
365 auto haveHighlight =
366 [this]( const SELECTION& sel )
367 {
368 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
369
370 return editFrame && !editFrame->GetHighlightedConnection().IsEmpty();
371 };
372
373 auto anyTextTool =
374 [this]( const SELECTION& aSel )
375 {
376 return ( m_frame->IsCurrentTool( SCH_ACTIONS::placeLabel )
377 || m_frame->IsCurrentTool( SCH_ACTIONS::placeClassLabel )
378 || m_frame->IsCurrentTool( SCH_ACTIONS::placeGlobalLabel )
379 || m_frame->IsCurrentTool( SCH_ACTIONS::placeHierLabel )
380 || m_frame->IsCurrentTool( SCH_ACTIONS::placeSchematicText ) );
381 };
382
383 auto duplicateCondition =
384 []( const SELECTION& aSel )
385 {
387 return false;
388
389 return true;
390 };
391
392 auto orientCondition =
393 []( const SELECTION& aSel )
394 {
396 return false;
397
399 };
400
401 const auto swapSelectionCondition = S_C::OnlyTypes( SwappableItems ) && SELECTION_CONDITIONS::MoreThan( 1 );
402
403 auto propertiesCondition =
404 [this]( const SELECTION& aSel )
405 {
406 if( aSel.GetSize() == 0 )
407 {
408 if( getView()->IsLayerVisible( LAYER_SCHEMATIC_DRAWINGSHEET ) )
409 {
410 DS_PROXY_VIEW_ITEM* ds = m_frame->GetCanvas()->GetView()->GetDrawingSheet();
411 VECTOR2D cursor = getViewControls()->GetCursorPosition( false );
412
413 if( ds && ds->HitTestDrawingSheetItems( getView(), cursor ) )
414 return true;
415 }
416
417 return false;
418 }
419
420 SCH_ITEM* firstItem = dynamic_cast<SCH_ITEM*>( aSel.Front() );
421 const SCH_SELECTION* eeSelection = dynamic_cast<const SCH_SELECTION*>( &aSel );
422
423 if( !firstItem || !eeSelection )
424 return false;
425
426 switch( firstItem->Type() )
427 {
428 case SCH_SYMBOL_T:
429 case SCH_SHEET_T:
430 case SCH_SHEET_PIN_T:
431 case SCH_TEXT_T:
432 case SCH_TEXTBOX_T:
433 case SCH_TABLE_T:
434 case SCH_TABLECELL_T:
435 case SCH_LABEL_T:
437 case SCH_HIER_LABEL_T:
439 case SCH_RULE_AREA_T:
440 case SCH_FIELD_T:
441 case SCH_SHAPE_T:
442 case SCH_BITMAP_T:
443 case SCH_GROUP_T:
444 return aSel.GetSize() == 1;
445
446 case SCH_LINE_T:
448 case SCH_JUNCTION_T:
449 if( std::all_of( aSel.Items().begin(), aSel.Items().end(),
450 [&]( const EDA_ITEM* item )
451 {
452 return item->Type() == SCH_LINE_T
453 && static_cast<const SCH_LINE*>( item )->IsGraphicLine();
454 } ) )
455 {
456 return true;
457 }
458 else if( std::all_of( aSel.Items().begin(), aSel.Items().end(),
459 [&]( const EDA_ITEM* item )
460 {
461 return item->Type() == SCH_JUNCTION_T;
462 } ) )
463 {
464 return true;
465 }
466 else if( std::all_of( aSel.Items().begin(), aSel.Items().end(),
467 [&]( const EDA_ITEM* item )
468 {
469 const SCH_ITEM* schItem = dynamic_cast<const SCH_ITEM*>( item );
470
471 wxCHECK( schItem, false );
472
473 return ( schItem->HasLineStroke() && schItem->IsConnectable() )
474 || item->Type() == SCH_JUNCTION_T;
475 } ) )
476 {
477 return true;
478 }
479
480 return false;
481
482 default:
483 return false;
484 }
485 };
486
487 auto autoplaceCondition =
488 []( const SELECTION& aSel )
489 {
490 for( const EDA_ITEM* item : aSel )
491 {
493 return true;
494 }
495
496 return false;
497 };
498
499 // allTextTypes does not include SCH_SHEET_PIN_T because one cannot convert other
500 // types to/from this type, living only in a SHEET
501 static const std::vector<KICAD_T> allTextTypes = { SCH_LABEL_T,
507
508 auto toChangeCondition = ( S_C::OnlyTypes( allTextTypes ) );
509
510 static const std::vector<KICAD_T> toLabelTypes = { SCH_DIRECTIVE_LABEL_T,
515
516 auto toLabelCondition = ( S_C::Count( 1 ) && S_C::OnlyTypes( toLabelTypes ) )
517 || ( S_C::MoreThan( 1 ) && S_C::OnlyTypes( allTextTypes ) );
518
519 static const std::vector<KICAD_T> toCLabelTypes = { SCH_LABEL_T,
524
525 auto toCLabelCondition = ( S_C::Count( 1 ) && S_C::OnlyTypes( toCLabelTypes ) )
526 || ( S_C::MoreThan( 1 ) && S_C::OnlyTypes( allTextTypes ) );
527
528 static const std::vector<KICAD_T> toHLabelTypes = { SCH_LABEL_T,
533
534 auto toHLabelCondition = ( S_C::Count( 1 ) && S_C::OnlyTypes( toHLabelTypes ) )
535 || ( S_C::MoreThan( 1 ) && S_C::OnlyTypes( allTextTypes ) );
536
537 static const std::vector<KICAD_T> toGLabelTypes = { SCH_LABEL_T,
542
543 auto toGLabelCondition = ( S_C::Count( 1 ) && S_C::OnlyTypes( toGLabelTypes ) )
544 || ( S_C::MoreThan( 1 ) && S_C::OnlyTypes( allTextTypes ) );
545
546 static const std::vector<KICAD_T> toTextTypes = { SCH_LABEL_T,
551
552 auto toTextCondition = ( S_C::Count( 1 ) && S_C::OnlyTypes( toTextTypes ) )
553 || ( S_C::MoreThan( 1 ) && S_C::OnlyTypes( allTextTypes ) );
554
555 static const std::vector<KICAD_T> toTextBoxTypes = { SCH_LABEL_T,
559 SCH_TEXT_T };
560
561 auto toTextBoxCondition = ( S_C::Count( 1 ) && S_C::OnlyTypes( toTextBoxTypes ) )
562 || ( S_C::MoreThan( 1 ) && S_C::OnlyTypes( allTextTypes ) );
563
564 static const std::vector<KICAD_T> busEntryTypes = { SCH_BUS_WIRE_ENTRY_T, SCH_BUS_BUS_ENTRY_T};
565
566 auto entryCondition = S_C::MoreThan( 0 ) && S_C::OnlyTypes( busEntryTypes );
567
568 auto singleSheetCondition = S_C::Count( 1 ) && S_C::OnlyTypes( sheetTypes );
569
570 auto makeSymbolUnitMenu =
571 [&]( TOOL_INTERACTIVE* tool )
572 {
573 std::shared_ptr<SYMBOL_UNIT_MENU> menu = std::make_shared<SYMBOL_UNIT_MENU>();
574 menu->SetTool( tool );
575 tool->GetToolMenu().RegisterSubMenu( menu );
576 return menu.get();
577 };
578
579 auto makeBodyStyleMenu =
580 [&]( TOOL_INTERACTIVE* tool )
581 {
582 std::shared_ptr<BODY_STYLE_MENU> menu = std::make_shared<BODY_STYLE_MENU>();
583 menu->SetTool( tool );
584 tool->GetToolMenu().RegisterSubMenu( menu );
585 return menu.get();
586 };
587
588 auto makePinFunctionMenu =
589 [&]( TOOL_INTERACTIVE* tool )
590 {
591 std::shared_ptr<ALT_PIN_FUNCTION_MENU> menu = std::make_shared<ALT_PIN_FUNCTION_MENU>();
592 menu->SetTool( tool );
593 tool->GetToolMenu().RegisterSubMenu( menu );
594 return menu.get();
595 };
596
597 auto makePinTricksMenu =
598 [&]( TOOL_INTERACTIVE* tool )
599 {
600 std::shared_ptr<PIN_TRICKS_MENU> menu = std::make_shared<PIN_TRICKS_MENU>();
601 menu->SetTool( tool );
602 tool->GetToolMenu().RegisterSubMenu( menu );
603 return menu.get();
604 };
605
606 auto makeTransformMenu =
607 [&]()
608 {
609 CONDITIONAL_MENU* menu = new CONDITIONAL_MENU( moveTool );
610 menu->SetUntranslatedTitle( _HKI( "Transform Selection" ) );
611
612 menu->AddItem( SCH_ACTIONS::rotateCCW, orientCondition );
613 menu->AddItem( SCH_ACTIONS::rotateCW, orientCondition );
614 menu->AddItem( SCH_ACTIONS::mirrorV, orientCondition );
615 menu->AddItem( SCH_ACTIONS::mirrorH, orientCondition );
616
617 return menu;
618 };
619
620 auto makeAttributesMenu =
621 [&]()
622 {
623 CONDITIONAL_MENU* menu = new CONDITIONAL_MENU( moveTool );
624 menu->SetUntranslatedTitle( _HKI( "Attributes" ) );
625
630
631 return menu;
632 };
633
634 auto makeEditFieldsMenu =
635 [&]()
636 {
637 CONDITIONAL_MENU* menu = new CONDITIONAL_MENU( m_selectionTool );
638 menu->SetUntranslatedTitle( _HKI( "Edit Main Fields" ) );
639
643
644 return menu;
645 };
646
647 auto makeConvertToMenu =
648 [&]()
649 {
650 CONDITIONAL_MENU* menu = new CONDITIONAL_MENU( m_selectionTool );
651 menu->SetUntranslatedTitle( _HKI( "Change To" ) );
652 menu->SetIcon( BITMAPS::right );
653
654 menu->AddItem( SCH_ACTIONS::toLabel, toLabelCondition );
655 menu->AddItem(SCH_ACTIONS::toDLabel, toCLabelCondition );
656 menu->AddItem( SCH_ACTIONS::toHLabel, toHLabelCondition );
657 menu->AddItem( SCH_ACTIONS::toGLabel, toGLabelCondition );
658 menu->AddItem( SCH_ACTIONS::toText, toTextCondition );
659 menu->AddItem( SCH_ACTIONS::toTextBox, toTextBoxCondition );
660
661 return menu;
662 };
663
664 const auto canCopyText = SCH_CONDITIONS::OnlyTypes( {
673 SCH_PIN_T,
676 } );
677
678 //
679 // Add edit actions to the move tool menu
680 //
681 CONDITIONAL_MENU& moveMenu = moveTool->GetToolMenu().GetMenu();
682
683 moveMenu.AddSeparator();
684 moveMenu.AddMenu( makeSymbolUnitMenu( moveTool ), S_C::SingleMultiUnitSymbol, 1 );
685 moveMenu.AddMenu( makeBodyStyleMenu( moveTool ), S_C::SingleMultiBodyStyleSymbol, 1 );
686
687 moveMenu.AddMenu( makeTransformMenu(), orientCondition, 200 );
688 moveMenu.AddMenu( makeAttributesMenu(), S_C::HasType( SCH_SYMBOL_T ), 200 );
689 moveMenu.AddItem( SCH_ACTIONS::swap, swapSelectionCondition, 200 );
690 moveMenu.AddItem( SCH_ACTIONS::properties, propertiesCondition, 200 );
691 moveMenu.AddMenu( makeEditFieldsMenu(), S_C::SingleSymbol, 200 );
692
693 moveMenu.AddSeparator();
694 moveMenu.AddItem( ACTIONS::cut, S_C::IdleSelection );
695 moveMenu.AddItem( ACTIONS::copy, S_C::IdleSelection );
696 moveMenu.AddItem( ACTIONS::copyAsText, canCopyText && S_C::IdleSelection );
697 moveMenu.AddItem( ACTIONS::doDelete, S_C::NotEmpty );
698 moveMenu.AddItem( ACTIONS::duplicate, duplicateCondition );
699
700 //
701 // Add editing actions to the drawing tool menu
702 //
703 CONDITIONAL_MENU& drawMenu = drawingTools->GetToolMenu().GetMenu();
704
705 drawMenu.AddItem( SCH_ACTIONS::clearHighlight, haveHighlight && SCH_CONDITIONS::Idle, 1 );
706 drawMenu.AddSeparator( haveHighlight && SCH_CONDITIONS::Idle, 1 );
707
708 drawMenu.AddItem( SCH_ACTIONS::enterSheet, sheetSelection && SCH_CONDITIONS::Idle, 1 );
709 drawMenu.AddSeparator( sheetSelection && SCH_CONDITIONS::Idle, 1 );
710
711 drawMenu.AddMenu( makeSymbolUnitMenu( drawingTools ), S_C::SingleMultiUnitSymbol, 1 );
712 drawMenu.AddMenu( makeBodyStyleMenu( drawingTools ), S_C::SingleMultiBodyStyleSymbol, 1 );
713
714 drawMenu.AddMenu( makeTransformMenu(), orientCondition, 200 );
715 drawMenu.AddMenu( makeAttributesMenu(), S_C::HasType( SCH_SYMBOL_T ), 200 );
716 drawMenu.AddItem( SCH_ACTIONS::properties, propertiesCondition, 200 );
717 drawMenu.AddMenu( makeEditFieldsMenu(), S_C::SingleSymbol, 200 );
718 drawMenu.AddItem( SCH_ACTIONS::autoplaceFields, autoplaceCondition, 200 );
719
721
722 drawMenu.AddItem( SCH_ACTIONS::toLabel, anyTextTool && S_C::Idle, 200 );
723 drawMenu.AddItem( SCH_ACTIONS::toHLabel, anyTextTool && S_C::Idle, 200 );
724 drawMenu.AddItem( SCH_ACTIONS::toGLabel, anyTextTool && S_C::Idle, 200 );
725 drawMenu.AddItem( SCH_ACTIONS::toText, anyTextTool && S_C::Idle, 200 );
726 drawMenu.AddItem( SCH_ACTIONS::toTextBox, anyTextTool && S_C::Idle, 200 );
727
728 //
729 // Add editing actions to the selection tool menu
730 //
731 CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
732
733 selToolMenu.AddMenu( makeSymbolUnitMenu( m_selectionTool ), S_C::SingleMultiUnitSymbol, 1 );
734 selToolMenu.AddMenu( makeBodyStyleMenu( m_selectionTool ), S_C::SingleMultiBodyStyleSymbol, 1 );
735 selToolMenu.AddMenu( makePinFunctionMenu( m_selectionTool ), S_C::SingleMultiFunctionPin, 1 );
736 selToolMenu.AddMenu( makePinTricksMenu( m_selectionTool ), S_C::AllPinsOrSheetPins, 1 );
737
738 selToolMenu.AddMenu( makeTransformMenu(), orientCondition, 200 );
739 selToolMenu.AddMenu( makeAttributesMenu(), S_C::HasType( SCH_SYMBOL_T ), 200 );
740 selToolMenu.AddItem( SCH_ACTIONS::swap, swapSelectionCondition, 200 );
741 selToolMenu.AddItem( SCH_ACTIONS::properties, propertiesCondition, 200 );
742 selToolMenu.AddMenu( makeEditFieldsMenu(), S_C::SingleSymbol, 200 );
743 selToolMenu.AddItem( SCH_ACTIONS::autoplaceFields, autoplaceCondition, 200 );
744
750 selToolMenu.AddMenu( makeConvertToMenu(), toChangeCondition, 200 );
751
752 selToolMenu.AddItem( SCH_ACTIONS::cleanupSheetPins, sheetHasUndefinedPins, 250 );
753
754 selToolMenu.AddSeparator( 300 );
755 selToolMenu.AddItem( ACTIONS::cut, S_C::IdleSelection, 300 );
756 selToolMenu.AddItem( ACTIONS::copy, S_C::IdleSelection, 300 );
757 selToolMenu.AddItem( ACTIONS::copyAsText, canCopyText && S_C::IdleSelection, 300 );
758 selToolMenu.AddItem( ACTIONS::paste, S_C::Idle, 300 );
759 selToolMenu.AddItem( ACTIONS::pasteSpecial, S_C::Idle, 300 );
760 selToolMenu.AddItem( ACTIONS::doDelete, S_C::NotEmpty, 300 );
761 selToolMenu.AddItem( ACTIONS::duplicate, duplicateCondition, 300 );
762
763 selToolMenu.AddSeparator( 400 );
764 selToolMenu.AddItem( ACTIONS::selectAll, hasElements, 400 );
765 selToolMenu.AddItem( ACTIONS::unselectAll, hasElements, 400 );
766
767 ACTION_MANAGER* mgr = m_toolMgr->GetActionManager();
768 // clang-format off
769 mgr->SetConditions( SCH_ACTIONS::setDNP, ACTION_CONDITIONS().Check( attribDNPCond ) );
770 mgr->SetConditions( SCH_ACTIONS::setExcludeFromSimulation, ACTION_CONDITIONS().Check( attribExcludeFromSimCond ) );
771 mgr->SetConditions( SCH_ACTIONS::setExcludeFromBOM, ACTION_CONDITIONS().Check( attribExcludeFromBOMCond ) );
772 mgr->SetConditions( SCH_ACTIONS::setExcludeFromBoard, ACTION_CONDITIONS().Check( attribExcludeFromBoardCond ) );
773 // clang-format on
774
775 return true;
776}
777
778
779const std::vector<KICAD_T> SCH_EDIT_TOOL::RotatableItems = {
785 SCH_TABLECELL_T, // will be promoted to parent table(s)
801};
802
803
804const std::vector<KICAD_T> SCH_EDIT_TOOL::SwappableItems = {
820};
821
822
824{
825 bool clockwise = ( aEvent.Matches( SCH_ACTIONS::rotateCW.MakeEvent() ) );
826 SCH_SELECTION& selection = m_selectionTool->RequestSelection( RotatableItems, true, false );
827
828 wxLogTrace( "KICAD_SCH_MOVE", "SCH_EDIT_TOOL::Rotate: start, clockwise=%d, selection size=%u",
829 clockwise, selection.GetSize() );
830
831 if( selection.GetSize() == 0 )
832 return 0;
833
834 SCH_ITEM* head = nullptr;
835 int principalItemCount = 0; // User-selected items (as opposed to connected wires)
836 VECTOR2I rotPoint;
837 bool moving = false;
838 SCH_COMMIT localCommit( m_toolMgr );
839 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
840
841 if( !commit )
842 commit = &localCommit;
843
844 for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
845 {
846 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
847
848 if( item->HasFlag( SELECTED_BY_DRAG ) )
849 continue;
850
851 principalItemCount++;
852
853 if( !head )
854 head = item;
855 }
856
857 if( head && head->IsMoving() )
858 moving = true;
859
860 if( principalItemCount == 1 )
861 {
862 if( moving && selection.HasReferencePoint() )
863 rotPoint = selection.GetReferencePoint();
864 else if( head->IsConnectable() )
865 rotPoint = head->GetPosition();
866 else
867 rotPoint = m_frame->GetNearestHalfGridPosition( head->GetBoundingBox().GetCenter() );
868
869 if( !moving )
870 commit->Modify( head, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
871
872 switch( head->Type() )
873 {
874 case SCH_SYMBOL_T:
875 {
876 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( head );
877
878 symbol->Rotate( rotPoint, !clockwise );
879
880 if( m_frame->eeconfig()->m_AutoplaceFields.enable )
881 {
882 AUTOPLACE_ALGO fieldsAutoplaced = symbol->GetFieldsAutoplaced();
883
884 if( fieldsAutoplaced == AUTOPLACE_AUTO || fieldsAutoplaced == AUTOPLACE_MANUAL )
885 symbol->AutoplaceFields( m_frame->GetScreen(), fieldsAutoplaced );
886 }
887
888 break;
889 }
890
891 case SCH_TEXT_T:
892 case SCH_LABEL_T:
894 case SCH_HIER_LABEL_T:
896 {
897 SCH_TEXT* textItem = static_cast<SCH_TEXT*>( head );
898 textItem->Rotate90( clockwise );
899 break;
900 }
901
902 case SCH_SHEET_PIN_T:
903 {
904 // Rotate pin within parent sheet
905 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( head );
906 SCH_SHEET* sheet = pin->GetParent();
907
908 pin->Rotate( sheet->GetBoundingBox().GetCenter(), !clockwise );
909
910 break;
911 }
912
913 case SCH_LINE_T:
914 {
915 SCH_LINE* line = static_cast<SCH_LINE*>( head );
916
917 // Equal checks for both and neither. We need this because on undo
918 // the item will have both flags cleared, but will be selected, so it is possible
919 // for the user to get a selected line with neither endpoint selected. We
920 // set flags to make sure Rotate() works when we call it.
921 if( line->HasFlag( STARTPOINT ) == line->HasFlag( ENDPOINT ) )
922 {
923 line->SetFlags( STARTPOINT | ENDPOINT );
924
925 // When we allow off grid items, the rotPoint should be set to the midpoint
926 // of the line to allow rotation around the center, and the next if
927 // should become an else-if
928 }
929
930 if( line->HasFlag( STARTPOINT ) )
931 rotPoint = line->GetEndPoint();
932 else if( line->HasFlag( ENDPOINT ) )
933 rotPoint = line->GetStartPoint();
934 }
935
937 case SCH_JUNCTION_T:
938 case SCH_NO_CONNECT_T:
941 head->Rotate( rotPoint, !clockwise );
942
943 break;
944
945 case SCH_FIELD_T:
946 {
947 SCH_FIELD* field = static_cast<SCH_FIELD*>( head );
948
949 if( field->GetTextAngle().IsHorizontal() )
951 else
953
954 // Now that we're moving a field, they're no longer autoplaced.
955 static_cast<SCH_ITEM*>( head->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
956
957 break;
958 }
959
960 case SCH_RULE_AREA_T:
961 case SCH_SHAPE_T:
962 case SCH_TEXTBOX_T:
963 head->Rotate( rotPoint, !clockwise );
964
965 break;
966
967 case SCH_GROUP_T:
968 {
969 // Rotate the group on itself. Groups do not have an anchor point.
970 SCH_GROUP* group = static_cast<SCH_GROUP*>( head );
971 rotPoint = m_frame->GetNearestHalfGridPosition( group->GetPosition() );
972
973 group->Rotate( rotPoint, !clockwise );
974
975 group->Move( rotPoint - m_frame->GetNearestHalfGridPosition( group->GetPosition() ) );
976
977 break;
978 }
979
980 case SCH_TABLE_T:
981 {
982 // Rotate the table on itself. Tables do not have an anchor point.
983 SCH_TABLE* table = static_cast<SCH_TABLE*>( head );
984 rotPoint = m_frame->GetNearestHalfGridPosition( table->GetCenter() );
985
986 table->Rotate( rotPoint, !clockwise );
987
988 table->Move( rotPoint - m_frame->GetNearestHalfGridPosition( table->GetCenter() ) );
989
990 break;
991 }
992
993 case SCH_BITMAP_T:
994 head->Rotate( rotPoint, !clockwise );
995
996 // The bitmap is cached in Opengl: clear the cache to redraw
998 break;
999
1000 case SCH_SHEET_T:
1001 {
1002 // Rotate the sheet on itself. Sheets do not have an anchor point.
1003 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( head );
1004
1005 rotPoint = m_frame->GetNearestHalfGridPosition( sheet->GetRotationCenter() );
1006 sheet->Rotate( rotPoint, !clockwise );
1007
1008 break;
1009 }
1010
1011 default:
1012 UNIMPLEMENTED_FOR( head->GetClass() );
1013 }
1014
1015 m_frame->UpdateItem( head, false, true );
1016 }
1017 else
1018 {
1019 if( moving && selection.HasReferencePoint() )
1020 rotPoint = selection.GetReferencePoint();
1021 else
1022 rotPoint = m_frame->GetNearestHalfGridPosition( selection.GetCenter() );
1023 }
1024
1025 for( EDA_ITEM* edaItem : selection )
1026 {
1027 SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
1028
1029 // We've already rotated the user selected item if there was only one. We're just
1030 // here to rotate the ends of wires that were attached to it.
1031 if( principalItemCount == 1 && !item->HasFlag( SELECTED_BY_DRAG ) )
1032 continue;
1033
1034 if( !moving )
1035 commit->Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1036
1037 if( item->Type() == SCH_LINE_T )
1038 {
1039 SCH_LINE* line = (SCH_LINE*) item;
1040
1041 line->Rotate( rotPoint, !clockwise );
1042 }
1043 else if( item->Type() == SCH_SHEET_PIN_T )
1044 {
1045 if( item->GetParent()->IsSelected() )
1046 {
1047 // parent will rotate us
1048 }
1049 else
1050 {
1051 // rotate within parent
1052 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
1053 SCH_SHEET* sheet = pin->GetParent();
1054
1055 pin->Rotate( sheet->GetBodyBoundingBox().GetCenter(), !clockwise );
1056 }
1057 }
1058 else if( item->Type() == SCH_FIELD_T )
1059 {
1060 if( item->GetParent()->IsSelected() )
1061 {
1062 // parent will rotate us
1063 }
1064 else
1065 {
1066 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
1067
1068 field->Rotate( rotPoint, !clockwise );
1069
1070 // Now that we're moving a field, they're no longer autoplaced.
1071 static_cast<SCH_ITEM*>( field->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
1072 }
1073 }
1074 else if( item->Type() == SCH_TABLE_T )
1075 {
1076 SCH_TABLE* table = static_cast<SCH_TABLE*>( item );
1077 VECTOR2I beforeCenter = table->GetCenter();
1078
1079 table->Rotate( rotPoint, !clockwise );
1080 RotatePoint( beforeCenter, rotPoint, clockwise ? -ANGLE_90 : ANGLE_90 );
1081
1082 table->Move( beforeCenter - table->GetCenter() );
1083 }
1084 else
1085 {
1086 VECTOR2I posBefore = item->GetPosition();
1087 item->Rotate( rotPoint, !clockwise );
1088 VECTOR2I posAfter = item->GetPosition();
1089 wxLogTrace( "KICAD_SCH_MOVE", " SCH_EDIT_TOOL::Rotate: item type=%d rotated, pos (%d,%d) -> (%d,%d)",
1090 item->Type(), posBefore.x, posBefore.y, posAfter.x, posAfter.y );
1091 }
1092
1093 m_frame->UpdateItem( item, false, true );
1094 updateItem( item, true );
1095 }
1096
1097 wxLogTrace( "KICAD_SCH_MOVE", "SCH_EDIT_TOOL::Rotate: complete, moving=%d", moving );
1098
1099 if( moving )
1100 {
1101 wxLogTrace( "KICAD_SCH_MOVE", "SCH_EDIT_TOOL::Rotate: posting refreshPreview" );
1102 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1103 }
1104 else
1105 {
1106 SCH_SELECTION selectionCopy = selection;
1107
1108 if( selection.IsHover() )
1109 m_toolMgr->RunAction( ACTIONS::selectionClear );
1110
1112 lwbTool->TrimOverLappingWires( commit, &selectionCopy );
1113 lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy );
1114
1115 m_frame->Schematic().CleanUp( commit );
1116
1117 if( !localCommit.Empty() )
1118 localCommit.Push( _( "Rotate" ) );
1119 }
1120
1121 return 0;
1122}
1123
1124
1126{
1127 SCH_SELECTION& selection = m_selectionTool->RequestSelection( RotatableItems, false, false );
1128
1129 if( selection.GetSize() == 0 )
1130 return 0;
1131
1132 bool vertical = ( aEvent.Matches( SCH_ACTIONS::mirrorV.MakeEvent() ) );
1133 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
1134 bool connections = false;
1135 bool moving = item->IsMoving();
1136 SCH_COMMIT localCommit( m_toolMgr );
1137 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
1138
1139 if( !commit )
1140 commit = &localCommit;
1141
1142 if( selection.GetSize() == 1 )
1143 {
1144 if( !moving )
1145 commit->Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1146
1147 switch( item->Type() )
1148 {
1149 case SCH_SYMBOL_T:
1150 {
1151 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1152
1153 if( vertical )
1154 symbol->SetOrientation( SYM_MIRROR_X );
1155 else
1156 symbol->SetOrientation( SYM_MIRROR_Y );
1157
1159 break;
1160 }
1161
1162 case SCH_TEXT_T:
1163 case SCH_LABEL_T:
1164 case SCH_GLOBAL_LABEL_T:
1165 case SCH_HIER_LABEL_T:
1167 {
1168 SCH_TEXT* textItem = static_cast<SCH_TEXT*>( item );
1169 textItem->MirrorSpinStyle( !vertical );
1170 break;
1171 }
1172
1173 case SCH_SHEET_PIN_T:
1174 {
1175 // mirror within parent sheet
1176 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
1177 SCH_SHEET* sheet = pin->GetParent();
1178
1179 if( vertical )
1180 pin->MirrorVertically( sheet->GetBoundingBox().GetCenter().y );
1181 else
1182 pin->MirrorHorizontally( sheet->GetBoundingBox().GetCenter().x );
1183
1184 break;
1185 }
1186
1187 case SCH_FIELD_T:
1188 {
1189 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
1190
1191 if( vertical )
1193 else
1195
1196 // Now that we're re-justifying a field, they're no longer autoplaced.
1197 static_cast<SCH_ITEM*>( field->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
1198
1199 break;
1200 }
1201
1202 case SCH_BITMAP_T:
1203 if( vertical )
1204 item->MirrorVertically( item->GetPosition().y );
1205 else
1206 item->MirrorHorizontally( item->GetPosition().x );
1207
1208 // The bitmap is cached in Opengl: clear the cache to redraw
1210 break;
1211
1212 case SCH_SHEET_T:
1213 {
1214 // Mirror the sheet on itself. Sheets do not have a anchor point.
1215 VECTOR2I mirrorPoint = m_frame->GetNearestHalfGridPosition( item->GetBoundingBox().Centre() );
1216
1217 if( vertical )
1218 item->MirrorVertically( mirrorPoint.y );
1219 else
1220 item->MirrorHorizontally( mirrorPoint.x );
1221
1222 break;
1223 }
1224
1225 default:
1226 if( vertical )
1227 item->MirrorVertically( item->GetPosition().y );
1228 else
1229 item->MirrorHorizontally( item->GetPosition().x );
1230
1231 break;
1232 }
1233
1234 connections = item->IsConnectable();
1235 m_frame->UpdateItem( item, false, true );
1236 }
1237 else if( selection.GetSize() > 1 )
1238 {
1239 VECTOR2I mirrorPoint = m_frame->GetNearestHalfGridPosition( selection.GetCenter() );
1240
1241 for( EDA_ITEM* edaItem : selection )
1242 {
1243 item = static_cast<SCH_ITEM*>( edaItem );
1244
1245 if( !moving )
1246 commit->Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1247
1248 if( item->Type() == SCH_SHEET_PIN_T )
1249 {
1250 if( item->GetParent()->IsSelected() )
1251 {
1252 // parent will mirror us
1253 }
1254 else
1255 {
1256 // mirror within parent sheet
1257 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
1258 SCH_SHEET* sheet = pin->GetParent();
1259
1260 if( vertical )
1261 pin->MirrorVertically( sheet->GetBoundingBox().GetCenter().y );
1262 else
1263 pin->MirrorHorizontally( sheet->GetBoundingBox().GetCenter().x );
1264 }
1265 }
1266 else if( item->Type() == SCH_FIELD_T )
1267 {
1268 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
1269
1270 if( vertical )
1272 else
1274
1275 // Now that we're re-justifying a field, they're no longer autoplaced.
1276 static_cast<SCH_ITEM*>( field->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
1277 }
1278 else
1279 {
1280 if( vertical )
1281 item->MirrorVertically( mirrorPoint.y );
1282 else
1283 item->MirrorHorizontally( mirrorPoint.x );
1284 }
1285
1286 connections |= item->IsConnectable();
1287 m_frame->UpdateItem( item, false, true );
1288 }
1289 }
1290
1291 // Update R-Tree for modified items
1292 for( EDA_ITEM* selected : selection )
1293 updateItem( selected, true );
1294
1295 if( item->IsMoving() )
1296 {
1297 m_toolMgr->RunAction( ACTIONS::refreshPreview );
1298 }
1299 else
1300 {
1301 SCH_SELECTION selectionCopy = selection;
1302
1303 if( selection.IsHover() )
1304 m_toolMgr->RunAction( ACTIONS::selectionClear );
1305
1306 if( connections )
1307 {
1309 lwbTool->TrimOverLappingWires( commit, &selectionCopy );
1310 lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy );
1311
1312 m_frame->Schematic().CleanUp( commit );
1313 }
1314
1315 if( !localCommit.Empty() )
1316 localCommit.Push( _( "Mirror" ) );
1317 }
1318
1319 return 0;
1320}
1321
1331static void swapFieldPositionsWithMatching( std::vector<SCH_FIELD>& aAFields,
1332 std::vector<SCH_FIELD>& aBFields,
1333 unsigned aFallbackRotationsCCW )
1334{
1335 std::set<wxString> handledKeys;
1336
1337 const auto swapFieldTextProps = []( SCH_FIELD& aField, SCH_FIELD& bField )
1338 {
1339 const VECTOR2I aRelPos = aField.GetPosition() - aField.GetParentPosition();
1340 const GR_TEXT_H_ALIGN_T aTextJustifyH = aField.GetHorizJustify();
1341 const GR_TEXT_V_ALIGN_T aTextJustifyV = aField.GetVertJustify();
1342 const EDA_ANGLE aTextAngle = aField.GetTextAngle();
1343
1344 const VECTOR2I bRelPos = bField.GetPosition() - bField.GetParentPosition();
1345 const GR_TEXT_H_ALIGN_T bTextJustifyH = bField.GetHorizJustify();
1346 const GR_TEXT_V_ALIGN_T bTextJustifyV = bField.GetVertJustify();
1347 const EDA_ANGLE bTextAngle = bField.GetTextAngle();
1348
1349 aField.SetPosition( aField.GetParentPosition() + bRelPos );
1350 aField.SetHorizJustify( bTextJustifyH );
1351 aField.SetVertJustify( bTextJustifyV );
1352 aField.SetTextAngle( bTextAngle );
1353
1354 bField.SetPosition( bField.GetParentPosition() + aRelPos );
1355 bField.SetHorizJustify( aTextJustifyH );
1356 bField.SetVertJustify( aTextJustifyV );
1357 bField.SetTextAngle( aTextAngle );
1358 };
1359
1360 for( SCH_FIELD& aField : aAFields )
1361 {
1362 const wxString name = aField.GetCanonicalName();
1363
1364 auto it = std::find_if( aBFields.begin(), aBFields.end(),
1365 [name]( const SCH_FIELD& bField )
1366 {
1367 return bField.GetCanonicalName() == name;
1368 } );
1369
1370 if( it != aBFields.end() )
1371 {
1372 // We have a field with the same key in both labels
1373 SCH_FIELD& bField = *it;
1374 swapFieldTextProps( aField, bField );
1375 }
1376 else
1377 {
1378 // We only have this field in A, so just rotate it
1379 for( unsigned ii = 0; ii < aFallbackRotationsCCW; ii++ )
1380 {
1381 aField.Rotate( aField.GetParentPosition(), true );
1382 }
1383 }
1384
1385 // And keep track that we did this one
1386 handledKeys.insert( name );
1387 }
1388
1389 // Any fields in B that weren't in A weren't handled and need to be rotated
1390 // in reverse
1391 for( SCH_FIELD& bField : aBFields )
1392 {
1393 const wxString bName = bField.GetCanonicalName();
1394 if( handledKeys.find( bName ) == handledKeys.end() )
1395 {
1396 for( unsigned ii = 0; ii < aFallbackRotationsCCW; ii++ )
1397 {
1398 bField.Rotate( bField.GetParentPosition(), false );
1399 }
1400 }
1401 }
1402}
1403
1404
1406{
1407 SCH_SELECTION& selection = m_selectionTool->RequestSelection( SwappableItems );
1408 std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
1409
1410 if( selection.Size() < 2 )
1411 return 0;
1412
1413 // Sheet pins are special, we need to make sure if we have any sheet pins,
1414 // that we only have sheet pins, and that they have the same parent
1415 if( selection.CountType( SCH_SHEET_PIN_T ) > 0 )
1416 {
1417 if( !selection.OnlyContains( { SCH_SHEET_PIN_T } ) )
1418 return 0;
1419
1420 EDA_ITEM* parent = selection.Front()->GetParent();
1421
1422 for( EDA_ITEM* item : selection )
1423 {
1424 if( item->GetParent() != parent )
1425 return 0;
1426 }
1427 }
1428
1429 bool moving = selection.Front()->IsMoving();
1430 bool connections = false;
1431
1432 SCH_COMMIT localCommit( m_toolMgr );
1433 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
1434
1435 if( !commit )
1436 commit = &localCommit;
1437
1438 for( size_t i = 0; i < sorted.size() - 1; i++ )
1439 {
1440 SCH_ITEM* a = static_cast<SCH_ITEM*>( sorted[i] );
1441 SCH_ITEM* b = static_cast<SCH_ITEM*>( sorted[( i + 1 ) % sorted.size()] );
1442
1443 if( !moving )
1444 {
1445 commit->Modify( a, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1446 commit->Modify( b, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1447 }
1448
1449 VECTOR2I aPos = a->GetPosition(), bPos = b->GetPosition();
1450 std::swap( aPos, bPos );
1451
1452 // Sheet pins need to have their sides swapped before we change their
1453 // positions
1454 if( a->Type() == SCH_SHEET_PIN_T )
1455 {
1456 SCH_SHEET_PIN* aPin = static_cast<SCH_SHEET_PIN*>( a );
1457 SCH_SHEET_PIN* bPin = static_cast<SCH_SHEET_PIN*>( b );
1458 SHEET_SIDE aSide = aPin->GetSide(), bSide = bPin->GetSide();
1459 std::swap( aSide, bSide );
1460 aPin->SetSide( aSide );
1461 bPin->SetSide( bSide );
1462 }
1463
1464 a->SetPosition( aPos );
1465 b->SetPosition( bPos );
1466
1467 if( a->Type() == b->Type() )
1468 {
1469 switch( a->Type() )
1470 {
1471 case SCH_LABEL_T:
1472 case SCH_GLOBAL_LABEL_T:
1473 case SCH_HIER_LABEL_T:
1475 {
1476 SCH_LABEL_BASE& aLabelBase = static_cast<SCH_LABEL_BASE&>( *a );
1477 SCH_LABEL_BASE& bLabelBase = static_cast<SCH_LABEL_BASE&>( *b );
1478
1479 const SPIN_STYLE aSpinStyle = aLabelBase.GetSpinStyle();
1480 const SPIN_STYLE bSpinStyle = bLabelBase.GetSpinStyle();
1481
1482 // First, swap the label orientations
1483 aLabelBase.SetSpinStyle( bSpinStyle );
1484 bLabelBase.SetSpinStyle( aSpinStyle );
1485
1486 // And swap the fields as best we can
1487 std::vector<SCH_FIELD>& aFields = aLabelBase.GetFields();
1488 std::vector<SCH_FIELD>& bFields = bLabelBase.GetFields();
1489
1490 const unsigned rotationsAtoB = aSpinStyle.CCWRotationsTo( bSpinStyle );
1491
1492 swapFieldPositionsWithMatching( aFields, bFields, rotationsAtoB );
1493 break;
1494 }
1495 case SCH_SYMBOL_T:
1496 {
1497 SCH_SYMBOL* aSymbol = static_cast<SCH_SYMBOL*>( a );
1498 SCH_SYMBOL* bSymbol = static_cast<SCH_SYMBOL*>( b );
1499 int aOrient = aSymbol->GetOrientation(), bOrient = bSymbol->GetOrientation();
1500 std::swap( aOrient, bOrient );
1501 aSymbol->SetOrientation( aOrient );
1502 bSymbol->SetOrientation( bOrient );
1503 break;
1504 }
1505 default: break;
1506 }
1507 }
1508
1509 connections |= a->IsConnectable();
1510 connections |= b->IsConnectable();
1511 m_frame->UpdateItem( a, false, true );
1512 m_frame->UpdateItem( b, false, true );
1513 }
1514
1515 if( moving )
1516 {
1517 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1518 }
1519 else
1520 {
1521 if( selection.IsHover() )
1522 m_toolMgr->RunAction( ACTIONS::selectionClear );
1523
1524 if( connections )
1525 m_frame->TestDanglingEnds();
1526 m_frame->OnModify();
1527
1528 if( !localCommit.Empty() )
1529 localCommit.Push( _( "Swap" ) );
1530 }
1531
1532 return 0;
1533}
1534
1535
1536/*
1537 * This command always works on the instance owned by the schematic, never directly on the
1538 * external library file. Pins that still reference their library definition are swapped by
1539 * touching that shared lib pin first; afterwards we call UpdatePins() so the schematic now owns a
1540 * cached copy with the new geometry. Pins that already have an instance-local copy simply swap in
1541 * place. In both cases the undo stack captures the modified pins (and the parent symbol) so the
1542 * user can revert the change. Saving the schematic writes the updated pin order into the sheet,
1543 * while the global symbol library remains untouched unless the user explicitly pushes it later.
1544 */
1546{
1547 wxCHECK( m_frame, 0 );
1548
1549 if( !m_frame->eeconfig()->m_Input.allow_unconstrained_pin_swaps )
1550 return 0;
1551
1552 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_PIN_T } );
1553 std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
1554
1555 if( selection.Size() < 2 )
1556 return 0;
1557
1558 EDA_ITEM* parent = selection.Front()->GetParent();
1559
1560 if( !parent || parent->Type() != SCH_SYMBOL_T )
1561 return 0;
1562
1563 SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( parent );
1564
1565 // All pins need to be on the same symbol
1566 for( EDA_ITEM* item : selection )
1567 {
1568 if( item->GetParent() != parent )
1569 return 0;
1570 }
1571
1572 std::set<wxString> sharedSheetPaths;
1573 std::set<wxString> sharedProjectNames;
1574
1575 if( SymbolHasSheetInstances( *parentSymbol, m_frame->Prj().GetProjectName(), &sharedSheetPaths,
1576 &sharedProjectNames ) )
1577 {
1578 // This will give us nice names for our project, but not when the sheet is shared across
1579 // multiple projects. But, in that case we bail early and just list the project names so it isn't an issue.
1580 std::set<wxString> friendlySheets;
1581
1582 if( !sharedSheetPaths.empty() )
1583 friendlySheets = GetSheetNamesFromPaths( sharedSheetPaths, m_frame->Schematic() );
1584
1585 if( !sharedProjectNames.empty() )
1586 {
1587 wxString projects = AccumulateDescriptions( sharedProjectNames );
1588
1589 if( projects.IsEmpty() )
1590 {
1591 m_frame->ShowInfoBarError(
1592 _( "Pin swaps are disabled for symbols shared across other projects. Duplicate the sheet to edit pins independently." ) );
1593 }
1594 else
1595 {
1596 m_frame->ShowInfoBarError(
1597 wxString::Format( _( "Pin swaps are disabled for symbols shared across other projects (%s). Duplicate the sheet to edit pins independently." ), projects ) );
1598 }
1599 }
1600 else if( !friendlySheets.empty() )
1601 {
1602 wxString sheets = AccumulateDescriptions( friendlySheets );
1603
1604 m_frame->ShowInfoBarError(
1605 wxString::Format( _( "Pin swaps are disabled for symbols used by multiple sheet instances (%s). Duplicate the sheet to edit pins independently." ), sheets ) );
1606 }
1607 else
1608 {
1609 m_frame->ShowInfoBarError(
1610 _( "Pin swaps are disabled for shared symbols. Duplicate the sheet to edit pins independently." ) );
1611 }
1612
1613 return 0;
1614 }
1615
1616 bool connections = false;
1617
1618 SCH_COMMIT localCommit( m_toolMgr );
1619 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
1620
1621 if( !commit )
1622 commit = &localCommit;
1623
1624 // Stage the parent symbol so undo/redo captures the cache copy that UpdatePins() may rebuild
1625 // after we touch any shared library pins.
1626 commit->Modify( parentSymbol, m_frame->GetScreen(), RECURSE_MODE::RECURSE ); // RECURSE is harmless here
1627
1628 bool swappedLibPins = false;
1629
1630 for( size_t i = 0; i < sorted.size() - 1; i++ )
1631 {
1632 SCH_PIN* aPin = static_cast<SCH_PIN*>( sorted[i] );
1633 SCH_PIN* bPin = static_cast<SCH_PIN*>( sorted[( i + 1 ) % sorted.size()] );
1634
1635 // Record both pins in the commit and swap their geometry. SwapPinGeometry returns true if
1636 // it had to operate on the shared library pins (meaning the schematic instance still
1637 // referenced them), in which case UpdatePins() below promotes the symbol to an instance
1638 // copy that reflects the new pin order.
1639 commit->Modify( aPin, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1640 commit->Modify( bPin, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1641
1642 swappedLibPins |= SwapPinGeometry( aPin, bPin );
1643
1644 connections |= aPin->IsConnectable();
1645 connections |= bPin->IsConnectable();
1646 m_frame->UpdateItem( aPin, false, true );
1647 m_frame->UpdateItem( bPin, false, true );
1648 }
1649
1650 if( swappedLibPins )
1651 parentSymbol->UpdatePins(); // clone the library data into the schematic cache with new geometry
1652
1653 // Refresh changed symbol in screen R-Tree / lib caches
1654 m_frame->UpdateItem( parentSymbol, false, true );
1655
1656 SCH_SELECTION selectionCopy = selection;
1657
1658 if( selection.IsHover() )
1659 m_toolMgr->RunAction( ACTIONS::selectionClear );
1660
1661 // Reconcile any wiring that was connected to the swapped pins so the schematic stays tidy and
1662 // the undo stack captures the resulting edits to wires and junctions.
1664 lwbTool->TrimOverLappingWires( commit, &selectionCopy );
1665 lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy );
1666
1667 m_frame->Schematic().CleanUp( commit );
1668
1669 if( connections )
1670 m_frame->TestDanglingEnds();
1671
1672 m_frame->OnModify();
1673
1674 if( !localCommit.Empty() )
1675 localCommit.Push( _( "Swap Pins" ) );
1676
1677 return 0;
1678}
1679
1680
1681// Used by SwapPinLabels() and SwapUnitLabels() to find the single net label connected to a pin
1683 const SCH_SHEET_PATH& aSheetPath )
1684{
1685 if( !aGraph || !aPin )
1686 return nullptr;
1687
1688 CONNECTION_SUBGRAPH* sg = aGraph->GetSubgraphForItem( aPin );
1689
1690 if( !sg )
1691 return nullptr;
1692
1693 const std::set<SCH_ITEM*>& items = sg->GetItems();
1694
1695 size_t pinCount = 0;
1696 SCH_LABEL_BASE* label = nullptr;
1697
1698 for( SCH_ITEM* item : items )
1699 {
1700 if( item->Type() == SCH_PIN_T )
1701 pinCount++;
1702
1703 switch( item->Type() )
1704 {
1705 case SCH_LABEL_T:
1706 case SCH_GLOBAL_LABEL_T:
1707 case SCH_HIER_LABEL_T:
1708 {
1709 SCH_CONNECTION* conn = item->Connection( &aSheetPath );
1710
1711 if( conn && conn->IsNet() )
1712 {
1713 if( label )
1714 return nullptr; // more than one label
1715
1716 label = static_cast<SCH_LABEL_BASE*>( item );
1717 }
1718
1719 break;
1720 }
1721 default: break;
1722 }
1723 }
1724
1725 if( pinCount != 1 )
1726 return nullptr;
1727
1728 return label;
1729}
1730
1731
1733{
1734 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_PIN_T } );
1735 std::vector<EDA_ITEM*> orderedPins = selection.GetItemsSortedBySelectionOrder();
1736
1737 if( orderedPins.size() < 2 )
1738 return 0;
1739
1740 CONNECTION_GRAPH* connectionGraph = m_frame->Schematic().ConnectionGraph();
1741
1742 const SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
1743
1744 std::vector<SCH_LABEL_BASE*> labels;
1745
1746 for( EDA_ITEM* item : orderedPins )
1747 {
1748 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
1749 SCH_LABEL_BASE* label = findSingleNetLabelForPin( pin, connectionGraph, sheetPath );
1750
1751 if( !label )
1752 {
1753 m_frame->ShowInfoBarError(
1754 _( "Each selected pin must have exactly one attached net label and no other pin connections." ) );
1755 return 0;
1756 }
1757
1758 labels.push_back( label );
1759 }
1760
1761 if( labels.size() >= 2 )
1762 {
1763 SCH_COMMIT commit( m_frame );
1764
1765 for( SCH_LABEL_BASE* lb : labels )
1766 commit.Modify( lb, m_frame->GetScreen() );
1767
1768 for( size_t i = 0; i < labels.size() - 1; ++i )
1769 {
1770 SCH_LABEL_BASE* a = labels[i];
1771 SCH_LABEL_BASE* b = labels[( i + 1 ) % labels.size()];
1772 wxString aText = a->GetText();
1773 wxString bText = b->GetText();
1774 a->SetText( bText );
1775 b->SetText( aText );
1776 }
1777
1778 commit.Push( _( "Swap Pin Labels" ) );
1779 }
1780
1781 return 0;
1782}
1783
1784
1786{
1787 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
1788 std::vector<SCH_SYMBOL*> selectedUnits = GetSameSymbolMultiUnitSelection( selection );
1789
1790 if( selectedUnits.size() < 2 )
1791 return 0;
1792
1793 CONNECTION_GRAPH* connectionGraph = m_frame->Schematic().ConnectionGraph();
1794
1795 const SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
1796
1797 // Build ordered label vectors (sorted by pin X/Y) for each selected unit
1798 std::vector<std::vector<SCH_LABEL_BASE*>> symbolLabelVectors;
1799
1800 for( SCH_SYMBOL* symbol : selectedUnits )
1801 {
1802 std::vector<std::pair<VECTOR2I, SCH_LABEL_BASE*>> byPos;
1803
1804 for( SCH_PIN* pin : symbol->GetPins( &sheetPath ) )
1805 {
1806 SCH_LABEL_BASE* label = findSingleNetLabelForPin( pin, connectionGraph, sheetPath );
1807
1808 if( !label )
1809 {
1810 m_frame->ShowInfoBarError( _( "Each pin of selected units must have exactly one attached net label and "
1811 "no other pin connections." ) );
1812 return 0;
1813 }
1814
1815 byPos.emplace_back( pin->GetPosition(), label );
1816 }
1817
1818 // Sort labels by pin position (X, then Y)
1819 std::sort( byPos.begin(), byPos.end(), []( const auto& a, const auto& b )
1820 {
1821 if( a.first.x != b.first.x )
1822 return a.first.x < b.first.x;
1823
1824 return a.first.y < b.first.y;
1825 } );
1826
1827 // Discard position, just keep the order
1828 std::vector<SCH_LABEL_BASE*> labels;
1829
1830 for( const auto& pr : byPos )
1831 labels.push_back( pr.second );
1832
1833 symbolLabelVectors.push_back( labels );
1834 }
1835
1836 // All selected units are guaranteed to have identical pin counts by GetSameSymbolMultiUnitSelection()
1837 const size_t pinCount = symbolLabelVectors.front().size();
1838
1839 // Perform cyclic swap of labels across all selected symbols, per pin index
1840 SCH_COMMIT commit( m_frame );
1841
1842 for( size_t pin = 0; pin < pinCount; pin++ )
1843 {
1844 for( auto& vec : symbolLabelVectors )
1845 commit.Modify( vec[pin], m_frame->GetScreen() );
1846
1847 wxString carry = symbolLabelVectors.back()[pin]->GetText();
1848
1849 for( size_t i = 0; i < symbolLabelVectors.size(); i++ )
1850 {
1851 SCH_LABEL_BASE* lbl = symbolLabelVectors[i][pin];
1852 wxString next = lbl->GetText();
1853 lbl->SetText( carry );
1854 carry = next;
1855 }
1856 }
1857
1858 if( !commit.Empty() )
1859 commit.Push( _( "Swap Unit Labels" ) );
1860
1861 return 0;
1862}
1863
1864
1866{
1867 const std::vector<std::unique_ptr<SCH_ITEM>>& sourceItems = m_frame->GetRepeatItems();
1868
1869 if( sourceItems.empty() )
1870 return 0;
1871
1872 m_toolMgr->RunAction( ACTIONS::selectionClear );
1873
1874 SCH_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1875 SCH_COMMIT commit( m_toolMgr );
1876 SCH_SELECTION newItems;
1877
1878 for( const std::unique_ptr<SCH_ITEM>& item : sourceItems )
1879 {
1880 SCH_ITEM* newItem = item->Duplicate( IGNORE_PARENT_GROUP );
1881 bool restore_state = false;
1882
1883 // Ensure newItem has a suitable parent: the current screen, because an item from
1884 // a list of items to repeat must be attached to this current screen
1885 newItem->SetParent( m_frame->GetScreen() );
1886
1887 if( SCH_GROUP* enteredGroup = selectionTool->GetEnteredGroup() )
1888 {
1889 if( newItem->IsGroupableType() )
1890 {
1891 commit.Modify( enteredGroup, m_frame->GetScreen(), RECURSE_MODE::NO_RECURSE );
1892 enteredGroup->AddItem( newItem );
1893 }
1894 }
1895
1896 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( newItem ) )
1897 {
1898 // If incrementing tries to go below zero, tell user why the value is repeated
1899 if( EESCHEMA_SETTINGS* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ) )
1900 {
1901 if( !label->IncrementLabel( cfg->m_Drawing.repeat_label_increment ) )
1902 m_frame->ShowInfoBarWarning( _( "Label value cannot go below zero" ), true );
1903 }
1904 }
1905
1906 // If cloning a symbol then put into 'move' mode.
1907 if( newItem->Type() == SCH_SYMBOL_T )
1908 {
1909 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( true );
1910 newItem->Move( cursorPos - newItem->GetPosition() );
1911 }
1912 else if( EESCHEMA_SETTINGS* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ) )
1913 {
1914 newItem->Move( VECTOR2I( schIUScale.MilsToIU( cfg->m_Drawing.default_repeat_offset_x ),
1915 schIUScale.MilsToIU( cfg->m_Drawing.default_repeat_offset_y ) ) );
1916 }
1917
1918 // If cloning a sheet, check that we aren't going to create recursion
1919 if( newItem->Type() == SCH_SHEET_T )
1920 {
1921 SCH_SHEET_PATH* currentSheet = &m_frame->GetCurrentSheet();
1922 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( newItem );
1923
1924 if( m_frame->CheckSheetForRecursion( sheet, currentSheet ) )
1925 {
1926 // Clear out the filename so that the user can pick a new one
1927 const wxString originalFileName = sheet->GetFileName();
1928 const wxString originalScreenFileName = sheet->GetScreen()->GetFileName();
1929
1930 sheet->SetFileName( wxEmptyString );
1931 sheet->GetScreen()->SetFileName( wxEmptyString );
1932 restore_state = !m_frame->EditSheetProperties( sheet, currentSheet );
1933
1934 if( restore_state )
1935 {
1936 sheet->SetFileName( originalFileName );
1937 sheet->GetScreen()->SetFileName( originalScreenFileName );
1938 }
1939 }
1940 }
1941
1942 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, newItem );
1943 newItem->SetFlags( IS_NEW );
1944 m_frame->AddToScreen( newItem, m_frame->GetScreen() );
1945 commit.Added( newItem, m_frame->GetScreen() );
1946
1947 if( newItem->Type() == SCH_SYMBOL_T )
1948 {
1949 SCHEMATIC_SETTINGS& projSettings = m_frame->Schematic().Settings();
1950 int annotateStartNum = projSettings.m_AnnotateStartNum;
1951 ANNOTATE_ORDER_T annotateOrder = static_cast<ANNOTATE_ORDER_T>( projSettings.m_AnnotateSortOrder );
1952 ANNOTATE_ALGO_T annotateAlgo = static_cast<ANNOTATE_ALGO_T>( projSettings.m_AnnotateMethod );
1953
1954 if( m_frame->eeconfig()->m_AnnotatePanel.automatic )
1955 {
1956 static_cast<SCH_SYMBOL*>( newItem )->ClearAnnotation( nullptr, false );
1957 NULL_REPORTER reporter;
1958 m_frame->AnnotateSymbols( &commit, ANNOTATE_SELECTION,
1959 annotateOrder,
1960 annotateAlgo, true /* recursive */,
1961 annotateStartNum, false, false, reporter );
1962 }
1963
1964 // Annotation clears the selection so re-add the item
1965 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, newItem );
1966
1967 restore_state = !m_toolMgr->RunSynchronousAction( SCH_ACTIONS::move, &commit );
1968 }
1969
1970 if( restore_state )
1971 {
1972 commit.Revert();
1973 }
1974 else
1975 {
1976 newItems.Add( newItem );
1977
1979 lwbTool->TrimOverLappingWires( &commit, &newItems );
1980 lwbTool->AddJunctionsIfNeeded( &commit, &newItems );
1981
1982 m_frame->Schematic().CleanUp( &commit );
1983 commit.Push( _( "Repeat Item" ) );
1984 }
1985 }
1986
1987 if( !newItems.Empty() )
1988 m_frame->SaveCopyForRepeatItem( static_cast<SCH_ITEM*>( newItems[0] ) );
1989
1990 for( size_t ii = 1; ii < newItems.GetSize(); ++ii )
1991 m_frame->AddCopyForRepeatItem( static_cast<SCH_ITEM*>( newItems[ii] ) );
1992
1993 return 0;
1994}
1995
1996
1998{
1999 SCH_SCREEN* screen = m_frame->GetScreen();
2000 std::deque<EDA_ITEM*> items = m_selectionTool->RequestSelection( SCH_COLLECTOR::DeletableItems ).GetItems();
2001 SCH_COMMIT commit( m_toolMgr );
2002 std::vector<VECTOR2I> pts;
2003 bool updateHierarchy = false;
2004
2005 if( items.empty() )
2006 return 0;
2007
2008 // Don't leave a freed pointer in the selection
2009 m_toolMgr->RunAction( ACTIONS::selectionClear );
2010
2011 for( EDA_ITEM* item : items )
2012 item->ClearFlags( STRUCT_DELETED );
2013
2014 for( EDA_ITEM* item : items )
2015 {
2016 SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( item );
2017
2018 if( !sch_item )
2019 continue;
2020
2021 if( sch_item->IsConnectable() )
2022 {
2023 std::vector<VECTOR2I> tmp_pts = sch_item->GetConnectionPoints();
2024 pts.insert( pts.end(), tmp_pts.begin(), tmp_pts.end() );
2025 }
2026
2027 if( sch_item->Type() == SCH_JUNCTION_T )
2028 {
2029 sch_item->SetFlags( STRUCT_DELETED );
2030 // clean up junctions at the end
2031 }
2032 else if( sch_item->Type() == SCH_SHEET_PIN_T )
2033 {
2034 SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) sch_item;
2035 SCH_SHEET* sheet = pin->GetParent();
2036
2037 if( !alg::contains( items, sheet ) )
2038 {
2039 commit.Modify( sheet, m_frame->GetScreen() );
2040 sheet->RemovePin( pin );
2041 }
2042 }
2043 else if( sch_item->Type() == SCH_FIELD_T )
2044 {
2045 // Hide field
2046 commit.Modify( item, m_frame->GetScreen() );
2047 static_cast<SCH_FIELD*>( sch_item )->SetVisible( false );
2048 }
2049 else if( sch_item->Type() == SCH_TABLECELL_T )
2050 {
2051 // Clear contents of table cell
2052 commit.Modify( item, m_frame->GetScreen() );
2053 static_cast<SCH_TABLECELL*>( sch_item )->SetText( wxEmptyString );
2054 }
2055 else if( sch_item->Type() == SCH_RULE_AREA_T )
2056 {
2057 sch_item->SetFlags( STRUCT_DELETED );
2058 commit.Remove( item, m_frame->GetScreen() );
2059 }
2060 else if( sch_item->Type() == SCH_GROUP_T )
2061 {
2062 // Groups need to delete their children
2063 sch_item->RunOnChildren(
2064 [&]( SCH_ITEM* aChild )
2065 {
2066 aChild->SetFlags( STRUCT_DELETED );
2067 commit.Remove( aChild, m_frame->GetScreen() );
2068 },
2070
2071 sch_item->SetFlags( STRUCT_DELETED );
2072 commit.Remove( sch_item, m_frame->GetScreen() );
2073 }
2074 else
2075 {
2076 sch_item->SetFlags( STRUCT_DELETED );
2077 commit.Remove( item, m_frame->GetScreen() );
2078 updateHierarchy |= ( sch_item->Type() == SCH_SHEET_T );
2079 }
2080 }
2081
2082 for( const VECTOR2I& point : pts )
2083 {
2084 SCH_ITEM* junction = screen->GetItem( point, 0, SCH_JUNCTION_T );
2085
2086 if( !junction )
2087 continue;
2088
2089 if( junction->HasFlag( STRUCT_DELETED ) || !screen->IsExplicitJunction( point ) )
2090 m_frame->DeleteJunction( &commit, junction );
2091 }
2092
2093 commit.Push( _( "Delete" ) );
2094
2095 if( updateHierarchy )
2096 m_frame->UpdateHierarchyNavigator();
2097
2098 return 0;
2099}
2100
2101
2103{
2104 KICAD_T parentType = aField->GetParent() ? aField->GetParent()->Type() : SCHEMATIC_T;
2105 SCH_COMMIT commit( m_toolMgr );
2106
2107 // Save old symbol in undo list if not already in edit, or moving.
2108 if( aField->GetEditFlags() == 0 ) // i.e. not edited, or moved
2109 commit.Modify( aField, m_frame->GetScreen() );
2110
2111 if( parentType == SCH_SYMBOL_T && aField->GetId() == FIELD_T::REFERENCE )
2112 static_cast<SCH_ITEM*>( aField->GetParent() )->SetConnectivityDirty();
2113
2114 wxString caption;
2115
2116 // Use title caps for mandatory fields. "Edit Sheet name Field" looks dorky.
2117 if( aField->IsMandatory() )
2118 {
2119 wxString fieldName = GetDefaultFieldName( aField->GetId(), DO_TRANSLATE );
2120 caption.Printf( _( "Edit %s Field" ), TitleCaps( fieldName ) );
2121 }
2122 else
2123 {
2124 caption.Printf( _( "Edit '%s' Field" ), aField->GetName() );
2125 }
2126
2127 DIALOG_FIELD_PROPERTIES dlg( m_frame, caption, aField );
2128
2129 // The footprint field dialog can invoke a KIWAY_PLAYER so we must use a quasi-modal
2130 if( dlg.ShowQuasiModal() != wxID_OK )
2131 return;
2132
2133 dlg.UpdateField( &commit, aField, &m_frame->GetCurrentSheet() );
2134
2135 if( m_frame->eeconfig()->m_AutoplaceFields.enable || parentType == SCH_SHEET_T )
2136 {
2137 SCH_ITEM* parent = static_cast<SCH_ITEM*>( aField->GetParent() );
2138 AUTOPLACE_ALGO fieldsAutoplaced = parent->GetFieldsAutoplaced();
2139
2140 if( fieldsAutoplaced == AUTOPLACE_AUTO || fieldsAutoplaced == AUTOPLACE_MANUAL )
2141 parent->AutoplaceFields( m_frame->GetScreen(), fieldsAutoplaced );
2142 }
2143
2144 if( !commit.Empty() )
2145 commit.Push( caption );
2146}
2147
2148
2150{
2151 SCH_SELECTION sel = m_selectionTool->RequestSelection( { SCH_FIELD_T,
2153 SCH_PIN_T } );
2154
2155 if( sel.Size() != 1 )
2156 return 0;
2157
2158 bool clearSelection = sel.IsHover();
2159 EDA_ITEM* item = sel.Front();
2160
2161 if( item->Type() == SCH_FIELD_T )
2162 {
2163 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
2164
2165 if( ( aEvent.IsAction( &SCH_ACTIONS::editReference ) && field->GetId() != FIELD_T::REFERENCE )
2166 || ( aEvent.IsAction( &SCH_ACTIONS::editValue ) && field->GetId() != FIELD_T::VALUE )
2167 || ( aEvent.IsAction( &SCH_ACTIONS::editFootprint ) && field->GetId() != FIELD_T::FOOTPRINT ) )
2168 {
2169 item = field->GetParentSymbol();
2170
2171 m_selectionTool->ClearSelection( true );
2172
2173 // If the field to edit is not a symbol field, we cannot edit the ref, value or footprint
2174 if( item == nullptr )
2175 return 0;
2176
2177 m_selectionTool->AddItemToSel( item );
2178 }
2179 }
2180
2181 if( item->Type() == SCH_SYMBOL_T )
2182 {
2183 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2184
2185 if( aEvent.IsAction( &SCH_ACTIONS::editReference ) )
2186 {
2188 }
2189 else if( aEvent.IsAction( &SCH_ACTIONS::editValue ) )
2190 {
2192 }
2193 else if( aEvent.IsAction( &SCH_ACTIONS::editFootprint ) )
2194 {
2195 if( !symbol->IsPower() )
2197 }
2198 }
2199 else if( item->Type() == SCH_FIELD_T )
2200 {
2201 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
2202
2203 editFieldText( field );
2204
2205 if( !field->IsVisible() )
2206 clearSelection = true;
2207 }
2208 else if( item->Type() == SCH_PIN_T )
2209 {
2210 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item->GetParent() );
2211
2212 if( symbol )
2213 {
2214 if( aEvent.IsAction( &SCH_ACTIONS::editReference ) )
2215 {
2217 }
2218 else if( aEvent.IsAction( &SCH_ACTIONS::editValue ) )
2219 {
2221 }
2222 else if( aEvent.IsAction( &SCH_ACTIONS::editFootprint ) )
2223 {
2224 if( !symbol->IsPower() )
2226 }
2227 }
2228 }
2229
2230 if( clearSelection )
2231 m_toolMgr->RunAction( ACTIONS::selectionClear );
2232
2233 return 0;
2234}
2235
2236
2238{
2239 SCH_SELECTION& selection = m_selectionTool->RequestSelection( RotatableItems );
2240 SCH_COMMIT commit( m_toolMgr );
2241 SCH_ITEM* head = static_cast<SCH_ITEM*>( selection.Front() );
2242 bool moving = head && head->IsMoving();
2243
2244 if( selection.Empty() )
2245 return 0;
2246
2247 std::vector<SCH_ITEM*> autoplaceItems;
2248
2249 for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
2250 {
2251 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
2252
2253 if( item->IsType( SCH_COLLECTOR::FieldOwners ) )
2254 autoplaceItems.push_back( item );
2255 else if( item->GetParent() && item->GetParent()->IsType( SCH_COLLECTOR::FieldOwners ) )
2256 autoplaceItems.push_back( static_cast<SCH_ITEM*>( item->GetParent() ) );
2257 }
2258
2259 for( SCH_ITEM* sch_item : autoplaceItems )
2260 {
2261 if( !moving && !sch_item->IsNew() )
2262 commit.Modify( sch_item, m_frame->GetScreen() );
2263
2264 sch_item->AutoplaceFields( m_frame->GetScreen(), AUTOPLACE_MANUAL );
2265
2266 updateItem( sch_item, true );
2267 }
2268
2269 if( moving )
2270 {
2271 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2272 }
2273 else
2274 {
2275 if( !commit.Empty() )
2276 commit.Push( _( "Autoplace Fields" ) );
2277
2278 if( selection.IsHover() )
2279 m_toolMgr->RunAction( ACTIONS::selectionClear );
2280 }
2281
2282 return 0;
2283}
2284
2285
2287{
2288 SCH_SYMBOL* selectedSymbol = nullptr;
2289 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
2290
2291 if( !selection.Empty() )
2292 selectedSymbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
2293
2295
2298
2299 DIALOG_CHANGE_SYMBOLS dlg( m_frame, selectedSymbol, mode );
2300
2301 // QuasiModal required to invoke symbol browser
2302 dlg.ShowQuasiModal();
2303
2304 if( selection.IsHover() )
2305 m_toolMgr->RunAction( ACTIONS::selectionClear );
2306
2307 return 0;
2308}
2309
2310
2312{
2313 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
2314
2315 if( selection.Empty() )
2316 return 0;
2317
2318 SCH_SYMBOL* symbol = (SCH_SYMBOL*) selection.Front();
2319 SCH_COMMIT commit( m_toolMgr );
2320
2321 if( !symbol->IsNew() )
2322 commit.Modify( symbol, m_frame->GetScreen() );
2323
2324 int nextBodyStyle = symbol->GetBodyStyle() + 1;
2325
2326 if( nextBodyStyle > symbol->GetBodyStyleCount() )
2327 nextBodyStyle = 1;
2328
2329 m_frame->SelectBodyStyle( symbol, nextBodyStyle );
2330
2331 if( symbol->IsNew() )
2332 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2333
2334 if( !commit.Empty() )
2335 commit.Push( _( "Change Body Style" ) );
2336
2337 if( selection.IsHover() )
2338 m_toolMgr->RunAction( ACTIONS::selectionClear );
2339
2340 return 0;
2341}
2342
2343
2345{
2346 SCH_SELECTION& selection = m_selectionTool->RequestSelection();
2347 bool clearSelection = selection.IsHover();
2348
2349 if( selection.Empty() )
2350 {
2351 if( getView()->IsLayerVisible( LAYER_SCHEMATIC_DRAWINGSHEET ) )
2352 {
2353 DS_PROXY_VIEW_ITEM* ds = m_frame->GetCanvas()->GetView()->GetDrawingSheet();
2354 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
2355
2356 if( ds && ds->HitTestDrawingSheetItems( getView(), cursorPos ) )
2357 m_toolMgr->PostAction( ACTIONS::pageSettings );
2358 }
2359
2360 return 0;
2361 }
2362
2363 EDA_ITEM* curr_item = selection.Front();
2364
2365 // If a single pin is selected, promote to its parent symbol
2366 if( ( selection.GetSize() == 1 ) && ( curr_item->Type() == SCH_PIN_T ) )
2367 {
2368 EDA_ITEM* parent = curr_item->GetParent();
2369
2370 if( parent->Type() == SCH_SYMBOL_T )
2371 curr_item = parent;
2372 }
2373
2374 switch( curr_item->Type() )
2375 {
2376 case SCH_LINE_T:
2378 case SCH_JUNCTION_T:
2380 {
2381 std::deque<SCH_LINE*> lines;
2382
2383 for( EDA_ITEM* selItem : selection.Items() )
2384 lines.push_back( static_cast<SCH_LINE*>( selItem ) );
2385
2386 DIALOG_LINE_PROPERTIES dlg( m_frame, lines );
2387
2388 dlg.ShowModal();
2389 }
2390 else if( SELECTION_CONDITIONS::OnlyTypes( { SCH_JUNCTION_T } )( selection ) )
2391 {
2392 std::deque<SCH_JUNCTION*> junctions;
2393
2394 for( EDA_ITEM* selItem : selection.Items() )
2395 junctions.push_back( static_cast<SCH_JUNCTION*>( selItem ) );
2396
2397 DIALOG_JUNCTION_PROPS dlg( m_frame, junctions );
2398
2399 dlg.ShowModal();
2400 }
2404 SCH_JUNCTION_T } )( selection ) )
2405 {
2406 std::deque<SCH_ITEM*> items;
2407
2408 for( EDA_ITEM* selItem : selection.Items() )
2409 items.push_back( static_cast<SCH_ITEM*>( selItem ) );
2410
2411 DIALOG_WIRE_BUS_PROPERTIES dlg( m_frame, items );
2412
2413 dlg.ShowModal();
2414 }
2415 else
2416 {
2417 return 0;
2418 }
2419
2420 break;
2421
2422 case SCH_MARKER_T:
2423 if( SELECTION_CONDITIONS::OnlyTypes( { SCH_MARKER_T } )( selection ) )
2424 {
2425 SCH_INSPECTION_TOOL* inspectionTool = m_toolMgr->GetTool<SCH_INSPECTION_TOOL>();
2426
2427 if( inspectionTool )
2428 inspectionTool->CrossProbe( static_cast<SCH_MARKER*> ( selection.Front() ) );
2429 }
2430 break;
2431
2432 case SCH_TABLECELL_T:
2433 if( SELECTION_CONDITIONS::OnlyTypes( { SCH_TABLECELL_T } )( selection ) )
2434 {
2435 std::vector<SCH_TABLECELL*> cells;
2436
2437 for( EDA_ITEM* item : selection.Items() )
2438 cells.push_back( static_cast<SCH_TABLECELL*>( item ) );
2439
2441
2442 dlg.ShowModal();
2443
2445 {
2446 SCH_TABLE* table = static_cast<SCH_TABLE*>( cells[0]->GetParent() );
2448
2449 tableDlg.ShowModal();
2450 }
2451 }
2452
2453 break;
2454
2455 default:
2456 if( selection.Size() > 1 )
2457 return 0;
2458
2459 EditProperties( curr_item );
2460 }
2461
2462 if( clearSelection )
2463 m_toolMgr->RunAction( ACTIONS::selectionClear );
2464
2465 return 0;
2466}
2467
2468
2470{
2471 switch( aItem->Type() )
2472 {
2473 case SCH_SYMBOL_T:
2474 {
2475 int retval;
2476 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( aItem );
2477
2478 // This needs to be scoped so the dialog destructor removes blocking status
2479 // before we launch the next dialog.
2480 {
2481 DIALOG_SYMBOL_PROPERTIES symbolPropsDialog( m_frame, symbol );
2482
2483 // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
2484 // frame. Therefore this dialog as a modal frame parent, MUST be run under
2485 // quasimodal mode for the quasimodal frame support to work. So don't use
2486 // the QUASIMODAL macros here.
2487 retval = symbolPropsDialog.ShowQuasiModal();
2488 }
2489
2490 if( retval == SYMBOL_PROPS_EDIT_OK )
2491 {
2492 if( m_frame->eeconfig()->m_AutoplaceFields.enable )
2493 {
2494 AUTOPLACE_ALGO fieldsAutoplaced = symbol->GetFieldsAutoplaced();
2495
2496 if( fieldsAutoplaced == AUTOPLACE_AUTO || fieldsAutoplaced == AUTOPLACE_MANUAL )
2497 symbol->AutoplaceFields( m_frame->GetScreen(), fieldsAutoplaced );
2498 }
2499
2500 m_frame->OnModify();
2501 }
2502 else if( retval == SYMBOL_PROPS_EDIT_SCHEMATIC_SYMBOL )
2503 {
2504 if( KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_SCH_SYMBOL_EDITOR, true ) )
2505 {
2506 SYMBOL_EDIT_FRAME* editor = static_cast<SYMBOL_EDIT_FRAME*>( frame );
2507
2508 if( wxWindow* blocking_win = editor->Kiway().GetBlockingDialog() )
2509 blocking_win->Close( true );
2510
2511 // The broken library symbol link indicator cannot be edited.
2512 if( symbol->IsMissingLibSymbol() )
2513 return;
2514
2515 editor->LoadSymbolFromSchematic( symbol );
2516 editor->Show( true );
2517 editor->Raise();
2518 }
2519 }
2520 else if( retval == SYMBOL_PROPS_EDIT_LIBRARY_SYMBOL )
2521 {
2522 if( KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_SCH_SYMBOL_EDITOR, true ) )
2523 {
2524 SYMBOL_EDIT_FRAME* editor = static_cast<SYMBOL_EDIT_FRAME*>( frame );
2525
2526 if( wxWindow* blocking_win = editor->Kiway().GetBlockingDialog() )
2527 blocking_win->Close( true );
2528
2529 editor->LoadSymbol( symbol->GetLibId(), symbol->GetUnit(), symbol->GetBodyStyle() );
2530 editor->Show( true );
2531 editor->Raise();
2532 }
2533 }
2534 else if( retval == SYMBOL_PROPS_WANT_UPDATE_SYMBOL )
2535 {
2537 dlg.ShowQuasiModal();
2538 }
2539 else if( retval == SYMBOL_PROPS_WANT_EXCHANGE_SYMBOL )
2540 {
2542 dlg.ShowQuasiModal();
2543 }
2544
2545 break;
2546 }
2547
2548 case SCH_SHEET_T:
2549 {
2550 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
2551 bool isUndoable = false;
2552 bool doClearAnnotation = false;
2553 bool okPressed = false;
2554 bool updateHierarchyNavigator = false;
2555
2556 // Keep track of existing sheet paths. EditSheet() can modify this list.
2557 // Note that we use the validity checking/repairing version here just to make sure
2558 // we've got a valid hierarchy to begin with.
2559 SCH_SHEET_LIST originalHierarchy;
2560 originalHierarchy.BuildSheetList( &m_frame->Schematic().Root(), true );
2561
2562 SCH_COMMIT commit( m_toolMgr );
2563 commit.Modify( sheet, m_frame->GetScreen() );
2564 okPressed = m_frame->EditSheetProperties( sheet, &m_frame->GetCurrentSheet(), &isUndoable,
2565 &doClearAnnotation, &updateHierarchyNavigator );
2566
2567 if( okPressed )
2568 {
2569 if( isUndoable )
2570 {
2571 commit.Push( _( "Edit Sheet Properties" ) );
2572 }
2573 else
2574 {
2575 std::vector<SCH_ITEM*> items;
2576
2577 items.emplace_back( sheet );
2578 m_frame->Schematic().OnItemsRemoved( items );
2579 m_frame->Schematic().OnItemsAdded( items );
2580 m_frame->OnModify();
2581 m_frame->Schematic().RefreshHierarchy();
2582 m_frame->UpdateHierarchyNavigator();
2583 }
2584 }
2585 else
2586 {
2587 // If we are renaming files, the undo/redo list becomes invalid and must be cleared.
2588 m_frame->ClearUndoRedoList();
2589 m_frame->OnModify();
2590 }
2591
2592 // If the sheet file is changed and new sheet contents are loaded then we have to
2593 // clear the annotations on the new content (as it may have been set from some other
2594 // sheet path reference)
2595 if( doClearAnnotation )
2596 {
2597 SCH_SCREENS screensList( &m_frame->Schematic().Root() );
2598
2599 // We clear annotation of new sheet paths here:
2600 screensList.ClearAnnotationOfNewSheetPaths( originalHierarchy );
2601
2602 // Clear annotation of g_CurrentSheet itself, because its sheetpath is not a new
2603 // path, but symbols managed by its sheet path must have their annotation cleared
2604 // because they are new:
2605 sheet->GetScreen()->ClearAnnotation( &m_frame->GetCurrentSheet(), false );
2606 }
2607
2608 if( okPressed )
2609 m_frame->GetCanvas()->Refresh();
2610
2611 if( updateHierarchyNavigator )
2612 m_frame->UpdateHierarchyNavigator();
2613
2614 break;
2615 }
2616
2617 case SCH_SHEET_PIN_T:
2618 {
2619 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( aItem );
2621
2622 // QuasiModal required for help dialog
2623 dlg.ShowQuasiModal();
2624 break;
2625 }
2626
2627 case SCH_TEXT_T:
2628 case SCH_TEXTBOX_T:
2629 {
2630 DIALOG_TEXT_PROPERTIES dlg( m_frame, static_cast<SCH_ITEM*>( aItem ) );
2631
2632 // QuasiModal required for syntax help and Scintilla auto-complete
2633 dlg.ShowQuasiModal();
2634 break;
2635 }
2636
2637 case SCH_TABLE_T:
2638 {
2639 DIALOG_TABLE_PROPERTIES dlg( m_frame, static_cast<SCH_TABLE*>( aItem ) );
2640
2641 // QuasiModal required for Scintilla auto-complete
2642 dlg.ShowQuasiModal();
2643 break;
2644 }
2645
2646 case SCH_LABEL_T:
2647 case SCH_GLOBAL_LABEL_T:
2648 case SCH_HIER_LABEL_T:
2650 {
2651 DIALOG_LABEL_PROPERTIES dlg( m_frame, static_cast<SCH_LABEL_BASE*>( aItem ), false );
2652
2653 // QuasiModal for syntax help and Scintilla auto-complete
2654 dlg.ShowQuasiModal();
2655 break;
2656 }
2657
2658 case SCH_FIELD_T:
2659 {
2660 SCH_FIELD* field = static_cast<SCH_FIELD*>( aItem );
2661
2662 editFieldText( field );
2663
2664 if( !field->IsVisible() )
2665 m_toolMgr->RunAction( ACTIONS::selectionClear );
2666
2667 break;
2668 }
2669
2670 case SCH_SHAPE_T:
2671 {
2672 DIALOG_SHAPE_PROPERTIES dlg( m_frame, static_cast<SCH_SHAPE*>( aItem ) );
2673
2674 dlg.ShowModal();
2675 break;
2676 }
2677
2678 case SCH_BITMAP_T:
2679 {
2680 SCH_BITMAP& bitmap = static_cast<SCH_BITMAP&>( *aItem );
2681 DIALOG_IMAGE_PROPERTIES dlg( m_frame, bitmap );
2682
2683 if( dlg.ShowModal() == wxID_OK )
2684 {
2685 // The bitmap is cached in Opengl: clear the cache in case it has become invalid
2687 }
2688
2689 break;
2690 }
2691
2692 case SCH_RULE_AREA_T:
2693 {
2694 DIALOG_SHAPE_PROPERTIES dlg( m_frame, static_cast<SCH_SHAPE*>( aItem ) );
2695 dlg.SetTitle( _( "Rule Area Properties" ) );
2696
2697 dlg.ShowModal();
2698 break;
2699 }
2700
2701 case SCH_NO_CONNECT_T:
2702 case SCH_PIN_T:
2703 break;
2704
2705 case SCH_GROUP_T:
2707 static_cast<EDA_GROUP*>( static_cast<SCH_GROUP*>( aItem ) ) );
2708
2709 break;
2710
2711 default: // Unexpected item
2712 wxFAIL_MSG( wxString( "Cannot edit schematic item type " ) + aItem->GetClass() );
2713 }
2714
2715 updateItem( aItem, true );
2716}
2717
2718
2720{
2721 KICAD_T convertTo = aEvent.Parameter<KICAD_T>();
2722 SCH_SELECTION selection = m_selectionTool->RequestSelection( { SCH_LABEL_LOCATE_ANY_T,
2723 SCH_TEXT_T,
2724 SCH_TEXTBOX_T } );
2725 SCH_COMMIT localCommit( m_toolMgr );
2726 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
2727
2728 if( !commit )
2729 commit = &localCommit;
2730
2731 for( unsigned int i = 0; i < selection.GetSize(); ++i )
2732 {
2733 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( selection.GetItem( i ) );
2734
2735 if( item && item->Type() != convertTo )
2736 {
2737 EDA_TEXT* sourceText = dynamic_cast<EDA_TEXT*>( item );
2738 bool selected = item->IsSelected();
2739 SCH_ITEM* newtext = nullptr;
2740 VECTOR2I position = item->GetPosition();
2741 wxString txt;
2742 wxString href;
2745
2746 wxCHECK2( sourceText, continue );
2747
2748 switch( item->Type() )
2749 {
2750 case SCH_LABEL_T:
2751 case SCH_GLOBAL_LABEL_T:
2752 case SCH_HIER_LABEL_T:
2753 {
2754 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( item );
2755
2756 txt = UnescapeString( label->GetText() );
2757 spinStyle = label->GetSpinStyle();
2758 shape = label->GetShape();
2759 href = label->GetHyperlink();
2760 break;
2761 }
2762
2764 {
2765 SCH_DIRECTIVE_LABEL* dirlabel = static_cast<SCH_DIRECTIVE_LABEL*>( item );
2766
2767 // a SCH_DIRECTIVE_LABEL has no text
2768 txt = _( "<empty>" );
2769
2770 spinStyle = dirlabel->GetSpinStyle();
2771 href = dirlabel->GetHyperlink();
2772 break;
2773 }
2774
2775 case SCH_TEXT_T:
2776 {
2777 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
2778
2779 txt = text->GetText();
2780 href = text->GetHyperlink();
2781 break;
2782 }
2783
2784 case SCH_TEXTBOX_T:
2785 {
2786 SCH_TEXTBOX* textbox = static_cast<SCH_TEXTBOX*>( item );
2787 BOX2I bbox = textbox->GetBoundingBox();
2788
2789 bbox.SetOrigin( bbox.GetLeft() + textbox->GetMarginLeft(),
2790 bbox.GetTop() + textbox->GetMarginTop() );
2791 bbox.SetEnd( bbox.GetRight() - textbox->GetMarginRight(),
2792 bbox.GetBottom() - textbox->GetMarginBottom() );
2793
2794 if( convertTo == SCH_LABEL_T
2795 || convertTo == SCH_HIER_LABEL_T
2796 || convertTo == SCH_GLOBAL_LABEL_T )
2797 {
2798 EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
2799 wxCHECK( text, 0 );
2800 int textSize = text->GetTextSize().y;
2801 bbox.Inflate( KiROUND( item->Schematic()->Settings().m_LabelSizeRatio * textSize ) );
2802 }
2803
2804 txt = textbox->GetText();
2805
2806 if( textbox->GetTextAngle().IsVertical() )
2807 {
2808 if( textbox->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
2809 {
2810 spinStyle = SPIN_STYLE::SPIN::BOTTOM;
2811 position = VECTOR2I( bbox.Centre().x, bbox.GetOrigin().y );
2812 }
2813 else
2814 {
2815 spinStyle = SPIN_STYLE::SPIN::UP;
2816 position = VECTOR2I( bbox.Centre().x, bbox.GetEnd().y );
2817 }
2818 }
2819 else
2820 {
2821 if( textbox->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
2822 {
2823 spinStyle = SPIN_STYLE::SPIN::LEFT;
2824 position = VECTOR2I( bbox.GetEnd().x, bbox.Centre().y );
2825 }
2826 else
2827 {
2828 spinStyle = SPIN_STYLE::SPIN::RIGHT;
2829 position = VECTOR2I( bbox.GetOrigin().x, bbox.Centre().y );
2830 }
2831 }
2832
2833 position = m_frame->GetNearestGridPosition( position );
2834 href = textbox->GetHyperlink();
2835 break;
2836 }
2837
2838 default:
2839 UNIMPLEMENTED_FOR( item->GetClass() );
2840 break;
2841 }
2842
2843 auto getValidNetname =
2844 []( const wxString& aText )
2845 {
2846 wxString local_txt = aText;
2847 local_txt.Replace( "\n", "_" );
2848 local_txt.Replace( "\r", "_" );
2849 local_txt.Replace( "\t", "_" );
2850
2851 // Bus groups can have spaces; bus vectors and signal names cannot
2852 if( !NET_SETTINGS::ParseBusGroup( aText, nullptr, nullptr ) )
2853 local_txt.Replace( " ", "_" );
2854
2855 // label strings are "escaped" i.e. a '/' is replaced by "{slash}"
2856 local_txt = EscapeString( local_txt, CTX_NETNAME );
2857
2858 if( local_txt.IsEmpty() )
2859 return _( "<empty>" );
2860 else
2861 return local_txt;
2862 };
2863
2864 switch( convertTo )
2865 {
2866 case SCH_LABEL_T:
2867 {
2868 SCH_LABEL_BASE* new_label = new SCH_LABEL( position, getValidNetname( txt ) );
2869
2870 new_label->SetShape( shape );
2871 new_label->SetAttributes( *sourceText, false );
2872 new_label->SetSpinStyle( spinStyle );
2873 new_label->SetHyperlink( href );
2874
2875 if( item->Type() == SCH_GLOBAL_LABEL_T || item->Type() == SCH_HIER_LABEL_T )
2876 {
2877 if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::UP )
2878 new_label->MirrorVertically( position.y );
2879 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::BOTTOM )
2880 new_label->MirrorVertically( position.y );
2881 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::LEFT )
2882 new_label->MirrorHorizontally( position.x );
2883 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::RIGHT )
2884 new_label->MirrorHorizontally( position.x );
2885 }
2886
2887 newtext = new_label;
2888 break;
2889 }
2890
2891 case SCH_GLOBAL_LABEL_T:
2892 {
2893 SCH_LABEL_BASE* new_label = new SCH_GLOBALLABEL( position, getValidNetname( txt ) );
2894
2895 new_label->SetShape( shape );
2896 new_label->SetAttributes( *sourceText, false );
2897 new_label->SetSpinStyle( spinStyle );
2898 new_label->SetHyperlink( href );
2899
2900 if( item->Type() == SCH_LABEL_T )
2901 {
2902 if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::UP )
2903 new_label->MirrorVertically( position.y );
2904 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::BOTTOM )
2905 new_label->MirrorVertically( position.y );
2906 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::LEFT )
2907 new_label->MirrorHorizontally( position.x );
2908 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::RIGHT )
2909 new_label->MirrorHorizontally( position.x );
2910 }
2911
2912 newtext = new_label;
2913 break;
2914 }
2915
2916 case SCH_HIER_LABEL_T:
2917 {
2918 SCH_LABEL_BASE* new_label = new SCH_HIERLABEL( position, getValidNetname( txt ) );
2919
2920 new_label->SetShape( shape );
2921 new_label->SetAttributes( *sourceText, false );
2922 new_label->SetSpinStyle( spinStyle );
2923 new_label->SetHyperlink( href );
2924
2925 if( item->Type() == SCH_LABEL_T )
2926 {
2927 if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::UP )
2928 new_label->MirrorVertically( position.y );
2929 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::BOTTOM )
2930 new_label->MirrorVertically( position.y );
2931 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::LEFT )
2932 new_label->MirrorHorizontally( position.x );
2933 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::RIGHT )
2934 new_label->MirrorHorizontally( position.x );
2935 }
2936
2937 newtext = new_label;
2938 break;
2939 }
2940
2942 {
2943 SCH_LABEL_BASE* new_label = new SCH_DIRECTIVE_LABEL( position );
2944
2945 // A SCH_DIRECTIVE_LABEL usually has at least one field containing the net class
2946 // name. If we're copying from a text object assume the text is the netclass
2947 // name. Otherwise, we'll just copy the fields which will either have a netclass
2948 // or not.
2949 if( !dynamic_cast<SCH_LABEL_BASE*>( item ) )
2950 {
2951 SCH_FIELD netclass( new_label, FIELD_T::USER, wxT( "Netclass" ) );
2952 netclass.SetText( txt );
2953 netclass.SetTextPos( position );
2954 new_label->GetFields().push_back( netclass );
2955 }
2956
2957 new_label->SetShape( LABEL_FLAG_SHAPE::F_ROUND );
2958 new_label->SetAttributes( *sourceText, false );
2959 new_label->SetSpinStyle( spinStyle );
2960 new_label->SetHyperlink( href );
2961 newtext = new_label;
2962 break;
2963 }
2964
2965 case SCH_TEXT_T:
2966 {
2967 SCH_TEXT* new_text = new SCH_TEXT( position, txt );
2968
2969 new_text->SetAttributes( *sourceText, false );
2970 new_text->SetHyperlink( href );
2971 newtext = new_text;
2972 break;
2973 }
2974
2975 case SCH_TEXTBOX_T:
2976 {
2977 SCH_TEXTBOX* new_textbox = new SCH_TEXTBOX( LAYER_NOTES, 0, FILL_T::NO_FILL, txt );
2978 BOX2I bbox = item->GetBoundingBox();
2979
2980 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item ) )
2981 bbox.Inflate( -label->GetLabelBoxExpansion() );
2982
2983 new_textbox->SetAttributes( *sourceText, false );
2984
2985 bbox.SetOrigin( bbox.GetLeft() - new_textbox->GetMarginLeft(),
2986 bbox.GetTop() - new_textbox->GetMarginTop() );
2987 bbox.SetEnd( bbox.GetRight() + new_textbox->GetMarginRight(),
2988 bbox.GetBottom() + new_textbox->GetMarginBottom() );
2989
2990 VECTOR2I topLeft = bbox.GetPosition();
2991 VECTOR2I botRight = bbox.GetEnd();
2992
2993 // Add 1/20 of the margin at the end to reduce line-breaking changes.
2994 int slop = new_textbox->GetLegacyTextMargin() / 20;
2995
2996 if( sourceText->GetTextAngle() == ANGLE_VERTICAL )
2997 {
2998 if( sourceText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
2999 botRight.y += slop;
3000 else
3001 topLeft.y -= slop;
3002 }
3003 else
3004 {
3005 if( sourceText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
3006 topLeft.x -= slop;
3007 else
3008 botRight.x += slop;
3009 }
3010
3011 new_textbox->SetPosition( topLeft );
3012 new_textbox->SetEnd( botRight );
3013
3014 new_textbox->SetHyperlink( href );
3015 newtext = new_textbox;
3016 break;
3017 }
3018
3019 default:
3020 UNIMPLEMENTED_FOR( wxString::Format( "%d.", convertTo ) );
3021 break;
3022 }
3023
3024 wxCHECK2( newtext, continue );
3025
3026 // Copy the old text item settings to the new one. Justifications are not copied
3027 // because they are not used in labels. Justifications will be set to default value
3028 // in the new text item type.
3029 //
3030 newtext->SetFlags( item->GetEditFlags() );
3031
3032 EDA_TEXT* eda_text = dynamic_cast<EDA_TEXT*>( item );
3033 EDA_TEXT* new_eda_text = dynamic_cast<EDA_TEXT*>( newtext );
3034
3035 wxCHECK2( eda_text && new_eda_text, continue );
3036
3037 new_eda_text->SetFont( eda_text->GetFont() );
3038 new_eda_text->SetTextSize( eda_text->GetTextSize() );
3039 new_eda_text->SetTextThickness( eda_text->GetTextThickness() );
3040
3041 // Must be after SetTextSize()
3042 new_eda_text->SetBold( eda_text->IsBold() );
3043 new_eda_text->SetItalic( eda_text->IsItalic() );
3044
3045 newtext->AutoplaceFields( m_frame->GetScreen(), AUTOPLACE_AUTO );
3046
3047 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item );
3048 SCH_LABEL_BASE* new_label = dynamic_cast<SCH_LABEL_BASE*>( newtext );
3049
3050 if( label && new_label )
3051 {
3052 new_label->AddFields( label->GetFields() );
3053
3054 // A SCH_GLOBALLABEL has a specific field for intersheet references that has
3055 // no meaning for other labels
3056 std::erase_if( new_label->GetFields(),
3057 [&]( SCH_FIELD& field )
3058 {
3059 return field.GetId() == FIELD_T::INTERSHEET_REFS
3060 && new_label->Type() != SCH_GLOBAL_LABEL_T;
3061 } );
3062 }
3063
3064 if( selected )
3065 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::unselectItem, item );
3066
3067 m_frame->RemoveFromScreen( item, m_frame->GetScreen() );
3068
3069 if( commit->GetStatus( item, m_frame->GetScreen() ) == CHT_ADD )
3070 commit->Unstage( item, m_frame->GetScreen() );
3071 else
3072 commit->Removed( item, m_frame->GetScreen() );
3073
3074 m_frame->AddToScreen( newtext, m_frame->GetScreen() );
3075 commit->Added( newtext, m_frame->GetScreen() );
3076
3077 if( selected )
3078 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, newtext );
3079 }
3080 }
3081
3082 if( !localCommit.Empty() )
3083 localCommit.Push( _( "Change To" ) );
3084
3085 if( selection.IsHover() )
3086 m_toolMgr->RunAction( ACTIONS::selectionClear );
3087
3088 return 0;
3089}
3090
3091
3093{
3094 static std::vector<KICAD_T> justifiableItems = {
3096 SCH_TEXT_T,
3099 };
3100
3101 SCH_SELECTION& selection = m_selectionTool->RequestSelection( justifiableItems );
3102
3103 if( selection.GetSize() == 0 )
3104 return 0;
3105
3106 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
3107 bool moving = item->IsMoving();
3108 SCH_COMMIT localCommit( m_toolMgr );
3109 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
3110
3111 if( !commit )
3112 commit = &localCommit;
3113
3114 auto setJustify =
3115 [&]( EDA_TEXT* aTextItem )
3116 {
3117 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
3118 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
3119 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
3120 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
3121 else
3122 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
3123 };
3124
3125 for( EDA_ITEM* edaItem : selection )
3126 {
3127 item = static_cast<SCH_ITEM*>( edaItem );
3128
3129 if( !moving )
3130 commit->Modify( item, m_frame->GetScreen() );
3131
3132 if( item->Type() == SCH_FIELD_T )
3133 {
3134 setJustify( static_cast<SCH_FIELD*>( item ) );
3135
3136 // Now that we're re-justifying a field, they're no longer autoplaced.
3137 static_cast<SCH_ITEM*>( item->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
3138 }
3139 else if( item->Type() == SCH_TEXT_T )
3140 {
3141 setJustify( static_cast<SCH_TEXT*>( item ) );
3142 }
3143 else if( item->Type() == SCH_TEXTBOX_T )
3144 {
3145 setJustify( static_cast<SCH_TEXTBOX*>( item ) );
3146 }
3147 else if( item->Type() == SCH_LABEL_T )
3148 {
3149 SCH_LABEL* label = static_cast<SCH_LABEL*>( item );
3150
3151 if( label->GetTextAngle() == ANGLE_HORIZONTAL )
3152 setJustify( label );
3153 }
3154
3155 m_frame->UpdateItem( item, false, true );
3156 }
3157
3158 // Update R-Tree for modified items
3159 for( EDA_ITEM* selected : selection )
3160 updateItem( selected, true );
3161
3162 if( item->IsMoving() )
3163 {
3164 m_toolMgr->RunAction( ACTIONS::refreshPreview );
3165 }
3166 else
3167 {
3168 SCH_SELECTION selectionCopy = selection;
3169
3170 if( selection.IsHover() )
3171 m_toolMgr->RunAction( ACTIONS::selectionClear );
3172
3173 if( !localCommit.Empty() )
3174 {
3175 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
3176 localCommit.Push( _( "Left Justify" ) );
3177 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
3178 localCommit.Push( _( "Center Justify" ) );
3179 else
3180 localCommit.Push( _( "Right Justify" ) );
3181 }
3182 }
3183
3184 return 0;
3185}
3186
3187
3189{
3190 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SHEET_T } );
3191 SCH_SHEET* sheet = (SCH_SHEET*) selection.Front();
3192 SCH_COMMIT commit( m_toolMgr );
3193
3194 if( !sheet || !sheet->HasUndefinedPins() )
3195 return 0;
3196
3197 if( !IsOK( m_frame, _( "Do you wish to delete the unreferenced pins from this sheet?" ) ) )
3198 return 0;
3199
3200 commit.Modify( sheet, m_frame->GetScreen() );
3201
3202 sheet->CleanupSheet();
3203
3204 updateItem( sheet, true );
3205
3206 commit.Push( _( "Cleanup Sheet Pins" ) );
3207
3208 if( selection.IsHover() )
3209 m_toolMgr->RunAction( ACTIONS::selectionClear );
3210
3211 return 0;
3212}
3213
3214
3216{
3217 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SHEET_T } );
3218
3219 if( selection.GetSize() > 1 )
3220 return 0;
3221
3222 SCH_SHEET* sheet = (SCH_SHEET*) selection.Front();
3223
3224 SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
3225
3226 SCH_SCREEN* screen;
3227
3228 if( sheet )
3229 {
3230 // When changing the page number of a selected sheet, the current screen owns the sheet.
3231 screen = m_frame->GetScreen();
3232
3233 instance.push_back( sheet );
3234 }
3235 else
3236 {
3237 SCH_SHEET_PATH prevInstance = instance;
3238
3239 // When change the page number in the screen, the previous screen owns the sheet.
3240 if( prevInstance.size() )
3241 {
3242 prevInstance.pop_back();
3243 screen = prevInstance.LastScreen();
3244 }
3245 else
3246 {
3247 // The root sheet and root screen are effectively the same thing.
3248 screen = m_frame->GetScreen();
3249 }
3250
3251 sheet = m_frame->GetCurrentSheet().Last();
3252 }
3253
3254 wxString msg;
3255 wxString sheetPath = instance.PathHumanReadable( false );
3256 wxString pageNumber = instance.GetPageNumber();
3257
3258 msg.Printf( _( "Enter page number for sheet path%s" ),
3259 ( sheetPath.Length() > 20 ) ? "\n" + sheetPath : " " + sheetPath );
3260
3261 wxTextEntryDialog dlg( m_frame, msg, _( "Edit Sheet Page Number" ), pageNumber );
3262
3263 dlg.SetTextValidator( wxFILTER_ALPHANUMERIC ); // No white space.
3264
3265 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == instance.GetPageNumber() )
3266 return 0;
3267
3268 SCH_COMMIT commit( m_frame );
3269
3270 commit.Modify( sheet, screen );
3271
3272 instance.SetPageNumber( dlg.GetValue() );
3273
3274 if( instance == m_frame->GetCurrentSheet() )
3275 {
3276 m_frame->GetScreen()->SetPageNumber( dlg.GetValue() );
3277 m_frame->OnPageSettingsChange();
3278 }
3279
3280 commit.Push( wxS( "Change Sheet Page Number" ) );
3281
3282 if( selection.IsHover() )
3283 m_toolMgr->RunAction( ACTIONS::selectionClear );
3284
3285 return 0;
3286}
3287
3288
3290{
3291 return m_toolMgr->RunAction( SCH_ACTIONS::importSheet, aEvent.Parameter<wxString*>() );
3292}
3293
3294
3296{
3297 wxString* filename = aEvent.Parameter<wxString*>();
3298
3299 if( !filename )
3300 return 0;
3301
3302 SCH_BITMAP* image = new SCH_BITMAP( VECTOR2I( 0, 0 ) );
3303
3304 if( !image->GetReferenceImage().ReadImageFile( *filename ) )
3305 {
3306 wxMessageBox( wxString::Format( _( "Could not load image from '%s'." ), *filename ) );
3307 delete image;
3308 return 0;
3309 }
3310
3311 return m_toolMgr->RunAction( SCH_ACTIONS::placeImage, image );
3312}
3313
3314
3316 std::set<std::pair<SCH_SYMBOL*, SCH_SCREEN*>>& aCollectedUnits )
3317{
3318 for( EDA_ITEM* item : aSelection )
3319 {
3320 if( item->Type() == SCH_SYMBOL_T )
3321 {
3322 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3323
3324 aCollectedUnits.insert( { symbol, m_frame->GetScreen() } );
3325
3326 // The attributes should be kept in sync in multi-unit parts.
3327 // Of course the symbol must be annotated to collect other units.
3328 if( symbol->IsAnnotated( &m_frame->GetCurrentSheet() ) )
3329 {
3330 wxString ref = symbol->GetRef( &m_frame->GetCurrentSheet() );
3331 int unit = symbol->GetUnit();
3332 LIB_ID libId = symbol->GetLibId();
3333
3334 for( SCH_SHEET_PATH& sheet : m_frame->Schematic().Hierarchy() )
3335 {
3336 SCH_SCREEN* screen = sheet.LastScreen();
3337 std::vector<SCH_SYMBOL*> otherUnits;
3338
3339 CollectOtherUnits( ref, unit, libId, sheet, &otherUnits );
3340
3341 for( SCH_SYMBOL* otherUnit : otherUnits )
3342 aCollectedUnits.insert( { otherUnit, screen } );
3343 }
3344 }
3345 }
3346 }
3347}
3348
3349
3351{
3352 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
3353 SCH_COMMIT commit( m_toolMgr );
3354
3355 std::set<std::pair<SCH_SYMBOL*, SCH_SCREEN*>> collectedUnits;
3356
3357 collectUnits( selection, collectedUnits );
3358 bool new_state = false;
3359
3360 for( const auto& [symbol, _] : collectedUnits )
3361 {
3362 if( ( aEvent.IsAction( &SCH_ACTIONS::setDNP ) && !symbol->GetDNP() )
3363 || ( aEvent.IsAction( &SCH_ACTIONS::setExcludeFromSimulation ) && !symbol->GetExcludedFromSim() )
3364 || ( aEvent.IsAction( &SCH_ACTIONS::setExcludeFromBOM ) && !symbol->GetExcludedFromBOM() )
3365 || ( aEvent.IsAction( &SCH_ACTIONS::setExcludeFromBoard ) && !symbol->GetExcludedFromBoard() ) )
3366 {
3367 new_state = true;
3368 break;
3369 }
3370 }
3371
3372 for( const auto& [symbol, screen] : collectedUnits )
3373 {
3374 commit.Modify( symbol, screen );
3375
3376 if( aEvent.IsAction( &SCH_ACTIONS::setDNP ) )
3377 symbol->SetDNP( new_state );
3378
3380 symbol->SetExcludedFromSim( new_state );
3381
3383 symbol->SetExcludedFromBOM( new_state );
3384
3386 symbol->SetExcludedFromBoard( new_state );
3387 }
3388
3389 if( !commit.Empty() )
3390 commit.Push( _( "Toggle Attribute" ) );
3391
3392 if( selection.IsHover() )
3393 m_toolMgr->RunAction( ACTIONS::selectionClear );
3394
3395 return 0;
3396}
3397
3398
3399wxString SCH_EDIT_TOOL::FixERCErrorMenuText( const std::shared_ptr<RC_ITEM>& aERCItem )
3400{
3401 if( aERCItem->GetErrorCode() == ERCE_SIMULATION_MODEL
3402 || aERCItem->GetErrorCode() == ERCE_FOOTPRINT_FILTERS
3403 || aERCItem->GetErrorCode() == ERCE_FOOTPRINT_LINK_ISSUES )
3404 {
3405 return _( "Edit Symbol Properties..." );
3406 }
3407 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_ISSUES )
3408 {
3409 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::showSymbolLibTable );
3410 }
3411 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_MISMATCH )
3412 {
3413 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::updateSymbol );
3414 }
3415 else if( aERCItem->GetErrorCode() == ERCE_UNANNOTATED
3416 || aERCItem->GetErrorCode() == ERCE_DUPLICATE_REFERENCE )
3417 {
3418 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::annotate );
3419 }
3420 else if( aERCItem->GetErrorCode() == ERCE_UNDEFINED_NETCLASS )
3421 {
3422 return _( "Edit Netclasses..." );
3423 }
3424
3425 return wxEmptyString;
3426}
3427
3428
3429void SCH_EDIT_TOOL::FixERCError( const std::shared_ptr<RC_ITEM>& aERCItem )
3430{
3431 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
3432
3433 wxCHECK( frame, /* void */ );
3434
3435 if( aERCItem->GetErrorCode() == ERCE_SIMULATION_MODEL
3436 || aERCItem->GetErrorCode() == ERCE_FOOTPRINT_FILTERS
3437 || aERCItem->GetErrorCode() == ERCE_FOOTPRINT_LINK_ISSUES )
3438 {
3439 if( EDA_ITEM* item = frame->ResolveItem( aERCItem->GetMainItemID() ) )
3440 EditProperties( item );
3441 }
3442 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_ISSUES )
3443 {
3445 }
3446 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_MISMATCH )
3447 {
3448 EDA_ITEM* item = frame->ResolveItem( aERCItem->GetMainItemID() );
3449
3450 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
3451 {
3453 dlg.ShowQuasiModal();
3454 }
3455 }
3456 else if( aERCItem->GetErrorCode() == ERCE_UNANNOTATED
3457 || aERCItem->GetErrorCode() == ERCE_DUPLICATE_REFERENCE )
3458 {
3459 m_toolMgr->RunAction( SCH_ACTIONS::annotate );
3460 }
3461 else if( aERCItem->GetErrorCode() == ERCE_UNDEFINED_NETCLASS )
3462 {
3463 frame->ShowSchematicSetupDialog( _( "Net Classes" ) );
3464 }
3465}
3466
3467
3469{
3470 // clang-format off
3476 Go( &SCH_EDIT_TOOL::Swap, SCH_ACTIONS::swap.MakeEvent() );
3482
3488
3508
3509
3514
3518
3521 // clang-format on
3522}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
@ add_hierarchical_label
@ component_select_unit
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
static TOOL_ACTION decrementPrimary
Definition actions.h:96
static TOOL_ACTION paste
Definition actions.h:80
static TOOL_ACTION unselectAll
Definition actions.h:83
static TOOL_ACTION decrementSecondary
Definition actions.h:98
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition actions.h:226
static TOOL_ACTION unselectItem
Definition actions.h:227
static TOOL_ACTION copy
Definition actions.h:78
static TOOL_ACTION showSymbolLibTable
Definition actions.h:283
static TOOL_ACTION pasteSpecial
Definition actions.h:81
static TOOL_ACTION groupProperties
Definition actions.h:246
static TOOL_ACTION rightJustify
Definition actions.h:89
static TOOL_ACTION pageSettings
Definition actions.h:63
static TOOL_ACTION incrementSecondary
Definition actions.h:97
static TOOL_ACTION duplicate
Definition actions.h:84
static TOOL_ACTION incrementPrimary
Definition actions.h:95
static TOOL_ACTION doDelete
Definition actions.h:85
static TOOL_ACTION deleteTool
Definition actions.h:86
static TOOL_ACTION increment
Definition actions.h:94
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:223
static TOOL_ACTION leftJustify
Definition actions.h:87
static TOOL_ACTION cut
Definition actions.h:77
static TOOL_ACTION copyAsText
Definition actions.h:79
static TOOL_ACTION refreshPreview
Definition actions.h:158
static TOOL_ACTION selectAll
Definition actions.h:82
static TOOL_ACTION centerJustify
Definition actions.h:88
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:64
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.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
void update() override
Update menu state stub.
void update() override
Update menu state stub.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
constexpr const Vec & GetPosition() const
Definition box2.h:211
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:558
constexpr const Vec GetEnd() const
Definition box2.h:212
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:237
constexpr Vec Centre() const
Definition box2.h:97
constexpr const Vec GetCenter() const
Definition box2.h:230
constexpr coord_type GetLeft() const
Definition box2.h:228
constexpr const Vec & GetOrigin() const
Definition box2.h:210
constexpr coord_type GetRight() const
Definition box2.h:217
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:297
constexpr coord_type GetTop() const
Definition box2.h:229
constexpr coord_type GetBottom() const
Definition box2.h:222
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:90
COMMIT & Added(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition commit.h:84
void Unstage(EDA_ITEM *aItem, BASE_SCREEN *aScreen)
Definition commit.cpp:136
bool Empty() const
Definition commit.h:137
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:106
COMMIT & Removed(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Definition commit.h:96
int GetStatus(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Returns status of an item.
Definition commit.cpp:167
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.
Calculate the connectivity of a schematic and generates netlists.
CONNECTION_SUBGRAPH * GetSubgraphForItem(SCH_ITEM *aItem) const
A subgraph is a set of items that are electrically connected on a single sheet.
const std::set< SCH_ITEM * > & GetItems() const
Provide a read-only reference to the items in the subgraph.
Dialog to update or change schematic library symbols.
This class is setup in expectation of its children possibly using Kiway player so DIALOG_SHIM::ShowQu...
void UpdateField(SCH_FIELD *aField)
int ShowModal() override
Dialog used to edit SCH_SYMBOL objects in a schematic.
bool HitTestDrawingSheetItems(KIGFX::VIEW *aView, const VECTOR2I &aPosition)
bool IsHorizontal() const
Definition eda_angle.h:142
bool IsVertical() const
Definition eda_angle.h:148
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:46
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:272
virtual void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:273
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:110
EDA_ITEM_FLAGS GetEditFlags() const
Definition eda_item.h:148
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:142
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:144
bool IsSelected() const
Definition eda_item.h:127
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:192
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.h:113
EDA_ITEM * GetParent() const
Definition eda_item.h:112
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:146
bool IsMoving() const
Definition eda_item.h:125
bool IsNew() const
Definition eda_item.h:124
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:219
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:80
bool IsItalic() const
Definition eda_text.h:169
const EDA_ANGLE & GetTextAngle() const
Definition eda_text.h:147
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:544
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:98
virtual bool IsVisible() const
Definition eda_text.h:187
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:589
KIFONT::FONT * GetFont() const
Definition eda_text.h:247
void SetAttributes(const EDA_TEXT &aSrc, bool aSetPosition=true)
Set the text attributes from another instance.
Definition eda_text.cpp:444
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:428
wxString GetHyperlink() const
Definition eda_text.h:401
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:200
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:295
void SetBold(bool aBold)
Set the text to be bold - this will also update the font if needed.
Definition eda_text.cpp:346
bool IsBold() const
Definition eda_text.h:184
void SetHyperlink(wxString aLink)
Definition eda_text.h:402
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:203
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:281
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:310
int GetTextThickness() const
Definition eda_text.h:128
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:318
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:510
VECTOR2I GetTextSize() const
Definition eda_text.h:261
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:420
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:1451
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:49
int GetUnitCount() const override
static bool ParseBusGroup(const wxString &aGroup, wxString *name, std::vector< wxString > *aMemberList)
Parse a bus group label into the name and a list of components.
A singleton reporter that reports to nowhere.
Definition reporter.h:216
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.
These are loaded from Eeschema settings but then overwritten by the project settings.
SCHEMATIC_SETTINGS & Settings() const
static TOOL_ACTION rotateCCW
static TOOL_ACTION placeClassLabel
Definition sch_actions.h:79
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:80
static TOOL_ACTION changeSymbols
static TOOL_ACTION updateSymbol
static TOOL_ACTION autoplaceFields
static TOOL_ACTION changeSymbol
static TOOL_ACTION ddAddImage
static TOOL_ACTION properties
static TOOL_ACTION editReference
static TOOL_ACTION placeHierLabel
Definition sch_actions.h:81
static TOOL_ACTION placeLabel
Definition sch_actions.h:78
static TOOL_ACTION toText
static TOOL_ACTION toHLabel
static TOOL_ACTION rotateCW
static TOOL_ACTION importSheet
Definition sch_actions.h:87
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 cycleBodyStyle
static TOOL_ACTION mirrorH
static TOOL_ACTION setExcludeFromSimulation
static TOOL_ACTION ddAppendFile
static TOOL_ACTION placeSchematicText
Definition sch_actions.h:92
static TOOL_ACTION toTextBox
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
Object to handle a bitmap image that can be inserted in a schematic.
Definition sch_bitmap.h:40
static const std::vector< KICAD_T > FieldOwners
static const std::vector< KICAD_T > DeletableItems
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
static SELECTION_CONDITION SingleMultiFunctionPin
static SELECTION_CONDITION SingleSymbol
static SELECTION_CONDITION MultipleSymbolsOrPower
static SELECTION_CONDITION AllPinsOrSheetPins
static SELECTION_CONDITION SingleSymbolOrPower
static SELECTION_CONDITION SingleMultiUnitSymbol
static SELECTION_CONDITION SingleMultiBodyStyleSymbol
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
bool IsNet() const
Tool responsible for drawing/placing items (symbols, wires, buses, labels, etc.).
Schematic editor (Eeschema) main window.
void ShowSchematicSetupDialog(const wxString &aInitialPage=wxEmptyString)
EDA_ITEM * ResolveItem(const KIID &aId, bool aAllowNullptrReturn=false) const override
Fetch an item by KIID.
const wxString & GetHighlightedConnection() const
wxString FixERCErrorMenuText(const std::shared_ptr< RC_ITEM > &aERCItem)
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
static const std::vector< KICAD_T > RotatableItems
int SwapUnitLabels(const TOOL_EVENT &aEvent)
int EditField(const TOOL_EVENT &aEvent)
int EditPageNumber(const TOOL_EVENT &aEvent)
int DoDelete(const TOOL_EVENT &aEvent)
bool Init() override
Init() is called once upon a registration of the tool.
int CleanupSheetPins(const TOOL_EVENT &aEvent)
void EditProperties(EDA_ITEM *aItem)
void editFieldText(SCH_FIELD *aField)
int SwapPins(const TOOL_EVENT &aEvent)
int Mirror(const TOOL_EVENT &aEvent)
int GlobalEdit(const TOOL_EVENT &aEvent)
Delete the selected items, or the item under the cursor.
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)
void collectUnits(const SCH_SELECTION &aSelection, std::set< std::pair< SCH_SYMBOL *, SCH_SCREEN * > > &aCollectedUnits)
Set up handlers for various events.
int DdAppendFile(const TOOL_EVENT &aEvent)
Drag and drop.
int JustifyText(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 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:116
wxString GetCanonicalName() const
Get a non-language-specific name for a field which can be used for storage, variable look-up,...
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
VECTOR2I GetParentPosition() const
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:52
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:167
virtual bool IsConnectable() const
Definition sch_item.h:507
virtual void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo)
Definition sch_item.h:609
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode)
Definition sch_item.h:611
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:250
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:244
int GetBodyStyle() const
Definition sch_item.h:247
virtual void MirrorHorizontally(int aCenter)
Mirror item horizontally about aCenter.
Definition sch_item.h:387
virtual void Move(const VECTOR2I &aMoveVector)
Move the item by aMoveVector to a new position.
Definition sch_item.h:379
int GetUnit() const
Definition sch_item.h:238
void SetConnectivityDirty(bool aDirty=true)
Definition sch_item.h:570
void SetFieldsAutoplaced(AUTOPLACE_ALGO aAlgo)
Definition sch_item.h:607
virtual void Rotate(const VECTOR2I &aCenter, bool aRotateCCW)
Rotate the item around aCenter 90 degrees in the clockwise direction.
Definition sch_item.h:403
AUTOPLACE_ALGO GetFieldsAutoplaced() const
Return whether the fields have been automatically placed.
Definition sch_item.h:606
wxString GetClass() const override
Return the class name.
Definition sch_item.h:177
bool IsGroupableType() const
Definition sch_item.cpp:105
virtual std::vector< VECTOR2I > GetConnectionPoints() const
Add all the connection points for this item to aPoints.
Definition sch_item.h:522
bool IsType(const std::vector< KICAD_T > &aScanTypes) const override
Check whether the item is one of the listed types.
Definition sch_item.h:182
virtual void MirrorVertically(int aCenter)
Mirror item vertically about aCenter.
Definition sch_item.h:395
void AddFields(const std::vector< SCH_FIELD > &aFields)
Definition sch_label.h:225
void MirrorHorizontally(int aCenter) override
Mirror item horizontally about aCenter.
SPIN_STYLE GetSpinStyle() const
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:181
LABEL_FLAG_SHAPE GetShape() const
Definition sch_label.h:180
void MirrorVertically(int aCenter) override
Mirror item vertically about aCenter.
std::vector< SCH_FIELD > & GetFields()
Definition sch_label.h:212
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:42
void Rotate(const VECTOR2I &aCenter, bool aRotateCCW) override
Rotate the item around aCenter 90 degrees in the clockwise direction.
Definition sch_line.cpp:408
VECTOR2I GetEndPoint() const
Definition sch_line.h:148
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
const std::map< wxString, ALT > & GetAlternates() const
Definition sch_pin.h:160
bool IsConnectable() const override
Definition sch_pin.h:313
const wxString & GetName() const
Definition sch_pin.cpp:401
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:728
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.
const wxString & GetFileName() const
Definition sch_screen.h:152
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:86
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void BuildSheetList(SCH_SHEET *aSheet, bool aCheckIntegrity)
Build the list of sheets and their sheet path from aSheet.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
wxString PathHumanReadable(bool aUseShortRootName=true, bool aStripTrailingSeparator=false) const
Return the sheet path in a human readable form made from the sheet names.
SCH_SCREEN * LastScreen()
wxString GetPageNumber() const
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
size_t size() const
Forwarded method from std::vector.
void pop_back()
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
SHEET_SIDE GetSide() const
void SetSide(SHEET_SIDE aEdge)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:47
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:347
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:341
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:116
const BOX2I GetBodyBoundingBox() const
Return a bounding box for the sheet body but not the fields.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
void Rotate(const VECTOR2I &aCenter, bool aRotateCCW) override
Rotate the item around aCenter 90 degrees in the clockwise direction.
Schematic symbol object.
Definition sch_symbol.h:76
wxString GetUnitDisplayName(int aUnit, bool aLabel) const override
Return the display name for a given unit aUnit.
bool IsAnnotated(const SCH_SHEET_PATH *aSheet) const
Check if the symbol has a valid annotation (reference) for the given sheet path.
bool IsMultiBodyStyle() const override
Definition sch_symbol.h:264
void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo) override
Automatically orient all the fields in the symbol.
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
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
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:66
int GetLegacyTextMargin() const
int GetMarginLeft() const
Definition sch_textbox.h:63
int GetMarginRight() const
Definition sch_textbox.h:65
int GetMarginTop() const
Definition sch_textbox.h:64
virtual void Rotate90(bool aClockwise)
Definition sch_text.cpp:210
virtual void MirrorSpinStyle(bool aLeftRight)
Definition sch_text.cpp:222
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)
Definition selection.cpp:42
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:89
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition selection.h:105
EDA_ITEM * Front() const
Definition selection.h:177
int Size() const
Returns the number of selected parts.
Definition selection.h:121
std::deque< EDA_ITEM * > & Items()
Definition selection.h:182
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:115
bool HasReferencePoint() const
Definition selection.h:216
size_t CountType(KICAD_T aType) const
unsigned CCWRotationsTo(const SPIN_STYLE &aOther) const
Get CCW rotation needed to get to the given spin style.
The symbol library editor main window.
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:44
KIGFX::VIEW * getView() const
Definition tool_base.cpp:38
Generic, UI-independent tool event.
Definition tool_event.h:171
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition tool_event.h:392
COMMIT * Commit() const
Definition tool_event.h:283
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:473
void Go(int(SCH_EDIT_FRAME::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
@ CHT_ADD
Definition commit.h:42
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:259
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_EDIT_LIBRARY_SYMBOL
@ SYMBOL_PROPS_EDIT_OK
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:408
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:407
@ RECURSE
Definition eda_item.h: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.
@ NO_FILL
Definition eda_shape.h:57
@ ID_POPUP_SCH_PIN_TRICKS_HIER_LABEL
@ ID_POPUP_SCH_PIN_TRICKS_WIRE
@ ID_POPUP_SCH_ALT_PIN_FUNCTION
@ ID_POPUP_SCH_SELECT_UNIT1
Definition eeschema_id.h:90
@ ID_POPUP_SCH_SELECT_UNIT
Definition eeschema_id.h:89
@ ID_POPUP_SCH_SELECT_BODY_STYLE
Definition eeschema_id.h:99
@ ID_POPUP_SCH_PLACE_UNIT1
Definition eeschema_id.h:96
@ ID_POPUP_SCH_SELECT_BODY_STYLE1
@ ID_POPUP_SCH_PIN_TRICKS_NET_LABEL
@ ID_POPUP_SCH_PIN_TRICKS_NO_CONNECT
@ ID_POPUP_SCH_SELECT_UNIT_END
Definition eeschema_id.h:93
@ 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_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:35
@ LAYER_NOTES
Definition layer_ids.h:467
@ LAYER_SCHEMATIC_DRAWINGSHEET
Definition layer_ids.h:496
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:83
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:96
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:100
#define _HKI(x)
Definition page_info.cpp:44
see class PGM_BASE
CITER next(CITER it)
Definition ptree.cpp:124
void CollectOtherUnits(const wxString &aRef, int aUnit, const LIB_ID &aLibId, SCH_SHEET_PATH &aSheet, std::vector< SCH_SYMBOL * > *otherUnits)
static SCH_LABEL_BASE * findSingleNetLabelForPin(SCH_PIN *aPin, CONNECTION_GRAPH *aGraph, const SCH_SHEET_PATH &aSheetPath)
static void swapFieldPositionsWithMatching(std::vector< SCH_FIELD > &aAFields, std::vector< SCH_FIELD > &aBFields, unsigned aFallbackRotationsCCW)
Swap the positions of the fields in the two lists, aAFields and aBFields, relative to their parent po...
SCH_CONDITIONS S_C
Class to handle a set of SCH_ITEMs.
AUTOPLACE_ALGO
Definition sch_item.h:68
@ AUTOPLACE_MANUAL
Definition sch_item.h:71
@ AUTOPLACE_NONE
Definition sch_item.h:69
@ AUTOPLACE_AUTO
Definition sch_item.h:70
@ BASE
Definition sch_item.h:59
LABEL_FLAG_SHAPE
Definition sch_label.h:99
@ L_UNSPECIFIED
Definition sch_label.h:104
@ F_ROUND
Definition sch_label.h:108
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
SHEET_SIDE
Define the edge of the sheet that the sheet pin is positioned.
std::set< wxString > GetSheetNamesFromPaths(const std::set< wxString > &aSheetPaths, const SCHEMATIC &aSchematic)
Get human-readable sheet names from a set of sheet paths, e.g.
bool SwapPinGeometry(SCH_PIN *aFirst, SCH_PIN *aSecond)
Swap the positions/lengths/etc.
std::set< int > GetUnplacedUnitsForSymbol(const SCH_SYMBOL &aSym)
Get a list of unplaced (i.e.
bool SymbolHasSheetInstances(const SCH_SYMBOL &aSymbol, const wxString &aCurrentProject, std::set< wxString > *aSheetPaths, std::set< wxString > *aProjectNames)
Returns true when the given symbol has instances, e.g.
std::vector< SCH_SYMBOL * > GetSameSymbolMultiUnitSelection(const SELECTION &aSel)
Validates and gathers a selection containing multiple symbol units that all belong to the same refere...
T * GetAppSettings(const char *aFilename)
wxString UnescapeString(const wxString &aSource)
wxString TitleCaps(const wxString &aString)
Capitalize the first letter in each word.
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_NETNAME
void AccumulateDescriptions(wxString &aDesc, const T &aItemCollection)
Build a comma-separated list from a collection of wxStrings.
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
@ SYM_MIRROR_Y
Definition symbol.h:44
@ SYM_MIRROR_X
Definition symbol.h:43
wxString GetDefaultFieldName(FIELD_T aFieldId, bool aTranslateForHI)
Return a default symbol field name for a mandatory field type.
#define DO_TRANSLATE
@ USER
The field ID hasn't been set yet; field is invalid.
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
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:229
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:78
@ SCH_GROUP_T
Definition typeinfo.h:177
@ SCH_TABLE_T
Definition typeinfo.h:169
@ SCH_LINE_T
Definition typeinfo.h:167
@ SCH_NO_CONNECT_T
Definition typeinfo.h:164
@ SCH_SYMBOL_T
Definition typeinfo.h:176
@ SCH_TABLECELL_T
Definition typeinfo.h:170
@ SCH_ITEM_LOCATE_WIRE_T
Definition typeinfo.h:190
@ SCH_FIELD_T
Definition typeinfo.h:154
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:175
@ SCH_LABEL_T
Definition typeinfo.h:171
@ SCH_SHEET_T
Definition typeinfo.h:179
@ SCH_ITEM_LOCATE_BUS_T
Definition typeinfo.h:191
@ SCH_MARKER_T
Definition typeinfo.h:162
@ SCH_SHAPE_T
Definition typeinfo.h:153
@ SCH_RULE_AREA_T
Definition typeinfo.h:174
@ SCH_HIER_LABEL_T
Definition typeinfo.h:173
@ SCH_BUS_BUS_ENTRY_T
Definition typeinfo.h:166
@ SCH_LABEL_LOCATE_ANY_T
Definition typeinfo.h:195
@ SCH_ITEM_LOCATE_GRAPHIC_LINE_T
Definition typeinfo.h:192
@ SCHEMATIC_T
Definition typeinfo.h:208
@ SCH_SHEET_PIN_T
Definition typeinfo.h:178
@ SCH_TEXT_T
Definition typeinfo.h:155
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:165
@ SCH_BITMAP_T
Definition typeinfo.h:168
@ SCH_TEXTBOX_T
Definition typeinfo.h:156
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:172
@ SCH_JUNCTION_T
Definition typeinfo.h:163
@ SCH_PIN_T
Definition typeinfo.h:157
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694