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 if( selection.GetSize() == 0 )
829 return 0;
830
831 SCH_ITEM* head = nullptr;
832 int principalItemCount = 0; // User-selected items (as opposed to connected wires)
833 VECTOR2I rotPoint;
834 bool moving = false;
835 SCH_COMMIT localCommit( m_toolMgr );
836 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
837
838 if( !commit )
839 commit = &localCommit;
840
841 for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
842 {
843 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
844
845 if( item->HasFlag( SELECTED_BY_DRAG ) )
846 continue;
847
848 principalItemCount++;
849
850 if( !head )
851 head = item;
852 }
853
854 if( head && head->IsMoving() )
855 moving = true;
856
857 if( principalItemCount == 1 )
858 {
859 if( moving && selection.HasReferencePoint() )
860 rotPoint = selection.GetReferencePoint();
861 else if( head->IsConnectable() )
862 rotPoint = head->GetPosition();
863 else
864 rotPoint = m_frame->GetNearestHalfGridPosition( head->GetBoundingBox().GetCenter() );
865
866 if( !moving )
867 commit->Modify( head, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
868
869 switch( head->Type() )
870 {
871 case SCH_SYMBOL_T:
872 {
873 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( head );
874
875 symbol->Rotate( rotPoint, !clockwise );
876
877 if( m_frame->eeconfig()->m_AutoplaceFields.enable )
878 {
879 AUTOPLACE_ALGO fieldsAutoplaced = symbol->GetFieldsAutoplaced();
880
881 if( fieldsAutoplaced == AUTOPLACE_AUTO || fieldsAutoplaced == AUTOPLACE_MANUAL )
882 symbol->AutoplaceFields( m_frame->GetScreen(), fieldsAutoplaced );
883 }
884
885 break;
886 }
887
888 case SCH_TEXT_T:
889 case SCH_LABEL_T:
891 case SCH_HIER_LABEL_T:
893 {
894 SCH_TEXT* textItem = static_cast<SCH_TEXT*>( head );
895 textItem->Rotate90( clockwise );
896 break;
897 }
898
899 case SCH_SHEET_PIN_T:
900 {
901 // Rotate pin within parent sheet
902 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( head );
903 SCH_SHEET* sheet = pin->GetParent();
904
905 pin->Rotate( sheet->GetBoundingBox().GetCenter(), !clockwise );
906
907 break;
908 }
909
910 case SCH_LINE_T:
911 {
912 SCH_LINE* line = static_cast<SCH_LINE*>( head );
913
914 // Equal checks for both and neither. We need this because on undo
915 // the item will have both flags cleared, but will be selected, so it is possible
916 // for the user to get a selected line with neither endpoint selected. We
917 // set flags to make sure Rotate() works when we call it.
918 if( line->HasFlag( STARTPOINT ) == line->HasFlag( ENDPOINT ) )
919 {
920 line->SetFlags( STARTPOINT | ENDPOINT );
921
922 // When we allow off grid items, the rotPoint should be set to the midpoint
923 // of the line to allow rotation around the center, and the next if
924 // should become an else-if
925 }
926
927 if( line->HasFlag( STARTPOINT ) )
928 rotPoint = line->GetEndPoint();
929 else if( line->HasFlag( ENDPOINT ) )
930 rotPoint = line->GetStartPoint();
931 }
932
934 case SCH_JUNCTION_T:
935 case SCH_NO_CONNECT_T:
938 head->Rotate( rotPoint, !clockwise );
939
940 break;
941
942 case SCH_FIELD_T:
943 {
944 SCH_FIELD* field = static_cast<SCH_FIELD*>( head );
945
946 if( field->GetTextAngle().IsHorizontal() )
948 else
950
951 // Now that we're moving a field, they're no longer autoplaced.
952 static_cast<SCH_ITEM*>( head->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
953
954 break;
955 }
956
957 case SCH_RULE_AREA_T:
958 case SCH_SHAPE_T:
959 case SCH_TEXTBOX_T:
960 head->Rotate( rotPoint, !clockwise );
961
962 break;
963
964 case SCH_GROUP_T:
965 {
966 // Rotate the group on itself. Groups do not have an anchor point.
967 SCH_GROUP* group = static_cast<SCH_GROUP*>( head );
968 rotPoint = m_frame->GetNearestHalfGridPosition( group->GetPosition() );
969
970 group->Rotate( rotPoint, !clockwise );
971
972 group->Move( rotPoint - m_frame->GetNearestHalfGridPosition( group->GetPosition() ) );
973
974 break;
975 }
976
977 case SCH_TABLE_T:
978 {
979 // Rotate the table on itself. Tables do not have an anchor point.
980 SCH_TABLE* table = static_cast<SCH_TABLE*>( head );
981 rotPoint = m_frame->GetNearestHalfGridPosition( table->GetCenter() );
982
983 table->Rotate( rotPoint, !clockwise );
984
985 table->Move( rotPoint - m_frame->GetNearestHalfGridPosition( table->GetCenter() ) );
986
987 break;
988 }
989
990 case SCH_BITMAP_T:
991 head->Rotate( rotPoint, !clockwise );
992
993 // The bitmap is cached in Opengl: clear the cache to redraw
995 break;
996
997 case SCH_SHEET_T:
998 {
999 // Rotate the sheet on itself. Sheets do not have an anchor point.
1000 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( head );
1001
1002 rotPoint = m_frame->GetNearestHalfGridPosition( sheet->GetRotationCenter() );
1003 sheet->Rotate( rotPoint, !clockwise );
1004
1005 break;
1006 }
1007
1008 default:
1009 UNIMPLEMENTED_FOR( head->GetClass() );
1010 }
1011
1012 m_frame->UpdateItem( head, false, true );
1013 }
1014 else
1015 {
1016 if( moving && selection.HasReferencePoint() )
1017 rotPoint = selection.GetReferencePoint();
1018 else
1019 rotPoint = m_frame->GetNearestHalfGridPosition( selection.GetCenter() );
1020 }
1021
1022 for( EDA_ITEM* edaItem : selection )
1023 {
1024 SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
1025
1026 // We've already rotated the user selected item if there was only one. We're just
1027 // here to rotate the ends of wires that were attached to it.
1028 if( principalItemCount == 1 && !item->HasFlag( SELECTED_BY_DRAG ) )
1029 continue;
1030
1031 if( !moving )
1032 commit->Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1033
1034 if( item->Type() == SCH_LINE_T )
1035 {
1036 SCH_LINE* line = (SCH_LINE*) item;
1037
1038 line->Rotate( rotPoint, !clockwise );
1039 }
1040 else if( item->Type() == SCH_SHEET_PIN_T )
1041 {
1042 if( item->GetParent()->IsSelected() )
1043 {
1044 // parent will rotate us
1045 }
1046 else
1047 {
1048 // rotate within parent
1049 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
1050 SCH_SHEET* sheet = pin->GetParent();
1051
1052 pin->Rotate( sheet->GetBodyBoundingBox().GetCenter(), !clockwise );
1053 }
1054 }
1055 else if( item->Type() == SCH_FIELD_T )
1056 {
1057 if( item->GetParent()->IsSelected() )
1058 {
1059 // parent will rotate us
1060 }
1061 else
1062 {
1063 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
1064
1065 field->Rotate( rotPoint, !clockwise );
1066
1067 // Now that we're moving a field, they're no longer autoplaced.
1068 static_cast<SCH_ITEM*>( field->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
1069 }
1070 }
1071 else if( item->Type() == SCH_TABLE_T )
1072 {
1073 SCH_TABLE* table = static_cast<SCH_TABLE*>( item );
1074 VECTOR2I beforeCenter = table->GetCenter();
1075
1076 table->Rotate( rotPoint, !clockwise );
1077 RotatePoint( beforeCenter, rotPoint, clockwise ? -ANGLE_90 : ANGLE_90 );
1078
1079 table->Move( beforeCenter - table->GetCenter() );
1080 }
1081 else
1082 {
1083 item->Rotate( rotPoint, !clockwise );
1084 }
1085
1086 m_frame->UpdateItem( item, false, true );
1087 updateItem( item, true );
1088 }
1089
1090 if( moving )
1091 {
1092 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1093 }
1094 else
1095 {
1096 SCH_SELECTION selectionCopy = selection;
1097
1098 if( selection.IsHover() )
1099 m_toolMgr->RunAction( ACTIONS::selectionClear );
1100
1102 lwbTool->TrimOverLappingWires( commit, &selectionCopy );
1103 lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy );
1104
1105 m_frame->Schematic().CleanUp( commit );
1106
1107 if( !localCommit.Empty() )
1108 localCommit.Push( _( "Rotate" ) );
1109 }
1110
1111 return 0;
1112}
1113
1114
1116{
1117 SCH_SELECTION& selection = m_selectionTool->RequestSelection( RotatableItems, false, false );
1118
1119 if( selection.GetSize() == 0 )
1120 return 0;
1121
1122 bool vertical = ( aEvent.Matches( SCH_ACTIONS::mirrorV.MakeEvent() ) );
1123 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
1124 bool connections = false;
1125 bool moving = item->IsMoving();
1126 SCH_COMMIT localCommit( m_toolMgr );
1127 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
1128
1129 if( !commit )
1130 commit = &localCommit;
1131
1132 if( selection.GetSize() == 1 )
1133 {
1134 if( !moving )
1135 commit->Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1136
1137 switch( item->Type() )
1138 {
1139 case SCH_SYMBOL_T:
1140 {
1141 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1142
1143 if( vertical )
1144 symbol->SetOrientation( SYM_MIRROR_X );
1145 else
1146 symbol->SetOrientation( SYM_MIRROR_Y );
1147
1149 break;
1150 }
1151
1152 case SCH_TEXT_T:
1153 case SCH_LABEL_T:
1154 case SCH_GLOBAL_LABEL_T:
1155 case SCH_HIER_LABEL_T:
1157 {
1158 SCH_TEXT* textItem = static_cast<SCH_TEXT*>( item );
1159 textItem->MirrorSpinStyle( !vertical );
1160 break;
1161 }
1162
1163 case SCH_SHEET_PIN_T:
1164 {
1165 // mirror within parent sheet
1166 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
1167 SCH_SHEET* sheet = pin->GetParent();
1168
1169 if( vertical )
1170 pin->MirrorVertically( sheet->GetBoundingBox().GetCenter().y );
1171 else
1172 pin->MirrorHorizontally( sheet->GetBoundingBox().GetCenter().x );
1173
1174 break;
1175 }
1176
1177 case SCH_FIELD_T:
1178 {
1179 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
1180
1181 if( vertical )
1183 else
1185
1186 // Now that we're re-justifying a field, they're no longer autoplaced.
1187 static_cast<SCH_ITEM*>( field->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
1188
1189 break;
1190 }
1191
1192 case SCH_BITMAP_T:
1193 if( vertical )
1194 item->MirrorVertically( item->GetPosition().y );
1195 else
1196 item->MirrorHorizontally( item->GetPosition().x );
1197
1198 // The bitmap is cached in Opengl: clear the cache to redraw
1200 break;
1201
1202 case SCH_SHEET_T:
1203 {
1204 // Mirror the sheet on itself. Sheets do not have a anchor point.
1205 VECTOR2I mirrorPoint = m_frame->GetNearestHalfGridPosition( item->GetBoundingBox().Centre() );
1206
1207 if( vertical )
1208 item->MirrorVertically( mirrorPoint.y );
1209 else
1210 item->MirrorHorizontally( mirrorPoint.x );
1211
1212 break;
1213 }
1214
1215 default:
1216 if( vertical )
1217 item->MirrorVertically( item->GetPosition().y );
1218 else
1219 item->MirrorHorizontally( item->GetPosition().x );
1220
1221 break;
1222 }
1223
1224 connections = item->IsConnectable();
1225 m_frame->UpdateItem( item, false, true );
1226 }
1227 else if( selection.GetSize() > 1 )
1228 {
1229 VECTOR2I mirrorPoint = m_frame->GetNearestHalfGridPosition( selection.GetCenter() );
1230
1231 for( EDA_ITEM* edaItem : selection )
1232 {
1233 item = static_cast<SCH_ITEM*>( edaItem );
1234
1235 if( !moving )
1236 commit->Modify( item, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1237
1238 if( item->Type() == SCH_SHEET_PIN_T )
1239 {
1240 if( item->GetParent()->IsSelected() )
1241 {
1242 // parent will mirror us
1243 }
1244 else
1245 {
1246 // mirror within parent sheet
1247 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
1248 SCH_SHEET* sheet = pin->GetParent();
1249
1250 if( vertical )
1251 pin->MirrorVertically( sheet->GetBoundingBox().GetCenter().y );
1252 else
1253 pin->MirrorHorizontally( sheet->GetBoundingBox().GetCenter().x );
1254 }
1255 }
1256 else if( item->Type() == SCH_FIELD_T )
1257 {
1258 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
1259
1260 if( vertical )
1262 else
1264
1265 // Now that we're re-justifying a field, they're no longer autoplaced.
1266 static_cast<SCH_ITEM*>( field->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
1267 }
1268 else
1269 {
1270 if( vertical )
1271 item->MirrorVertically( mirrorPoint.y );
1272 else
1273 item->MirrorHorizontally( mirrorPoint.x );
1274 }
1275
1276 connections |= item->IsConnectable();
1277 m_frame->UpdateItem( item, false, true );
1278 }
1279 }
1280
1281 // Update R-Tree for modified items
1282 for( EDA_ITEM* selected : selection )
1283 updateItem( selected, true );
1284
1285 if( item->IsMoving() )
1286 {
1287 m_toolMgr->RunAction( ACTIONS::refreshPreview );
1288 }
1289 else
1290 {
1291 SCH_SELECTION selectionCopy = selection;
1292
1293 if( selection.IsHover() )
1294 m_toolMgr->RunAction( ACTIONS::selectionClear );
1295
1296 if( connections )
1297 {
1299 lwbTool->TrimOverLappingWires( commit, &selectionCopy );
1300 lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy );
1301
1302 m_frame->Schematic().CleanUp( commit );
1303 }
1304
1305 if( !localCommit.Empty() )
1306 localCommit.Push( _( "Mirror" ) );
1307 }
1308
1309 return 0;
1310}
1311
1321static void swapFieldPositionsWithMatching( std::vector<SCH_FIELD>& aAFields,
1322 std::vector<SCH_FIELD>& aBFields,
1323 unsigned aFallbackRotationsCCW )
1324{
1325 std::set<wxString> handledKeys;
1326
1327 const auto swapFieldTextProps = []( SCH_FIELD& aField, SCH_FIELD& bField )
1328 {
1329 const VECTOR2I aRelPos = aField.GetPosition() - aField.GetParentPosition();
1330 const GR_TEXT_H_ALIGN_T aTextJustifyH = aField.GetHorizJustify();
1331 const GR_TEXT_V_ALIGN_T aTextJustifyV = aField.GetVertJustify();
1332 const EDA_ANGLE aTextAngle = aField.GetTextAngle();
1333
1334 const VECTOR2I bRelPos = bField.GetPosition() - bField.GetParentPosition();
1335 const GR_TEXT_H_ALIGN_T bTextJustifyH = bField.GetHorizJustify();
1336 const GR_TEXT_V_ALIGN_T bTextJustifyV = bField.GetVertJustify();
1337 const EDA_ANGLE bTextAngle = bField.GetTextAngle();
1338
1339 aField.SetPosition( aField.GetParentPosition() + bRelPos );
1340 aField.SetHorizJustify( bTextJustifyH );
1341 aField.SetVertJustify( bTextJustifyV );
1342 aField.SetTextAngle( bTextAngle );
1343
1344 bField.SetPosition( bField.GetParentPosition() + aRelPos );
1345 bField.SetHorizJustify( aTextJustifyH );
1346 bField.SetVertJustify( aTextJustifyV );
1347 bField.SetTextAngle( aTextAngle );
1348 };
1349
1350 for( SCH_FIELD& aField : aAFields )
1351 {
1352 const wxString name = aField.GetCanonicalName();
1353
1354 auto it = std::find_if( aBFields.begin(), aBFields.end(),
1355 [name]( const SCH_FIELD& bField )
1356 {
1357 return bField.GetCanonicalName() == name;
1358 } );
1359
1360 if( it != aBFields.end() )
1361 {
1362 // We have a field with the same key in both labels
1363 SCH_FIELD& bField = *it;
1364 swapFieldTextProps( aField, bField );
1365 }
1366 else
1367 {
1368 // We only have this field in A, so just rotate it
1369 for( unsigned ii = 0; ii < aFallbackRotationsCCW; ii++ )
1370 {
1371 aField.Rotate( aField.GetParentPosition(), true );
1372 }
1373 }
1374
1375 // And keep track that we did this one
1376 handledKeys.insert( name );
1377 }
1378
1379 // Any fields in B that weren't in A weren't handled and need to be rotated
1380 // in reverse
1381 for( SCH_FIELD& bField : aBFields )
1382 {
1383 const wxString bName = bField.GetCanonicalName();
1384 if( handledKeys.find( bName ) == handledKeys.end() )
1385 {
1386 for( unsigned ii = 0; ii < aFallbackRotationsCCW; ii++ )
1387 {
1388 bField.Rotate( bField.GetParentPosition(), false );
1389 }
1390 }
1391 }
1392}
1393
1394
1396{
1397 SCH_SELECTION& selection = m_selectionTool->RequestSelection( SwappableItems );
1398 std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
1399
1400 if( selection.Size() < 2 )
1401 return 0;
1402
1403 // Sheet pins are special, we need to make sure if we have any sheet pins,
1404 // that we only have sheet pins, and that they have the same parent
1405 if( selection.CountType( SCH_SHEET_PIN_T ) > 0 )
1406 {
1407 if( !selection.OnlyContains( { SCH_SHEET_PIN_T } ) )
1408 return 0;
1409
1410 EDA_ITEM* parent = selection.Front()->GetParent();
1411
1412 for( EDA_ITEM* item : selection )
1413 {
1414 if( item->GetParent() != parent )
1415 return 0;
1416 }
1417 }
1418
1419 bool moving = selection.Front()->IsMoving();
1420 bool connections = false;
1421
1422 SCH_COMMIT localCommit( m_toolMgr );
1423 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
1424
1425 if( !commit )
1426 commit = &localCommit;
1427
1428 for( size_t i = 0; i < sorted.size() - 1; i++ )
1429 {
1430 SCH_ITEM* a = static_cast<SCH_ITEM*>( sorted[i] );
1431 SCH_ITEM* b = static_cast<SCH_ITEM*>( sorted[( i + 1 ) % sorted.size()] );
1432
1433 if( !moving )
1434 {
1435 commit->Modify( a, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1436 commit->Modify( b, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1437 }
1438
1439 VECTOR2I aPos = a->GetPosition(), bPos = b->GetPosition();
1440 std::swap( aPos, bPos );
1441
1442 // Sheet pins need to have their sides swapped before we change their
1443 // positions
1444 if( a->Type() == SCH_SHEET_PIN_T )
1445 {
1446 SCH_SHEET_PIN* aPin = static_cast<SCH_SHEET_PIN*>( a );
1447 SCH_SHEET_PIN* bPin = static_cast<SCH_SHEET_PIN*>( b );
1448 SHEET_SIDE aSide = aPin->GetSide(), bSide = bPin->GetSide();
1449 std::swap( aSide, bSide );
1450 aPin->SetSide( aSide );
1451 bPin->SetSide( bSide );
1452 }
1453
1454 a->SetPosition( aPos );
1455 b->SetPosition( bPos );
1456
1457 if( a->Type() == b->Type() )
1458 {
1459 switch( a->Type() )
1460 {
1461 case SCH_LABEL_T:
1462 case SCH_GLOBAL_LABEL_T:
1463 case SCH_HIER_LABEL_T:
1465 {
1466 SCH_LABEL_BASE& aLabelBase = static_cast<SCH_LABEL_BASE&>( *a );
1467 SCH_LABEL_BASE& bLabelBase = static_cast<SCH_LABEL_BASE&>( *b );
1468
1469 const SPIN_STYLE aSpinStyle = aLabelBase.GetSpinStyle();
1470 const SPIN_STYLE bSpinStyle = bLabelBase.GetSpinStyle();
1471
1472 // First, swap the label orientations
1473 aLabelBase.SetSpinStyle( bSpinStyle );
1474 bLabelBase.SetSpinStyle( aSpinStyle );
1475
1476 // And swap the fields as best we can
1477 std::vector<SCH_FIELD>& aFields = aLabelBase.GetFields();
1478 std::vector<SCH_FIELD>& bFields = bLabelBase.GetFields();
1479
1480 const unsigned rotationsAtoB = aSpinStyle.CCWRotationsTo( bSpinStyle );
1481
1482 swapFieldPositionsWithMatching( aFields, bFields, rotationsAtoB );
1483 break;
1484 }
1485 case SCH_SYMBOL_T:
1486 {
1487 SCH_SYMBOL* aSymbol = static_cast<SCH_SYMBOL*>( a );
1488 SCH_SYMBOL* bSymbol = static_cast<SCH_SYMBOL*>( b );
1489 int aOrient = aSymbol->GetOrientation(), bOrient = bSymbol->GetOrientation();
1490 std::swap( aOrient, bOrient );
1491 aSymbol->SetOrientation( aOrient );
1492 bSymbol->SetOrientation( bOrient );
1493 break;
1494 }
1495 default: break;
1496 }
1497 }
1498
1499 connections |= a->IsConnectable();
1500 connections |= b->IsConnectable();
1501 m_frame->UpdateItem( a, false, true );
1502 m_frame->UpdateItem( b, false, true );
1503 }
1504
1505 if( moving )
1506 {
1507 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1508 }
1509 else
1510 {
1511 if( selection.IsHover() )
1512 m_toolMgr->RunAction( ACTIONS::selectionClear );
1513
1514 if( connections )
1515 m_frame->TestDanglingEnds();
1516 m_frame->OnModify();
1517
1518 if( !localCommit.Empty() )
1519 localCommit.Push( _( "Swap" ) );
1520 }
1521
1522 return 0;
1523}
1524
1525
1526/*
1527 * This command always works on the instance owned by the schematic, never directly on the
1528 * external library file. Pins that still reference their library definition are swapped by
1529 * touching that shared lib pin first; afterwards we call UpdatePins() so the schematic now owns a
1530 * cached copy with the new geometry. Pins that already have an instance-local copy simply swap in
1531 * place. In both cases the undo stack captures the modified pins (and the parent symbol) so the
1532 * user can revert the change. Saving the schematic writes the updated pin order into the sheet,
1533 * while the global symbol library remains untouched unless the user explicitly pushes it later.
1534 */
1536{
1537 wxCHECK( m_frame, 0 );
1538
1539 if( !m_frame->eeconfig()->m_Input.allow_unconstrained_pin_swaps )
1540 return 0;
1541
1542 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_PIN_T } );
1543 std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
1544
1545 if( selection.Size() < 2 )
1546 return 0;
1547
1548 EDA_ITEM* parent = selection.Front()->GetParent();
1549
1550 if( !parent || parent->Type() != SCH_SYMBOL_T )
1551 return 0;
1552
1553 SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( parent );
1554
1555 // All pins need to be on the same symbol
1556 for( EDA_ITEM* item : selection )
1557 {
1558 if( item->GetParent() != parent )
1559 return 0;
1560 }
1561
1562 std::set<wxString> sharedSheetPaths;
1563 std::set<wxString> sharedProjectNames;
1564
1565 if( SymbolHasSheetInstances( *parentSymbol, m_frame->Prj().GetProjectName(), &sharedSheetPaths,
1566 &sharedProjectNames ) )
1567 {
1568 // This will give us nice names for our project, but not when the sheet is shared across
1569 // multiple projects. But, in that case we bail early and just list the project names so it isn't an issue.
1570 std::set<wxString> friendlySheets;
1571
1572 if( !sharedSheetPaths.empty() )
1573 friendlySheets = GetSheetNamesFromPaths( sharedSheetPaths, m_frame->Schematic() );
1574
1575 if( !sharedProjectNames.empty() )
1576 {
1577 wxString projects = AccumulateDescriptions( sharedProjectNames );
1578
1579 if( projects.IsEmpty() )
1580 {
1581 m_frame->ShowInfoBarError(
1582 _( "Pin swaps are disabled for symbols shared across other projects. Duplicate the sheet to edit pins independently." ) );
1583 }
1584 else
1585 {
1586 m_frame->ShowInfoBarError(
1587 wxString::Format( _( "Pin swaps are disabled for symbols shared across other projects (%s). Duplicate the sheet to edit pins independently." ), projects ) );
1588 }
1589 }
1590 else if( !friendlySheets.empty() )
1591 {
1592 wxString sheets = AccumulateDescriptions( friendlySheets );
1593
1594 m_frame->ShowInfoBarError(
1595 wxString::Format( _( "Pin swaps are disabled for symbols used by multiple sheet instances (%s). Duplicate the sheet to edit pins independently." ), sheets ) );
1596 }
1597 else
1598 {
1599 m_frame->ShowInfoBarError(
1600 _( "Pin swaps are disabled for shared symbols. Duplicate the sheet to edit pins independently." ) );
1601 }
1602
1603 return 0;
1604 }
1605
1606 bool connections = false;
1607
1608 SCH_COMMIT localCommit( m_toolMgr );
1609 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
1610
1611 if( !commit )
1612 commit = &localCommit;
1613
1614 // Stage the parent symbol so undo/redo captures the cache copy that UpdatePins() may rebuild
1615 // after we touch any shared library pins.
1616 commit->Modify( parentSymbol, m_frame->GetScreen(), RECURSE_MODE::RECURSE ); // RECURSE is harmless here
1617
1618 bool swappedLibPins = false;
1619
1620 for( size_t i = 0; i < sorted.size() - 1; i++ )
1621 {
1622 SCH_PIN* aPin = static_cast<SCH_PIN*>( sorted[i] );
1623 SCH_PIN* bPin = static_cast<SCH_PIN*>( sorted[( i + 1 ) % sorted.size()] );
1624
1625 // Record both pins in the commit and swap their geometry. SwapPinGeometry returns true if
1626 // it had to operate on the shared library pins (meaning the schematic instance still
1627 // referenced them), in which case UpdatePins() below promotes the symbol to an instance
1628 // copy that reflects the new pin order.
1629 commit->Modify( aPin, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1630 commit->Modify( bPin, m_frame->GetScreen(), RECURSE_MODE::RECURSE );
1631
1632 swappedLibPins |= SwapPinGeometry( aPin, bPin );
1633
1634 connections |= aPin->IsConnectable();
1635 connections |= bPin->IsConnectable();
1636 m_frame->UpdateItem( aPin, false, true );
1637 m_frame->UpdateItem( bPin, false, true );
1638 }
1639
1640 if( swappedLibPins )
1641 parentSymbol->UpdatePins(); // clone the library data into the schematic cache with new geometry
1642
1643 // Refresh changed symbol in screen R-Tree / lib caches
1644 m_frame->UpdateItem( parentSymbol, false, true );
1645
1646 SCH_SELECTION selectionCopy = selection;
1647
1648 if( selection.IsHover() )
1649 m_toolMgr->RunAction( ACTIONS::selectionClear );
1650
1651 // Reconcile any wiring that was connected to the swapped pins so the schematic stays tidy and
1652 // the undo stack captures the resulting edits to wires and junctions.
1654 lwbTool->TrimOverLappingWires( commit, &selectionCopy );
1655 lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy );
1656
1657 m_frame->Schematic().CleanUp( commit );
1658
1659 if( connections )
1660 m_frame->TestDanglingEnds();
1661
1662 m_frame->OnModify();
1663
1664 if( !localCommit.Empty() )
1665 localCommit.Push( _( "Swap Pins" ) );
1666
1667 return 0;
1668}
1669
1670
1671// Used by SwapPinLabels() and SwapUnitLabels() to find the single net label connected to a pin
1673 const SCH_SHEET_PATH& aSheetPath )
1674{
1675 if( !aGraph || !aPin )
1676 return nullptr;
1677
1678 CONNECTION_SUBGRAPH* sg = aGraph->GetSubgraphForItem( aPin );
1679
1680 if( !sg )
1681 return nullptr;
1682
1683 const std::set<SCH_ITEM*>& items = sg->GetItems();
1684
1685 size_t pinCount = 0;
1686 SCH_LABEL_BASE* label = nullptr;
1687
1688 for( SCH_ITEM* item : items )
1689 {
1690 if( item->Type() == SCH_PIN_T )
1691 pinCount++;
1692
1693 switch( item->Type() )
1694 {
1695 case SCH_LABEL_T:
1696 case SCH_GLOBAL_LABEL_T:
1697 case SCH_HIER_LABEL_T:
1698 {
1699 SCH_CONNECTION* conn = item->Connection( &aSheetPath );
1700
1701 if( conn && conn->IsNet() )
1702 {
1703 if( label )
1704 return nullptr; // more than one label
1705
1706 label = static_cast<SCH_LABEL_BASE*>( item );
1707 }
1708
1709 break;
1710 }
1711 default: break;
1712 }
1713 }
1714
1715 if( pinCount != 1 )
1716 return nullptr;
1717
1718 return label;
1719}
1720
1721
1723{
1724 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_PIN_T } );
1725 std::vector<EDA_ITEM*> orderedPins = selection.GetItemsSortedBySelectionOrder();
1726
1727 if( orderedPins.size() < 2 )
1728 return 0;
1729
1730 CONNECTION_GRAPH* connectionGraph = m_frame->Schematic().ConnectionGraph();
1731
1732 const SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
1733
1734 std::vector<SCH_LABEL_BASE*> labels;
1735
1736 for( EDA_ITEM* item : orderedPins )
1737 {
1738 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
1739 SCH_LABEL_BASE* label = findSingleNetLabelForPin( pin, connectionGraph, sheetPath );
1740
1741 if( !label )
1742 {
1743 m_frame->ShowInfoBarError(
1744 _( "Each selected pin must have exactly one attached net label and no other pin connections." ) );
1745 return 0;
1746 }
1747
1748 labels.push_back( label );
1749 }
1750
1751 if( labels.size() >= 2 )
1752 {
1753 SCH_COMMIT commit( m_frame );
1754
1755 for( SCH_LABEL_BASE* lb : labels )
1756 commit.Modify( lb, m_frame->GetScreen() );
1757
1758 for( size_t i = 0; i < labels.size() - 1; ++i )
1759 {
1760 SCH_LABEL_BASE* a = labels[i];
1761 SCH_LABEL_BASE* b = labels[( i + 1 ) % labels.size()];
1762 wxString aText = a->GetText();
1763 wxString bText = b->GetText();
1764 a->SetText( bText );
1765 b->SetText( aText );
1766 }
1767
1768 commit.Push( _( "Swap Pin Labels" ) );
1769 }
1770
1771 return 0;
1772}
1773
1774
1776{
1777 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
1778 std::vector<SCH_SYMBOL*> selectedUnits = GetSameSymbolMultiUnitSelection( selection );
1779
1780 if( selectedUnits.size() < 2 )
1781 return 0;
1782
1783 CONNECTION_GRAPH* connectionGraph = m_frame->Schematic().ConnectionGraph();
1784
1785 const SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
1786
1787 // Build ordered label vectors (sorted by pin X/Y) for each selected unit
1788 std::vector<std::vector<SCH_LABEL_BASE*>> symbolLabelVectors;
1789
1790 for( SCH_SYMBOL* symbol : selectedUnits )
1791 {
1792 std::vector<std::pair<VECTOR2I, SCH_LABEL_BASE*>> byPos;
1793
1794 for( SCH_PIN* pin : symbol->GetPins( &sheetPath ) )
1795 {
1796 SCH_LABEL_BASE* label = findSingleNetLabelForPin( pin, connectionGraph, sheetPath );
1797
1798 if( !label )
1799 {
1800 m_frame->ShowInfoBarError( _( "Each pin of selected units must have exactly one attached net label and "
1801 "no other pin connections." ) );
1802 return 0;
1803 }
1804
1805 byPos.emplace_back( pin->GetPosition(), label );
1806 }
1807
1808 // Sort labels by pin position (X, then Y)
1809 std::sort( byPos.begin(), byPos.end(), []( const auto& a, const auto& b )
1810 {
1811 if( a.first.x != b.first.x )
1812 return a.first.x < b.first.x;
1813
1814 return a.first.y < b.first.y;
1815 } );
1816
1817 // Discard position, just keep the order
1818 std::vector<SCH_LABEL_BASE*> labels;
1819
1820 for( const auto& pr : byPos )
1821 labels.push_back( pr.second );
1822
1823 symbolLabelVectors.push_back( labels );
1824 }
1825
1826 // All selected units are guaranteed to have identical pin counts by GetSameSymbolMultiUnitSelection()
1827 const size_t pinCount = symbolLabelVectors.front().size();
1828
1829 // Perform cyclic swap of labels across all selected symbols, per pin index
1830 SCH_COMMIT commit( m_frame );
1831
1832 for( size_t pin = 0; pin < pinCount; pin++ )
1833 {
1834 for( auto& vec : symbolLabelVectors )
1835 commit.Modify( vec[pin], m_frame->GetScreen() );
1836
1837 wxString carry = symbolLabelVectors.back()[pin]->GetText();
1838
1839 for( size_t i = 0; i < symbolLabelVectors.size(); i++ )
1840 {
1841 SCH_LABEL_BASE* lbl = symbolLabelVectors[i][pin];
1842 wxString next = lbl->GetText();
1843 lbl->SetText( carry );
1844 carry = next;
1845 }
1846 }
1847
1848 if( !commit.Empty() )
1849 commit.Push( _( "Swap Unit Labels" ) );
1850
1851 return 0;
1852}
1853
1854
1856{
1857 const std::vector<std::unique_ptr<SCH_ITEM>>& sourceItems = m_frame->GetRepeatItems();
1858
1859 if( sourceItems.empty() )
1860 return 0;
1861
1862 m_toolMgr->RunAction( ACTIONS::selectionClear );
1863
1864 SCH_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1865 SCH_COMMIT commit( m_toolMgr );
1866 SCH_SELECTION newItems;
1867
1868 for( const std::unique_ptr<SCH_ITEM>& item : sourceItems )
1869 {
1870 SCH_ITEM* newItem = item->Duplicate( IGNORE_PARENT_GROUP );
1871 bool restore_state = false;
1872
1873 // Ensure newItem has a suitable parent: the current screen, because an item from
1874 // a list of items to repeat must be attached to this current screen
1875 newItem->SetParent( m_frame->GetScreen() );
1876
1877 if( SCH_GROUP* enteredGroup = selectionTool->GetEnteredGroup() )
1878 {
1879 if( newItem->IsGroupableType() )
1880 {
1881 commit.Modify( enteredGroup, m_frame->GetScreen(), RECURSE_MODE::NO_RECURSE );
1882 enteredGroup->AddItem( newItem );
1883 }
1884 }
1885
1886 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( newItem ) )
1887 {
1888 // If incrementing tries to go below zero, tell user why the value is repeated
1889 if( EESCHEMA_SETTINGS* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ) )
1890 {
1891 if( !label->IncrementLabel( cfg->m_Drawing.repeat_label_increment ) )
1892 m_frame->ShowInfoBarWarning( _( "Label value cannot go below zero" ), true );
1893 }
1894 }
1895
1896 // If cloning a symbol then put into 'move' mode.
1897 if( newItem->Type() == SCH_SYMBOL_T )
1898 {
1899 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( true );
1900 newItem->Move( cursorPos - newItem->GetPosition() );
1901 }
1902 else if( EESCHEMA_SETTINGS* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ) )
1903 {
1904 newItem->Move( VECTOR2I( schIUScale.MilsToIU( cfg->m_Drawing.default_repeat_offset_x ),
1905 schIUScale.MilsToIU( cfg->m_Drawing.default_repeat_offset_y ) ) );
1906 }
1907
1908 // If cloning a sheet, check that we aren't going to create recursion
1909 if( newItem->Type() == SCH_SHEET_T )
1910 {
1911 SCH_SHEET_PATH* currentSheet = &m_frame->GetCurrentSheet();
1912 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( newItem );
1913
1914 if( m_frame->CheckSheetForRecursion( sheet, currentSheet ) )
1915 {
1916 // Clear out the filename so that the user can pick a new one
1917 const wxString originalFileName = sheet->GetFileName();
1918 const wxString originalScreenFileName = sheet->GetScreen()->GetFileName();
1919
1920 sheet->SetFileName( wxEmptyString );
1921 sheet->GetScreen()->SetFileName( wxEmptyString );
1922 restore_state = !m_frame->EditSheetProperties( sheet, currentSheet );
1923
1924 if( restore_state )
1925 {
1926 sheet->SetFileName( originalFileName );
1927 sheet->GetScreen()->SetFileName( originalScreenFileName );
1928 }
1929 }
1930 }
1931
1932 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, newItem );
1933 newItem->SetFlags( IS_NEW );
1934 m_frame->AddToScreen( newItem, m_frame->GetScreen() );
1935 commit.Added( newItem, m_frame->GetScreen() );
1936
1937 if( newItem->Type() == SCH_SYMBOL_T )
1938 {
1939 SCHEMATIC_SETTINGS& projSettings = m_frame->Schematic().Settings();
1940 int annotateStartNum = projSettings.m_AnnotateStartNum;
1941 ANNOTATE_ORDER_T annotateOrder = static_cast<ANNOTATE_ORDER_T>( projSettings.m_AnnotateSortOrder );
1942 ANNOTATE_ALGO_T annotateAlgo = static_cast<ANNOTATE_ALGO_T>( projSettings.m_AnnotateMethod );
1943
1944 if( m_frame->eeconfig()->m_AnnotatePanel.automatic )
1945 {
1946 static_cast<SCH_SYMBOL*>( newItem )->ClearAnnotation( nullptr, false );
1947 NULL_REPORTER reporter;
1948 m_frame->AnnotateSymbols( &commit, ANNOTATE_SELECTION,
1949 annotateOrder,
1950 annotateAlgo, true /* recursive */,
1951 annotateStartNum, false, false, reporter );
1952 }
1953
1954 // Annotation clears the selection so re-add the item
1955 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, newItem );
1956
1957 restore_state = !m_toolMgr->RunSynchronousAction( SCH_ACTIONS::move, &commit );
1958 }
1959
1960 if( restore_state )
1961 {
1962 commit.Revert();
1963 }
1964 else
1965 {
1966 newItems.Add( newItem );
1967
1969 lwbTool->TrimOverLappingWires( &commit, &newItems );
1970 lwbTool->AddJunctionsIfNeeded( &commit, &newItems );
1971
1972 m_frame->Schematic().CleanUp( &commit );
1973 commit.Push( _( "Repeat Item" ) );
1974 }
1975 }
1976
1977 if( !newItems.Empty() )
1978 m_frame->SaveCopyForRepeatItem( static_cast<SCH_ITEM*>( newItems[0] ) );
1979
1980 for( size_t ii = 1; ii < newItems.GetSize(); ++ii )
1981 m_frame->AddCopyForRepeatItem( static_cast<SCH_ITEM*>( newItems[ii] ) );
1982
1983 return 0;
1984}
1985
1986
1988{
1989 SCH_SCREEN* screen = m_frame->GetScreen();
1990 std::deque<EDA_ITEM*> items = m_selectionTool->RequestSelection( SCH_COLLECTOR::DeletableItems ).GetItems();
1991 SCH_COMMIT commit( m_toolMgr );
1992 std::vector<VECTOR2I> pts;
1993 bool updateHierarchy = false;
1994
1995 if( items.empty() )
1996 return 0;
1997
1998 // Don't leave a freed pointer in the selection
1999 m_toolMgr->RunAction( ACTIONS::selectionClear );
2000
2001 for( EDA_ITEM* item : items )
2002 item->ClearFlags( STRUCT_DELETED );
2003
2004 for( EDA_ITEM* item : items )
2005 {
2006 SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( item );
2007
2008 if( !sch_item )
2009 continue;
2010
2011 if( sch_item->IsConnectable() )
2012 {
2013 std::vector<VECTOR2I> tmp_pts = sch_item->GetConnectionPoints();
2014 pts.insert( pts.end(), tmp_pts.begin(), tmp_pts.end() );
2015 }
2016
2017 if( sch_item->Type() == SCH_JUNCTION_T )
2018 {
2019 sch_item->SetFlags( STRUCT_DELETED );
2020 // clean up junctions at the end
2021 }
2022 else if( sch_item->Type() == SCH_SHEET_PIN_T )
2023 {
2024 SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) sch_item;
2025 SCH_SHEET* sheet = pin->GetParent();
2026
2027 if( !alg::contains( items, sheet ) )
2028 {
2029 commit.Modify( sheet, m_frame->GetScreen() );
2030 sheet->RemovePin( pin );
2031 }
2032 }
2033 else if( sch_item->Type() == SCH_FIELD_T )
2034 {
2035 // Hide field
2036 commit.Modify( item, m_frame->GetScreen() );
2037 static_cast<SCH_FIELD*>( sch_item )->SetVisible( false );
2038 }
2039 else if( sch_item->Type() == SCH_TABLECELL_T )
2040 {
2041 // Clear contents of table cell
2042 commit.Modify( item, m_frame->GetScreen() );
2043 static_cast<SCH_TABLECELL*>( sch_item )->SetText( wxEmptyString );
2044 }
2045 else if( sch_item->Type() == SCH_RULE_AREA_T )
2046 {
2047 sch_item->SetFlags( STRUCT_DELETED );
2048 commit.Remove( item, m_frame->GetScreen() );
2049 }
2050 else if( sch_item->Type() == SCH_GROUP_T )
2051 {
2052 // Groups need to delete their children
2053 sch_item->RunOnChildren(
2054 [&]( SCH_ITEM* aChild )
2055 {
2056 aChild->SetFlags( STRUCT_DELETED );
2057 commit.Remove( aChild, m_frame->GetScreen() );
2058 },
2060
2061 sch_item->SetFlags( STRUCT_DELETED );
2062 commit.Remove( sch_item, m_frame->GetScreen() );
2063 }
2064 else
2065 {
2066 sch_item->SetFlags( STRUCT_DELETED );
2067 commit.Remove( item, m_frame->GetScreen() );
2068 updateHierarchy |= ( sch_item->Type() == SCH_SHEET_T );
2069 }
2070 }
2071
2072 for( const VECTOR2I& point : pts )
2073 {
2074 SCH_ITEM* junction = screen->GetItem( point, 0, SCH_JUNCTION_T );
2075
2076 if( !junction )
2077 continue;
2078
2079 if( junction->HasFlag( STRUCT_DELETED ) || !screen->IsExplicitJunction( point ) )
2080 m_frame->DeleteJunction( &commit, junction );
2081 }
2082
2083 commit.Push( _( "Delete" ) );
2084
2085 if( updateHierarchy )
2086 m_frame->UpdateHierarchyNavigator();
2087
2088 return 0;
2089}
2090
2091
2093{
2094 KICAD_T parentType = aField->GetParent() ? aField->GetParent()->Type() : SCHEMATIC_T;
2095 SCH_COMMIT commit( m_toolMgr );
2096
2097 // Save old symbol in undo list if not already in edit, or moving.
2098 if( aField->GetEditFlags() == 0 ) // i.e. not edited, or moved
2099 commit.Modify( aField, m_frame->GetScreen() );
2100
2101 if( parentType == SCH_SYMBOL_T && aField->GetId() == FIELD_T::REFERENCE )
2102 static_cast<SCH_ITEM*>( aField->GetParent() )->SetConnectivityDirty();
2103
2104 wxString caption;
2105
2106 // Use title caps for mandatory fields. "Edit Sheet name Field" looks dorky.
2107 if( aField->IsMandatory() )
2108 {
2109 wxString fieldName = GetDefaultFieldName( aField->GetId(), DO_TRANSLATE );
2110 caption.Printf( _( "Edit %s Field" ), TitleCaps( fieldName ) );
2111 }
2112 else
2113 {
2114 caption.Printf( _( "Edit '%s' Field" ), aField->GetName() );
2115 }
2116
2117 DIALOG_FIELD_PROPERTIES dlg( m_frame, caption, aField );
2118
2119 // The footprint field dialog can invoke a KIWAY_PLAYER so we must use a quasi-modal
2120 if( dlg.ShowQuasiModal() != wxID_OK )
2121 return;
2122
2123 dlg.UpdateField( &commit, aField, &m_frame->GetCurrentSheet() );
2124
2125 if( m_frame->eeconfig()->m_AutoplaceFields.enable || parentType == SCH_SHEET_T )
2126 {
2127 SCH_ITEM* parent = static_cast<SCH_ITEM*>( aField->GetParent() );
2128 AUTOPLACE_ALGO fieldsAutoplaced = parent->GetFieldsAutoplaced();
2129
2130 if( fieldsAutoplaced == AUTOPLACE_AUTO || fieldsAutoplaced == AUTOPLACE_MANUAL )
2131 parent->AutoplaceFields( m_frame->GetScreen(), fieldsAutoplaced );
2132 }
2133
2134 if( !commit.Empty() )
2135 commit.Push( caption );
2136}
2137
2138
2140{
2141 SCH_SELECTION sel = m_selectionTool->RequestSelection( { SCH_FIELD_T,
2143 SCH_PIN_T } );
2144
2145 if( sel.Size() != 1 )
2146 return 0;
2147
2148 bool clearSelection = sel.IsHover();
2149 EDA_ITEM* item = sel.Front();
2150
2151 if( item->Type() == SCH_FIELD_T )
2152 {
2153 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
2154
2155 if( ( aEvent.IsAction( &SCH_ACTIONS::editReference ) && field->GetId() != FIELD_T::REFERENCE )
2156 || ( aEvent.IsAction( &SCH_ACTIONS::editValue ) && field->GetId() != FIELD_T::VALUE )
2157 || ( aEvent.IsAction( &SCH_ACTIONS::editFootprint ) && field->GetId() != FIELD_T::FOOTPRINT ) )
2158 {
2159 item = field->GetParentSymbol();
2160
2161 m_selectionTool->ClearSelection( true );
2162
2163 // If the field to edit is not a symbol field, we cannot edit the ref, value or footprint
2164 if( item == nullptr )
2165 return 0;
2166
2167 m_selectionTool->AddItemToSel( item );
2168 }
2169 }
2170
2171 if( item->Type() == SCH_SYMBOL_T )
2172 {
2173 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2174
2175 if( aEvent.IsAction( &SCH_ACTIONS::editReference ) )
2176 {
2178 }
2179 else if( aEvent.IsAction( &SCH_ACTIONS::editValue ) )
2180 {
2182 }
2183 else if( aEvent.IsAction( &SCH_ACTIONS::editFootprint ) )
2184 {
2185 if( !symbol->IsPower() )
2187 }
2188 }
2189 else if( item->Type() == SCH_FIELD_T )
2190 {
2191 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
2192
2193 editFieldText( field );
2194
2195 if( !field->IsVisible() )
2196 clearSelection = true;
2197 }
2198 else if( item->Type() == SCH_PIN_T )
2199 {
2200 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item->GetParent() );
2201
2202 if( symbol )
2203 {
2204 if( aEvent.IsAction( &SCH_ACTIONS::editReference ) )
2205 {
2207 }
2208 else if( aEvent.IsAction( &SCH_ACTIONS::editValue ) )
2209 {
2211 }
2212 else if( aEvent.IsAction( &SCH_ACTIONS::editFootprint ) )
2213 {
2214 if( !symbol->IsPower() )
2216 }
2217 }
2218 }
2219
2220 if( clearSelection )
2221 m_toolMgr->RunAction( ACTIONS::selectionClear );
2222
2223 return 0;
2224}
2225
2226
2228{
2229 SCH_SELECTION& selection = m_selectionTool->RequestSelection( RotatableItems );
2230 SCH_COMMIT commit( m_toolMgr );
2231 SCH_ITEM* head = static_cast<SCH_ITEM*>( selection.Front() );
2232 bool moving = head && head->IsMoving();
2233
2234 if( selection.Empty() )
2235 return 0;
2236
2237 std::vector<SCH_ITEM*> autoplaceItems;
2238
2239 for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
2240 {
2241 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
2242
2243 if( item->IsType( SCH_COLLECTOR::FieldOwners ) )
2244 autoplaceItems.push_back( item );
2245 else if( item->GetParent() && item->GetParent()->IsType( SCH_COLLECTOR::FieldOwners ) )
2246 autoplaceItems.push_back( static_cast<SCH_ITEM*>( item->GetParent() ) );
2247 }
2248
2249 for( SCH_ITEM* sch_item : autoplaceItems )
2250 {
2251 if( !moving && !sch_item->IsNew() )
2252 commit.Modify( sch_item, m_frame->GetScreen() );
2253
2254 sch_item->AutoplaceFields( m_frame->GetScreen(), AUTOPLACE_MANUAL );
2255
2256 updateItem( sch_item, true );
2257 }
2258
2259 if( moving )
2260 {
2261 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2262 }
2263 else
2264 {
2265 if( !commit.Empty() )
2266 commit.Push( _( "Autoplace Fields" ) );
2267
2268 if( selection.IsHover() )
2269 m_toolMgr->RunAction( ACTIONS::selectionClear );
2270 }
2271
2272 return 0;
2273}
2274
2275
2277{
2278 SCH_SYMBOL* selectedSymbol = nullptr;
2279 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
2280
2281 if( !selection.Empty() )
2282 selectedSymbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
2283
2285
2288
2289 DIALOG_CHANGE_SYMBOLS dlg( m_frame, selectedSymbol, mode );
2290
2291 // QuasiModal required to invoke symbol browser
2292 dlg.ShowQuasiModal();
2293
2294 if( selection.IsHover() )
2295 m_toolMgr->RunAction( ACTIONS::selectionClear );
2296
2297 return 0;
2298}
2299
2300
2302{
2303 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
2304
2305 if( selection.Empty() )
2306 return 0;
2307
2308 SCH_SYMBOL* symbol = (SCH_SYMBOL*) selection.Front();
2309 SCH_COMMIT commit( m_toolMgr );
2310
2311 if( !symbol->IsNew() )
2312 commit.Modify( symbol, m_frame->GetScreen() );
2313
2314 int nextBodyStyle = symbol->GetBodyStyle() + 1;
2315
2316 if( nextBodyStyle > symbol->GetBodyStyleCount() )
2317 nextBodyStyle = 1;
2318
2319 m_frame->SelectBodyStyle( symbol, nextBodyStyle );
2320
2321 if( symbol->IsNew() )
2322 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2323
2324 if( !commit.Empty() )
2325 commit.Push( _( "Change Body Style" ) );
2326
2327 if( selection.IsHover() )
2328 m_toolMgr->RunAction( ACTIONS::selectionClear );
2329
2330 return 0;
2331}
2332
2333
2335{
2336 SCH_SELECTION& selection = m_selectionTool->RequestSelection();
2337 bool clearSelection = selection.IsHover();
2338
2339 if( selection.Empty() )
2340 {
2341 if( getView()->IsLayerVisible( LAYER_SCHEMATIC_DRAWINGSHEET ) )
2342 {
2343 DS_PROXY_VIEW_ITEM* ds = m_frame->GetCanvas()->GetView()->GetDrawingSheet();
2344 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
2345
2346 if( ds && ds->HitTestDrawingSheetItems( getView(), cursorPos ) )
2347 m_toolMgr->PostAction( ACTIONS::pageSettings );
2348 }
2349
2350 return 0;
2351 }
2352
2353 EDA_ITEM* curr_item = selection.Front();
2354
2355 // If a single pin is selected, promote to its parent symbol
2356 if( ( selection.GetSize() == 1 ) && ( curr_item->Type() == SCH_PIN_T ) )
2357 {
2358 EDA_ITEM* parent = curr_item->GetParent();
2359
2360 if( parent->Type() == SCH_SYMBOL_T )
2361 curr_item = parent;
2362 }
2363
2364 switch( curr_item->Type() )
2365 {
2366 case SCH_LINE_T:
2368 case SCH_JUNCTION_T:
2370 {
2371 std::deque<SCH_LINE*> lines;
2372
2373 for( EDA_ITEM* selItem : selection.Items() )
2374 lines.push_back( static_cast<SCH_LINE*>( selItem ) );
2375
2376 DIALOG_LINE_PROPERTIES dlg( m_frame, lines );
2377
2378 dlg.ShowModal();
2379 }
2380 else if( SELECTION_CONDITIONS::OnlyTypes( { SCH_JUNCTION_T } )( selection ) )
2381 {
2382 std::deque<SCH_JUNCTION*> junctions;
2383
2384 for( EDA_ITEM* selItem : selection.Items() )
2385 junctions.push_back( static_cast<SCH_JUNCTION*>( selItem ) );
2386
2387 DIALOG_JUNCTION_PROPS dlg( m_frame, junctions );
2388
2389 dlg.ShowModal();
2390 }
2394 SCH_JUNCTION_T } )( selection ) )
2395 {
2396 std::deque<SCH_ITEM*> items;
2397
2398 for( EDA_ITEM* selItem : selection.Items() )
2399 items.push_back( static_cast<SCH_ITEM*>( selItem ) );
2400
2401 DIALOG_WIRE_BUS_PROPERTIES dlg( m_frame, items );
2402
2403 dlg.ShowModal();
2404 }
2405 else
2406 {
2407 return 0;
2408 }
2409
2410 break;
2411
2412 case SCH_MARKER_T:
2413 if( SELECTION_CONDITIONS::OnlyTypes( { SCH_MARKER_T } )( selection ) )
2414 {
2415 SCH_INSPECTION_TOOL* inspectionTool = m_toolMgr->GetTool<SCH_INSPECTION_TOOL>();
2416
2417 if( inspectionTool )
2418 inspectionTool->CrossProbe( static_cast<SCH_MARKER*> ( selection.Front() ) );
2419 }
2420 break;
2421
2422 case SCH_TABLECELL_T:
2423 if( SELECTION_CONDITIONS::OnlyTypes( { SCH_TABLECELL_T } )( selection ) )
2424 {
2425 std::vector<SCH_TABLECELL*> cells;
2426
2427 for( EDA_ITEM* item : selection.Items() )
2428 cells.push_back( static_cast<SCH_TABLECELL*>( item ) );
2429
2431
2432 dlg.ShowModal();
2433
2435 {
2436 SCH_TABLE* table = static_cast<SCH_TABLE*>( cells[0]->GetParent() );
2438
2439 tableDlg.ShowModal();
2440 }
2441 }
2442
2443 break;
2444
2445 default:
2446 if( selection.Size() > 1 )
2447 return 0;
2448
2449 EditProperties( curr_item );
2450 }
2451
2452 if( clearSelection )
2453 m_toolMgr->RunAction( ACTIONS::selectionClear );
2454
2455 return 0;
2456}
2457
2458
2460{
2461 switch( aItem->Type() )
2462 {
2463 case SCH_SYMBOL_T:
2464 {
2465 int retval;
2466 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( aItem );
2467
2468 // This needs to be scoped so the dialog destructor removes blocking status
2469 // before we launch the next dialog.
2470 {
2471 DIALOG_SYMBOL_PROPERTIES symbolPropsDialog( m_frame, symbol );
2472
2473 // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
2474 // frame. Therefore this dialog as a modal frame parent, MUST be run under
2475 // quasimodal mode for the quasimodal frame support to work. So don't use
2476 // the QUASIMODAL macros here.
2477 retval = symbolPropsDialog.ShowQuasiModal();
2478 }
2479
2480 if( retval == SYMBOL_PROPS_EDIT_OK )
2481 {
2482 if( m_frame->eeconfig()->m_AutoplaceFields.enable )
2483 {
2484 AUTOPLACE_ALGO fieldsAutoplaced = symbol->GetFieldsAutoplaced();
2485
2486 if( fieldsAutoplaced == AUTOPLACE_AUTO || fieldsAutoplaced == AUTOPLACE_MANUAL )
2487 symbol->AutoplaceFields( m_frame->GetScreen(), fieldsAutoplaced );
2488 }
2489
2490 m_frame->OnModify();
2491 }
2492 else if( retval == SYMBOL_PROPS_EDIT_SCHEMATIC_SYMBOL )
2493 {
2494 if( KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_SCH_SYMBOL_EDITOR, true ) )
2495 {
2496 SYMBOL_EDIT_FRAME* editor = static_cast<SYMBOL_EDIT_FRAME*>( frame );
2497
2498 if( wxWindow* blocking_win = editor->Kiway().GetBlockingDialog() )
2499 blocking_win->Close( true );
2500
2501 // The broken library symbol link indicator cannot be edited.
2502 if( symbol->IsMissingLibSymbol() )
2503 return;
2504
2505 editor->LoadSymbolFromSchematic( symbol );
2506 editor->Show( true );
2507 editor->Raise();
2508 }
2509 }
2510 else if( retval == SYMBOL_PROPS_EDIT_LIBRARY_SYMBOL )
2511 {
2512 if( KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_SCH_SYMBOL_EDITOR, true ) )
2513 {
2514 SYMBOL_EDIT_FRAME* editor = static_cast<SYMBOL_EDIT_FRAME*>( frame );
2515
2516 if( wxWindow* blocking_win = editor->Kiway().GetBlockingDialog() )
2517 blocking_win->Close( true );
2518
2519 editor->LoadSymbol( symbol->GetLibId(), symbol->GetUnit(), symbol->GetBodyStyle() );
2520 editor->Show( true );
2521 editor->Raise();
2522 }
2523 }
2524 else if( retval == SYMBOL_PROPS_WANT_UPDATE_SYMBOL )
2525 {
2527 dlg.ShowQuasiModal();
2528 }
2529 else if( retval == SYMBOL_PROPS_WANT_EXCHANGE_SYMBOL )
2530 {
2532 dlg.ShowQuasiModal();
2533 }
2534
2535 break;
2536 }
2537
2538 case SCH_SHEET_T:
2539 {
2540 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
2541 bool isUndoable = false;
2542 bool doClearAnnotation = false;
2543 bool okPressed = false;
2544 bool updateHierarchyNavigator = false;
2545
2546 // Keep track of existing sheet paths. EditSheet() can modify this list.
2547 // Note that we use the validity checking/repairing version here just to make sure
2548 // we've got a valid hierarchy to begin with.
2549 SCH_SHEET_LIST originalHierarchy;
2550 originalHierarchy.BuildSheetList( &m_frame->Schematic().Root(), true );
2551
2552 SCH_COMMIT commit( m_toolMgr );
2553 commit.Modify( sheet, m_frame->GetScreen() );
2554 okPressed = m_frame->EditSheetProperties( sheet, &m_frame->GetCurrentSheet(), &isUndoable,
2555 &doClearAnnotation, &updateHierarchyNavigator );
2556
2557 if( okPressed )
2558 {
2559 if( isUndoable )
2560 {
2561 commit.Push( _( "Edit Sheet Properties" ) );
2562 }
2563 else
2564 {
2565 std::vector<SCH_ITEM*> items;
2566
2567 items.emplace_back( sheet );
2568 m_frame->Schematic().OnItemsRemoved( items );
2569 m_frame->Schematic().OnItemsAdded( items );
2570 m_frame->OnModify();
2571 m_frame->Schematic().RefreshHierarchy();
2572 m_frame->UpdateHierarchyNavigator();
2573 }
2574 }
2575 else
2576 {
2577 // If we are renaming files, the undo/redo list becomes invalid and must be cleared.
2578 m_frame->ClearUndoRedoList();
2579 m_frame->OnModify();
2580 }
2581
2582 // If the sheet file is changed and new sheet contents are loaded then we have to
2583 // clear the annotations on the new content (as it may have been set from some other
2584 // sheet path reference)
2585 if( doClearAnnotation )
2586 {
2587 SCH_SCREENS screensList( &m_frame->Schematic().Root() );
2588
2589 // We clear annotation of new sheet paths here:
2590 screensList.ClearAnnotationOfNewSheetPaths( originalHierarchy );
2591
2592 // Clear annotation of g_CurrentSheet itself, because its sheetpath is not a new
2593 // path, but symbols managed by its sheet path must have their annotation cleared
2594 // because they are new:
2595 sheet->GetScreen()->ClearAnnotation( &m_frame->GetCurrentSheet(), false );
2596 }
2597
2598 if( okPressed )
2599 m_frame->GetCanvas()->Refresh();
2600
2601 if( updateHierarchyNavigator )
2602 m_frame->UpdateHierarchyNavigator();
2603
2604 break;
2605 }
2606
2607 case SCH_SHEET_PIN_T:
2608 {
2609 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( aItem );
2611
2612 // QuasiModal required for help dialog
2613 dlg.ShowQuasiModal();
2614 break;
2615 }
2616
2617 case SCH_TEXT_T:
2618 case SCH_TEXTBOX_T:
2619 {
2620 DIALOG_TEXT_PROPERTIES dlg( m_frame, static_cast<SCH_ITEM*>( aItem ) );
2621
2622 // QuasiModal required for syntax help and Scintilla auto-complete
2623 dlg.ShowQuasiModal();
2624 break;
2625 }
2626
2627 case SCH_TABLE_T:
2628 {
2629 DIALOG_TABLE_PROPERTIES dlg( m_frame, static_cast<SCH_TABLE*>( aItem ) );
2630
2631 // QuasiModal required for Scintilla auto-complete
2632 dlg.ShowQuasiModal();
2633 break;
2634 }
2635
2636 case SCH_LABEL_T:
2637 case SCH_GLOBAL_LABEL_T:
2638 case SCH_HIER_LABEL_T:
2640 {
2641 DIALOG_LABEL_PROPERTIES dlg( m_frame, static_cast<SCH_LABEL_BASE*>( aItem ), false );
2642
2643 // QuasiModal for syntax help and Scintilla auto-complete
2644 dlg.ShowQuasiModal();
2645 break;
2646 }
2647
2648 case SCH_FIELD_T:
2649 {
2650 SCH_FIELD* field = static_cast<SCH_FIELD*>( aItem );
2651
2652 editFieldText( field );
2653
2654 if( !field->IsVisible() )
2655 m_toolMgr->RunAction( ACTIONS::selectionClear );
2656
2657 break;
2658 }
2659
2660 case SCH_SHAPE_T:
2661 {
2662 DIALOG_SHAPE_PROPERTIES dlg( m_frame, static_cast<SCH_SHAPE*>( aItem ) );
2663
2664 dlg.ShowModal();
2665 break;
2666 }
2667
2668 case SCH_BITMAP_T:
2669 {
2670 SCH_BITMAP& bitmap = static_cast<SCH_BITMAP&>( *aItem );
2671 DIALOG_IMAGE_PROPERTIES dlg( m_frame, bitmap );
2672
2673 if( dlg.ShowModal() == wxID_OK )
2674 {
2675 // The bitmap is cached in Opengl: clear the cache in case it has become invalid
2677 }
2678
2679 break;
2680 }
2681
2682 case SCH_RULE_AREA_T:
2683 {
2684 DIALOG_SHAPE_PROPERTIES dlg( m_frame, static_cast<SCH_SHAPE*>( aItem ) );
2685 dlg.SetTitle( _( "Rule Area Properties" ) );
2686
2687 dlg.ShowModal();
2688 break;
2689 }
2690
2691 case SCH_NO_CONNECT_T:
2692 case SCH_PIN_T:
2693 break;
2694
2695 case SCH_GROUP_T:
2697 static_cast<EDA_GROUP*>( static_cast<SCH_GROUP*>( aItem ) ) );
2698
2699 break;
2700
2701 default: // Unexpected item
2702 wxFAIL_MSG( wxString( "Cannot edit schematic item type " ) + aItem->GetClass() );
2703 }
2704
2705 updateItem( aItem, true );
2706}
2707
2708
2710{
2711 KICAD_T convertTo = aEvent.Parameter<KICAD_T>();
2712 SCH_SELECTION selection = m_selectionTool->RequestSelection( { SCH_LABEL_LOCATE_ANY_T,
2713 SCH_TEXT_T,
2714 SCH_TEXTBOX_T } );
2715 SCH_COMMIT localCommit( m_toolMgr );
2716 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
2717
2718 if( !commit )
2719 commit = &localCommit;
2720
2721 for( unsigned int i = 0; i < selection.GetSize(); ++i )
2722 {
2723 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( selection.GetItem( i ) );
2724
2725 if( item && item->Type() != convertTo )
2726 {
2727 EDA_TEXT* sourceText = dynamic_cast<EDA_TEXT*>( item );
2728 bool selected = item->IsSelected();
2729 SCH_ITEM* newtext = nullptr;
2730 VECTOR2I position = item->GetPosition();
2731 wxString txt;
2732 wxString href;
2735
2736 wxCHECK2( sourceText, continue );
2737
2738 switch( item->Type() )
2739 {
2740 case SCH_LABEL_T:
2741 case SCH_GLOBAL_LABEL_T:
2742 case SCH_HIER_LABEL_T:
2743 {
2744 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( item );
2745
2746 txt = UnescapeString( label->GetText() );
2747 spinStyle = label->GetSpinStyle();
2748 shape = label->GetShape();
2749 href = label->GetHyperlink();
2750 break;
2751 }
2752
2754 {
2755 SCH_DIRECTIVE_LABEL* dirlabel = static_cast<SCH_DIRECTIVE_LABEL*>( item );
2756
2757 // a SCH_DIRECTIVE_LABEL has no text
2758 txt = _( "<empty>" );
2759
2760 spinStyle = dirlabel->GetSpinStyle();
2761 href = dirlabel->GetHyperlink();
2762 break;
2763 }
2764
2765 case SCH_TEXT_T:
2766 {
2767 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
2768
2769 txt = text->GetText();
2770 href = text->GetHyperlink();
2771 break;
2772 }
2773
2774 case SCH_TEXTBOX_T:
2775 {
2776 SCH_TEXTBOX* textbox = static_cast<SCH_TEXTBOX*>( item );
2777 BOX2I bbox = textbox->GetBoundingBox();
2778
2779 bbox.SetOrigin( bbox.GetLeft() + textbox->GetMarginLeft(),
2780 bbox.GetTop() + textbox->GetMarginTop() );
2781 bbox.SetEnd( bbox.GetRight() - textbox->GetMarginRight(),
2782 bbox.GetBottom() - textbox->GetMarginBottom() );
2783
2784 if( convertTo == SCH_LABEL_T
2785 || convertTo == SCH_HIER_LABEL_T
2786 || convertTo == SCH_GLOBAL_LABEL_T )
2787 {
2788 EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
2789 wxCHECK( text, 0 );
2790 int textSize = text->GetTextSize().y;
2791 bbox.Inflate( KiROUND( item->Schematic()->Settings().m_LabelSizeRatio * textSize ) );
2792 }
2793
2794 txt = textbox->GetText();
2795
2796 if( textbox->GetTextAngle().IsVertical() )
2797 {
2798 if( textbox->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
2799 {
2800 spinStyle = SPIN_STYLE::SPIN::BOTTOM;
2801 position = VECTOR2I( bbox.Centre().x, bbox.GetOrigin().y );
2802 }
2803 else
2804 {
2805 spinStyle = SPIN_STYLE::SPIN::UP;
2806 position = VECTOR2I( bbox.Centre().x, bbox.GetEnd().y );
2807 }
2808 }
2809 else
2810 {
2811 if( textbox->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
2812 {
2813 spinStyle = SPIN_STYLE::SPIN::LEFT;
2814 position = VECTOR2I( bbox.GetEnd().x, bbox.Centre().y );
2815 }
2816 else
2817 {
2818 spinStyle = SPIN_STYLE::SPIN::RIGHT;
2819 position = VECTOR2I( bbox.GetOrigin().x, bbox.Centre().y );
2820 }
2821 }
2822
2823 position = m_frame->GetNearestGridPosition( position );
2824 href = textbox->GetHyperlink();
2825 break;
2826 }
2827
2828 default:
2829 UNIMPLEMENTED_FOR( item->GetClass() );
2830 break;
2831 }
2832
2833 auto getValidNetname =
2834 []( const wxString& aText )
2835 {
2836 wxString local_txt = aText;
2837 local_txt.Replace( "\n", "_" );
2838 local_txt.Replace( "\r", "_" );
2839 local_txt.Replace( "\t", "_" );
2840
2841 // Bus groups can have spaces; bus vectors and signal names cannot
2842 if( !NET_SETTINGS::ParseBusGroup( aText, nullptr, nullptr ) )
2843 local_txt.Replace( " ", "_" );
2844
2845 // label strings are "escaped" i.e. a '/' is replaced by "{slash}"
2846 local_txt = EscapeString( local_txt, CTX_NETNAME );
2847
2848 if( local_txt.IsEmpty() )
2849 return _( "<empty>" );
2850 else
2851 return local_txt;
2852 };
2853
2854 switch( convertTo )
2855 {
2856 case SCH_LABEL_T:
2857 {
2858 SCH_LABEL_BASE* new_label = new SCH_LABEL( position, getValidNetname( txt ) );
2859
2860 new_label->SetShape( shape );
2861 new_label->SetAttributes( *sourceText, false );
2862 new_label->SetSpinStyle( spinStyle );
2863 new_label->SetHyperlink( href );
2864
2865 if( item->Type() == SCH_GLOBAL_LABEL_T || item->Type() == SCH_HIER_LABEL_T )
2866 {
2867 if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::UP )
2868 new_label->MirrorVertically( position.y );
2869 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::BOTTOM )
2870 new_label->MirrorVertically( position.y );
2871 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::LEFT )
2872 new_label->MirrorHorizontally( position.x );
2873 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::RIGHT )
2874 new_label->MirrorHorizontally( position.x );
2875 }
2876
2877 newtext = new_label;
2878 break;
2879 }
2880
2881 case SCH_GLOBAL_LABEL_T:
2882 {
2883 SCH_LABEL_BASE* new_label = new SCH_GLOBALLABEL( position, getValidNetname( txt ) );
2884
2885 new_label->SetShape( shape );
2886 new_label->SetAttributes( *sourceText, false );
2887 new_label->SetSpinStyle( spinStyle );
2888 new_label->SetHyperlink( href );
2889
2890 if( item->Type() == SCH_LABEL_T )
2891 {
2892 if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::UP )
2893 new_label->MirrorVertically( position.y );
2894 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::BOTTOM )
2895 new_label->MirrorVertically( position.y );
2896 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::LEFT )
2897 new_label->MirrorHorizontally( position.x );
2898 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::RIGHT )
2899 new_label->MirrorHorizontally( position.x );
2900 }
2901
2902 newtext = new_label;
2903 break;
2904 }
2905
2906 case SCH_HIER_LABEL_T:
2907 {
2908 SCH_LABEL_BASE* new_label = new SCH_HIERLABEL( position, getValidNetname( txt ) );
2909
2910 new_label->SetShape( shape );
2911 new_label->SetAttributes( *sourceText, false );
2912 new_label->SetSpinStyle( spinStyle );
2913 new_label->SetHyperlink( href );
2914
2915 if( item->Type() == SCH_LABEL_T )
2916 {
2917 if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::UP )
2918 new_label->MirrorVertically( position.y );
2919 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::BOTTOM )
2920 new_label->MirrorVertically( position.y );
2921 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::LEFT )
2922 new_label->MirrorHorizontally( position.x );
2923 else if( static_cast<SCH_LABEL_BASE*>( item )->GetSpinStyle() == SPIN_STYLE::SPIN::RIGHT )
2924 new_label->MirrorHorizontally( position.x );
2925 }
2926
2927 newtext = new_label;
2928 break;
2929 }
2930
2932 {
2933 SCH_LABEL_BASE* new_label = new SCH_DIRECTIVE_LABEL( position );
2934
2935 // A SCH_DIRECTIVE_LABEL usually has at least one field containing the net class
2936 // name. If we're copying from a text object assume the text is the netclass
2937 // name. Otherwise, we'll just copy the fields which will either have a netclass
2938 // or not.
2939 if( !dynamic_cast<SCH_LABEL_BASE*>( item ) )
2940 {
2941 SCH_FIELD netclass( new_label, FIELD_T::USER, wxT( "Netclass" ) );
2942 netclass.SetText( txt );
2943 netclass.SetTextPos( position );
2944 new_label->GetFields().push_back( netclass );
2945 }
2946
2947 new_label->SetShape( LABEL_FLAG_SHAPE::F_ROUND );
2948 new_label->SetAttributes( *sourceText, false );
2949 new_label->SetSpinStyle( spinStyle );
2950 new_label->SetHyperlink( href );
2951 newtext = new_label;
2952 break;
2953 }
2954
2955 case SCH_TEXT_T:
2956 {
2957 SCH_TEXT* new_text = new SCH_TEXT( position, txt );
2958
2959 new_text->SetAttributes( *sourceText, false );
2960 new_text->SetHyperlink( href );
2961 newtext = new_text;
2962 break;
2963 }
2964
2965 case SCH_TEXTBOX_T:
2966 {
2967 SCH_TEXTBOX* new_textbox = new SCH_TEXTBOX( LAYER_NOTES, 0, FILL_T::NO_FILL, txt );
2968 BOX2I bbox = item->GetBoundingBox();
2969
2970 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item ) )
2971 bbox.Inflate( -label->GetLabelBoxExpansion() );
2972
2973 new_textbox->SetAttributes( *sourceText, false );
2974
2975 bbox.SetOrigin( bbox.GetLeft() - new_textbox->GetMarginLeft(),
2976 bbox.GetTop() - new_textbox->GetMarginTop() );
2977 bbox.SetEnd( bbox.GetRight() + new_textbox->GetMarginRight(),
2978 bbox.GetBottom() + new_textbox->GetMarginBottom() );
2979
2980 VECTOR2I topLeft = bbox.GetPosition();
2981 VECTOR2I botRight = bbox.GetEnd();
2982
2983 // Add 1/20 of the margin at the end to reduce line-breaking changes.
2984 int slop = new_textbox->GetLegacyTextMargin() / 20;
2985
2986 if( sourceText->GetTextAngle() == ANGLE_VERTICAL )
2987 {
2988 if( sourceText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
2989 botRight.y += slop;
2990 else
2991 topLeft.y -= slop;
2992 }
2993 else
2994 {
2995 if( sourceText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
2996 topLeft.x -= slop;
2997 else
2998 botRight.x += slop;
2999 }
3000
3001 new_textbox->SetPosition( topLeft );
3002 new_textbox->SetEnd( botRight );
3003
3004 new_textbox->SetHyperlink( href );
3005 newtext = new_textbox;
3006 break;
3007 }
3008
3009 default:
3010 UNIMPLEMENTED_FOR( wxString::Format( "%d.", convertTo ) );
3011 break;
3012 }
3013
3014 wxCHECK2( newtext, continue );
3015
3016 // Copy the old text item settings to the new one. Justifications are not copied
3017 // because they are not used in labels. Justifications will be set to default value
3018 // in the new text item type.
3019 //
3020 newtext->SetFlags( item->GetEditFlags() );
3021
3022 EDA_TEXT* eda_text = dynamic_cast<EDA_TEXT*>( item );
3023 EDA_TEXT* new_eda_text = dynamic_cast<EDA_TEXT*>( newtext );
3024
3025 wxCHECK2( eda_text && new_eda_text, continue );
3026
3027 new_eda_text->SetFont( eda_text->GetFont() );
3028 new_eda_text->SetTextSize( eda_text->GetTextSize() );
3029 new_eda_text->SetTextThickness( eda_text->GetTextThickness() );
3030
3031 // Must be after SetTextSize()
3032 new_eda_text->SetBold( eda_text->IsBold() );
3033 new_eda_text->SetItalic( eda_text->IsItalic() );
3034
3035 newtext->AutoplaceFields( m_frame->GetScreen(), AUTOPLACE_AUTO );
3036
3037 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item );
3038 SCH_LABEL_BASE* new_label = dynamic_cast<SCH_LABEL_BASE*>( newtext );
3039
3040 if( label && new_label )
3041 {
3042 new_label->AddFields( label->GetFields() );
3043
3044 // A SCH_GLOBALLABEL has a specific field for intersheet references that has
3045 // no meaning for other labels
3046 std::erase_if( new_label->GetFields(),
3047 [&]( SCH_FIELD& field )
3048 {
3049 return field.GetId() == FIELD_T::INTERSHEET_REFS
3050 && new_label->Type() != SCH_GLOBAL_LABEL_T;
3051 } );
3052 }
3053
3054 if( selected )
3055 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::unselectItem, item );
3056
3057 m_frame->RemoveFromScreen( item, m_frame->GetScreen() );
3058
3059 if( commit->GetStatus( item, m_frame->GetScreen() ) == CHT_ADD )
3060 commit->Unstage( item, m_frame->GetScreen() );
3061 else
3062 commit->Removed( item, m_frame->GetScreen() );
3063
3064 m_frame->AddToScreen( newtext, m_frame->GetScreen() );
3065 commit->Added( newtext, m_frame->GetScreen() );
3066
3067 if( selected )
3068 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, newtext );
3069 }
3070 }
3071
3072 if( !localCommit.Empty() )
3073 localCommit.Push( _( "Change To" ) );
3074
3075 if( selection.IsHover() )
3076 m_toolMgr->RunAction( ACTIONS::selectionClear );
3077
3078 return 0;
3079}
3080
3081
3083{
3084 static std::vector<KICAD_T> justifiableItems = {
3086 SCH_TEXT_T,
3089 };
3090
3091 SCH_SELECTION& selection = m_selectionTool->RequestSelection( justifiableItems );
3092
3093 if( selection.GetSize() == 0 )
3094 return 0;
3095
3096 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
3097 bool moving = item->IsMoving();
3098 SCH_COMMIT localCommit( m_toolMgr );
3099 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
3100
3101 if( !commit )
3102 commit = &localCommit;
3103
3104 auto setJustify =
3105 [&]( EDA_TEXT* aTextItem )
3106 {
3107 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
3108 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
3109 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
3110 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
3111 else
3112 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
3113 };
3114
3115 for( EDA_ITEM* edaItem : selection )
3116 {
3117 item = static_cast<SCH_ITEM*>( edaItem );
3118
3119 if( !moving )
3120 commit->Modify( item, m_frame->GetScreen() );
3121
3122 if( item->Type() == SCH_FIELD_T )
3123 {
3124 setJustify( static_cast<SCH_FIELD*>( item ) );
3125
3126 // Now that we're re-justifying a field, they're no longer autoplaced.
3127 static_cast<SCH_ITEM*>( item->GetParent() )->SetFieldsAutoplaced( AUTOPLACE_NONE );
3128 }
3129 else if( item->Type() == SCH_TEXT_T )
3130 {
3131 setJustify( static_cast<SCH_TEXT*>( item ) );
3132 }
3133 else if( item->Type() == SCH_TEXTBOX_T )
3134 {
3135 setJustify( static_cast<SCH_TEXTBOX*>( item ) );
3136 }
3137 else if( item->Type() == SCH_LABEL_T )
3138 {
3139 SCH_LABEL* label = static_cast<SCH_LABEL*>( item );
3140
3141 if( label->GetTextAngle() == ANGLE_HORIZONTAL )
3142 setJustify( label );
3143 }
3144
3145 m_frame->UpdateItem( item, false, true );
3146 }
3147
3148 // Update R-Tree for modified items
3149 for( EDA_ITEM* selected : selection )
3150 updateItem( selected, true );
3151
3152 if( item->IsMoving() )
3153 {
3154 m_toolMgr->RunAction( ACTIONS::refreshPreview );
3155 }
3156 else
3157 {
3158 SCH_SELECTION selectionCopy = selection;
3159
3160 if( selection.IsHover() )
3161 m_toolMgr->RunAction( ACTIONS::selectionClear );
3162
3163 if( !localCommit.Empty() )
3164 {
3165 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
3166 localCommit.Push( _( "Left Justify" ) );
3167 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
3168 localCommit.Push( _( "Center Justify" ) );
3169 else
3170 localCommit.Push( _( "Right Justify" ) );
3171 }
3172 }
3173
3174 return 0;
3175}
3176
3177
3179{
3180 bool isSlice = aEvent.Matches( SCH_ACTIONS::slice.MakeEvent() );
3182 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_LINE_T } );
3183 SCH_SCREEN* screen = m_frame->GetScreen();
3184 SCH_COMMIT commit( m_toolMgr );
3185
3187 std::vector<SCH_LINE*> lines;
3188
3189 // Save the current orthogonal mode so we can restore it later
3190 static enum LINE_MODE lineMode = LINE_MODE::LINE_MODE_90;
3191 static bool lineModeChanged = false;
3192
3193 auto revertLineMode =
3194 [&]()
3195 {
3196 if( lineModeChanged )
3197 {
3198 if( lineMode == LINE_MODE::LINE_MODE_90 )
3199 m_toolMgr->RunAction( SCH_ACTIONS::lineMode90 );
3200 else if( lineMode == LINE_MODE::LINE_MODE_45)
3201 m_toolMgr->RunAction( SCH_ACTIONS::lineMode45 );
3202
3203 lineModeChanged = false;
3204 }
3205 };
3206
3207 for( EDA_ITEM* item : selection )
3208 {
3209 if( item->Type() == SCH_LINE_T )
3210 lines.push_back( static_cast<SCH_LINE*>( item ) );
3211 }
3212
3213 m_selectionTool->ClearSelection();
3214
3215 for( SCH_LINE* line : lines )
3216 {
3217 SCH_LINE* newLine;
3218
3219 // We let the user select the break point if they're on a single line
3220 if( lines.size() == 1 && line->HitTest( cursorPos ) && !line->IsEndPoint( cursorPos ) )
3221 lwbTool->BreakSegment( &commit, line, cursorPos, &newLine, screen );
3222 else
3223 lwbTool->BreakSegment( &commit, line, line->GetMidPoint(), &newLine, screen );
3224
3225 // Make sure both endpoints are deselected
3226 newLine->ClearFlags( ENDPOINT | STARTPOINT );
3227 line->SetFlags( ENDPOINT );
3228
3229 // If we're a break, we want to drag both wires.
3230 // Side note: the drag/move tool only checks whether the first item is
3231 // new to determine if it should append undo or not, someday this should
3232 // be cleaned up and explictly controlled but for now the newLine
3233 // selection addition must be after the existing line.
3234 if( !isSlice )
3235 {
3236 m_selectionTool->AddItemToSel( newLine );
3237 newLine->SetFlags( STARTPOINT );
3238 }
3239 }
3240
3241 if( !lines.empty() )
3242 {
3243 if( EESCHEMA_SETTINGS* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ) )
3244 {
3245 if( cfg->m_Drawing.line_mode != LINE_MODE::LINE_MODE_FREE )
3246 {
3247 lineMode = (enum LINE_MODE) cfg->m_Drawing.line_mode;
3248 lineModeChanged = true;
3250 }
3251 }
3252
3253 m_frame->TestDanglingEnds();
3254
3255 if( m_toolMgr->RunSynchronousAction( SCH_ACTIONS::drag, &commit, isSlice ) )
3256 {
3257 commit.Push( isSlice ? _( "Slice Wire" ) : _( "Break Wire" ) );
3258
3259 // Breaking wires is usually a repeated action, e.g. to add bends
3260 if( !isSlice )
3261 m_toolMgr->PostAction( SCH_ACTIONS::breakWire );
3262 else
3263 revertLineMode();
3264 }
3265 else
3266 {
3267 commit.Revert();
3268 revertLineMode();
3269 }
3270 }
3271
3272 if( selection.IsHover() )
3273 m_toolMgr->RunAction( ACTIONS::selectionClear );
3274
3275 return 0;
3276}
3277
3278
3280{
3281 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SHEET_T } );
3282 SCH_SHEET* sheet = (SCH_SHEET*) selection.Front();
3283 SCH_COMMIT commit( m_toolMgr );
3284
3285 if( !sheet || !sheet->HasUndefinedPins() )
3286 return 0;
3287
3288 if( !IsOK( m_frame, _( "Do you wish to delete the unreferenced pins from this sheet?" ) ) )
3289 return 0;
3290
3291 commit.Modify( sheet, m_frame->GetScreen() );
3292
3293 sheet->CleanupSheet();
3294
3295 updateItem( sheet, true );
3296
3297 commit.Push( _( "Cleanup Sheet Pins" ) );
3298
3299 if( selection.IsHover() )
3300 m_toolMgr->RunAction( ACTIONS::selectionClear );
3301
3302 return 0;
3303}
3304
3305
3307{
3308 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SHEET_T } );
3309
3310 if( selection.GetSize() > 1 )
3311 return 0;
3312
3313 SCH_SHEET* sheet = (SCH_SHEET*) selection.Front();
3314
3315 SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
3316
3317 SCH_SCREEN* screen;
3318
3319 if( sheet )
3320 {
3321 // When changing the page number of a selected sheet, the current screen owns the sheet.
3322 screen = m_frame->GetScreen();
3323
3324 instance.push_back( sheet );
3325 }
3326 else
3327 {
3328 SCH_SHEET_PATH prevInstance = instance;
3329
3330 // When change the page number in the screen, the previous screen owns the sheet.
3331 if( prevInstance.size() )
3332 {
3333 prevInstance.pop_back();
3334 screen = prevInstance.LastScreen();
3335 }
3336 else
3337 {
3338 // The root sheet and root screen are effectively the same thing.
3339 screen = m_frame->GetScreen();
3340 }
3341
3342 sheet = m_frame->GetCurrentSheet().Last();
3343 }
3344
3345 wxString msg;
3346 wxString sheetPath = instance.PathHumanReadable( false );
3347 wxString pageNumber = instance.GetPageNumber();
3348
3349 msg.Printf( _( "Enter page number for sheet path%s" ),
3350 ( sheetPath.Length() > 20 ) ? "\n" + sheetPath : " " + sheetPath );
3351
3352 wxTextEntryDialog dlg( m_frame, msg, _( "Edit Sheet Page Number" ), pageNumber );
3353
3354 dlg.SetTextValidator( wxFILTER_ALPHANUMERIC ); // No white space.
3355
3356 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == instance.GetPageNumber() )
3357 return 0;
3358
3359 SCH_COMMIT commit( m_frame );
3360
3361 commit.Modify( sheet, screen );
3362
3363 instance.SetPageNumber( dlg.GetValue() );
3364
3365 if( instance == m_frame->GetCurrentSheet() )
3366 {
3367 m_frame->GetScreen()->SetPageNumber( dlg.GetValue() );
3368 m_frame->OnPageSettingsChange();
3369 }
3370
3371 commit.Push( wxS( "Change Sheet Page Number" ) );
3372
3373 if( selection.IsHover() )
3374 m_toolMgr->RunAction( ACTIONS::selectionClear );
3375
3376 return 0;
3377}
3378
3379
3381{
3382 return m_toolMgr->RunAction( SCH_ACTIONS::importSheet, aEvent.Parameter<wxString*>() );
3383}
3384
3385
3387{
3388 wxString* filename = aEvent.Parameter<wxString*>();
3389
3390 if( !filename )
3391 return 0;
3392
3393 SCH_BITMAP* image = new SCH_BITMAP( VECTOR2I( 0, 0 ) );
3394
3395 if( !image->GetReferenceImage().ReadImageFile( *filename ) )
3396 {
3397 wxMessageBox( wxString::Format( _( "Could not load image from '%s'." ), *filename ) );
3398 delete image;
3399 return 0;
3400 }
3401
3402 return m_toolMgr->RunAction( SCH_ACTIONS::placeImage, image );
3403}
3404
3405
3407 std::set<std::pair<SCH_SYMBOL*, SCH_SCREEN*>>& aCollectedUnits )
3408{
3409 for( EDA_ITEM* item : aSelection )
3410 {
3411 if( item->Type() == SCH_SYMBOL_T )
3412 {
3413 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3414
3415 aCollectedUnits.insert( { symbol, m_frame->GetScreen() } );
3416
3417 // The attributes should be kept in sync in multi-unit parts.
3418 // Of course the symbol must be annotated to collect other units.
3419 if( symbol->IsAnnotated( &m_frame->GetCurrentSheet() ) )
3420 {
3421 wxString ref = symbol->GetRef( &m_frame->GetCurrentSheet() );
3422 int unit = symbol->GetUnit();
3423 LIB_ID libId = symbol->GetLibId();
3424
3425 for( SCH_SHEET_PATH& sheet : m_frame->Schematic().Hierarchy() )
3426 {
3427 SCH_SCREEN* screen = sheet.LastScreen();
3428 std::vector<SCH_SYMBOL*> otherUnits;
3429
3430 CollectOtherUnits( ref, unit, libId, sheet, &otherUnits );
3431
3432 for( SCH_SYMBOL* otherUnit : otherUnits )
3433 aCollectedUnits.insert( { otherUnit, screen } );
3434 }
3435 }
3436 }
3437 }
3438}
3439
3440
3442{
3443 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
3444 SCH_COMMIT commit( m_toolMgr );
3445
3446 std::set<std::pair<SCH_SYMBOL*, SCH_SCREEN*>> collectedUnits;
3447
3448 collectUnits( selection, collectedUnits );
3449 bool new_state = false;
3450
3451 for( const auto& [symbol, _] : collectedUnits )
3452 {
3453 if( ( aEvent.IsAction( &SCH_ACTIONS::setDNP ) && !symbol->GetDNP() )
3454 || ( aEvent.IsAction( &SCH_ACTIONS::setExcludeFromSimulation ) && !symbol->GetExcludedFromSim() )
3455 || ( aEvent.IsAction( &SCH_ACTIONS::setExcludeFromBOM ) && !symbol->GetExcludedFromBOM() )
3456 || ( aEvent.IsAction( &SCH_ACTIONS::setExcludeFromBoard ) && !symbol->GetExcludedFromBoard() ) )
3457 {
3458 new_state = true;
3459 break;
3460 }
3461 }
3462
3463 for( const auto& [symbol, screen] : collectedUnits )
3464 {
3465 commit.Modify( symbol, screen );
3466
3467 if( aEvent.IsAction( &SCH_ACTIONS::setDNP ) )
3468 symbol->SetDNP( new_state );
3469
3471 symbol->SetExcludedFromSim( new_state );
3472
3474 symbol->SetExcludedFromBOM( new_state );
3475
3477 symbol->SetExcludedFromBoard( new_state );
3478 }
3479
3480 if( !commit.Empty() )
3481 commit.Push( _( "Toggle Attribute" ) );
3482
3483 if( selection.IsHover() )
3484 m_toolMgr->RunAction( ACTIONS::selectionClear );
3485
3486 return 0;
3487}
3488
3489
3490wxString SCH_EDIT_TOOL::FixERCErrorMenuText( const std::shared_ptr<RC_ITEM>& aERCItem )
3491{
3492 if( aERCItem->GetErrorCode() == ERCE_SIMULATION_MODEL
3493 || aERCItem->GetErrorCode() == ERCE_FOOTPRINT_FILTERS
3494 || aERCItem->GetErrorCode() == ERCE_FOOTPRINT_LINK_ISSUES )
3495 {
3496 return _( "Edit Symbol Properties..." );
3497 }
3498 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_ISSUES )
3499 {
3500 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::showSymbolLibTable );
3501 }
3502 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_MISMATCH )
3503 {
3504 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::updateSymbol );
3505 }
3506 else if( aERCItem->GetErrorCode() == ERCE_UNANNOTATED
3507 || aERCItem->GetErrorCode() == ERCE_DUPLICATE_REFERENCE )
3508 {
3509 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::annotate );
3510 }
3511 else if( aERCItem->GetErrorCode() == ERCE_UNDEFINED_NETCLASS )
3512 {
3513 return _( "Edit Netclasses..." );
3514 }
3515
3516 return wxEmptyString;
3517}
3518
3519
3520void SCH_EDIT_TOOL::FixERCError( const std::shared_ptr<RC_ITEM>& aERCItem )
3521{
3522 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
3523
3524 wxCHECK( frame, /* void */ );
3525
3526 if( aERCItem->GetErrorCode() == ERCE_SIMULATION_MODEL
3527 || aERCItem->GetErrorCode() == ERCE_FOOTPRINT_FILTERS
3528 || aERCItem->GetErrorCode() == ERCE_FOOTPRINT_LINK_ISSUES )
3529 {
3530 if( EDA_ITEM* item = frame->ResolveItem( aERCItem->GetMainItemID() ) )
3531 EditProperties( item );
3532 }
3533 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_ISSUES )
3534 {
3536 }
3537 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_MISMATCH )
3538 {
3539 EDA_ITEM* item = frame->ResolveItem( aERCItem->GetMainItemID() );
3540
3541 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
3542 {
3544 dlg.ShowQuasiModal();
3545 }
3546 }
3547 else if( aERCItem->GetErrorCode() == ERCE_UNANNOTATED
3548 || aERCItem->GetErrorCode() == ERCE_DUPLICATE_REFERENCE )
3549 {
3550 m_toolMgr->RunAction( SCH_ACTIONS::annotate );
3551 }
3552 else if( aERCItem->GetErrorCode() == ERCE_UNDEFINED_NETCLASS )
3553 {
3554 frame->ShowSchematicSetupDialog( _( "Net Classes" ) );
3555 }
3556}
3557
3558
3560{
3561 // clang-format off
3567 Go( &SCH_EDIT_TOOL::Swap, SCH_ACTIONS::swap.MakeEvent() );
3573
3579
3599
3602
3607
3611
3614 // clang-format on
3615}
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 breakWire
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 lineMode45
static TOOL_ACTION rotateCW
static TOOL_ACTION importSheet
Definition sch_actions.h:87
static TOOL_ACTION drag
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 lineMode90
static TOOL_ACTION ddAppendFile
static TOOL_ACTION slice
static TOOL_ACTION placeSchematicText
Definition sch_actions.h:92
static TOOL_ACTION toTextBox
static TOOL_ACTION lineModeFree
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.
virtual void Revert() override
Revert the commit by restoring the modified items state.
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 BreakWire(const TOOL_EVENT &aEvent)
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:505
virtual void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo)
Definition sch_item.h:607
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode)
Definition sch_item.h:609
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:385
virtual void Move(const VECTOR2I &aMoveVector)
Move the item by aMoveVector to a new position.
Definition sch_item.h:377
int GetUnit() const
Definition sch_item.h:238
void SetConnectivityDirty(bool aDirty=true)
Definition sch_item.h:568
void SetFieldsAutoplaced(AUTOPLACE_ALGO aAlgo)
Definition sch_item.h:605
virtual void Rotate(const VECTOR2I &aCenter, bool aRotateCCW)
Rotate the item around aCenter 90 degrees in the clockwise direction.
Definition sch_item.h:401
AUTOPLACE_ALGO GetFieldsAutoplaced() const
Return whether the fields have been automatically placed.
Definition sch_item.h:604
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:520
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:393
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)
void BreakSegment(SCH_COMMIT *aCommit, SCH_LINE *aSegment, const VECTOR2I &aPoint, SCH_LINE **aNewSegment, SCH_SCREEN *aScreen)
Break a single segment into two at the specified point.
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:147
bool IsConnectable() const override
Definition sch_pin.h:300
const wxString & GetName() const
Definition sch_pin.cpp:400
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:731
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:327
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:321
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:75
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:261
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:164
int GetOrientation() const override
Get the display symbol orientation.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:183
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 DisableGridSnapping() const
Definition tool_event.h:371
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:251
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
@ LINE_MODE_90
@ LINE_MODE_45
@ LINE_MODE_FREE
@ 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