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