KiCad PCB EDA Suite
Loading...
Searching...
No Matches
symbol_editor_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
26
27#include <tool/picker_tool.h>
33#include <clipboard.h>
34#include <sch_actions.h>
35#include <increment.h>
36#include <pin_layout_cache.h>
37#include <string_utils.h>
38#include <symbol_edit_frame.h>
39#include <sch_commit.h>
41#include <dialogs/dialog_text_properties.h>
46#include <view/view_controls.h>
47#include <richio.h>
49#include <sch_textbox.h>
51#include <wx/textdlg.h> // for wxTextEntryDialog
52#include <math/util.h> // for KiROUND
54#include <trace_helpers.h>
55
57 SCH_TOOL_BASE( "eeschema.SymbolEditTool" )
58{
59}
60
61
62const std::vector<KICAD_T> SYMBOL_EDITOR_EDIT_TOOL::SwappableItems = {
63 LIB_SYMBOL_T, // Allows swapping the anchor
69};
70
71
73{
75
78
79 wxASSERT_MSG( drawingTools, "eeschema.SymbolDrawing tool is not available" );
80
81 auto haveSymbolCondition =
82 [&]( const SELECTION& sel )
83 {
84 return m_isSymbolEditor && m_frame->GetCurSymbol();
85 };
86
87 auto canEdit =
88 [&]( const SELECTION& sel )
89 {
90 if( !m_frame->IsSymbolEditable() )
91 return false;
92
93 if( m_frame->IsSymbolAlias() )
94 {
95 for( EDA_ITEM* item : sel )
96 {
97 if( item->Type() != SCH_FIELD_T )
98 return false;
99 }
100 }
101
102 return true;
103 };
104
105 auto swapSelectionCondition =
107
108 const auto canCopyText = SCH_CONDITIONS::OnlyTypes( {
112 SCH_PIN_T,
115 } );
116
117 const auto canConvertStackedPins =
118 [&]( const SELECTION& sel )
119 {
120 // If multiple pins are selected, check they are all at same location
121 if( sel.Size() >= 2 )
122 {
123 std::vector<SCH_PIN*> pins;
124 for( EDA_ITEM* item : sel )
125 {
126 if( item->Type() != SCH_PIN_T )
127 return false;
128 pins.push_back( static_cast<SCH_PIN*>( item ) );
129 }
130
131 // Check that all pins are at the same location
132 VECTOR2I pos = pins[0]->GetPosition();
133 for( size_t i = 1; i < pins.size(); ++i )
134 {
135 if( pins[i]->GetPosition() != pos )
136 return false;
137 }
138 return true;
139 }
140
141 // If single pin is selected, check if there are other pins at same location
142 if( sel.Size() == 1 && sel.Front()->Type() == SCH_PIN_T )
143 {
144 SCH_PIN* selectedPin = static_cast<SCH_PIN*>( sel.Front() );
145 VECTOR2I pos = selectedPin->GetPosition();
146
147 // Get the symbol and check for other pins at same location
148 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
149 if( !symbol )
150 return false;
151
152 int coLocatedCount = 0;
153
154 for( SCH_PIN* pin : symbol->GetPins() )
155 {
156 if( pin->GetPosition() == pos )
157 {
158 coLocatedCount++;
159
160 if( coLocatedCount >= 2 )
161 return true;
162 }
163 }
164 }
165
166 return false;
167 };
168
169 const auto canExplodeStackedPin =
170 [&]( const SELECTION& sel )
171 {
172 if( sel.Size() != 1 || sel.Front()->Type() != SCH_PIN_T )
173 return false;
174
175 SCH_PIN* pin = static_cast<SCH_PIN*>( sel.Front() );
176 bool isValid;
177 std::vector<wxString> stackedNumbers = pin->GetStackedPinNumbers( &isValid );
178 return isValid && stackedNumbers.size() > 1;
179 };
180
181 // clang-format off
182 // Add edit actions to the move tool menu
183 if( moveTool )
184 {
185 CONDITIONAL_MENU& moveMenu = moveTool->GetToolMenu().GetMenu();
186
187 moveMenu.AddSeparator( 200 );
188 moveMenu.AddItem( SCH_ACTIONS::rotateCCW, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
189 moveMenu.AddItem( SCH_ACTIONS::rotateCW, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
190 moveMenu.AddItem( SCH_ACTIONS::mirrorV, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
191 moveMenu.AddItem( SCH_ACTIONS::mirrorH, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
192
193 moveMenu.AddItem( SCH_ACTIONS::swap, swapSelectionCondition, 200 );
194 moveMenu.AddItem( SCH_ACTIONS::properties, canEdit && SCH_CONDITIONS::Count( 1 ), 200 );
195
196 moveMenu.AddSeparator( 300 );
199 moveMenu.AddItem( ACTIONS::copyAsText, canCopyText && SCH_CONDITIONS::IdleSelection, 300 );
200 moveMenu.AddItem( ACTIONS::duplicate, canEdit && SCH_CONDITIONS::NotEmpty, 300 );
201 moveMenu.AddItem( ACTIONS::doDelete, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
202
203 moveMenu.AddSeparator( 400 );
204 moveMenu.AddItem( ACTIONS::selectAll, haveSymbolCondition, 400 );
205 moveMenu.AddItem( ACTIONS::unselectAll, haveSymbolCondition, 400 );
206 }
207
208 // Add editing actions to the drawing tool menu
209 CONDITIONAL_MENU& drawMenu = drawingTools->GetToolMenu().GetMenu();
210
211 drawMenu.AddSeparator( 200 );
216
217 drawMenu.AddItem( SCH_ACTIONS::properties, canEdit && SCH_CONDITIONS::Count( 1 ), 200 );
218
219 // Add editing actions to the selection tool menu
220 CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
221
222 selToolMenu.AddItem( SCH_ACTIONS::rotateCCW, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
223 selToolMenu.AddItem( SCH_ACTIONS::rotateCW, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
224 selToolMenu.AddItem( SCH_ACTIONS::mirrorV, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
225 selToolMenu.AddItem( SCH_ACTIONS::mirrorH, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
226
227 selToolMenu.AddItem( SCH_ACTIONS::swap, swapSelectionCondition, 200 );
228 selToolMenu.AddItem( SCH_ACTIONS::properties, canEdit && SCH_CONDITIONS::Count( 1 ), 200 );
229
230 selToolMenu.AddSeparator( 250 );
231 selToolMenu.AddItem( SCH_ACTIONS::convertStackedPins, canEdit && canConvertStackedPins, 250 );
232 selToolMenu.AddItem( SCH_ACTIONS::explodeStackedPin, canEdit && canExplodeStackedPin, 250 );
233
234 selToolMenu.AddSeparator( 300 );
237 selToolMenu.AddItem( ACTIONS::copyAsText, canCopyText && SCH_CONDITIONS::IdleSelection, 300 );
238 selToolMenu.AddItem( ACTIONS::paste, canEdit && SCH_CONDITIONS::Idle, 300 );
239 selToolMenu.AddItem( ACTIONS::duplicate, canEdit && SCH_CONDITIONS::NotEmpty, 300 );
240 selToolMenu.AddItem( ACTIONS::doDelete, canEdit && SCH_CONDITIONS::NotEmpty, 300 );
241
242 selToolMenu.AddSeparator( 400 );
243 selToolMenu.AddItem( ACTIONS::selectAll, haveSymbolCondition, 400 );
244 selToolMenu.AddItem( ACTIONS::unselectAll, haveSymbolCondition, 400 );
245 // clang-format on
246
247 return true;
248}
249
250
252{
253 SCH_SELECTION& selection = m_selectionTool->RequestSelection();
254
255 if( selection.GetSize() == 0 )
256 return 0;
257
258 VECTOR2I rotPoint;
259 bool ccw = ( aEvent.Matches( SCH_ACTIONS::rotateCCW.MakeEvent() ) );
260 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
261 SCH_COMMIT localCommit( m_toolMgr );
262 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
263
264 if( !commit )
265 commit = &localCommit;
266
267 if( !item->IsMoving() )
268 commit->Modify( m_frame->GetCurSymbol(), m_frame->GetScreen(), RECURSE_MODE::RECURSE );
269
270 if( selection.GetSize() == 1 )
271 rotPoint = item->GetPosition();
272 else
273 rotPoint = m_frame->GetNearestHalfGridPosition( selection.GetCenter() );
274
275 for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
276 {
277 item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
278 item->Rotate( rotPoint, ccw );
279 m_frame->UpdateItem( item, false, true );
280 }
281
282 if( item->IsMoving() )
283 {
285 }
286 else
287 {
288 if( selection.IsHover() )
290
291 if( !localCommit.Empty() )
292 localCommit.Push( _( "Rotate" ) );
293 }
294
295 return 0;
296}
297
298
300{
301 SCH_SELECTION& selection = m_selectionTool->RequestSelection();
302
303 if( selection.GetSize() == 0 )
304 return 0;
305
306 VECTOR2I mirrorPoint;
307 bool xAxis = ( aEvent.Matches( SCH_ACTIONS::mirrorV.MakeEvent() ) );
308 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
309
310 if( !item->IsMoving() )
312
313 if( selection.GetSize() == 1 )
314 {
315 mirrorPoint = item->GetPosition();
316
317 switch( item->Type() )
318 {
319 case SCH_FIELD_T:
320 {
321 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
322
323 if( xAxis )
325 else
327
328 break;
329 }
330
331 default:
332 if( xAxis )
333 item->MirrorVertically( mirrorPoint.y );
334 else
335 item->MirrorHorizontally( mirrorPoint.x );
336
337 break;
338 }
339
340
341 m_frame->UpdateItem( item, false, true );
342 }
343 else
344 {
345 mirrorPoint = m_frame->GetNearestHalfGridPosition( selection.GetCenter() );
346
347 for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
348 {
349 item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
350
351 if( xAxis )
352 item->MirrorVertically( mirrorPoint.y );
353 else
354 item->MirrorHorizontally( mirrorPoint.x );
355
356 m_frame->UpdateItem( item, false, true );
357 }
358 }
359
360 if( item->IsMoving() )
361 {
363 }
364 else
365 {
366 if( selection.IsHover() )
368
369 m_frame->OnModify();
370 }
371
372 return 0;
373}
375{
376 SCH_SELECTION& selection = m_selectionTool->RequestSelection( SwappableItems );
377 std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
378
379 if( selection.Size() < 2 )
380 return 0;
381
382 EDA_ITEM* front = selection.Front();
383 bool isMoving = front->IsMoving();
384
385 // Save copy for undo if not in edit (edit command already handle the save copy)
386 if( front->GetEditFlags() == 0 )
388
389 for( size_t i = 0; i < sorted.size() - 1; i++ )
390 {
391 SCH_ITEM* a = static_cast<SCH_ITEM*>( sorted[i] );
392 SCH_ITEM* b = static_cast<SCH_ITEM*>( sorted[( i + 1 ) % sorted.size()] );
393
394 VECTOR2I aPos = a->GetPosition(), bPos = b->GetPosition();
395 std::swap( aPos, bPos );
396
397 a->SetPosition( aPos );
398 b->SetPosition( bPos );
399
400 // Special case some common swaps
401 if( a->Type() == b->Type() )
402 {
403 switch( a->Type() )
404 {
405 case SCH_PIN_T:
406 {
407 SCH_PIN* aPin = static_cast<SCH_PIN*>( a );
408 SCH_PIN* bBpin = static_cast<SCH_PIN*>( b );
409
410 PIN_ORIENTATION aOrient = aPin->GetOrientation();
411 PIN_ORIENTATION bOrient = bBpin->GetOrientation();
412
413 aPin->SetOrientation( bOrient );
414 bBpin->SetOrientation( aOrient );
415
416 break;
417 }
418 default: break;
419 }
420 }
421
422 m_frame->UpdateItem( a, false, true );
423 m_frame->UpdateItem( b, false, true );
424 }
425
426 // Update R-Tree for modified items
427 for( EDA_ITEM* selected : selection )
428 updateItem( selected, true );
429
430 if( isMoving )
431 {
432 m_toolMgr->PostAction( ACTIONS::refreshPreview );
433 }
434 else
435 {
436 if( selection.IsHover() )
438
439 m_frame->OnModify();
440 }
441
442 return 0;
443}
444
445
446static std::vector<KICAD_T> nonFields =
447{
453};
454
455
457{
458 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
459 std::deque<EDA_ITEM*> items = m_selectionTool->RequestSelection().GetItems();
460 SCH_COMMIT commit( m_frame );
461
462 if( items.empty() )
463 return 0;
464
465 // Don't leave a freed pointer in the selection
467
468 commit.Modify( symbol, m_frame->GetScreen() );
469
470 std::set<SCH_ITEM*> toDelete;
471 int fieldsHidden = 0;
472 int fieldsAlreadyHidden = 0;
473
474 for( EDA_ITEM* item : items )
475 {
476 if( item->Type() == SCH_PIN_T )
477 {
478 SCH_PIN* curr_pin = static_cast<SCH_PIN*>( item );
479 VECTOR2I pos = curr_pin->GetPosition();
480
481 toDelete.insert( curr_pin );
482
483 // when pin editing is synchronized, pins in the same position, with the same name
484 // in different units are also removed. But only one pin per unit (matching)
485 if( m_frame->SynchronizePins() )
486 {
487 std::vector<bool> got_unit( symbol->GetUnitCount() + 1 );
488
489 got_unit[curr_pin->GetUnit()] = true;
490
491 for( SCH_PIN* pin : symbol->GetPins() )
492 {
493 if( got_unit[pin->GetUnit()] )
494 continue;
495
496 if( pin->GetPosition() != pos )
497 continue;
498
499 if( pin->GetBodyStyle() != curr_pin->GetBodyStyle() )
500 continue;
501
502 if( pin->GetType() != curr_pin->GetType() )
503 continue;
504
505 if( pin->GetName() != curr_pin->GetName() )
506 continue;
507
508 toDelete.insert( pin );
509 got_unit[pin->GetUnit()] = true;
510 }
511 }
512 }
513 else if( item->Type() == SCH_FIELD_T )
514 {
515 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
516
517 // Hide "deleted" fields
518 if( field->IsVisible() )
519 {
520 field->SetVisible( false );
521 fieldsHidden++;
522 }
523 else
524 {
525 fieldsAlreadyHidden++;
526 }
527 }
528 else if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
529 {
530 toDelete.insert( schItem );
531 }
532 }
533
534 for( SCH_ITEM* item : toDelete )
535 symbol->RemoveDrawItem( item );
536
537 if( toDelete.size() == 0 )
538 {
539 if( fieldsHidden == 1 )
540 commit.Push( _( "Hide Field" ) );
541 else if( fieldsHidden > 1 )
542 commit.Push( _( "Hide Fields" ) );
543 else if( fieldsAlreadyHidden > 0 )
544 m_frame->ShowInfoBarError( _( "Use the Symbol Properties dialog to remove fields." ) );
545 }
546 else
547 {
548 commit.Push( _( "Delete" ) );
549 }
550
551 m_frame->RebuildView();
552 return 0;
553}
554
555
557{
558 SCH_SELECTION& selection = m_selectionTool->RequestSelection();
559
560 if( selection.Empty() || aEvent.IsAction( &SCH_ACTIONS::symbolProperties ) )
561 {
562 // If called from tree context menu, edit properties without loading into canvas
564 {
565 LIB_ID treeLibId = m_frame->GetTreeLIBID();
566
567 // Check if the selected symbol in tree is different from the currently loaded one
568 if( treeLibId.IsValid() &&
569 ( !m_frame->GetCurSymbol() || m_frame->GetCurSymbol()->GetLibId() != treeLibId ) )
570 {
571 // Edit properties directly from library buffer without loading to canvas
573 return 0;
574 }
575 }
576
577 if( m_frame->GetCurSymbol() )
579 }
580 else if( selection.Size() == 1 )
581 {
582 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
583
584 // Save copy for undo if not in edit (edit command already handle the save copy)
585 if( item->GetEditFlags() == 0 )
587
588 switch( item->Type() )
589 {
590 case SCH_PIN_T:
591 {
592 SCH_PIN& pin = static_cast<SCH_PIN&>( *item );
593
594 // Mouse, not cursor, as grid points may well not be under any text
595 const VECTOR2I& mousePos = m_toolMgr->GetMousePosition();
596 PIN_LAYOUT_CACHE& layout = pin.GetLayoutCache();
597
598 bool mouseOverNumber = false;
599 if( OPT_BOX2I numberBox = layout.GetPinNumberBBox() )
600 {
601 mouseOverNumber = numberBox->Contains( mousePos );
602 }
603
604 if( SYMBOL_EDITOR_PIN_TOOL* pinTool = m_toolMgr->GetTool<SYMBOL_EDITOR_PIN_TOOL>() )
605 pinTool->EditPinProperties( &pin, mouseOverNumber );
606
607 break;
608 }
609 case SCH_SHAPE_T:
610 editShapeProperties( static_cast<SCH_SHAPE*>( item ) );
611 break;
612
613 case SCH_TEXT_T:
614 editTextProperties( item );
615 break;
616
617 case SCH_TEXTBOX_T:
618 editTextBoxProperties( item );
619 break;
620
621 case SCH_FIELD_T:
622 editFieldProperties( static_cast<SCH_FIELD*>( item ) );
623 break;
624
625 default:
626 wxFAIL_MSG( wxT( "Unhandled item <" ) + item->GetClass() + wxT( ">" ) );
627 break;
628 }
629 }
630
631 if( selection.IsHover() )
633
634 return 0;
635}
636
637
639{
640 DIALOG_SHAPE_PROPERTIES dlg( m_frame, aShape );
641
642 if( dlg.ShowModal() != wxID_OK )
643 return;
644
645 updateItem( aShape, true );
646 m_frame->GetCanvas()->Refresh();
647 m_frame->OnModify();
648
651 drawingTools->SetDrawSpecificUnit( !dlg.GetApplyToAllUnits() );
652
653 std::vector<MSG_PANEL_ITEM> items;
654 aShape->GetMsgPanelInfo( m_frame, items );
655 m_frame->SetMsgPanel( items );
656}
657
658
660{
661 if ( aItem->Type() != SCH_TEXT_T )
662 return;
663
664 DIALOG_TEXT_PROPERTIES dlg( m_frame, static_cast<SCH_TEXT*>( aItem ) );
665
666 if( dlg.ShowModal() != wxID_OK )
667 return;
668
669 updateItem( aItem, true );
670 m_frame->GetCanvas()->Refresh();
671 m_frame->OnModify( );
672}
673
674
676{
677 if ( aItem->Type() != SCH_TEXTBOX_T )
678 return;
679
680 DIALOG_TEXT_PROPERTIES dlg( m_frame, static_cast<SCH_TEXTBOX*>( aItem ) );
681
682 if( dlg.ShowModal() != wxID_OK )
683 return;
684
685 updateItem( aItem, true );
686 m_frame->GetCanvas()->Refresh();
687 m_frame->OnModify( );
688}
689
690
692{
693 if( aField == nullptr )
694 return;
695
696 wxString caption;
697
698 if( aField->IsMandatory() )
699 caption.Printf( _( "Edit %s Field" ), TitleCaps( aField->GetName() ) );
700 else
701 caption.Printf( _( "Edit '%s' Field" ), aField->GetName() );
702
703 DIALOG_FIELD_PROPERTIES dlg( m_frame, caption, aField );
704
705 // The dialog may invoke a kiway player for footprint fields
706 // so we must use a quasimodal dialog.
707 if( dlg.ShowQuasiModal() != wxID_OK )
708 return;
709
710 SCH_COMMIT commit( m_toolMgr );
711 commit.Modify( aField, m_frame->GetScreen() );
712
713 dlg.UpdateField( aField );
714
715 commit.Push( caption );
716
717 m_frame->GetCanvas()->Refresh();
718 m_frame->UpdateSymbolMsgPanelInfo();
719}
720
721
723{
724 LIB_SYMBOL_LIBRARY_MANAGER& libMgr = m_frame->GetLibManager();
725 wxString libName = aLibId.GetLibNickname();
726 wxString symbolName = aLibId.GetLibItemName();
727
728 // Get the symbol from the library buffer (without loading it into the editor)
729 LIB_SYMBOL* bufferedSymbol = libMgr.GetBufferedSymbol( symbolName, libName );
730
731 if( !bufferedSymbol )
732 return;
733
734 // Create a copy to work with
735 LIB_SYMBOL tempSymbol( *bufferedSymbol );
736
739
740 DIALOG_LIB_SYMBOL_PROPERTIES dlg( m_frame, &tempSymbol );
741
742 // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
743 // frame. Therefore this dialog as a modal frame parent, MUST be run under
744 // quasimodal mode for the quasimodal frame support to work. So don't use
745 // the QUASIMODAL macros here.
746 if( dlg.ShowQuasiModal() != wxID_OK )
747 return;
748
749 // Update the buffered symbol with the changes
750 libMgr.UpdateSymbol( &tempSymbol, libName );
751
752 // Mark the library as modified
753 libMgr.SetSymbolModified( symbolName, libName );
754
755 // Update the tree view
756 wxDataViewItem treeItem = libMgr.GetAdapter()->FindItem( aLibId );
757 m_frame->UpdateLibraryTree( treeItem, &tempSymbol );
758}
759
760
762{
763 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
764 bool partLocked = symbol->UnitsLocked();
765
768
770
771 // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
772 // frame. Therefore this dialog as a modal frame parent, MUST be run under
773 // quasimodal mode for the quasimodal frame support to work. So don't use
774 // the QUASIMODAL macros here.
775 if( dlg.ShowQuasiModal() != wxID_OK )
776 return;
777
778 m_frame->RebuildSymbolUnitAndBodyStyleLists();
779 m_frame->OnModify();
780
781 // if m_UnitSelectionLocked has changed, set some edit options or defaults
782 // to the best value
783 if( partLocked != symbol->UnitsLocked() )
784 {
786
787 // Enable synchronized pin edit mode for symbols with interchangeable units
788 m_frame->m_SyncPinEdit = !symbol->UnitsLocked();
789
790 // also set default edit options to the better value
791 // Usually if units are locked, graphic items are specific to each unit
792 // and if units are interchangeable, graphic items are common to units
793 tools->SetDrawSpecificUnit( symbol->UnitsLocked() );
794 }
795}
796
797
799{
800 SCH_COMMIT commit( m_frame );
801 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
802
803 if( !symbol )
804 return 0;
805
806 commit.Modify( symbol, m_frame->GetScreen() );
807
808 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
809 wxCHECK( selTool, -1 );
810
811 std::vector<SCH_PIN*> selectedPins;
812
813 SCH_SELECTION& selection = selTool->GetSelection();
814
815 for( EDA_ITEM* item : selection )
816 {
817 if( item->Type() == SCH_PIN_T )
818 {
819 SCH_PIN* pinItem = static_cast<SCH_PIN*>( item );
820 selectedPins.push_back( pinItem );
821 }
822 }
823
824 // And now clear the selection so if we change the pins we don't have dangling pointers
825 // in the selection.
827
828 DIALOG_LIB_EDIT_PIN_TABLE dlg( m_frame, symbol, selectedPins );
829
830 if( dlg.ShowModal() == wxID_CANCEL )
831 return -1;
832
833 commit.Push( _( "Edit Pins" ) );
834 m_frame->RebuildView();
835
836 return 0;
837}
838
839
841{
842 SCH_COMMIT commit( m_frame );
843 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
844
845 if( !symbol )
846 return 0;
847
848 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
849 wxCHECK( selTool, -1 );
850
851 SCH_SELECTION& selection = selTool->GetSelection();
852
853 // Collect pins to convert - accept pins with any number format
854 std::vector<SCH_PIN*> pinsToConvert;
855
856 if( selection.Size() == 1 && selection.Front()->Type() == SCH_PIN_T )
857 {
858 // Single pin selected - find all pins at the same location
859 SCH_PIN* selectedPin = static_cast<SCH_PIN*>( selection.Front() );
860 VECTOR2I pos = selectedPin->GetPosition();
861
862 for( SCH_PIN* pin : symbol->GetPins() )
863 {
864 if( pin->GetPosition() == pos )
865 pinsToConvert.push_back( pin );
866 }
867 }
868 else
869 {
870 // Multiple pins selected - use them directly, accepting any pin numbers
871 for( EDA_ITEM* item : selection )
872 {
873 if( item->Type() == SCH_PIN_T )
874 pinsToConvert.push_back( static_cast<SCH_PIN*>( item ) );
875 }
876 }
877
878 if( pinsToConvert.size() < 2 )
879 {
880 m_frame->ShowInfoBarError( _( "At least two pins are needed to convert to stacked pins" ) );
881 return 0;
882 }
883
884 // Check that all pins are at the same location
885 VECTOR2I pos = pinsToConvert[0]->GetPosition();
886 for( size_t i = 1; i < pinsToConvert.size(); ++i )
887 {
888 if( pinsToConvert[i]->GetPosition() != pos )
889 {
890 m_frame->ShowInfoBarError( _( "All pins must be at the same location" ) );
891 return 0;
892 }
893 }
894
895 commit.Modify( symbol, m_frame->GetScreen() );
896
897 // Clear selection before modifying pins, like the Delete command does
899
900 // Sort pins for consistent ordering - handle arbitrary pin number formats
901 std::sort( pinsToConvert.begin(), pinsToConvert.end(),
902 []( SCH_PIN* a, SCH_PIN* b )
903 {
904 wxString numA = a->GetNumber();
905 wxString numB = b->GetNumber();
906
907 // Try to convert to integers for proper numeric sorting
908 long longA, longB;
909 bool aIsNumeric = numA.ToLong( &longA );
910 bool bIsNumeric = numB.ToLong( &longB );
911
912 // Both are purely numeric - sort numerically
913 if( aIsNumeric && bIsNumeric )
914 return longA < longB;
915
916 // Mixed numeric/non-numeric - numeric pins come first
917 if( aIsNumeric && !bIsNumeric )
918 return true;
919 if( !aIsNumeric && bIsNumeric )
920 return false;
921
922 // Both non-numeric or mixed alphanumeric - use lexicographic sorting
923 return numA < numB;
924 });
925
926 // Build the stacked notation string with range collapsing
927 wxString stackedNotation = wxT("[");
928
929 // Helper function to collapse consecutive numbers into ranges - handles arbitrary pin formats
930 auto collapseRanges = [&]() -> wxString
931 {
932 if( pinsToConvert.empty() )
933 return wxT("");
934
935 wxString result;
936
937 // Group pins by their alphanumeric prefix for range collapsing
938 std::map<wxString, std::vector<long>> prefixGroups;
939 std::vector<wxString> nonNumericPins;
940
941 // Parse each pin number to separate prefix from numeric suffix
942 for( SCH_PIN* pin : pinsToConvert )
943 {
944 wxString pinNumber = pin->GetNumber();
945
946 // Skip empty pin numbers (shouldn't happen, but be defensive)
947 if( pinNumber.IsEmpty() )
948 {
949 nonNumericPins.push_back( wxT("(empty)") );
950 continue;
951 }
952
953 wxString prefix;
954 wxString numericPart;
955
956 // Find where numeric part starts (scan from end)
957 size_t numStart = pinNumber.length();
958 for( int i = pinNumber.length() - 1; i >= 0; i-- )
959 {
960 if( !wxIsdigit( pinNumber[i] ) )
961 {
962 numStart = i + 1;
963 break;
964 }
965 if( i == 0 ) // All digits
966 numStart = 0;
967 }
968
969 if( numStart < pinNumber.length() ) // Has numeric suffix
970 {
971 prefix = pinNumber.Left( numStart );
972 numericPart = pinNumber.Mid( numStart );
973
974 long numValue;
975 if( numericPart.ToLong( &numValue ) && numValue >= 0 ) // Valid non-negative number
976 {
977 prefixGroups[prefix].push_back( numValue );
978 }
979 else
980 {
981 // Numeric part couldn't be parsed or is negative - treat as non-numeric
982 nonNumericPins.push_back( pinNumber );
983 }
984 }
985 else // No numeric suffix - consolidate as individual value
986 {
987 nonNumericPins.push_back( pinNumber );
988 }
989 }
990
991 // Process each prefix group
992 for( auto& [prefix, numbers] : prefixGroups )
993 {
994 if( !result.IsEmpty() )
995 result += wxT(",");
996
997 // Sort numeric values for this prefix
998 std::sort( numbers.begin(), numbers.end() );
999
1000 // Collapse consecutive ranges within this prefix
1001 size_t i = 0;
1002 while( i < numbers.size() )
1003 {
1004 if( i > 0 ) // Not first number in this prefix group
1005 result += wxT(",");
1006
1007 long start = numbers[i];
1008 long end = start;
1009
1010 // Find the end of consecutive sequence
1011 while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 )
1012 {
1013 i++;
1014 end = numbers[i];
1015 }
1016
1017 // Add range or single number with prefix
1018 if( end > start + 1 ) // Range of 3+ numbers
1019 result += wxString::Format( wxT("%s%ld-%s%ld"), prefix, start, prefix, end );
1020 else if( end == start + 1 ) // Two consecutive numbers
1021 result += wxString::Format( wxT("%s%ld,%s%ld"), prefix, start, prefix, end );
1022 else // Single number
1023 result += wxString::Format( wxT("%s%ld"), prefix, start );
1024
1025 i++;
1026 }
1027 }
1028
1029 // Add non-numeric pin numbers as individual comma-separated values
1030 for( const wxString& nonNum : nonNumericPins )
1031 {
1032 if( !result.IsEmpty() )
1033 result += wxT(",");
1034 result += nonNum;
1035 }
1036
1037 return result;
1038 };
1039
1040 stackedNotation += collapseRanges();
1041 stackedNotation += wxT("]");
1042
1043 // Keep the first pin and give it the stacked notation
1044 SCH_PIN* masterPin = pinsToConvert[0];
1045 masterPin->SetNumber( stackedNotation );
1046
1047 // Log information about pins being removed before we remove them
1048 wxLogTrace( traceStackedPins,
1049 wxString::Format( "Converting %zu pins to stacked notation '%s'",
1050 pinsToConvert.size(), stackedNotation ) );
1051
1052 // Remove all other pins from the symbol that were consolidated into the stacked notation
1053 // Collect pins to remove first, then remove them all at once like the Delete command
1054 std::vector<SCH_PIN*> pinsToRemove;
1055 for( size_t i = 1; i < pinsToConvert.size(); ++i )
1056 {
1057 SCH_PIN* pinToRemove = pinsToConvert[i];
1058
1059 // Log the pin before removing it
1060 wxLogTrace( traceStackedPins,
1061 wxString::Format( "Will remove pin '%s' at position (%d, %d)",
1062 pinToRemove->GetNumber(),
1063 pinToRemove->GetPosition().x,
1064 pinToRemove->GetPosition().y ) );
1065
1066 pinsToRemove.push_back( pinToRemove );
1067 }
1068
1069 // Remove all pins at once, like the Delete command does
1070 for( SCH_PIN* pin : pinsToRemove )
1071 {
1072 symbol->RemoveDrawItem( pin );
1073 }
1074
1075 commit.Push( wxString::Format( _( "Convert %zu Stacked Pins to '%s'" ),
1076 pinsToConvert.size(), stackedNotation ) );
1077 m_frame->RebuildView();
1078 return 0;
1079}
1080
1081
1083{
1084 SCH_COMMIT commit( m_frame );
1085 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1086
1087 if( !symbol )
1088 return 0;
1089
1090 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1091 wxCHECK( selTool, -1 );
1092
1093 SCH_SELECTION& selection = selTool->GetSelection();
1094
1095 if( selection.GetSize() != 1 || selection.Front()->Type() != SCH_PIN_T )
1096 {
1097 m_frame->ShowInfoBarError( _( "Select a single pin with stacked notation to explode" ) );
1098 return 0;
1099 }
1100
1101 SCH_PIN* pin = static_cast<SCH_PIN*>( selection.Front() );
1102
1103 // Check if the pin has stacked notation
1104 bool isValid;
1105 std::vector<wxString> stackedNumbers = pin->GetStackedPinNumbers( &isValid );
1106
1107 if( !isValid || stackedNumbers.size() <= 1 )
1108 {
1109 m_frame->ShowInfoBarError( _( "Selected pin does not have valid stacked notation" ) );
1110 return 0;
1111 }
1112
1113 commit.Modify( symbol, m_frame->GetScreen() );
1114
1115 // Clear selection before modifying pins
1116 m_toolMgr->RunAction( ACTIONS::selectionClear );
1117
1118 // Sort the stacked numbers to find the smallest one
1119 std::sort( stackedNumbers.begin(), stackedNumbers.end(),
1120 []( const wxString& a, const wxString& b )
1121 {
1122 // Try to convert to integers for proper numeric sorting
1123 long numA, numB;
1124 if( a.ToLong( &numA ) && b.ToLong( &numB ) )
1125 return numA < numB;
1126
1127 // Fall back to string comparison if not numeric
1128 return a < b;
1129 });
1130
1131 // Change the original pin to use the first (smallest) number and make it visible
1132 pin->SetNumber( stackedNumbers[0] );
1133 pin->SetVisible( true );
1134
1135 // Create additional pins for the remaining numbers and make them invisible
1136 for( size_t i = 1; i < stackedNumbers.size(); ++i )
1137 {
1138 SCH_PIN* newPin = new SCH_PIN( symbol );
1139
1140 // Copy all properties from the original pin
1141 newPin->SetPosition( pin->GetPosition() );
1142 newPin->SetOrientation( pin->GetOrientation() );
1143 newPin->SetShape( pin->GetShape() );
1144 newPin->SetLength( pin->GetLength() );
1145 newPin->SetType( pin->GetType() );
1146 newPin->SetName( pin->GetName() );
1147 newPin->SetNumber( stackedNumbers[i] );
1148 newPin->SetNameTextSize( pin->GetNameTextSize() );
1149 newPin->SetNumberTextSize( pin->GetNumberTextSize() );
1150 newPin->SetUnit( pin->GetUnit() );
1151 newPin->SetBodyStyle( pin->GetBodyStyle() );
1152 newPin->SetVisible( false ); // Make all other pins invisible
1153
1154 // Add the new pin to the symbol
1155 symbol->AddDrawItem( newPin );
1156 }
1157
1158 commit.Push( _( "Explode Stacked Pin" ) );
1159 m_frame->RebuildView();
1160 return 0;
1161}
1162
1163
1165{
1166 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1167
1168 if( !symbol )
1169 return 0;
1170
1171 if( !symbol->IsDerived() )
1172 {
1173 m_frame->ShowInfoBarError( _( "Symbol is not derived from another symbol." ) );
1174 }
1175 else
1176 {
1177 DIALOG_UPDATE_SYMBOL_FIELDS dlg( m_frame, symbol );
1178
1179 if( dlg.ShowModal() == wxID_CANCEL )
1180 return -1;
1181 }
1182
1183 return 0;
1184}
1185
1186
1188{
1189 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1190
1191 // Nuke the selection for later rebuilding. This does *not* clear the flags on any items;
1192 // it just clears the SELECTION's reference to them.
1193 selTool->GetSelection().Clear();
1194 {
1195 m_frame->GetSymbolFromUndoList();
1196 }
1197 selTool->RebuildSelection();
1198
1199 return 0;
1200}
1201
1202
1204{
1205 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1206
1207 // Nuke the selection for later rebuilding. This does *not* clear the flags on any items;
1208 // it just clears the SELECTION's reference to them.
1209 selTool->GetSelection().Clear();
1210 {
1211 m_frame->GetSymbolFromRedoList();
1212 }
1213 selTool->RebuildSelection();
1214
1215 return 0;
1216}
1217
1218
1220{
1221 int retVal = Copy( aEvent );
1222
1223 if( retVal == 0 )
1224 retVal = DoDelete( aEvent );
1225
1226 return retVal;
1227}
1228
1229
1231{
1232 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1233 SCH_SELECTION& selection = m_selectionTool->RequestSelection( nonFields );
1234
1235 if( !symbol || !selection.GetSize() )
1236 return 0;
1237
1238 for( SCH_ITEM& item : symbol->GetDrawItems() )
1239 {
1240 if( item.Type() == SCH_FIELD_T )
1241 continue;
1242
1243 wxASSERT( !item.HasFlag( STRUCT_DELETED ) );
1244
1245 if( !item.IsSelected() )
1246 item.SetFlags( STRUCT_DELETED );
1247 }
1248
1249 LIB_SYMBOL* partCopy = new LIB_SYMBOL( *symbol );
1250
1251 STRING_FORMATTER formatter;
1252 SCH_IO_KICAD_SEXPR::FormatLibSymbol( partCopy, formatter );
1253
1254 delete partCopy;
1255
1256 for( SCH_ITEM& item : symbol->GetDrawItems() )
1257 item.ClearFlags( STRUCT_DELETED );
1258
1259 std::string prettyData = formatter.GetString();
1260 KICAD_FORMAT::Prettify( prettyData, KICAD_FORMAT::FORMAT_MODE::COMPACT_TEXT_PROPERTIES );
1261
1262 if( SaveClipboard( prettyData ) )
1263 return 0;
1264 else
1265 return -1;
1266}
1267
1268
1270{
1271 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1272 SCH_SELECTION& selection = selTool->RequestSelection();
1273
1274 if( selection.Empty() )
1275 return 0;
1276
1277 wxString itemsAsText = GetSelectedItemsAsText( selection );
1278
1279 if( selection.IsHover() )
1280 m_toolMgr->RunAction( ACTIONS::selectionClear );
1281
1282 return SaveClipboard( itemsAsText.ToStdString() );
1283}
1284
1285
1287{
1288 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1289 LIB_SYMBOL* newPart = nullptr;
1290
1291 if( !symbol || symbol->IsDerived() )
1292 return 0;
1293
1294 std::string clipboardData = GetClipboardUTF8();
1295
1296 try
1297 {
1298 std::vector<LIB_SYMBOL*> newParts = SCH_IO_KICAD_SEXPR::ParseLibSymbols( clipboardData, "Clipboard" );
1299
1300 if( newParts.empty() || !newParts[0] )
1301 return -1;
1302
1303 newPart = newParts[0];
1304 }
1305 catch( IO_ERROR& )
1306 {
1307 // If it's not a symbol then paste as text
1308 newPart = new LIB_SYMBOL( "dummy_part" );
1309
1310 wxString pasteText( clipboardData );
1311
1312 // Limit of 5000 is totally arbitrary. Without a limit, pasting a bitmap image from
1313 // eeschema makes KiCad appear to hang.
1314 if( pasteText.Length() > 5000 )
1315 pasteText = pasteText.Left( 5000 ) + wxT( "..." );
1316
1317 SCH_TEXT* newText = new SCH_TEXT( { 0, 0 }, pasteText, LAYER_DEVICE );
1318 newPart->AddDrawItem( newText );
1319 }
1320
1321 SCH_COMMIT commit( m_toolMgr );
1322
1323 commit.Modify( symbol, m_frame->GetScreen() );
1324 m_selectionTool->ClearSelection();
1325
1326 for( SCH_ITEM& item : symbol->GetDrawItems() )
1327 item.ClearFlags( IS_NEW | IS_PASTED | SELECTED );
1328
1329 for( SCH_ITEM& item : newPart->GetDrawItems() )
1330 {
1331 if( item.Type() == SCH_FIELD_T )
1332 continue;
1333
1334 SCH_ITEM* newItem = item.Duplicate( true, &commit );
1335 newItem->SetParent( symbol );
1336 newItem->SetFlags( IS_NEW | IS_PASTED | SELECTED );
1337
1338 newItem->SetUnit( newItem->GetUnit() ? m_frame->GetUnit() : 0 );
1339 newItem->SetBodyStyle( newItem->GetBodyStyle() ? m_frame->GetBodyStyle() : 0 );
1340
1341 symbol->AddDrawItem( newItem );
1342 getView()->Add( newItem );
1343 }
1344
1345 delete newPart;
1346
1347 m_selectionTool->RebuildSelection();
1348
1349 SCH_SELECTION& selection = m_selectionTool->GetSelection();
1350
1351 if( !selection.Empty() )
1352 {
1353 selection.SetReferencePoint( getViewControls()->GetCursorPosition( true ) );
1354
1355 if( m_toolMgr->RunSynchronousAction( SCH_ACTIONS::move, &commit ) )
1356 commit.Push( _( "Paste" ) );
1357 else
1358 commit.Revert();
1359 }
1360
1361 return 0;
1362}
1363
1364
1366{
1367 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1368 SCH_SELECTION& selection = m_selectionTool->RequestSelection( nonFields );
1369 SCH_COMMIT commit( m_toolMgr );
1370
1371 if( selection.GetSize() == 0 )
1372 return 0;
1373
1374 commit.Modify( symbol, m_frame->GetScreen() );
1375
1376 std::vector<EDA_ITEM*> oldItems;
1377 std::vector<EDA_ITEM*> newItems;
1378
1379 std::copy( selection.begin(), selection.end(), std::back_inserter( oldItems ) );
1380 std::sort( oldItems.begin(), oldItems.end(), []( EDA_ITEM* a, EDA_ITEM* b )
1381 {
1382 int cmp;
1383
1384 if( a->Type() != b->Type() )
1385 return a->Type() < b->Type();
1386
1387 // Create the new pins in the same order as the old pins
1388 if( a->Type() == SCH_PIN_T )
1389 {
1390 const wxString& aNum = static_cast<SCH_PIN*>( a )->GetNumber();
1391 const wxString& bNum = static_cast<SCH_PIN*>( b )->GetNumber();
1392
1393 cmp = StrNumCmp( aNum, bNum );
1394
1395 // If the pin numbers are not numeric, then just number them by their position
1396 // on the screen.
1397 if( aNum.IsNumber() && bNum.IsNumber() && cmp != 0 )
1398 return cmp < 0;
1399 }
1400
1402
1403 if( cmp != 0 )
1404 return cmp < 0;
1405
1406 return a->m_Uuid < b->m_Uuid;
1407 } );
1408
1409 for( EDA_ITEM* item : oldItems )
1410 {
1411 SCH_ITEM* oldItem = static_cast<SCH_ITEM*>( item );
1412 SCH_ITEM* newItem = oldItem->Duplicate( true, &commit );
1413
1414 if( newItem->Type() == SCH_PIN_T )
1415 {
1416 SCH_PIN* newPin = static_cast<SCH_PIN*>( newItem );
1417
1418 if( !newPin->GetNumber().IsEmpty() )
1419 newPin->SetNumber( wxString::Format( wxT( "%i" ), symbol->GetMaxPinNumber() + 1 ) );
1420 }
1421
1422 oldItem->ClearFlags( IS_NEW | IS_PASTED | SELECTED );
1423 newItem->SetFlags( IS_NEW | IS_PASTED | SELECTED );
1424 newItem->SetParent( symbol );
1425 newItems.push_back( newItem );
1426
1427 symbol->AddDrawItem( newItem );
1428 getView()->Add( newItem );
1429 }
1430
1431 m_toolMgr->RunAction( ACTIONS::selectionClear );
1432 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &newItems );
1433
1434 selection.SetReferencePoint( getViewControls()->GetCursorPosition( true ) );
1435
1436 if( m_toolMgr->RunSynchronousAction( SCH_ACTIONS::move, &commit ) )
1437 commit.Push( _( "Duplicate" ) );
1438 else
1439 commit.Revert();
1440
1441 return 0;
1442}
1443
1444
1446{
1447 // clang-format off
1455
1463
1469
1476 // clang-format on
1477}
std::optional< BOX2I > OPT_BOX2I
Definition box2.h:926
static TOOL_ACTION decrementPrimary
Definition actions.h:96
static TOOL_ACTION paste
Definition actions.h:80
static TOOL_ACTION cancelInteractive
Definition actions.h:72
static TOOL_ACTION unselectAll
Definition actions.h:83
static TOOL_ACTION decrementSecondary
Definition actions.h:98
static TOOL_ACTION copy
Definition actions.h:78
static TOOL_ACTION undo
Definition actions.h:75
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 redo
Definition actions.h:76
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 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 selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:232
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
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.
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 to update or change schematic library symbols.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
virtual VECTOR2I GetPosition() const
Definition eda_item.h:272
virtual void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:273
EDA_ITEM_FLAGS GetEditFlags() const
Definition eda_item.h:148
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:142
const KIID m_Uuid
Definition eda_item.h:516
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:144
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.h:113
EDA_ITEM * GetParent() const
Definition eda_item.h:112
bool IsMoving() const
Definition eda_item.h:125
virtual bool IsVisible() const
Definition eda_text.h:187
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:425
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:200
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:394
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:203
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:417
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:298
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:172
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:87
Symbol library management helper that is specific to the symbol library editor frame.
wxObjectDataPtr< LIB_TREE_MODEL_ADAPTER > & GetAdapter()
Return the adapter object that provides the stored data.
Define a library symbol object.
Definition lib_symbol.h:83
bool UnitsLocked() const
Check whether symbol units are interchangeable.
Definition lib_symbol.h:288
bool IsDerived() const
Definition lib_symbol.h:204
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:685
void RemoveDrawItem(SCH_ITEM *aItem)
Remove draw aItem from list.
std::vector< SCH_PIN * > GetPins() const override
int GetUnitCount() const override
void AddDrawItem(SCH_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
A pin layout helper is a class that manages the layout of the parts of a pin on a schematic symbol:
OPT_BOX2I GetPinNumberBBox()
Get the bounding box of the pin number, if there is one.
static TOOL_ACTION rotateCCW
static TOOL_ACTION mirrorV
static TOOL_ACTION swap
static TOOL_ACTION convertStackedPins
static TOOL_ACTION pinTable
static TOOL_ACTION properties
static TOOL_ACTION rotateCW
static TOOL_ACTION mirrorH
static TOOL_ACTION symbolProperties
static TOOL_ACTION explodeStackedPin
static TOOL_ACTION updateSymbolFields
static TOOL_ACTION move
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
virtual void Revert() override
Revert the commit by restoring the modified items state.
bool IsMandatory() const
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
static void FormatLibSymbol(LIB_SYMBOL *aPart, OUTPUTFORMATTER &aFormatter)
static std::vector< LIB_SYMBOL * > ParseLibSymbols(std::string &aSymbolText, std::string aSource, int aFileVersion=SEXPR_SCHEMATIC_FILE_VERSION)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
SCH_ITEM * Duplicate(bool addToParentGroup, SCH_COMMIT *aCommit=nullptr, bool doClone=false) const
Routine to create a new copy of given item.
Definition sch_item.cpp:137
virtual void SetBodyStyle(int aBodyStyle)
Definition sch_item.h:246
int GetBodyStyle() const
Definition sch_item.h:247
virtual void MirrorHorizontally(int aCenter)
Mirror item horizontally about aCenter.
Definition sch_item.h:387
int GetUnit() const
Definition sch_item.h:238
virtual void Rotate(const VECTOR2I &aCenter, bool aRotateCCW)
Rotate the item around aCenter 90 degrees in the clockwise direction.
Definition sch_item.h:403
virtual void SetUnit(int aUnit)
Definition sch_item.h:237
wxString GetClass() const override
Return the class name.
Definition sch_item.h:177
virtual void MirrorVertically(int aCenter)
Mirror item vertically about aCenter.
Definition sch_item.h:395
void SetNumber(const wxString &aNumber)
Definition sch_pin.cpp:642
void SetVisible(bool aVisible)
Definition sch_pin.h:114
void SetOrientation(PIN_ORIENTATION aOrientation)
Definition sch_pin.h:93
void SetName(const wxString &aName)
Definition sch_pin.cpp:419
void SetPosition(const VECTOR2I &aPos) override
Definition sch_pin.h:251
const wxString & GetName() const
Definition sch_pin.cpp:401
void SetLength(int aLength)
Definition sch_pin.h:99
PIN_ORIENTATION GetOrientation() const
Definition sch_pin.cpp:264
void SetNumberTextSize(int aSize)
Definition sch_pin.cpp:693
void SetShape(GRAPHIC_PINSHAPE aShape)
Definition sch_pin.h:96
VECTOR2I GetPosition() const override
Definition sch_pin.cpp:256
void SetType(ELECTRICAL_PINTYPE aType)
Definition sch_pin.cpp:333
const wxString & GetNumber() const
Definition sch_pin.h:124
ELECTRICAL_PINTYPE GetType() const
Definition sch_pin.cpp:313
void SetNameTextSize(int aSize)
Definition sch_pin.cpp:669
void RebuildSelection()
Rebuild the selection from the EDA_ITEMs' selection flags.
SCH_SELECTION & GetSelection()
SCH_SELECTION & RequestSelection(const std::vector< KICAD_T > &aScanTypes={ SCH_LOCATE_ANY_T }, bool aPromoteCellSelections=false, bool aPromoteGroups=false)
Return either an existing selection (filtered), or the selection at the current cursor position if th...
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Populate aList of MSG_PANEL_ITEM objects with it's internal state for display purposes.
void updateItem(EDA_ITEM *aItem, bool aUpdateRTree) const
int Increment(const TOOL_EVENT &aEvent)
void saveCopyInUndoList(EDA_ITEM *aItem, UNDO_REDO aType, bool aAppend=false, bool aDirtyConnectivity=true)
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 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 SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
virtual KIGFX::VIEW_ITEM * GetItem(unsigned int aIdx) const override
Definition selection.cpp:75
ITER end()
Definition selection.h:80
ITER begin()
Definition selection.h:79
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
virtual void Clear() override
Remove all the stored items from the group.
Definition selection.h:98
int Size() const
Returns the number of selected parts.
Definition selection.h:121
std::vector< EDA_ITEM * > GetItemsSortedBySelectionOrder() const
void SetReferencePoint(const VECTOR2I &aP)
bool Empty() const
Checks if there is anything selected.
Definition selection.h:115
Implement an OUTPUTFORMATTER to a memory buffer.
Definition richio.h:422
const std::string & GetString()
Definition richio.h:445
int Undo(const TOOL_EVENT &aEvent)
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
void editTextBoxProperties(SCH_ITEM *aItem)
int PinTable(const TOOL_EVENT &aEvent)
int Copy(const TOOL_EVENT &aEvent)
int CopyAsText(const TOOL_EVENT &aEvent)
int Paste(const TOOL_EVENT &aEvent)
int Cut(const TOOL_EVENT &aEvent)
bool Init() override
Init() is called once upon a registration of the tool.
int Redo(const TOOL_EVENT &aEvent)
void editTextProperties(SCH_ITEM *aItem)
int Swap(const TOOL_EVENT &aEvent)
void editFieldProperties(SCH_FIELD *aField)
void editShapeProperties(SCH_SHAPE *aShape)
int Duplicate(const TOOL_EVENT &aEvent)
int Mirror(const TOOL_EVENT &aEvent)
int Rotate(const TOOL_EVENT &aEvent)
int Properties(const TOOL_EVENT &aEvent)
int ExplodeStackedPin(const TOOL_EVENT &aEvent)
void editSymbolPropertiesFromLibrary(const LIB_ID &aLibId)
Set up handlers for various events.
int ConvertStackedPins(const TOOL_EVENT &aEvent)
int UpdateSymbolFields(const TOOL_EVENT &aEvent)
int DoDelete(const TOOL_EVENT &aEvent)
Delete the selected items, or the item under the cursor.
static const std::vector< KICAD_T > SwappableItems
LIB_SYMBOL * GetBufferedSymbol(const wxString &aSymbolName, const wxString &aLibrary)
Return the symbol copy from the buffer.
void SetSymbolModified(const wxString &aSymbolName, const wxString &aLibrary)
bool UpdateSymbol(LIB_SYMBOL *aSymbol, const wxString &aLibrary)
Update the symbol buffer with a new version of the symbol.
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.
void Go(int(SYMBOL_EDIT_FRAME::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
TOOL_MENU & GetToolMenu()
CONDITIONAL_MENU & GetMenu()
Definition tool_menu.cpp:44
bool SaveClipboard(const std::string &aTextUTF8)
Store information to the system clipboard.
Definition clipboard.cpp:38
std::string GetClipboardUTF8()
Return the information currently stored in the system clipboard.
Definition clipboard.cpp:58
#define _(s)
@ RECURSE
Definition eda_item.h:51
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition eda_item.h:566
#define IS_PASTED
Modifier on IS_NEW which indicates it came from clipboard.
#define IS_NEW
New item, just created.
#define SELECTED
Item was manually selected by the user.
#define STRUCT_DELETED
flag indication structures to be erased
const wxChar *const traceStackedPins
Flag to enable debug output for stacked pins handling in symbol/pin code.
@ LAYER_DEVICE
Definition layer_ids.h:466
void Prettify(std::string &aSource, FORMAT_MODE aMode)
Pretty-prints s-expression text according to KiCad format rules.
PIN_ORIENTATION
The symbol library pin object orientations.
Definition pin_type.h:105
wxString GetSelectedItemsAsText(const SELECTION &aSel)
wxString TitleCaps(const wxString &aString)
Capitalize the first letter in each word.
static std::vector< KICAD_T > nonFields
KIBIS_PIN * pin
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.
constexpr GR_TEXT_H_ALIGN_T GetFlippedAlignment(GR_TEXT_H_ALIGN_T aAlign)
Get the reverse alignment: left-right are swapped, others are unchanged.
wxLogTrace helper definitions.
@ SCH_TABLE_T
Definition typeinfo.h:169
@ LIB_SYMBOL_T
Definition typeinfo.h:152
@ SCH_TABLECELL_T
Definition typeinfo.h:170
@ SCH_FIELD_T
Definition typeinfo.h:154
@ SCH_SHAPE_T
Definition typeinfo.h:153
@ SCH_TEXT_T
Definition typeinfo.h:155
@ SCH_TEXTBOX_T
Definition typeinfo.h:156
@ SCH_PIN_T
Definition typeinfo.h:157
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
constexpr int LexicographicalCompare(const VECTOR2< T > &aA, const VECTOR2< T > &aB)
Definition vector2d.h:644