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, see <https://www.gnu.org/licenses/>.
19 */
20
22
23#include <tool/picker_tool.h>
29#include <clipboard.h>
30#include <sch_actions.h>
31#include <increment.h>
32#include <pin_layout_cache.h>
33#include <string_utils.h>
34#include <symbol_edit_frame.h>
35#include <sch_commit.h>
37#include <dialogs/dialog_text_properties.h>
42#include <view/view_controls.h>
43#include <view/view.h>
44#include <richio.h>
46#include <sch_textbox.h>
48#include <widgets/lib_tree.h>
49#include <wx/textdlg.h> // for wxTextEntryDialog
50#include <math/util.h> // for KiROUND
52#include <trace_helpers.h>
54#include <sch_painter.h>
55#include <sch_plotter.h>
56#include <locale_io.h>
57#include <gal/gal_print.h>
59#include <zoom_defines.h>
60#include <wx/ffile.h>
61#include <wx/mstream.h>
62#include <wx/dcmemory.h>
63
64
65namespace
66{
67constexpr int clipboardMaxBitmapSize = 4096;
68constexpr double clipboardBboxInflation = 0.02;
69
70
71void appendMimeData( std::vector<CLIPBOARD_MIME_DATA>& aMimeData, const wxString& aMimeType,
72 const wxMemoryBuffer& aBuffer )
73{
74 if( aBuffer.GetDataLen() == 0 )
75 return;
76
78 entry.m_mimeType = aMimeType;
79 entry.m_data = aBuffer;
80 aMimeData.push_back( entry );
81}
82
83
84void appendMimeData( std::vector<CLIPBOARD_MIME_DATA>& aMimeData, const wxString& aMimeType,
85 wxImage&& aImage )
86{
87 if( !aImage.IsOk() )
88 return;
89
91 entry.m_mimeType = aMimeType;
92 entry.m_image = std::move( aImage );
93 aMimeData.push_back( std::move( entry ) );
94}
95
96
97bool loadFileToBuffer( const wxString& aFileName, wxMemoryBuffer& aBuffer )
98{
99 wxFFile file( aFileName, wxS( "rb" ) );
100
101 if( !file.IsOpened() )
102 return false;
103
104 wxFileOffset size = file.Length();
105
106 if( size <= 0 )
107 return false;
108
109 void* data = aBuffer.GetWriteBuf( size );
110
111 if( file.Read( data, size ) != static_cast<size_t>( size ) )
112 {
113 aBuffer.UngetWriteBuf( 0 );
114 return false;
115 }
116
117 aBuffer.UngetWriteBuf( size );
118 return true;
119}
120
121
122bool plotSymbolToSvg( SYMBOL_EDIT_FRAME* aFrame, LIB_SYMBOL* aSymbol, const BOX2I& aBBox,
123 int aUnit, int aBodyStyle, wxMemoryBuffer& aBuffer )
124{
125 if( !aSymbol )
126 return false;
127
128 SCH_RENDER_SETTINGS renderSettings;
129 renderSettings.LoadColors( aFrame->GetColorSettings() );
130 renderSettings.SetDefaultPenWidth( aFrame->GetRenderSettings()->GetDefaultPenWidth() );
131
132 std::unique_ptr<SVG_PLOTTER> plotter = std::make_unique<SVG_PLOTTER>();
133 plotter->SetRenderSettings( &renderSettings );
134
135 PAGE_INFO pageInfo = aFrame->GetScreen()->GetPageSettings();
136 pageInfo.SetWidthMils( schIUScale.IUToMils( aBBox.GetWidth() ) );
137 pageInfo.SetHeightMils( schIUScale.IUToMils( aBBox.GetHeight() ) );
138
139 plotter->SetPageSettings( pageInfo );
140 plotter->SetColorMode( true );
141
142 VECTOR2I plot_offset = aBBox.GetOrigin();
143 plotter->SetViewport( plot_offset, schIUScale.IU_PER_MILS / 10, 1.0, false );
144 plotter->SetCreator( wxT( "Eeschema-SVG" ) );
145
146 wxFileName tempFile( wxFileName::CreateTempFileName( wxS( "kicad_symbol_svg" ) ) );
147
148 if( !plotter->OpenFile( tempFile.GetFullPath() ) )
149 {
150 wxRemoveFile( tempFile.GetFullPath() );
151 return false;
152 }
153
154 LOCALE_IO toggle;
155 SCH_PLOT_OPTS plotOpts;
156
157 plotter->StartPlot( wxT( "1" ) );
158
159 constexpr bool background = true;
160 aSymbol->Plot( plotter.get(), background, plotOpts, aUnit, aBodyStyle, VECTOR2I( 0, 0 ), false );
161 aSymbol->Plot( plotter.get(), !background, plotOpts, aUnit, aBodyStyle, VECTOR2I( 0, 0 ), false );
162 aSymbol->PlotFields( plotter.get(), !background, plotOpts, aUnit, aBodyStyle, VECTOR2I( 0, 0 ), false );
163
164 plotter->EndPlot();
165 plotter.reset();
166
167 bool ok = loadFileToBuffer( tempFile.GetFullPath(), aBuffer );
168 wxRemoveFile( tempFile.GetFullPath() );
169 return ok;
170}
171
172
173wxImage renderSymbolToBitmap( SYMBOL_EDIT_FRAME* aFrame, LIB_SYMBOL* aSymbol, const BOX2I& aBBox,
174 int aUnit, int aBodyStyle, int aWidth, int aHeight,
175 double aViewScale, const wxColour& aBgColor )
176{
177 if( !aSymbol )
178 return wxImage();
179
180 wxBitmap bitmap( aWidth, aHeight, 24 );
181 wxMemoryDC dc;
182 dc.SelectObject( bitmap );
183 dc.SetBackground( wxBrush( aBgColor ) );
184 dc.Clear();
185
188 std::unique_ptr<KIGFX::GAL_PRINT> galPrint = KIGFX::GAL_PRINT::Create( options, &dc );
189
190 if( !galPrint )
191 return wxImage();
192
193 KIGFX::GAL* gal = galPrint->GetGAL();
194 KIGFX::PRINT_CONTEXT* printCtx = galPrint->GetPrintCtx();
195 std::unique_ptr<KIGFX::SCH_PAINTER> painter = std::make_unique<KIGFX::SCH_PAINTER>( gal );
196 std::unique_ptr<KIGFX::VIEW> view = std::make_unique<KIGFX::VIEW>();
197
198 // For symbol editor, we don't have a full schematic context
199 // but SCH_PAINTER can still work for rendering individual items
200 view->SetGAL( gal );
201 view->SetPainter( painter.get() );
202 view->SetScaleLimits( ZOOM_MAX_LIMIT_EESCHEMA, ZOOM_MIN_LIMIT_EESCHEMA );
203 view->SetScale( 1.0 );
205
206 // Clone items and add to view
207 std::vector<std::unique_ptr<SCH_ITEM>> clonedItems;
208
209 for( SCH_ITEM& item : aSymbol->GetDrawItems() )
210 {
211 if( aUnit && item.GetUnit() && item.GetUnit() != aUnit )
212 continue;
213
214 if( aBodyStyle && item.GetBodyStyle() && item.GetBodyStyle() != aBodyStyle )
215 continue;
216
217 SCH_ITEM* clone = static_cast<SCH_ITEM*>( item.Clone() );
218 clonedItems.emplace_back( clone );
219 view->Add( clone );
220 }
221
222 SCH_RENDER_SETTINGS* dstSettings = painter->GetSettings();
223 dstSettings->LoadColors( aFrame->GetColorSettings() );
224 dstSettings->SetDefaultPenWidth( aFrame->GetRenderSettings()->GetDefaultPenWidth() );
225 dstSettings->SetIsPrinting( true );
226
227 COLOR4D bgColor4D( aBgColor.Red() / 255.0, aBgColor.Green() / 255.0,
228 aBgColor.Blue() / 255.0, 1.0 );
229 dstSettings->SetBackgroundColor( bgColor4D );
230
231 for( int i = 0; i < KIGFX::VIEW::VIEW_MAX_LAYERS; ++i )
232 {
233 view->SetLayerVisible( i, true );
234 view->SetLayerTarget( i, KIGFX::TARGET_NONCACHED );
235 }
236
237 view->SetLayerVisible( LAYER_DRAWINGSHEET, false );
238
239 // Calculate effective output DPI for the print context.
240 // On GTK, Cairo uses device scale 72/4800 and SetSheetSize doubles internal resolution.
241 // On Windows/macOS, there's no device scale, so effective DPI = native DPI * 2.
242#ifdef __WXGTK__
243 double ppi = 144.0;
244#else
245 double ppi = printCtx->GetNativeDPI() * 2.0;
246#endif
247 double inch2Iu = 1000.0 * schIUScale.IU_PER_MILS;
248 VECTOR2D pageSizeIn( (double) aWidth / ppi, (double) aHeight / ppi );
249
250 galPrint->SetSheetSize( pageSizeIn );
251 galPrint->SetNativePaperSize( pageSizeIn, printCtx->HasNativeLandscapeRotation() );
252
253 // SetSheetSize creates an internal canvas at 2× the nominal page size for quality.
254 // The × 2 multiplier ensures content fills this internal canvas.
255 double zoomFactor = 2.0 * aViewScale * inch2Iu / ppi;
256
257 // Set up both the GAL and VIEW to center on the bbox.
258 view->SetCenter( aBBox.Centre() );
259 view->SetScale( aViewScale * zoomFactor );
260
261 gal->SetLookAtPoint( aBBox.Centre() );
262 gal->SetZoomFactor( zoomFactor );
263 gal->SetClearColor( bgColor4D );
264 gal->ClearScreen();
265
266 view->UseDrawPriority( true );
267
268 {
270 view->Redraw();
271 }
272
273 dc.SelectObject( wxNullBitmap );
274 return bitmap.ConvertToImage();
275}
276
277
278wxImage renderSymbolToImageWithAlpha( SYMBOL_EDIT_FRAME* aFrame, LIB_SYMBOL* aSymbol,
279 const BOX2I& aBBox, int aUnit, int aBodyStyle )
280{
281 if( !aSymbol )
282 return wxImage();
283
284 VECTOR2I size = aBBox.GetSize();
285
286 if( size.x <= 0 || size.y <= 0 )
287 return wxImage();
288
289 // Use the current view scale to match what the user sees on screen
290 double viewScale = aFrame->GetCanvas()->GetView()->GetScale();
291 int bitmapWidth = KiROUND( size.x * viewScale );
292 int bitmapHeight = KiROUND( size.y * viewScale );
293
294 // Clamp to maximum size while preserving aspect ratio
295 if( bitmapWidth > clipboardMaxBitmapSize || bitmapHeight > clipboardMaxBitmapSize )
296 {
297 double scaleDown = (double) clipboardMaxBitmapSize / std::max( bitmapWidth, bitmapHeight );
298 bitmapWidth = KiROUND( bitmapWidth * scaleDown );
299 bitmapHeight = KiROUND( bitmapHeight * scaleDown );
300 viewScale *= scaleDown;
301 }
302
303 if( bitmapWidth <= 0 || bitmapHeight <= 0 )
304 return wxImage();
305
306 // Render twice with different backgrounds for alpha computation
307 wxImage imageOnWhite = renderSymbolToBitmap( aFrame, aSymbol, aBBox, aUnit, aBodyStyle,
308 bitmapWidth, bitmapHeight, viewScale, *wxWHITE );
309 wxImage imageOnBlack = renderSymbolToBitmap( aFrame, aSymbol, aBBox, aUnit, aBodyStyle,
310 bitmapWidth, bitmapHeight, viewScale, *wxBLACK );
311
312 if( !imageOnWhite.IsOk() || !imageOnBlack.IsOk() )
313 return wxImage();
314
315 // Create output image with alpha channel
316 wxImage result( bitmapWidth, bitmapHeight );
317 result.InitAlpha();
318
319 unsigned char* rgbWhite = imageOnWhite.GetData();
320 unsigned char* rgbBlack = imageOnBlack.GetData();
321 unsigned char* rgbResult = result.GetData();
322 unsigned char* alphaResult = result.GetAlpha();
323
324 int pixelCount = bitmapWidth * bitmapHeight;
325
326 for( int i = 0; i < pixelCount; ++i )
327 {
328 int idx = i * 3;
329
330 int rW = rgbWhite[idx], gW = rgbWhite[idx + 1], bW = rgbWhite[idx + 2];
331 int rB = rgbBlack[idx], gB = rgbBlack[idx + 1], bB = rgbBlack[idx + 2];
332
333 // Alpha computation: α = 1 - (white - black) / 255
334 int diffR = rW - rB;
335 int diffG = gW - gB;
336 int diffB = bW - bB;
337 int avgDiff = ( diffR + diffG + diffB ) / 3;
338
339 int alpha = 255 - avgDiff;
340 alpha = std::max( 0, std::min( 255, alpha ) );
341 alphaResult[i] = static_cast<unsigned char>( alpha );
342
343 if( alpha > 0 )
344 {
345 rgbResult[idx] = static_cast<unsigned char>( std::min( 255, rB * 255 / alpha ) );
346 rgbResult[idx + 1] = static_cast<unsigned char>( std::min( 255, gB * 255 / alpha ) );
347 rgbResult[idx + 2] = static_cast<unsigned char>( std::min( 255, bB * 255 / alpha ) );
348 }
349 else
350 {
351 rgbResult[idx] = 0;
352 rgbResult[idx + 1] = 0;
353 rgbResult[idx + 2] = 0;
354 }
355 }
356
357 return result;
358}
359
360} // namespace
361
362
364 SCH_TOOL_BASE( "eeschema.SymbolEditTool" )
365{
366}
367
368
369const std::vector<KICAD_T> SYMBOL_EDITOR_EDIT_TOOL::SwappableItems = {
370 LIB_SYMBOL_T, // Allows swapping the anchor
371 SCH_PIN_T,
376};
377
378
380{
382
385
386 wxASSERT_MSG( drawingTools, "eeschema.SymbolDrawing tool is not available" );
387
388 auto haveSymbolCondition =
389 [&]( const SELECTION& sel )
390 {
391 return m_isSymbolEditor && m_frame->GetCurSymbol();
392 };
393
394 auto canEdit =
395 [&]( const SELECTION& sel )
396 {
397 if( !m_frame->IsSymbolEditable() )
398 return false;
399
400 if( m_frame->IsSymbolAlias() )
401 {
402 for( EDA_ITEM* item : sel )
403 {
404 if( item->Type() != SCH_FIELD_T )
405 return false;
406 }
407 }
408
409 return true;
410 };
411
412 auto swapSelectionCondition =
414
415 const auto canCopyText = SCH_CONDITIONS::OnlyTypes( {
419 SCH_PIN_T,
422 } );
423
424 const auto canConvertStackedPins =
425 [&]( const SELECTION& sel )
426 {
427 // If multiple pins are selected, check they are all at same location
428 if( sel.Size() >= 2 )
429 {
430 std::vector<SCH_PIN*> pins;
431 for( EDA_ITEM* item : sel )
432 {
433 if( item->Type() != SCH_PIN_T )
434 return false;
435 pins.push_back( static_cast<SCH_PIN*>( item ) );
436 }
437
438 // Check that all pins are at the same location
439 VECTOR2I pos = pins[0]->GetPosition();
440 for( size_t i = 1; i < pins.size(); ++i )
441 {
442 if( pins[i]->GetPosition() != pos )
443 return false;
444 }
445 return true;
446 }
447
448 // If single pin is selected, check if there are other pins at same location
449 if( sel.Size() == 1 && sel.Front()->Type() == SCH_PIN_T )
450 {
451 SCH_PIN* selectedPin = static_cast<SCH_PIN*>( sel.Front() );
452 VECTOR2I pos = selectedPin->GetPosition();
453
454 // Get the symbol and check for other pins at same location
455 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
456 if( !symbol )
457 return false;
458
459 int coLocatedCount = 0;
460
461 for( SCH_PIN* pin : symbol->GetPins() )
462 {
463 if( pin->GetPosition() == pos )
464 {
465 coLocatedCount++;
466
467 if( coLocatedCount >= 2 )
468 return true;
469 }
470 }
471 }
472
473 return false;
474 };
475
476 const auto canExplodeStackedPin =
477 [&]( const SELECTION& sel )
478 {
479 if( sel.Size() != 1 || sel.Front()->Type() != SCH_PIN_T )
480 return false;
481
482 SCH_PIN* pin = static_cast<SCH_PIN*>( sel.Front() );
483 bool isValid;
484 std::vector<wxString> stackedNumbers = pin->GetStackedPinNumbers( &isValid );
485 return isValid && stackedNumbers.size() > 1;
486 };
487
488 // clang-format off
489 // Add edit actions to the move tool menu
490 if( moveTool )
491 {
492 CONDITIONAL_MENU& moveMenu = moveTool->GetToolMenu().GetMenu();
493
494 moveMenu.AddSeparator( 200 );
495 moveMenu.AddItem( SCH_ACTIONS::rotateCCW, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
496 moveMenu.AddItem( SCH_ACTIONS::rotateCW, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
497 moveMenu.AddItem( SCH_ACTIONS::mirrorV, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
498 moveMenu.AddItem( SCH_ACTIONS::mirrorH, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
499
500 moveMenu.AddItem( SCH_ACTIONS::swap, swapSelectionCondition, 200 );
501 moveMenu.AddItem( SCH_ACTIONS::properties, canEdit && SCH_CONDITIONS::Count( 1 ), 200 );
502
503 moveMenu.AddSeparator( 300 );
506 moveMenu.AddItem( ACTIONS::copyAsText, canCopyText && SCH_CONDITIONS::IdleSelection, 300 );
507 moveMenu.AddItem( ACTIONS::duplicate, canEdit && SCH_CONDITIONS::NotEmpty, 300 );
508 moveMenu.AddItem( ACTIONS::doDelete, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
509
510 moveMenu.AddSeparator( 400 );
511 moveMenu.AddItem( ACTIONS::selectAll, haveSymbolCondition, 400 );
512 moveMenu.AddItem( ACTIONS::unselectAll, haveSymbolCondition, 400 );
513 }
514
515 // Add editing actions to the drawing tool menu
516 CONDITIONAL_MENU& drawMenu = drawingTools->GetToolMenu().GetMenu();
517
518 drawMenu.AddSeparator( 200 );
523
524 drawMenu.AddItem( SCH_ACTIONS::properties, canEdit && SCH_CONDITIONS::Count( 1 ), 200 );
525
526 // Add editing actions to the selection tool menu
527 CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
528
529 selToolMenu.AddItem( SCH_ACTIONS::rotateCCW, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
530 selToolMenu.AddItem( SCH_ACTIONS::rotateCW, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
531 selToolMenu.AddItem( SCH_ACTIONS::mirrorV, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
532 selToolMenu.AddItem( SCH_ACTIONS::mirrorH, canEdit && SCH_CONDITIONS::NotEmpty, 200 );
533
534 selToolMenu.AddItem( SCH_ACTIONS::swap, swapSelectionCondition, 200 );
535 selToolMenu.AddItem( SCH_ACTIONS::properties, canEdit && SCH_CONDITIONS::Count( 1 ), 200 );
536
537 selToolMenu.AddSeparator( 250 );
538 selToolMenu.AddItem( SCH_ACTIONS::convertStackedPins, canEdit && canConvertStackedPins, 250 );
539 selToolMenu.AddItem( SCH_ACTIONS::explodeStackedPin, canEdit && canExplodeStackedPin, 250 );
540
541 selToolMenu.AddSeparator( 300 );
544 selToolMenu.AddItem( ACTIONS::copyAsText, canCopyText && SCH_CONDITIONS::IdleSelection, 300 );
545 selToolMenu.AddItem( ACTIONS::paste, canEdit && SCH_CONDITIONS::Idle, 300 );
546 selToolMenu.AddItem( ACTIONS::duplicate, canEdit && SCH_CONDITIONS::NotEmpty, 300 );
547 selToolMenu.AddItem( ACTIONS::doDelete, canEdit && SCH_CONDITIONS::NotEmpty, 300 );
548
549 selToolMenu.AddSeparator( 400 );
550 selToolMenu.AddItem( ACTIONS::selectAll, haveSymbolCondition, 400 );
551 selToolMenu.AddItem( ACTIONS::unselectAll, haveSymbolCondition, 400 );
552 // clang-format on
553
554 return true;
555}
556
557
559{
560 SCH_SELECTION& selection = m_selectionTool->RequestSelection();
561
562 if( selection.GetSize() == 0 )
563 return 0;
564
565 VECTOR2I rotPoint;
566 bool ccw = ( aEvent.Matches( SCH_ACTIONS::rotateCCW.MakeEvent() ) );
567 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
568 SCH_COMMIT localCommit( m_toolMgr );
569 SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
570
571 if( !commit )
572 commit = &localCommit;
573
574 if( !item->IsMoving() )
575 commit->Modify( m_frame->GetCurSymbol(), m_frame->GetScreen(), RECURSE_MODE::RECURSE );
576
577 if( selection.GetSize() == 1 )
578 rotPoint = item->GetPosition();
579 else
580 rotPoint = m_frame->GetNearestHalfGridPosition( selection.GetCenter() );
581
582 for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
583 {
584 item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
585 item->Rotate( rotPoint, ccw );
586 m_frame->UpdateItem( item, false, true );
587 }
588
589 if( item->IsMoving() )
590 {
592 }
593 else
594 {
595 if( selection.IsHover() )
597
598 if( !localCommit.Empty() )
599 localCommit.Push( _( "Rotate" ) );
600 }
601
602 return 0;
603}
604
605
607{
608 SCH_SELECTION& selection = m_selectionTool->RequestSelection();
609
610 if( selection.GetSize() == 0 )
611 return 0;
612
613 VECTOR2I mirrorPoint;
614 bool xAxis = ( aEvent.Matches( SCH_ACTIONS::mirrorV.MakeEvent() ) );
615 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
616
617 if( !item->IsMoving() )
619
620 if( selection.GetSize() == 1 )
621 {
622 mirrorPoint = item->GetPosition();
623
624 switch( item->Type() )
625 {
626 case SCH_FIELD_T:
627 {
628 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
629
630 if( xAxis )
632 else
634
635 break;
636 }
637
638 default:
639 if( xAxis )
640 item->MirrorVertically( mirrorPoint.y );
641 else
642 item->MirrorHorizontally( mirrorPoint.x );
643
644 break;
645 }
646
647
648 m_frame->UpdateItem( item, false, true );
649 }
650 else
651 {
652 mirrorPoint = m_frame->GetNearestHalfGridPosition( selection.GetCenter() );
653
654 for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
655 {
656 item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
657
658 if( xAxis )
659 item->MirrorVertically( mirrorPoint.y );
660 else
661 item->MirrorHorizontally( mirrorPoint.x );
662
663 m_frame->UpdateItem( item, false, true );
664 }
665 }
666
667 if( item->IsMoving() )
668 {
670 }
671 else
672 {
673 if( selection.IsHover() )
675
676 m_frame->OnModify();
677 }
678
679 return 0;
680}
682{
683 SCH_SELECTION& selection = m_selectionTool->RequestSelection( SwappableItems );
684 std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
685
686 if( selection.Size() < 2 )
687 return 0;
688
689 EDA_ITEM* front = selection.Front();
690 bool isMoving = front->IsMoving();
691
692 // Save copy for undo if not in edit (edit command already handle the save copy)
693 if( front->GetEditFlags() == 0 )
695
696 for( size_t i = 0; i < sorted.size() - 1; i++ )
697 {
698 SCH_ITEM* a = static_cast<SCH_ITEM*>( sorted[i] );
699 SCH_ITEM* b = static_cast<SCH_ITEM*>( sorted[( i + 1 ) % sorted.size()] );
700
701 VECTOR2I aPos = a->GetPosition(), bPos = b->GetPosition();
702 std::swap( aPos, bPos );
703
704 a->SetPosition( aPos );
705 b->SetPosition( bPos );
706
707 // Special case some common swaps
708 if( a->Type() == b->Type() )
709 {
710 switch( a->Type() )
711 {
712 case SCH_PIN_T:
713 {
714 SCH_PIN* aPin = static_cast<SCH_PIN*>( a );
715 SCH_PIN* bBpin = static_cast<SCH_PIN*>( b );
716
717 PIN_ORIENTATION aOrient = aPin->GetOrientation();
718 PIN_ORIENTATION bOrient = bBpin->GetOrientation();
719
720 aPin->SetOrientation( bOrient );
721 bBpin->SetOrientation( aOrient );
722
723 break;
724 }
725 default: break;
726 }
727 }
728
729 m_frame->UpdateItem( a, false, true );
730 m_frame->UpdateItem( b, false, true );
731 }
732
733 // Update R-Tree for modified items
734 for( EDA_ITEM* selected : selection )
735 updateItem( selected, true );
736
737 if( isMoving )
738 {
739 m_toolMgr->PostAction( ACTIONS::refreshPreview );
740 }
741 else
742 {
743 if( selection.IsHover() )
745
746 m_frame->OnModify();
747 }
748
749 return 0;
750}
751
752
753static std::vector<KICAD_T> nonFields =
754{
760};
761
762
764{
765 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
766 std::deque<EDA_ITEM*> items = m_selectionTool->RequestSelection().GetItems();
767 SCH_COMMIT commit( m_frame );
768
769 if( items.empty() )
770 return 0;
771
772 // Don't leave a freed pointer in the selection
774
775 commit.Modify( symbol, m_frame->GetScreen() );
776
777 std::set<SCH_ITEM*> toDelete;
778 int fieldsHidden = 0;
779 int fieldsAlreadyHidden = 0;
780
781 for( EDA_ITEM* item : items )
782 {
783 if( item->Type() == SCH_PIN_T )
784 {
785 SCH_PIN* curr_pin = static_cast<SCH_PIN*>( item );
786 VECTOR2I pos = curr_pin->GetPosition();
787
788 toDelete.insert( curr_pin );
789
790 // when pin editing is synchronized, pins in the same position, with the same name
791 // in different units are also removed. But only one pin per unit (matching)
792 if( m_frame->SynchronizePins() )
793 {
794 std::vector<bool> got_unit( symbol->GetUnitCount() + 1 );
795
796 got_unit[curr_pin->GetUnit()] = true;
797
798 for( SCH_PIN* pin : symbol->GetPins() )
799 {
800 if( got_unit[pin->GetUnit()] )
801 continue;
802
803 if( pin->GetPosition() != pos )
804 continue;
805
806 if( pin->GetBodyStyle() != curr_pin->GetBodyStyle() )
807 continue;
808
809 if( pin->GetType() != curr_pin->GetType() )
810 continue;
811
812 if( pin->GetName() != curr_pin->GetName() )
813 continue;
814
815 toDelete.insert( pin );
816 got_unit[pin->GetUnit()] = true;
817 }
818 }
819 }
820 else if( item->Type() == SCH_FIELD_T )
821 {
822 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
823
824 // Hide "deleted" fields
825 if( field->IsVisible() )
826 {
827 field->SetVisible( false );
828 fieldsHidden++;
829 }
830 else
831 {
832 fieldsAlreadyHidden++;
833 }
834 }
835 else if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
836 {
837 toDelete.insert( schItem );
838 }
839 }
840
841 for( SCH_ITEM* item : toDelete )
842 symbol->RemoveDrawItem( item );
843
844 if( toDelete.size() == 0 )
845 {
846 if( fieldsHidden == 1 )
847 commit.Push( _( "Hide Field" ) );
848 else if( fieldsHidden > 1 )
849 commit.Push( _( "Hide Fields" ) );
850 else if( fieldsAlreadyHidden > 0 )
851 m_frame->ShowInfoBarError( _( "Use the Symbol Properties dialog to remove fields." ) );
852 }
853 else
854 {
855 commit.Push( _( "Delete" ) );
856 }
857
858 m_frame->RebuildView();
859 return 0;
860}
861
862
864{
865 SCH_SELECTION& selection = m_selectionTool->RequestSelection();
866
867 if( selection.Empty() || aEvent.IsAction( &SCH_ACTIONS::symbolProperties ) )
868 {
869 // If called from tree context menu, edit properties without loading into canvas
871 {
872 LIB_ID treeLibId = m_frame->GetTreeLIBID();
873
874 // Check if the selected symbol in tree is different from the currently loaded one
875 if( treeLibId.IsValid() &&
876 ( !m_frame->GetCurSymbol() || m_frame->GetCurSymbol()->GetLibId() != treeLibId ) )
877 {
878 // Edit properties directly from library buffer without loading to canvas
880 return 0;
881 }
882 }
883
884 if( m_frame->GetCurSymbol() )
886 }
887 else if( selection.Size() == 1 )
888 {
889 SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
890
891 // Save copy for undo if not in edit (edit command already handle the save copy)
892 if( item->GetEditFlags() == 0 )
894
895 switch( item->Type() )
896 {
897 case SCH_PIN_T:
898 {
899 SCH_PIN& pin = static_cast<SCH_PIN&>( *item );
900
901 // Mouse, not cursor, as grid points may well not be under any text
902 const VECTOR2I& mousePos = m_toolMgr->GetMousePosition();
903 PIN_LAYOUT_CACHE& layout = pin.GetLayoutCache();
904
905 bool mouseOverNumber = false;
906 if( OPT_BOX2I numberBox = layout.GetPinNumberBBox() )
907 {
908 mouseOverNumber = numberBox->Contains( mousePos );
909 }
910
911 if( SYMBOL_EDITOR_PIN_TOOL* pinTool = m_toolMgr->GetTool<SYMBOL_EDITOR_PIN_TOOL>() )
912 pinTool->EditPinProperties( &pin, mouseOverNumber );
913
914 break;
915 }
916 case SCH_SHAPE_T:
917 editShapeProperties( static_cast<SCH_SHAPE*>( item ) );
918 break;
919
920 case SCH_TEXT_T:
921 editTextProperties( item );
922 break;
923
924 case SCH_TEXTBOX_T:
925 editTextBoxProperties( item );
926 break;
927
928 case SCH_FIELD_T:
929 editFieldProperties( static_cast<SCH_FIELD*>( item ) );
930 break;
931
932 default:
933 wxFAIL_MSG( wxT( "Unhandled item <" ) + item->GetClass() + wxT( ">" ) );
934 break;
935 }
936 }
937
938 if( selection.IsHover() )
940
941 return 0;
942}
943
944
946{
947 DIALOG_SHAPE_PROPERTIES dlg( m_frame, aShape );
948
949 if( dlg.ShowModal() != wxID_OK )
950 return;
951
952 updateItem( aShape, true );
953 m_frame->GetCanvas()->Refresh();
954 m_frame->OnModify();
955
958 drawingTools->SetDrawSpecificUnit( !dlg.GetApplyToAllUnits() );
959
960 std::vector<MSG_PANEL_ITEM> items;
961 aShape->GetMsgPanelInfo( m_frame, items );
962 m_frame->SetMsgPanel( items );
963}
964
965
967{
968 if ( aItem->Type() != SCH_TEXT_T )
969 return;
970
971 DIALOG_TEXT_PROPERTIES dlg( m_frame, static_cast<SCH_TEXT*>( aItem ) );
972
973 if( dlg.ShowModal() != wxID_OK )
974 return;
975
976 updateItem( aItem, true );
977 m_frame->GetCanvas()->Refresh();
978 m_frame->OnModify( );
979}
980
981
983{
984 if ( aItem->Type() != SCH_TEXTBOX_T )
985 return;
986
987 DIALOG_TEXT_PROPERTIES dlg( m_frame, static_cast<SCH_TEXTBOX*>( aItem ) );
988
989 if( dlg.ShowModal() != wxID_OK )
990 return;
991
992 updateItem( aItem, true );
993 m_frame->GetCanvas()->Refresh();
994 m_frame->OnModify( );
995}
996
997
999{
1000 if( aField == nullptr )
1001 return;
1002
1003 wxString caption;
1004
1005 if( aField->IsMandatory() )
1006 caption.Printf( _( "Edit %s Field" ), TitleCaps( aField->GetName() ) );
1007 else
1008 caption.Printf( _( "Edit '%s' Field" ), aField->GetName() );
1009
1010 DIALOG_FIELD_PROPERTIES dlg( m_frame, caption, aField );
1011
1012 // The dialog may invoke a kiway player for footprint fields
1013 // so we must use a quasimodal dialog.
1014 if( dlg.ShowQuasiModal() != wxID_OK )
1015 return;
1016
1017 SCH_COMMIT commit( m_toolMgr );
1018 commit.Modify( aField, m_frame->GetScreen() );
1019
1020 dlg.UpdateField( aField );
1021
1022 commit.Push( caption );
1023
1024 m_frame->GetCanvas()->Refresh();
1025 m_frame->UpdateSymbolMsgPanelInfo();
1026}
1027
1028
1030{
1031 LIB_SYMBOL_LIBRARY_MANAGER& libMgr = m_frame->GetLibManager();
1032 wxString libName = aLibId.GetLibNickname();
1033 wxString symbolName = aLibId.GetLibItemName();
1034
1035 // Get the symbol from the library buffer (without loading it into the editor)
1036 LIB_SYMBOL* bufferedSymbol = libMgr.GetBufferedSymbol( symbolName, libName );
1037
1038 if( !bufferedSymbol )
1039 return;
1040
1041 // Create a copy to work with
1042 LIB_SYMBOL tempSymbol( *bufferedSymbol );
1043
1045 m_toolMgr->RunAction( ACTIONS::selectionClear );
1046
1047 DIALOG_LIB_SYMBOL_PROPERTIES dlg( m_frame, &tempSymbol );
1048
1049 // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
1050 // frame. Therefore this dialog as a modal frame parent, MUST be run under
1051 // quasimodal mode for the quasimodal frame support to work. So don't use
1052 // the QUASIMODAL macros here.
1053 if( dlg.ShowQuasiModal() != wxID_OK )
1054 return;
1055
1056 wxString newName = tempSymbol.GetName();
1057
1058 // Update the buffered symbol with the changes. A rename has to be keyed on the old name so
1059 // the existing buffer is renamed in place rather than a duplicate being created under the new
1060 // name.
1061 if( newName != symbolName )
1062 libMgr.UpdateSymbolAfterRename( &tempSymbol, symbolName, libName );
1063 else
1064 libMgr.UpdateSymbol( &tempSymbol, libName );
1065
1066 // Mark the library as modified
1067 libMgr.SetSymbolModified( newName, libName );
1068
1069 // Update the tree view
1070 LIB_ID newLibId( libName, newName );
1071 wxDataViewItem treeItem = libMgr.GetAdapter()->FindItem( newLibId );
1072 m_frame->UpdateLibraryTree( treeItem, &tempSymbol );
1073 m_frame->GetLibTree()->SelectLibId( newLibId );
1074}
1075
1076
1078{
1079 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1080 bool partLocked = symbol->UnitsLocked();
1081
1083 m_toolMgr->RunAction( ACTIONS::selectionClear );
1084
1086
1087 // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
1088 // frame. Therefore this dialog as a modal frame parent, MUST be run under
1089 // quasimodal mode for the quasimodal frame support to work. So don't use
1090 // the QUASIMODAL macros here.
1091 if( dlg.ShowQuasiModal() != wxID_OK )
1092 return;
1093
1094 m_frame->RebuildSymbolUnitAndBodyStyleLists();
1095 m_frame->OnModify();
1096
1097 // if m_UnitSelectionLocked has changed, set some edit options or defaults
1098 // to the best value
1099 if( partLocked != symbol->UnitsLocked() )
1100 {
1102
1103 // Enable synchronized pin edit mode for symbols with interchangeable units
1104 m_frame->m_SyncPinEdit = !symbol->UnitsLocked();
1105
1106 // also set default edit options to the better value
1107 // Usually if units are locked, graphic items are specific to each unit
1108 // and if units are interchangeable, graphic items are common to units
1109 tools->SetDrawSpecificUnit( symbol->UnitsLocked() );
1110 }
1111}
1112
1113
1115{
1116 SCH_COMMIT commit( m_frame );
1117 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1118
1119 if( !symbol )
1120 return 0;
1121
1122 commit.Modify( symbol, m_frame->GetScreen() );
1123
1124 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1125 wxCHECK( selTool, -1 );
1126
1127 std::vector<SCH_PIN*> selectedPins;
1128
1129 SCH_SELECTION& selection = selTool->GetSelection();
1130
1131 for( EDA_ITEM* item : selection )
1132 {
1133 if( item->Type() == SCH_PIN_T )
1134 {
1135 SCH_PIN* pinItem = static_cast<SCH_PIN*>( item );
1136 selectedPins.push_back( pinItem );
1137 }
1138 }
1139
1140 // And now clear the selection so if we change the pins we don't have dangling pointers
1141 // in the selection.
1142 m_toolMgr->RunAction( ACTIONS::selectionClear );
1143
1144 DIALOG_LIB_EDIT_PIN_TABLE dlg( m_frame, symbol, selectedPins );
1145
1146 if( dlg.ShowModal() == wxID_CANCEL )
1147 return -1;
1148
1149 commit.Push( _( "Edit Pins" ) );
1150 m_frame->RebuildView();
1151
1152 return 0;
1153}
1154
1155
1157{
1158 SCH_COMMIT commit( m_frame );
1159 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1160
1161 if( !symbol )
1162 return 0;
1163
1164 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1165 wxCHECK( selTool, -1 );
1166
1167 SCH_SELECTION& selection = selTool->GetSelection();
1168
1169 // Collect pins to convert - accept pins with any number format
1170 std::vector<SCH_PIN*> pinsToConvert;
1171
1172 if( selection.Size() == 1 && selection.Front()->Type() == SCH_PIN_T )
1173 {
1174 // Single pin selected - find all pins at the same location
1175 SCH_PIN* selectedPin = static_cast<SCH_PIN*>( selection.Front() );
1176 VECTOR2I pos = selectedPin->GetPosition();
1177
1178 for( SCH_PIN* pin : symbol->GetPins() )
1179 {
1180 if( pin->GetPosition() == pos )
1181 pinsToConvert.push_back( pin );
1182 }
1183 }
1184 else
1185 {
1186 // Multiple pins selected - use them directly, accepting any pin numbers
1187 for( EDA_ITEM* item : selection )
1188 {
1189 if( item->Type() == SCH_PIN_T )
1190 pinsToConvert.push_back( static_cast<SCH_PIN*>( item ) );
1191 }
1192 }
1193
1194 if( pinsToConvert.size() < 2 )
1195 {
1196 m_frame->ShowInfoBarError( _( "At least two pins are needed to convert to stacked pins" ) );
1197 return 0;
1198 }
1199
1200 // Check that all pins are at the same location
1201 VECTOR2I pos = pinsToConvert[0]->GetPosition();
1202 for( size_t i = 1; i < pinsToConvert.size(); ++i )
1203 {
1204 if( pinsToConvert[i]->GetPosition() != pos )
1205 {
1206 m_frame->ShowInfoBarError( _( "All pins must be at the same location" ) );
1207 return 0;
1208 }
1209 }
1210
1211 commit.Modify( symbol, m_frame->GetScreen() );
1212
1213 // Clear selection before modifying pins, like the Delete command does
1214 m_toolMgr->RunAction( ACTIONS::selectionClear );
1215
1216 // Sort pins for consistent ordering - handle arbitrary pin number formats
1217 std::sort( pinsToConvert.begin(), pinsToConvert.end(),
1218 []( SCH_PIN* a, SCH_PIN* b )
1219 {
1220 wxString numA = a->GetNumber();
1221 wxString numB = b->GetNumber();
1222
1223 // Try to convert to integers for proper numeric sorting
1224 long longA, longB;
1225 bool aIsNumeric = numA.ToLong( &longA );
1226 bool bIsNumeric = numB.ToLong( &longB );
1227
1228 // Both are purely numeric - sort numerically
1229 if( aIsNumeric && bIsNumeric )
1230 return longA < longB;
1231
1232 // Mixed numeric/non-numeric - numeric pins come first
1233 if( aIsNumeric && !bIsNumeric )
1234 return true;
1235 if( !aIsNumeric && bIsNumeric )
1236 return false;
1237
1238 // Both non-numeric or mixed alphanumeric - use lexicographic sorting
1239 return numA < numB;
1240 });
1241
1242 // Build the stacked notation string with range collapsing
1243 wxString stackedNotation = wxT("[");
1244
1245 // Helper function to collapse consecutive numbers into ranges - handles arbitrary pin formats
1246 auto collapseRanges = [&]() -> wxString
1247 {
1248 if( pinsToConvert.empty() )
1249 return wxT("");
1250
1251 wxString result;
1252
1253 // Group pins by their alphanumeric prefix for range collapsing
1254 std::map<wxString, std::vector<long>> prefixGroups;
1255 std::vector<wxString> nonNumericPins;
1256
1257 // Parse each pin number to separate prefix from numeric suffix
1258 for( SCH_PIN* pin : pinsToConvert )
1259 {
1260 wxString pinNumber = pin->GetNumber();
1261
1262 // Skip empty pin numbers (shouldn't happen, but be defensive)
1263 if( pinNumber.IsEmpty() )
1264 {
1265 nonNumericPins.push_back( wxT("(empty)") );
1266 continue;
1267 }
1268
1269 wxString prefix;
1270 wxString numericPart;
1271
1272 // Find where numeric part starts (scan from end)
1273 size_t numStart = pinNumber.length();
1274 for( int i = pinNumber.length() - 1; i >= 0; i-- )
1275 {
1276 if( !wxIsdigit( pinNumber[i] ) )
1277 {
1278 numStart = i + 1;
1279 break;
1280 }
1281 if( i == 0 ) // All digits
1282 numStart = 0;
1283 }
1284
1285 if( numStart < pinNumber.length() ) // Has numeric suffix
1286 {
1287 prefix = pinNumber.Left( numStart );
1288 numericPart = pinNumber.Mid( numStart );
1289
1290 long numValue;
1291 if( numericPart.ToLong( &numValue ) && numValue >= 0 ) // Valid non-negative number
1292 {
1293 prefixGroups[prefix].push_back( numValue );
1294 }
1295 else
1296 {
1297 // Numeric part couldn't be parsed or is negative - treat as non-numeric
1298 nonNumericPins.push_back( pinNumber );
1299 }
1300 }
1301 else // No numeric suffix - consolidate as individual value
1302 {
1303 nonNumericPins.push_back( pinNumber );
1304 }
1305 }
1306
1307 // Process each prefix group
1308 for( auto& [prefix, numbers] : prefixGroups )
1309 {
1310 if( !result.IsEmpty() )
1311 result += wxT(",");
1312
1313 // The prefix may contain characters that are structural in stacked notation (e.g. a pin
1314 // numbered "foo,bar3"); escape it so it round-trips as a single pin number.
1315 wxString escPrefix = EscapeStackedPinItem( prefix );
1316
1317 // Sort numeric values for this prefix
1318 std::sort( numbers.begin(), numbers.end() );
1319
1320 // Collapse consecutive ranges within this prefix
1321 size_t i = 0;
1322 while( i < numbers.size() )
1323 {
1324 if( i > 0 ) // Not first number in this prefix group
1325 result += wxT(",");
1326
1327 long start = numbers[i];
1328 long end = start;
1329
1330 // Find the end of consecutive sequence
1331 while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 )
1332 {
1333 i++;
1334 end = numbers[i];
1335 }
1336
1337 // Add range or single number with prefix
1338 if( end > start + 1 ) // Range of 3+ numbers
1339 result += wxString::Format( wxT("%s%ld-%s%ld"), escPrefix, start, escPrefix, end );
1340 else if( end == start + 1 ) // Two consecutive numbers
1341 result += wxString::Format( wxT("%s%ld,%s%ld"), escPrefix, start, escPrefix, end );
1342 else // Single number
1343 result += wxString::Format( wxT("%s%ld"), escPrefix, start );
1344
1345 i++;
1346 }
1347 }
1348
1349 // Add non-numeric pin numbers as individual comma-separated values
1350 for( const wxString& nonNum : nonNumericPins )
1351 {
1352 if( !result.IsEmpty() )
1353 result += wxT(",");
1354 result += EscapeStackedPinItem( nonNum );
1355 }
1356
1357 return result;
1358 };
1359
1360 stackedNotation += collapseRanges();
1361 stackedNotation += wxT("]");
1362
1363 // Keep the first pin and give it the stacked notation
1364 SCH_PIN* masterPin = pinsToConvert[0];
1365 masterPin->SetNumber( stackedNotation );
1366
1367 // Log information about pins being removed before we remove them
1368 wxLogTrace( traceStackedPins,
1369 wxString::Format( "Converting %zu pins to stacked notation '%s'",
1370 pinsToConvert.size(), stackedNotation ) );
1371
1372 // Remove all other pins from the symbol that were consolidated into the stacked notation
1373 // Collect pins to remove first, then remove them all at once like the Delete command
1374 std::vector<SCH_PIN*> pinsToRemove;
1375 for( size_t i = 1; i < pinsToConvert.size(); ++i )
1376 {
1377 SCH_PIN* pinToRemove = pinsToConvert[i];
1378
1379 // Log the pin before removing it
1380 wxLogTrace( traceStackedPins,
1381 wxString::Format( "Will remove pin '%s' at position (%d, %d)",
1382 pinToRemove->GetNumber(),
1383 pinToRemove->GetPosition().x,
1384 pinToRemove->GetPosition().y ) );
1385
1386 pinsToRemove.push_back( pinToRemove );
1387 }
1388
1389 // Remove all pins at once, like the Delete command does
1390 for( SCH_PIN* pin : pinsToRemove )
1391 {
1392 symbol->RemoveDrawItem( pin );
1393 }
1394
1395 commit.Push( wxString::Format( _( "Convert %zu Stacked Pins to '%s'" ),
1396 pinsToConvert.size(), stackedNotation ) );
1397 m_frame->RebuildView();
1398 return 0;
1399}
1400
1401
1403{
1404 SCH_COMMIT commit( m_frame );
1405 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1406
1407 if( !symbol )
1408 return 0;
1409
1410 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1411 wxCHECK( selTool, -1 );
1412
1413 SCH_SELECTION& selection = selTool->GetSelection();
1414
1415 if( selection.GetSize() != 1 || selection.Front()->Type() != SCH_PIN_T )
1416 {
1417 m_frame->ShowInfoBarError( _( "Select a single pin with stacked notation to explode" ) );
1418 return 0;
1419 }
1420
1421 SCH_PIN* pin = static_cast<SCH_PIN*>( selection.Front() );
1422
1423 // Check if the pin has stacked notation
1424 bool isValid;
1425 std::vector<wxString> stackedNumbers = pin->GetStackedPinNumbers( &isValid );
1426
1427 if( !isValid || stackedNumbers.size() <= 1 )
1428 {
1429 m_frame->ShowInfoBarError( _( "Selected pin does not have valid stacked notation" ) );
1430 return 0;
1431 }
1432
1433 commit.Modify( symbol, m_frame->GetScreen() );
1434
1435 // Clear selection before modifying pins
1436 m_toolMgr->RunAction( ACTIONS::selectionClear );
1437
1438 // Sort the stacked numbers to find the smallest one
1439 std::sort( stackedNumbers.begin(), stackedNumbers.end(),
1440 []( const wxString& a, const wxString& b )
1441 {
1442 // Try to convert to integers for proper numeric sorting
1443 long numA, numB;
1444 if( a.ToLong( &numA ) && b.ToLong( &numB ) )
1445 return numA < numB;
1446
1447 // Fall back to string comparison if not numeric
1448 return a < b;
1449 });
1450
1451 // Change the original pin to use the first (smallest) number and make it visible
1452 pin->SetNumber( stackedNumbers[0] );
1453 pin->SetVisible( true );
1454
1455 // Create additional pins for the remaining numbers and make them invisible
1456 for( size_t i = 1; i < stackedNumbers.size(); ++i )
1457 {
1458 SCH_PIN* newPin = new SCH_PIN( symbol );
1459
1460 // Copy all properties from the original pin
1461 newPin->SetPosition( pin->GetPosition() );
1462 newPin->SetOrientation( pin->GetOrientation() );
1463 newPin->SetShape( pin->GetShape() );
1464 newPin->SetLength( pin->GetLength() );
1465 // Hidden power input pins act as global labels, so demote them to passive
1466 if( pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN )
1468 else
1469 newPin->SetType( pin->GetType() );
1470
1471 newPin->SetName( pin->GetName() );
1472 newPin->SetNumber( stackedNumbers[i] );
1473 newPin->SetNameTextSize( pin->GetNameTextSize() );
1474 newPin->SetNumberTextSize( pin->GetNumberTextSize() );
1475 newPin->SetUnit( pin->GetUnit() );
1476 newPin->SetBodyStyle( pin->GetBodyStyle() );
1477 newPin->SetVisible( false );
1478
1479 // Add the new pin to the symbol
1480 symbol->AddDrawItem( newPin );
1481 }
1482
1483 commit.Push( _( "Explode Stacked Pin" ) );
1484 m_frame->RebuildView();
1485 return 0;
1486}
1487
1488
1490{
1491 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1492
1493 if( !symbol )
1494 return 0;
1495
1496 if( !symbol->IsDerived() )
1497 {
1498 m_frame->ShowInfoBarError( _( "Symbol is not derived from another symbol." ) );
1499 }
1500 else
1501 {
1502 DIALOG_UPDATE_SYMBOL_FIELDS dlg( m_frame, symbol );
1503
1504 if( dlg.ShowModal() == wxID_CANCEL )
1505 return -1;
1506 }
1507
1508 return 0;
1509}
1510
1511
1513{
1514 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1515
1516 // Nuke the selection for later rebuilding. This does *not* clear the flags on any items;
1517 // it just clears the SELECTION's reference to them.
1518 selTool->GetSelection().Clear();
1519 {
1520 m_frame->GetSymbolFromUndoList();
1521 }
1522 selTool->RebuildSelection();
1523
1524 return 0;
1525}
1526
1527
1529{
1530 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1531
1532 // Nuke the selection for later rebuilding. This does *not* clear the flags on any items;
1533 // it just clears the SELECTION's reference to them.
1534 selTool->GetSelection().Clear();
1535 {
1536 m_frame->GetSymbolFromRedoList();
1537 }
1538 selTool->RebuildSelection();
1539
1540 return 0;
1541}
1542
1543
1545{
1546 int retVal = Copy( aEvent );
1547
1548 if( retVal == 0 )
1549 retVal = DoDelete( aEvent );
1550
1551 return retVal;
1552}
1553
1554
1556{
1557 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1558 SCH_SELECTION& selection = m_selectionTool->RequestSelection( nonFields );
1559
1560 if( !symbol || !selection.GetSize() )
1561 return 0;
1562
1563 for( SCH_ITEM& item : symbol->GetDrawItems() )
1564 {
1565 if( item.Type() == SCH_FIELD_T )
1566 continue;
1567
1568 wxASSERT( !item.HasFlag( STRUCT_DELETED ) );
1569
1570 if( !item.IsSelected() )
1571 item.SetFlags( STRUCT_DELETED );
1572 }
1573
1574 LIB_SYMBOL* partCopy = new LIB_SYMBOL( *symbol );
1575
1576 STRING_FORMATTER formatter;
1577 SCH_IO_KICAD_SEXPR::FormatLibSymbol( partCopy, formatter );
1578
1579 delete partCopy;
1580
1581 for( SCH_ITEM& item : symbol->GetDrawItems() )
1582 item.ClearFlags( STRUCT_DELETED );
1583
1584 std::string prettyData = formatter.GetString();
1585 KICAD_FORMAT::Prettify( prettyData, KICAD_FORMAT::FORMAT_MODE::COMPACT_TEXT_PROPERTIES );
1586
1587 // Generate SVG and PNG for multi-format clipboard
1588 std::vector<CLIPBOARD_MIME_DATA> mimeData;
1589
1590 // Get the bounding box for just the selected items
1591 BOX2I bbox;
1592
1593 for( EDA_ITEM* item : selection )
1594 {
1595 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
1596 if( bbox.GetWidth() == 0 && bbox.GetHeight() == 0 )
1597 bbox = schItem->GetBoundingBox();
1598 else
1599 bbox.Merge( schItem->GetBoundingBox() );
1600 }
1601
1602 if( bbox.GetWidth() > 0 && bbox.GetHeight() > 0 )
1603 {
1604 bbox.Inflate( bbox.GetWidth() * clipboardBboxInflation,
1605 bbox.GetHeight() * clipboardBboxInflation );
1606
1607 // Create a temporary symbol with just the selected items for plotting
1608 LIB_SYMBOL* plotSymbol = new LIB_SYMBOL( *symbol );
1609
1610 // Mark unselected items as deleted in the plot copy
1611 for( SCH_ITEM& item : plotSymbol->GetDrawItems() )
1612 {
1613 if( item.Type() == SCH_FIELD_T )
1614 continue;
1615
1616 // Find matching item in selection by position/type
1617 bool found = false;
1618
1619 for( EDA_ITEM* selItem : selection )
1620 {
1621 SCH_ITEM* selSchItem = static_cast<SCH_ITEM*>( selItem );
1622
1623 if( selSchItem->Type() == item.Type()
1624 && selSchItem->GetPosition() == item.GetPosition() )
1625 {
1626 found = true;
1627 break;
1628 }
1629 }
1630
1631 if( !found )
1632 item.SetFlags( STRUCT_DELETED );
1633 }
1634
1635 // Now copy only the non-deleted items to a clean symbol for plotting
1636 LIB_SYMBOL* cleanSymbol = new LIB_SYMBOL( *plotSymbol );
1637 delete plotSymbol;
1638
1639 int unit = m_frame->GetUnit();
1640 int bodyStyle = m_frame->GetBodyStyle();
1641
1642 wxMemoryBuffer svgBuffer;
1643
1644 if( plotSymbolToSvg( m_frame, cleanSymbol, bbox, unit, bodyStyle, svgBuffer ) )
1645 appendMimeData( mimeData, wxS( "image/svg+xml" ), svgBuffer );
1646
1647 wxImage pngImage = renderSymbolToImageWithAlpha( m_frame, cleanSymbol, bbox, unit, bodyStyle );
1648
1649 if( pngImage.IsOk() )
1650 appendMimeData( mimeData, wxS( "image/png" ), std::move( pngImage ) );
1651
1652 delete cleanSymbol;
1653 }
1654
1655 if( SaveClipboard( prettyData, mimeData ) )
1656 return 0;
1657 else
1658 return -1;
1659}
1660
1661
1663{
1664 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1665 SCH_SELECTION& selection = selTool->RequestSelection();
1666
1667 if( selection.Empty() )
1668 return 0;
1669
1670 wxString itemsAsText = GetSelectedItemsAsText( selection );
1671
1672 if( selection.IsHover() )
1673 m_toolMgr->RunAction( ACTIONS::selectionClear );
1674
1675 return SaveClipboard( itemsAsText.ToStdString() );
1676}
1677
1678
1680{
1681 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1682 LIB_SYMBOL* newPart = nullptr;
1683
1684 if( !symbol || symbol->IsDerived() )
1685 return 0;
1686
1687 std::string clipboardData = GetClipboardUTF8();
1688
1689 try
1690 {
1691 std::vector<LIB_SYMBOL*> newParts = SCH_IO_KICAD_SEXPR::ParseLibSymbols( clipboardData, "Clipboard" );
1692
1693 if( newParts.empty() || !newParts[0] )
1694 return -1;
1695
1696 newPart = newParts[0];
1697 }
1698 catch( IO_ERROR& )
1699 {
1700 // If it's not a symbol then paste as text
1701 newPart = new LIB_SYMBOL( "dummy_part" );
1702
1703 wxString pasteText( clipboardData );
1704
1705 // Limit of 5000 is totally arbitrary. Without a limit, pasting a bitmap image from
1706 // eeschema makes KiCad appear to hang.
1707 if( pasteText.Length() > 5000 )
1708 pasteText = pasteText.Left( 5000 ) + wxT( "..." );
1709
1710 SCH_TEXT* newText = new SCH_TEXT( { 0, 0 }, pasteText, LAYER_DEVICE );
1711 newPart->AddDrawItem( newText );
1712 }
1713
1714 SCH_COMMIT commit( m_toolMgr );
1715
1716 commit.Modify( symbol, m_frame->GetScreen() );
1717 m_selectionTool->ClearSelection();
1718
1719 for( SCH_ITEM& item : symbol->GetDrawItems() )
1720 item.ClearFlags( IS_NEW | IS_PASTED | SELECTED );
1721
1722 for( SCH_ITEM& item : newPart->GetDrawItems() )
1723 {
1724 if( item.Type() == SCH_FIELD_T )
1725 continue;
1726
1727 SCH_ITEM* newItem = item.Duplicate( true, &commit );
1728 newItem->SetParent( symbol );
1729 newItem->SetFlags( IS_NEW | IS_PASTED | SELECTED );
1730
1731 newItem->SetUnit( newItem->GetUnit() ? m_frame->GetUnit() : 0 );
1732 newItem->SetBodyStyle( newItem->GetBodyStyle() ? m_frame->GetBodyStyle() : 0 );
1733
1734 symbol->AddDrawItem( newItem );
1735 getView()->Add( newItem );
1736 }
1737
1738 delete newPart;
1739
1740 m_selectionTool->RebuildSelection();
1741
1742 SCH_SELECTION& selection = m_selectionTool->GetSelection();
1743
1744 if( !selection.Empty() )
1745 {
1746 selection.SetReferencePoint( getViewControls()->GetCursorPosition( true ) );
1747
1748 if( m_toolMgr->RunSynchronousAction( SCH_ACTIONS::move, &commit ) )
1749 commit.Push( _( "Paste" ) );
1750 else
1751 commit.Revert();
1752 }
1753
1754 return 0;
1755}
1756
1757
1759{
1760 LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
1761 SCH_SELECTION& selection = m_selectionTool->RequestSelection( nonFields );
1762 SCH_COMMIT commit( m_toolMgr );
1763
1764 if( selection.GetSize() == 0 )
1765 return 0;
1766
1767 commit.Modify( symbol, m_frame->GetScreen() );
1768
1769 std::vector<EDA_ITEM*> oldItems;
1770 std::vector<EDA_ITEM*> newItems;
1771
1772 std::copy( selection.begin(), selection.end(), std::back_inserter( oldItems ) );
1773 std::sort( oldItems.begin(), oldItems.end(), []( EDA_ITEM* a, EDA_ITEM* b )
1774 {
1775 int cmp;
1776
1777 if( a->Type() != b->Type() )
1778 return a->Type() < b->Type();
1779
1780 // Create the new pins in the same order as the old pins
1781 if( a->Type() == SCH_PIN_T )
1782 {
1783 const wxString& aNum = static_cast<SCH_PIN*>( a )->GetNumber();
1784 const wxString& bNum = static_cast<SCH_PIN*>( b )->GetNumber();
1785
1786 cmp = StrNumCmp( aNum, bNum );
1787
1788 // If the pin numbers are not numeric, then just number them by their position
1789 // on the screen.
1790 if( aNum.IsNumber() && bNum.IsNumber() && cmp != 0 )
1791 return cmp < 0;
1792 }
1793
1795
1796 if( cmp != 0 )
1797 return cmp < 0;
1798
1799 return a->m_Uuid < b->m_Uuid;
1800 } );
1801
1802 for( EDA_ITEM* item : oldItems )
1803 {
1804 SCH_ITEM* oldItem = static_cast<SCH_ITEM*>( item );
1805 SCH_ITEM* newItem = oldItem->Duplicate( true, &commit );
1806
1807 if( newItem->Type() == SCH_PIN_T )
1808 {
1809 SCH_PIN* newPin = static_cast<SCH_PIN*>( newItem );
1810
1811 if( !newPin->GetNumber().IsEmpty() )
1812 newPin->SetNumber( wxString::Format( wxT( "%i" ), symbol->GetMaxPinNumber() + 1 ) );
1813 }
1814
1815 oldItem->ClearFlags( IS_NEW | IS_PASTED | SELECTED );
1816 newItem->SetFlags( IS_NEW | IS_PASTED | SELECTED );
1817 newItem->SetParent( symbol );
1818 newItems.push_back( newItem );
1819
1820 symbol->AddDrawItem( newItem );
1821 getView()->Add( newItem );
1822 }
1823
1824 m_toolMgr->RunAction( ACTIONS::selectionClear );
1825 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &newItems );
1826
1827 selection.SetReferencePoint( getViewControls()->GetCursorPosition( true ) );
1828
1829 if( m_toolMgr->RunSynchronousAction( SCH_ACTIONS::move, &commit ) )
1830 commit.Push( _( "Duplicate" ) );
1831 else
1832 commit.Revert();
1833
1834 return 0;
1835}
1836
1837
1839{
1840 // clang-format off
1848
1856
1862
1869 // clang-format on
1870}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
std::optional< BOX2I > OPT_BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
static TOOL_ACTION decrementPrimary
Definition actions.h:92
static TOOL_ACTION paste
Definition actions.h:76
static TOOL_ACTION cancelInteractive
Definition actions.h:68
static TOOL_ACTION unselectAll
Definition actions.h:79
static TOOL_ACTION decrementSecondary
Definition actions.h:94
static TOOL_ACTION copy
Definition actions.h:74
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION incrementSecondary
Definition actions.h:93
static TOOL_ACTION duplicate
Definition actions.h:80
static TOOL_ACTION incrementPrimary
Definition actions.h:91
static TOOL_ACTION doDelete
Definition actions.h:81
static TOOL_ACTION redo
Definition actions.h:72
static TOOL_ACTION deleteTool
Definition actions.h:82
static TOOL_ACTION increment
Definition actions.h:90
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION cut
Definition actions.h:73
static TOOL_ACTION copyAsText
Definition actions.h:75
static TOOL_ACTION refreshPreview
Definition actions.h:155
static TOOL_ACTION selectAll
Definition actions.h:78
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:228
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:554
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr Vec Centre() const
Definition box2.h:93
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:654
constexpr size_type GetHeight() const
Definition box2.h:211
constexpr const Vec & GetOrigin() const
Definition box2.h:206
constexpr const SizeVec & GetSize() const
Definition box2.h:202
bool Empty() const
Definition commit.h:134
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:102
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:96
virtual VECTOR2I GetPosition() const
Definition eda_item.h:282
virtual void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:283
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:135
EDA_ITEM_FLAGS GetEditFlags() const
Definition eda_item.h:158
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:154
EDA_ITEM * GetParent() const
Definition eda_item.h:110
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:89
bool IsMoving() const
Definition eda_item.h:130
virtual bool IsVisible() const
Definition eda_text.h:208
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:412
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:221
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:381
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:224
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:404
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
GAL_ANTIALIASING_MODE antialiasing_mode
The grid style to draw the grid in.
static std::unique_ptr< GAL_PRINT > Create(GAL_DISPLAY_OPTIONS &aOptions, wxDC *aDC)
Abstract interface for drawing on a 2D-surface.
void SetZoomFactor(double aZoomFactor)
void SetLookAtPoint(const VECTOR2D &aPoint)
Get/set the Point in world space to look at.
virtual void ClearScreen()
Clear the screen.
void SetWorldUnitLength(double aWorldUnitLength)
Set the unit length.
void SetClearColor(const COLOR4D &aColor)
virtual double GetNativeDPI() const =0
virtual bool HasNativeLandscapeRotation() const =0
void SetDefaultPenWidth(int aWidth)
void SetIsPrinting(bool isPrinting)
double GetScale() const
Definition view.h:281
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:300
static constexpr int VIEW_MAX_LAYERS
Maximum number of layers that may be shown.
Definition view.h:771
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:168
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
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:79
bool UnitsLocked() const
Check whether symbol units are interchangeable.
Definition lib_symbol.h:283
bool IsDerived() const
Definition lib_symbol.h:196
void Plot(PLOTTER *aPlotter, bool aBackground, const SCH_PLOT_OPTS &aPlotOpts, int aUnit, int aBodyStyle, const VECTOR2I &aOffset, bool aDimmed) override
Plot the item to aPlotter.
void PlotFields(PLOTTER *aPlotter, bool aBackground, const SCH_PLOT_OPTS &aPlotOpts, int aUnit, int aBodyStyle, const VECTOR2I &aOffset, bool aDimmed)
Plot symbol fields.
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:709
wxString GetName() const override
Definition lib_symbol.h:141
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.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:37
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
void SetHeightMils(double aHeightInMils)
void SetWidthMils(double aWidthInMils)
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
SCH_RENDER_SETTINGS * GetRenderSettings()
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
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.
KIGFX::SCH_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
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:162
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:160
virtual void SetBodyStyle(int aBodyStyle)
Definition sch_item.h:241
int GetBodyStyle() const
Definition sch_item.h:242
virtual void MirrorHorizontally(int aCenter)
Mirror item horizontally about aCenter.
Definition sch_item.h:404
int GetUnit() const
Definition sch_item.h:233
virtual void Rotate(const VECTOR2I &aCenter, bool aRotateCCW)
Rotate the item around aCenter 90 degrees in the clockwise direction.
Definition sch_item.h:420
virtual void SetUnit(int aUnit)
Definition sch_item.h:232
wxString GetClass() const override
Return the class name.
Definition sch_item.h:172
virtual void MirrorVertically(int aCenter)
Mirror item vertically about aCenter.
Definition sch_item.h:412
void SetNumber(const wxString &aNumber)
Definition sch_pin.cpp:734
void SetVisible(bool aVisible)
Definition sch_pin.h:117
void SetOrientation(PIN_ORIENTATION aOrientation)
Definition sch_pin.h:96
void SetName(const wxString &aName)
Definition sch_pin.cpp:512
void SetPosition(const VECTOR2I &aPos) override
Definition sch_pin.h:254
const wxString & GetName() const
Definition sch_pin.cpp:494
void SetLength(int aLength)
Definition sch_pin.h:102
PIN_ORIENTATION GetOrientation() const
Definition sch_pin.cpp:353
void SetNumberTextSize(int aSize)
Definition sch_pin.cpp:788
void SetShape(GRAPHIC_PINSHAPE aShape)
Definition sch_pin.h:99
VECTOR2I GetPosition() const override
Definition sch_pin.cpp:345
void SetType(ELECTRICAL_PINTYPE aType)
Definition sch_pin.cpp:422
const wxString & GetNumber() const
Definition sch_pin.h:127
ELECTRICAL_PINTYPE GetType() const
Definition sch_pin.cpp:402
void SetNameTextSize(int aSize)
Definition sch_pin.cpp:762
void SetBackgroundColor(const COLOR4D &aColor) override
Set the background color.
void LoadColors(const COLOR_SETTINGS *aSettings) override
const PAGE_INFO & GetPageSettings() const
Definition sch_screen.h:137
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:71
ITER end()
Definition selection.h:76
ITER begin()
Definition selection.h:75
virtual VECTOR2I GetCenter() const
Returns the center point of the selection area bounding box.
Definition selection.cpp:88
bool IsHover() const
Definition selection.h:85
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition selection.h:101
EDA_ITEM * Front() const
Definition selection.h:173
virtual void Clear() override
Remove all the stored items from the group.
Definition selection.h:94
int Size() const
Returns the number of selected parts.
Definition selection.h:117
std::vector< EDA_ITEM * > GetItemsSortedBySelectionOrder() const
void SetReferencePoint(const VECTOR2I &aP)
bool Empty() const
Checks if there is anything selected.
Definition selection.h:111
Implement an OUTPUTFORMATTER to a memory buffer.
Definition richio.h:418
const std::string & GetString()
Definition richio.h:441
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
The symbol library editor main window.
COLOR_SETTINGS * GetColorSettings(bool aForceRefresh=false) const override
Returns a pointer to the active color theme settings.
LIB_SYMBOL * GetBufferedSymbol(const wxString &aSymbolName, const wxString &aLibrary)
Return the symbol copy from the buffer.
bool UpdateSymbolAfterRename(LIB_SYMBOL *aSymbol, const wxString &aOldSymbolName, const wxString &aLibrary)
Update the symbol buffer with a new version of the symbol when the name has changed.
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:40
KIGFX::VIEW * getView() const
Definition tool_base.cpp:34
Generic, UI-independent tool event.
Definition tool_event.h:167
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition tool_event.h:388
COMMIT * Commit() const
Definition tool_event.h:279
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:40
bool SaveClipboard(const std::string &aTextUTF8)
Store information to the system clipboard.
Definition clipboard.cpp:32
std::string GetClipboardUTF8()
Return the information currently stored in the system clipboard.
#define _(s)
@ RECURSE
Definition eda_item.h:49
#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_DRAWINGSHEET
Sheet frame and title block.
Definition layer_ids.h:274
@ LAYER_DEVICE
Definition layer_ids.h:464
void Prettify(std::string &aSource, FORMAT_MODE aMode)
Pretty-prints s-expression text according to KiCad format rules.
@ TARGET_NONCACHED
Auxiliary rendering target (noncached)
Definition definitions.h:34
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:42
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:39
PIN_ORIENTATION
The symbol library pin object orientations.
Definition pin_type.h:101
Plotting engines similar to ps (PostScript, Gerber, svg)
std::vector< EDA_ITEM * > EDA_ITEMS
wxString GetSelectedItemsAsText(const SELECTION &aSel)
constexpr double SCH_WORLD_UNIT(1e-7/0.0254)
wxString TitleCaps(const wxString &aString)
Capitalize the first letter in each word.
wxString EscapeStackedPinItem(const wxString &aPinNumber)
Escape the characters that carry structural meaning inside stacked pin notation ('[',...
wxString m_mimeType
Definition clipboard.h:35
wxMemoryBuffer m_data
Definition clipboard.h:36
std::optional< wxBitmap > m_image
Optional bitmap image to add to clipboard via wxBitmapDataObject.
Definition clipboard.h:41
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:162
@ LIB_SYMBOL_T
Definition typeinfo.h:145
@ SCH_TABLECELL_T
Definition typeinfo.h:163
@ SCH_FIELD_T
Definition typeinfo.h:147
@ SCH_SHAPE_T
Definition typeinfo.h:146
@ SCH_TEXT_T
Definition typeinfo.h:148
@ SCH_TEXTBOX_T
Definition typeinfo.h:149
@ SCH_PIN_T
Definition typeinfo.h:150
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682
constexpr int LexicographicalCompare(const VECTOR2< T > &aA, const VECTOR2< T > &aB)
Definition vector2d.h:632
#define ZOOM_MIN_LIMIT_EESCHEMA
#define ZOOM_MAX_LIMIT_EESCHEMA