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