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