KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_drawing_tools.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-2023 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
21#include <eda_shape.h>
22#include <tool/tool_manager.h>
23#include "sch_sheet_path.h"
24#include <limits>
25#include <memory>
26#include <set>
27#include <unordered_set>
28
29#include <advanced_config.h>
31#include <kiplatform/ui.h>
32#include <optional>
33#include <project_sch.h>
41#include <sch_actions.h>
42#include <sch_tool_utils.h>
43#include <sch_edit_frame.h>
44#include <widgets/wx_infobar.h>
45#include <pgm_base.h>
46#include <design_block.h>
48#include <eeschema_id.h>
49#include <confirm.h>
50#include <view/view_controls.h>
51#include <view/view.h>
52#include <sch_symbol.h>
53#include <sch_no_connect.h>
54#include <sch_group.h>
55#include <sch_line.h>
56#include <sch_junction.h>
57#include <sch_bus_entry.h>
58#include <sch_table.h>
59#include <sch_tablecell.h>
60#include <sch_sheet.h>
61#include <sch_sheet_pin.h>
62#include <sch_label.h>
63#include <sch_bitmap.h>
64#include <schematic.h>
65#include <sch_commit.h>
66#include <scoped_set_reset.h>
68#include <eeschema_settings.h>
76#include <string_utils.h>
78#include <wx/filedlg.h>
79#include <wx/msgdlg.h>
80
81
83
84
102
103
105{
107
108 auto belowRootSheetCondition =
109 [this]( const SELECTION& aSel )
110 {
111 return m_frame->GetCurrentSheet().Last() != &m_frame->Schematic().Root();
112 };
113
114 // some interactive drawing tools can undo the last point
115 auto canUndoPoint =
116 [this]( const SELECTION& aSel )
117 {
118 return ( m_mode == MODE::RULE_AREA );
119 };
120
121 auto inDrawingRuleArea =
122 [this]( const SELECTION& aSel )
123 {
124 return m_mode == MODE::RULE_AREA;
125 };
126
127 CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
128
129 // clang-format off
130 ctxMenu.AddItem( SCH_ACTIONS::leaveSheet, belowRootSheetCondition, 150 );
131 ctxMenu.AddItem( SCH_ACTIONS::closeOutline, inDrawingRuleArea, 200 );
132 ctxMenu.AddItem( ACTIONS::deleteLastPoint, canUndoPoint, 200 );
133 // clang-format on
134
135 return true;
136}
137
138
140{
142
143 SCH_SYMBOL* symbol = toolParams.m_Symbol;
144
145 // If we get a parameterised symbol, we probably just want to place that and get out of the placement tool,
146 // rather than popping up the chooser afterwards. A multi-unit symbol may still request that its remaining
147 // units be placed before the tool exits.
148 bool placeOneOnly = symbol != nullptr;
149
151 std::vector<PICKED_SYMBOL>* historyList = nullptr;
152 bool ignorePrimePosition = false;
153 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
154 SCHEMATIC_SETTINGS& schSettings = m_frame->Schematic().Settings();
155 SCH_SCREEN* screen = m_frame->GetScreen();
156 bool keepSymbol = false;
157 bool placeAllUnits = toolParams.m_PlaceAllUnits;
158
159 if( m_inDrawingTool )
160 return 0;
161
163
166 VECTOR2I cursorPos;
167
168 // First we need to get all instances of this sheet so we can annotate whatever symbols we place on all copies
169 SCH_SHEET_LIST hierarchy = m_frame->Schematic().Hierarchy();
170 SCH_SHEET_LIST newInstances = hierarchy.FindAllSheetsForScreen( m_frame->GetCurrentSheet().LastScreen() );
171 newInstances.SortByPageNumbers();
172
173 // Get a list of all references in the schematic to avoid duplicates wherever they're placed
174 SCH_REFERENCE_LIST existingRefs;
175 hierarchy.GetSymbols( existingRefs, SYMBOL_FILTER_ALL );
176 existingRefs.SortByReferenceOnly();
177
178 if( aEvent.IsAction( &SCH_ACTIONS::placeSymbol ) )
179 {
180 historyList = &m_symbolHistoryList;
181 }
182 else if (aEvent.IsAction( &SCH_ACTIONS::placePower ) )
183 {
184 historyList = &m_powerHistoryList;
185 filter.FilterPowerSymbols( true );
186 }
187 else
188 {
189 wxFAIL_MSG( "PlaceSymbol(): unexpected request" );
190 }
191
192 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
193 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
194
195 auto addSymbol =
196 [this]( SCH_SYMBOL* aSymbol )
197 {
199 m_selectionTool->AddItemToSel( aSymbol );
200
201 aSymbol->SetFlags( IS_NEW | IS_MOVING );
202
203 m_view->ClearPreview();
204 m_view->AddToPreview( aSymbol, false ); // Add, but not give ownership
205
206 // Set IS_MOVING again, as AddItemToCommitAndScreen() will have cleared it.
207 aSymbol->SetFlags( IS_MOVING );
208 m_toolMgr->PostAction( ACTIONS::refreshPreview );
209 };
210
211 auto setCursor =
212 [&]()
213 {
214 m_frame->GetCanvas()->SetCurrentCursor( symbol ? KICURSOR::MOVING : KICURSOR::COMPONENT );
215 };
216
217 auto cleanup =
218 [&]()
219 {
221 m_view->ClearPreview();
222 delete symbol;
223 symbol = nullptr;
224
225 existingRefs.Clear();
226 hierarchy.GetSymbols( existingRefs, SYMBOL_FILTER_ALL );
227 existingRefs.SortByReferenceOnly();
228 };
229
230 auto annotate =
231 [&]()
232 {
233 EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
234
235 // Then we need to annotate all instances by sheet
236 for( SCH_SHEET_PATH& instance : newInstances )
237 {
238 SCH_REFERENCE newReference( symbol, instance );
240 refs.AddItem( newReference );
241 refs.SetRefDesTracker( schSettings.m_refDesTracker );
242
243 if( cfg->m_AnnotatePanel.automatic || newReference.AlwaysAnnotate() )
244 {
246 (ANNOTATE_ALGO_T) schSettings.m_AnnotateMethod,
247 schSettings.m_AnnotateStartNum, existingRefs, false,
248 &hierarchy );
249
250 refs.UpdateAnnotation();
251
252 // Update existing refs for next iteration
253 for( size_t i = 0; i < refs.GetCount(); i++ )
254 existingRefs.AddItem( refs[i] );
255 }
256 }
257
258 m_frame->GetCurrentSheet().UpdateAllScreenReferences();
259 };
260
261 Activate();
262
263 // Must be done after Activate() so that it gets set into the correct context
264 getViewControls()->ShowCursor( true );
265
266 // Set initial cursor
267 setCursor();
268
269 // Prime the pump
270 if( symbol )
271 {
272 addSymbol( symbol );
273
274 if( toolParams.m_Reannotate )
275 annotate();
276
277 // Seed the placed-reference list so multi-unit stepping sees this symbol's first unit
278 // as taken. The chooser path seeds it when it builds the symbol; this path bypasses
279 // that branch.
280 SCH_REFERENCE placedSymbolReference( symbol, m_frame->GetCurrentSheet() );
281 existingRefs.AddItem( placedSymbolReference );
282 existingRefs.SortByReferenceOnly();
283
284 getViewControls()->WarpMouseCursor( getViewControls()->GetMousePosition( false ) );
285 }
286 else if( aEvent.HasPosition() )
287 {
288 m_toolMgr->PrimeTool( aEvent.Position() );
289 }
290 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
291 {
292 m_toolMgr->PrimeTool( { 0, 0 } );
293 ignorePrimePosition = true;
294 }
295
296 // Main loop: keep receiving events
297 while( TOOL_EVENT* evt = Wait() )
298 {
299 setCursor();
300 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
301 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
302
303 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_CONNECTABLE );
304 controls->ForceCursorPosition( true, cursorPos );
305
306 // The tool hotkey is interpreted as a click when drawing
307 bool isSyntheticClick = symbol && evt->IsActivate() && evt->HasPosition() && evt->Matches( aEvent );
308
309 if( evt->IsCancelInteractive() || ( symbol && evt->IsAction( &ACTIONS::undo ) ) )
310 {
311 m_frame->GetInfoBar()->Dismiss();
312
313 if( symbol )
314 {
315 cleanup();
316
317 if( keepSymbol )
318 {
319 // Re-enter symbol chooser
320 m_toolMgr->PostAction( ACTIONS::cursorClick );
321 }
322 }
323 else
324 {
325 break;
326 }
327 }
328 else if( evt->IsActivate() && !isSyntheticClick )
329 {
330 if( symbol && evt->IsMoveTool() )
331 {
332 // we're already moving our own item; ignore the move tool
333 evt->SetPassEvent( false );
334 continue;
335 }
336
337 if( symbol )
338 {
339 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel symbol creation." ) );
340 evt->SetPassEvent( false );
341 continue;
342 }
343
344 if( evt->IsMoveTool() )
345 {
346 // Make sure we come back after the move tool runs
347 frame()->PushTool( originalEvent );
348 }
349
350 break;
351 }
352 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
353 || isSyntheticClick
354 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
355 {
356 if( !symbol )
357 {
359
362
363 std::set<UTF8> unique_libid;
364 std::vector<PICKED_SYMBOL> alreadyPlaced;
365
366 for( SCH_SHEET_PATH& sheet : hierarchy )
367 {
368 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
369 {
370 SCH_SYMBOL* s = static_cast<SCH_SYMBOL*>( item );
371
372 if( !unique_libid.insert( s->GetLibId().Format() ).second )
373 continue;
374
375 LIB_SYMBOL* libSymbol = SchGetLibSymbol( s->GetLibId(), libs, cache );
376
377 if( libSymbol )
378 {
379 if( libSymbol->IsPower() != filter.GetFilterPowerSymbols() )
380 continue;
381
382 PICKED_SYMBOL pickedSymbol;
383 pickedSymbol.LibId = libSymbol->GetLibId();
384 alreadyPlaced.push_back( pickedSymbol );
385 }
386 }
387 }
388
389 // Pick the symbol to be placed
390 bool footprintPreviews = m_frame->eeconfig()->m_Appearance.footprint_preview;
391 PICKED_SYMBOL sel = m_frame->PickSymbolFromLibrary( &filter, *historyList, alreadyPlaced,
392 footprintPreviews );
393
394 keepSymbol = sel.KeepSymbol;
395 placeAllUnits = sel.PlaceAllUnits;
396
397 LIB_SYMBOL* libSymbol = sel.LibId.IsValid() ? m_frame->GetLibSymbol( sel.LibId ) : nullptr;
398
399 if( !libSymbol )
400 continue;
401
402 // If we started with a hotkey which has a position then warp back to that.
403 // Otherwise update to the current mouse position pinned inside the autoscroll
404 // boundaries.
405 if( evt->IsPrime() && !ignorePrimePosition )
406 {
407 cursorPos = grid.Align( evt->Position(), GRID_HELPER_GRIDS::GRID_CONNECTABLE );
408 getViewControls()->WarpMouseCursor( cursorPos, true );
409 }
410 else
411 {
413 cursorPos = grid.Align( getViewControls()->GetMousePosition(),
415 }
416
417 EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
418
419 // Only convert between power symbol types. Regular (non-power) symbols must
420 // never be promoted to power symbols just because the default is set to
421 // Global or Local. The preference's Default option means "follow the symbol
422 // definition" and any conversion only applies to symbols that are already
423 // power symbols.
424 if( libSymbol->IsPower()
425 && !libSymbol->IsLocalPower()
427 {
428 libSymbol->SetLocalPower();
429 wxString keywords = libSymbol->GetKeyWords();
430
431 // Adjust the KiCad library default fields to match the new power symbol type
432 if( keywords.Contains( wxT( "global power" ) ) )
433 {
434 keywords.Replace( wxT( "global power" ), wxT( "local power" ) );
435 libSymbol->SetKeyWords( keywords );
436 }
437
438 wxString desc = libSymbol->GetDescription();
439
440 if( desc.Contains( wxT( "global label" ) ) )
441 {
442 desc.Replace( wxT( "global label" ), wxT( "local label" ) );
443 libSymbol->SetDescription( desc );
444 }
445 }
446 else if( libSymbol->IsPower()
447 && !libSymbol->IsGlobalPower()
449 {
450 // We do not currently have local power symbols in the KiCad library, so
451 // don't update any fields
452 libSymbol->SetGlobalPower();
453 }
454
455 symbol = new SCH_SYMBOL( *libSymbol, &m_frame->GetCurrentSheet(), sel, cursorPos,
456 &m_frame->Schematic() );
457 addSymbol( symbol );
458 annotate();
459
460 // Update the list of references for the next symbol placement.
461 SCH_REFERENCE placedSymbolReference( symbol, m_frame->GetCurrentSheet() );
462 existingRefs.AddItem( placedSymbolReference );
463 existingRefs.SortByReferenceOnly();
464
465 if( m_frame->eeconfig()->m_AutoplaceFields.enable )
466 {
467 // Not placed yet, so pass a nullptr screen reference
468 symbol->AutoplaceFields( nullptr, AUTOPLACE_AUTO );
469 }
470
471 // Update cursor now that we have a symbol
472 setCursor();
473 }
474 else
475 {
476 m_view->ClearPreview();
477 m_frame->AddToScreen( symbol, screen );
478
479 if( m_frame->eeconfig()->m_AutoplaceFields.enable )
480 symbol->AutoplaceFields( screen, AUTOPLACE_AUTO );
481
482 m_frame->SaveCopyForRepeatItem( symbol );
483
484 SCH_COMMIT commit( m_toolMgr );
485 commit.Added( symbol, screen );
486
488 lwbTool->TrimOverLappingWires( &commit, &m_selectionTool->GetSelection() );
489 lwbTool->AddJunctionsIfNeeded( &commit, &m_selectionTool->GetSelection() );
490
491 commit.Push( _( "Place Symbol" ) );
492
493 // A preselected single-unit symbol exits here rather than re-opening the
494 // chooser. Multi-unit placement must fall through to the unit continuation
495 // below, which exits once the units are exhausted.
496 if( placeOneOnly && !placeAllUnits )
497 break;
498
499 SCH_SYMBOL* nextSymbol = nullptr;
500
501 if( keepSymbol || placeAllUnits )
502 {
503 SCH_REFERENCE currentReference( symbol, m_frame->GetCurrentSheet() );
504 SCHEMATIC& schematic = m_frame->Schematic();
505
506 if( placeAllUnits )
507 {
508 // For unannotated references all U?-prefix symbols share the same ref
509 // string regardless of the library symbol they originate from. Only
510 // consider units already used by THIS library symbol when stepping
511 // through units, so different multi-unit parts that share a reference
512 // prefix do not collide pre-annotation.
513 const wxString currentRefStr = currentReference.GetRef();
514 const bool isUnannotated = !currentRefStr.IsEmpty() && currentRefStr.Last() == '?';
515 const LIB_ID symLibId = symbol->GetLibId();
516
517 auto unitOccupied =
518 [&]( int aUnit ) -> bool
519 {
520 if( !isUnannotated )
521 {
522 SCH_REFERENCE candidate = currentReference;
523 candidate.SetUnit( aUnit );
524 return schematic.Contains( candidate );
525 }
526
527 return IsUnannotatedUnitOccupied( existingRefs, currentRefStr, symLibId, aUnit );
528 };
529
530 while( currentReference.GetUnit() <= symbol->GetUnitCount()
531 && unitOccupied( currentReference.GetUnit() ) )
532 {
533 currentReference.SetUnit( currentReference.GetUnit() + 1 );
534 }
535
536 if( currentReference.GetUnit() > symbol->GetUnitCount() )
537 {
538 currentReference.SetUnit( 1 );
539 }
540 }
541
542 // We are either stepping to the next unit or next symbol
543 if( keepSymbol || currentReference.GetUnit() > 1 )
544 {
545 nextSymbol = static_cast<SCH_SYMBOL*>( symbol->Duplicate( IGNORE_PARENT_GROUP ) );
546 nextSymbol->SetUnit( currentReference.GetUnit() );
547 nextSymbol->SetUnitSelection( currentReference.GetUnit() );
548
549 addSymbol( nextSymbol );
550 symbol = nextSymbol;
551
552 if( currentReference.GetUnit() == 1 )
553 annotate();
554
555 // Update the list of references for the next symbol placement.
556 SCH_REFERENCE placedSymbolReference( symbol, m_frame->GetCurrentSheet() );
557 existingRefs.AddItem( placedSymbolReference );
558 existingRefs.SortByReferenceOnly();
559 }
560 }
561
562 symbol = nextSymbol;
563
564 // A preselected multi-unit symbol leaves the tool once its last unit is placed.
565 if( placeOneOnly && !symbol )
566 break;
567 }
568 }
569 else if( evt->IsClick( BUT_RIGHT ) )
570 {
571 // Warp after context menu only if dragging...
572 if( !symbol )
573 m_toolMgr->VetoContextMenuMouseWarp();
574
575 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
576 }
577 else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
578 {
579 if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_UNIT
580 && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_UNIT_END )
581 {
582 int unit = *evt->GetCommandId() - ID_POPUP_SCH_SELECT_UNIT;
583
584 if( symbol )
585 {
586 m_frame->SelectUnit( symbol, unit );
587 m_toolMgr->PostAction( ACTIONS::refreshPreview );
588 }
589 }
590 else if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_BODY_STYLE
591 && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_BODY_STYLE_END )
592 {
593 int bodyStyle = ( *evt->GetCommandId() - ID_POPUP_SCH_SELECT_BODY_STYLE ) + 1;
594
595 if( symbol && symbol->GetBodyStyle() != bodyStyle )
596 {
597 m_frame->SelectBodyStyle( symbol, bodyStyle );
598 m_toolMgr->PostAction( ACTIONS::refreshPreview );
599 }
600 }
601 }
602 else if( evt->IsAction( &ACTIONS::duplicate )
603 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem )
604 || evt->IsAction( &ACTIONS::paste ) )
605 {
606 if( symbol )
607 {
608 wxBell();
609 continue;
610 }
611
612 // Exit. The duplicate/repeat/paste will run in its own loop.
613 evt->SetPassEvent();
614 break;
615 }
616 else if( symbol && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
617 {
618 symbol->SetPosition( cursorPos );
619 m_view->ClearPreview();
620 m_view->AddToPreview( symbol, false ); // Add, but not give ownership
621 m_frame->SetMsgPanel( symbol );
622 }
623 else if( symbol && evt->IsAction( &ACTIONS::doDelete ) )
624 {
625 cleanup();
626 }
627 else if( symbol && ( evt->IsAction( &ACTIONS::redo )
628 || evt->IsAction( &SCH_ACTIONS::editWithLibEdit )
629 || evt->IsAction( &SCH_ACTIONS::changeSymbol ) ) )
630 {
631 wxBell();
632 }
633 else if( symbol
634 && ( evt->IsAction( &SCH_ACTIONS::properties ) || evt->IsAction( &SCH_ACTIONS::editReference )
635 || evt->IsAction( &SCH_ACTIONS::editValue ) || evt->IsAction( &SCH_ACTIONS::editFootprint )
636 || evt->IsAction( &SCH_ACTIONS::autoplaceFields ) || evt->IsAction( &SCH_ACTIONS::cycleBodyStyle )
637 || evt->IsAction( &SCH_ACTIONS::setExcludeFromBOM )
638 || evt->IsAction( &SCH_ACTIONS::setExcludeFromBoard )
639 || evt->IsAction( &SCH_ACTIONS::setExcludeFromSim )
640 || evt->IsAction( &SCH_ACTIONS::setExcludeFromPosFiles ) || evt->IsAction( &SCH_ACTIONS::setDNP )
641 || evt->IsAction( &SCH_ACTIONS::rotateCW ) || evt->IsAction( &SCH_ACTIONS::rotateCCW )
642 || evt->IsAction( &SCH_ACTIONS::mirrorV ) || evt->IsAction( &SCH_ACTIONS::mirrorH ) ) )
643 {
644 m_toolMgr->PostAction( ACTIONS::refreshPreview );
645 evt->SetPassEvent();
646 }
647 else
648 {
649 evt->SetPassEvent();
650 }
651
652 // Enable autopanning and cursor capture only when there is a symbol to be placed
653 getViewControls()->SetAutoPan( symbol != nullptr );
654 getViewControls()->CaptureCursor( symbol != nullptr );
655 }
656
657 getViewControls()->SetAutoPan( false );
658 getViewControls()->CaptureCursor( false );
659 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
660
661 return 0;
662}
663
664
666{
668 SCH_SYMBOL* symbol = params.m_Symbol;
669 int requestedUnit = params.m_Unit;
670
671 // TODO: get from selection
672 if( !symbol )
673 {
674 static const std::vector<KICAD_T> symbolTypes = { SCH_SYMBOL_T };
675 SCH_SELECTION& selection = m_selectionTool->RequestSelection( symbolTypes );
676
677 if( selection.Size() != 1 )
678 {
679 m_frame->ShowInfoBarMsg( _( "Select a single symbol to place the next unit." ) );
680 return 0;
681 }
682
683 wxCHECK( selection.Front()->Type() == SCH_SYMBOL_T, 0 );
684 symbol = static_cast<SCH_SYMBOL*>( selection.Front() );
685 }
686
687 if( !symbol )
688 return 0;
689
690 if( !symbol->IsMultiUnit() )
691 {
692 m_frame->ShowInfoBarMsg( _( "This symbol has only one unit." ) );
693 return 0;
694 }
695
696 const std::set<int> missingUnits = GetUnplacedUnitsForSymbol( *symbol );
697
698 if( missingUnits.empty() )
699 {
700 m_frame->ShowInfoBarMsg( _( "All units of this symbol are already placed." ) );
701 return 0;
702 }
703
704 int nextMissing;
705
706 if( requestedUnit > 0 )
707 {
708 if( missingUnits.count( requestedUnit ) == 0 )
709 {
710 m_frame->ShowInfoBarMsg( _( "Requested unit already placed." ) );
711 return 0;
712 }
713
714 nextMissing = requestedUnit;
715 }
716 else
717 {
718 // Find the lowest unit number that is missing
719 nextMissing = *std::min_element( missingUnits.begin(), missingUnits.end() );
720 }
721
722 std::unique_ptr<SCH_SYMBOL> newSymbol = std::make_unique<SCH_SYMBOL>( *symbol );
723 const SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
724
725 // Use SetUnitSelection(int) to update ALL instance references at once.
726 // This is important for shared sheets where the same screen is used by multiple
727 // sheet instances - we want the new symbol unit to appear correctly on all instances.
728 newSymbol->SetUnitSelection( nextMissing );
729 newSymbol->SetUnit( nextMissing );
730 newSymbol->SetRefProp( symbol->GetRef( &sheetPath, false ) );
731
732 // Post the new symbol - don't reannotate it - we set the reference ourselves
733 m_toolMgr->PostAction( SCH_ACTIONS::placeSymbol, SCH_ACTIONS::PLACE_SYMBOL_PARAMS{ newSymbol.release(), false } );
734 return 0;
735}
736
737
745static std::optional<VECTOR2I> designBlockAnchor( const std::vector<SCH_ITEM*>& aItems )
746{
747 std::optional<VECTOR2I> connectionAnchor;
748 std::optional<VECTOR2I> positionAnchor;
749
750 auto keepTopLeft =
751 []( std::optional<VECTOR2I>& aBest, const VECTOR2I& aCandidate )
752 {
753 if( !aBest || aCandidate.y < aBest->y || ( aCandidate.y == aBest->y && aCandidate.x < aBest->x ) )
754 aBest = aCandidate;
755 };
756
757 for( SCH_ITEM* item : aItems )
758 {
759 if( item->Type() == SCH_GROUP_T )
760 continue;
761
762 for( const VECTOR2I& pt : item->GetConnectionPoints() )
763 keepTopLeft( connectionAnchor, pt );
764
765 keepTopLeft( positionAnchor, item->GetPosition() );
766 }
767
768 return connectionAnchor ? connectionAnchor : positionAnchor;
769}
770
771
773{
774 if( m_inDrawingTool )
775 return 0;
776
778
779 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
780 EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
781 SCHEMATIC_SETTINGS& schSettings = m_frame->Schematic().Settings();
782 SCH_SCREEN* screen = m_frame->GetScreen();
783 SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
784
787 VECTOR2I cursorPos;
788
789 // Guard to reset forced cursor positioning on exit, regardless of error path
790 struct RESET_FORCED_CURSOR_GUARD
791 {
792 KIGFX::VIEW_CONTROLS* m_controls;
793
794 ~RESET_FORCED_CURSOR_GUARD()
795 {
796 m_controls->ForceCursorPosition( false );
797 }
798 };
799
800 RESET_FORCED_CURSOR_GUARD forcedCursorGuard{ controls };
801
802 if( !cfg || !common_settings )
803 return 0;
804
805 bool placingDesignBlock = aEvent.IsAction( &SCH_ACTIONS::placeDesignBlock );
806
807 std::unique_ptr<DESIGN_BLOCK> designBlock;
808 wxString sheetFileName = wxEmptyString;
809
810 if( placingDesignBlock )
811 {
812 SCH_DESIGN_BLOCK_PANE* designBlockPane = m_frame->GetDesignBlockPane();
813
814 if( designBlockPane->GetSelectedLibId().IsValid() )
815 {
816 designBlock.reset( designBlockPane->GetDesignBlock( designBlockPane->GetSelectedLibId(), true, true ) );
817
818 if( !designBlock )
819 return 0;
820
821 sheetFileName = designBlock->GetSchematicFile();
822
823 if( sheetFileName.IsEmpty() || !wxFileExists( sheetFileName ) )
824 {
825 m_frame->ShowInfoBarError( _( "Design block has no schematic to place." ), true );
826 return 0;
827 }
828 }
829 }
830 else
831 {
832 wxString* importSourceFile = aEvent.Parameter<wxString*>();
833
834 if( importSourceFile != nullptr )
835 sheetFileName = *importSourceFile;
836 }
837
838 auto setCursor =
839 [&]()
840 {
841 m_frame->GetCanvas()->SetCurrentCursor( designBlock ? KICURSOR::MOVING
843 };
844
845 auto placeSheetContents =
846 [&]()
847 {
848 SCH_COMMIT commit( m_toolMgr );
849 SCH_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
850
851 EDA_ITEMS newItems;
852 bool keepAnnotations = cfg->m_DesignBlockChooserPanel.keep_annotations;
853 bool placeAsGroup = cfg->m_DesignBlockChooserPanel.place_as_group;
854 bool screenWasModified = screen->IsContentModified();
855
856 selectionTool->ClearSelection();
857
858 // Remember existing items so we only select and commit imported items.
859 std::unordered_set<EDA_ITEM*> existingItems;
860
861 for( EDA_ITEM* item : screen->Items() )
862 existingItems.insert( item );
863
864 if( !m_frame->LoadSheetFromFile( sheetPath.Last(), &sheetPath, sheetFileName, true,
865 placingDesignBlock ) )
866 {
867 return false;
868 }
869
870 m_frame->SetSheetNumberAndCount();
871
872 m_frame->SyncView();
873 m_frame->OnModify();
874 m_frame->HardRedraw(); // Full reinit of the current screen and the display.
875
876 SCH_GROUP* group = nullptr;
877
878 if( placeAsGroup )
879 {
880 group = new SCH_GROUP( screen );
881
882 wxString baseName;
883
884 if( designBlock )
885 {
886 baseName = designBlock->GetLibId().GetLibItemName().wx_str();
887 group->SetDesignBlockLibId( designBlock->GetLibId() );
888 }
889 else
890 {
891 baseName = wxFileName( sheetFileName ).GetName();
892 }
893
894 group->SetName( UniqueGroupName( screen, baseName ) );
895 }
896
897 bool autoAnnotate = !keepAnnotations && cfg->m_AnnotatePanel.automatic;
898
899 // Select all new items
900 for( EDA_ITEM* item : screen->Items() )
901 {
902 if( !existingItems.contains( item ) )
903 {
904 // When auto-annotating, preserve original refs so that
905 // AnnotateSymbols can build correct locked groups for
906 // multi-unit symbols before assigning new references.
907 // Clearing first would leave locked groups empty, causing
908 // units from different same-value arrays to get mixed.
909 if( item->Type() == SCH_SYMBOL_T && !keepAnnotations && !autoAnnotate )
910 static_cast<SCH_SYMBOL*>( item )->ClearAnnotation( &sheetPath, false );
911
912 if( item->Type() == SCH_LINE_T )
913 item->SetFlags( STARTPOINT | ENDPOINT );
914
915 if( !item->GetParentGroup() )
916 {
917 if( placeAsGroup )
918 group->AddItem( item );
919
920 newItems.emplace_back( item );
921 }
922
923 commit.Added( item, screen );
924 }
925 }
926
927 if( placeAsGroup )
928 {
929 commit.Add( group, screen );
930 selectionTool->AddItemToSel( group );
931 }
932 else
933 {
934 selectionTool->AddItemsToSel( &newItems, true );
935 }
936
937 cursorPos = grid.Align( controls->GetMousePosition(),
938 grid.GetSelectionGrid( selectionTool->GetSelection() ) );
939 controls->ForceCursorPosition( true, cursorPos );
940
941 std::vector<SCH_ITEM*> blockItems = FlattenGroups( newItems );
942 std::optional<VECTOR2I> anchorPos = designBlockAnchor( blockItems );
943 VECTOR2I delta = anchorPos ? cursorPos - *anchorPos : VECTOR2I( 0, 0 );
944
945 // Will all be SCH_ITEMs as these were pulled from the screen->Items()
946 for( EDA_ITEM* item : newItems )
947 static_cast<SCH_ITEM*>( item )->Move( delta );
948
949 if( !keepAnnotations || placingDesignBlock )
950 {
951 if( autoAnnotate )
952 {
954 m_frame->AnnotateSymbols( &commit, ANNOTATE_SELECTION,
956 (ANNOTATE_ALGO_T) schSettings.m_AnnotateMethod,
957 true /* recursive */,
958 schSettings.m_AnnotateStartNum,
959 true /* aResetAnnotation */,
960 false, false, reporter );
961 }
962
963 // Annotation will clear selection, so we need to restore it
964 for( EDA_ITEM* item : newItems )
965 {
966 if( item->Type() == SCH_LINE_T )
967 item->SetFlags( STARTPOINT | ENDPOINT );
968 }
969
970 if( placeAsGroup )
971 selectionTool->AddItemToSel( group );
972 else
973 selectionTool->AddItemsToSel( &newItems, true );
974 }
975
976 // IS_NEW is what makes the move tool use the reference point set below, and
977 // IS_MOVING stops the selection tool replacing it on the way there.
978 for( SCH_ITEM* item : blockItems )
979 item->SetFlags( IS_NEW | IS_MOVING );
980
981 if( group )
982 group->SetFlags( IS_NEW | IS_MOVING );
983
984 selectionTool->GetSelection().SetReferencePoint( cursorPos );
985
986 // Start moving selection, cancel undoes the insertion
987 bool placed = m_toolMgr->RunSynchronousAction( SCH_ACTIONS::move, &commit );
988
989 // Update our cursor position to the new location in case we're placing repeated copies
990 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_CONNECTABLE );
991
992 if( placed )
993 {
994 commit.Push( placingDesignBlock ? _( "Add Design Block" )
995 : _( "Import Schematic Sheet Content" ) );
996 }
997 else
998 {
999 commit.Revert();
1000 screen->SetContentModified( screenWasModified );
1001 }
1002
1003 selectionTool->RebuildSelection();
1004 m_frame->UpdateHierarchyNavigator();
1005
1006 return placed;
1007 };
1008
1009 // Whether we are placing the sheet as a sheet, or as its contents, we need to get a filename
1010 // if we weren't provided one
1011 if( sheetFileName.IsEmpty() )
1012 {
1013 wxString path;
1014 wxString file;
1015
1016 if (!placingDesignBlock)
1017 {
1018 if( sheetFileName.IsEmpty() )
1019 {
1020 path = wxPathOnly( m_frame->Prj().GetProjectFullName() );
1021 file = wxEmptyString;
1022 }
1023 else
1024 {
1025 path = wxPathOnly( sheetFileName );
1026 file = wxFileName( sheetFileName ).GetFullName();
1027 }
1028
1029 // Open file chooser dialog even if we have been provided a file so the user
1030 // can select the options they want
1031 wxFileDialog dlg( m_frame, _( "Choose Schematic" ), path, file,
1032 FILEEXT::KiCadSchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
1033
1034 FILEDLG_IMPORT_SHEET_CONTENTS dlgHook( cfg );
1035 dlg.SetCustomizeHook( dlgHook );
1036
1038
1039 if( dlg.ShowModal() == wxID_CANCEL )
1040 return 0;
1041
1042 sheetFileName = dlg.GetPath();
1043
1044 m_frame->GetDesignBlockPane()->UpdateCheckboxes();
1045 }
1046
1047 if( sheetFileName.IsEmpty() )
1048 return 0;
1049 }
1050
1051 // If we're placing sheet contents, we don't even want to run our tool loop, just add the items
1052 // to the canvas and run the move tool
1054 {
1055 while( placeSheetContents() && cfg->m_DesignBlockChooserPanel.repeated_placement )
1056 {}
1057
1058 m_toolMgr->RunAction( ACTIONS::selectionClear );
1059 m_view->ClearPreview();
1060 return 0;
1061 }
1062
1063 // We're placing a sheet as a sheet, we need to run a small tool loop to get the starting
1064 // coordinate of the sheet drawing
1065 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
1066
1067 Activate();
1068
1069 // Must be done after Activate() so that it gets set into the correct context
1070 getViewControls()->ShowCursor( true );
1071
1072 // Set initial cursor
1073 setCursor();
1074
1075 if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
1076 m_toolMgr->PrimeTool( { 0, 0 } );
1077
1078 // Main loop: keep receiving events
1079 while( TOOL_EVENT* evt = Wait() )
1080 {
1081 setCursor();
1082 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1083 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1084
1085 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_CONNECTABLE );
1086 controls->ForceCursorPosition( true, cursorPos );
1087
1088 // The tool hotkey is interpreted as a click when drawing
1089 bool isSyntheticClick = designBlock && evt->IsActivate() && evt->HasPosition() && evt->Matches( aEvent );
1090
1091 if( evt->IsCancelInteractive() || ( designBlock && evt->IsAction( &ACTIONS::undo ) ) )
1092 {
1093 m_frame->GetInfoBar()->Dismiss();
1094 break;
1095 }
1096 else if( evt->IsActivate() && !isSyntheticClick )
1097 {
1098 m_frame->GetInfoBar()->Dismiss();
1099 break;
1100 }
1101 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
1102 || isSyntheticClick
1103 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
1104 {
1105 if( placingDesignBlock )
1106 {
1107 // drawSheet must delete designBlock
1108 m_toolMgr->PostAction( SCH_ACTIONS::drawSheetFromDesignBlock, designBlock.release() );
1109 }
1110 else
1111 {
1112 // drawSheet must delete sheetFileName
1113 m_toolMgr->PostAction( SCH_ACTIONS::drawSheetFromFile, new wxString( sheetFileName ) );
1114 }
1115
1116 break;
1117 }
1118 else if( evt->IsClick( BUT_RIGHT ) )
1119 {
1120 // Warp after context menu only if dragging...
1121 if( !designBlock )
1122 m_toolMgr->VetoContextMenuMouseWarp();
1123
1124 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
1125 }
1126 else if( evt->IsAction( &ACTIONS::duplicate )
1127 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem ) )
1128 {
1129 wxBell();
1130 }
1131 else
1132 {
1133 evt->SetPassEvent();
1134 }
1135 }
1136
1137 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1138 return 0;
1139}
1140
1141
1143{
1144 SCH_BITMAP* image = aEvent.Parameter<SCH_BITMAP*>();
1145 bool immediateMode = image != nullptr;
1146 bool ignorePrimePosition = false;
1147 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
1148
1149 if( m_inDrawingTool )
1150 return 0;
1151
1153
1156 VECTOR2I cursorPos;
1157
1158 m_toolMgr->RunAction( ACTIONS::selectionClear );
1159
1160 // Add all the drawable symbols to preview
1161 if( image )
1162 {
1163 image->SetPosition( getViewControls()->GetCursorPosition() );
1164 m_view->ClearPreview();
1165 m_view->AddToPreview( image, false ); // Add, but not give ownership
1166 }
1167
1168 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
1169 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
1170
1171 auto setCursor =
1172 [&]()
1173 {
1174 if( image )
1175 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1176 else
1177 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1178 };
1179
1180 auto cleanup =
1181 [&]()
1182 {
1183 m_toolMgr->RunAction( ACTIONS::selectionClear );
1184 m_view->ClearPreview();
1185 m_view->RecacheAllItems();
1186 delete image;
1187 image = nullptr;
1188 };
1189
1190 Activate();
1191
1192 // Must be done after Activate() so that it gets set into the correct context
1193 getViewControls()->ShowCursor( true );
1194
1195 // Set initial cursor
1196 setCursor();
1197
1198 // Prime the pump
1199 if( image )
1200 {
1201 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1202 }
1203 else if( aEvent.HasPosition() )
1204 {
1205 m_toolMgr->PrimeTool( aEvent.Position() );
1206 }
1207 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
1208 {
1209 m_toolMgr->PrimeTool( { 0, 0 } );
1210 ignorePrimePosition = true;
1211 }
1212
1213 // Main loop: keep receiving events
1214 while( TOOL_EVENT* evt = Wait() )
1215 {
1216 setCursor();
1217 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1218 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1219
1220 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_GRAPHICS );
1221 controls->ForceCursorPosition( true, cursorPos );
1222
1223 // The tool hotkey is interpreted as a click when drawing
1224 bool isSyntheticClick = image && evt->IsActivate() && evt->HasPosition() && evt->Matches( aEvent );
1225
1226 if( evt->IsCancelInteractive() || ( image && evt->IsAction( &ACTIONS::undo ) ) )
1227 {
1228 m_frame->GetInfoBar()->Dismiss();
1229
1230 if( image )
1231 cleanup();
1232 else
1233 break;
1234
1235 if( immediateMode )
1236 break;
1237 }
1238 else if( evt->IsActivate() && !isSyntheticClick )
1239 {
1240 if( image && evt->IsMoveTool() )
1241 {
1242 // we're already moving our own item; ignore the move tool
1243 evt->SetPassEvent( false );
1244 continue;
1245 }
1246
1247 if( image )
1248 {
1249 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel image creation." ) );
1250 evt->SetPassEvent( false );
1251 continue;
1252 }
1253
1254 if( evt->IsMoveTool() )
1255 {
1256 // Make sure we come back after the move tool is done
1257 m_frame->PushTool( originalEvent );
1258 }
1259
1260 break;
1261 }
1262 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
1263 || isSyntheticClick
1264 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
1265 {
1266 if( !image )
1267 {
1268 m_toolMgr->RunAction( ACTIONS::selectionClear );
1269
1270 wxFileDialog dlg( m_frame, _( "Choose Image" ), m_mruPath, wxEmptyString, FILEEXT::ImageFileWildcard(),
1271 wxFD_OPEN );
1272
1274
1275 bool cancelled = false;
1276
1278 [&]()
1279 {
1280 cancelled = dlg.ShowModal() != wxID_OK;
1281 } );
1282
1283 if( cancelled )
1284 continue;
1285
1286 // If we started with a hotkey which has a position then warp back to that.
1287 // Otherwise update to the current mouse position pinned inside the autoscroll
1288 // boundaries.
1289 if( evt->IsPrime() && !ignorePrimePosition )
1290 {
1291 cursorPos = grid.Align( evt->Position() );
1292 getViewControls()->WarpMouseCursor( cursorPos, true );
1293 }
1294 else
1295 {
1297 cursorPos = getViewControls()->GetMousePosition();
1298 }
1299
1300 wxString fullFilename = dlg.GetPath();
1301 m_mruPath = wxPathOnly( fullFilename );
1302
1303 if( wxFileExists( fullFilename ) )
1304 image = new SCH_BITMAP( cursorPos );
1305
1306 if( !image || !image->GetReferenceImage().ReadImageFile( fullFilename ) )
1307 {
1308 wxMessageBox( wxString::Format( _( "Could not load image from '%s'." ), fullFilename ) );
1309 delete image;
1310 image = nullptr;
1311 continue;
1312 }
1313
1314 image->SetFlags( IS_NEW | IS_MOVING );
1315
1316 m_frame->SaveCopyForRepeatItem( image );
1317
1318 m_view->ClearPreview();
1319 m_view->AddToPreview( image, false ); // Add, but not give ownership
1320 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
1321
1322 m_selectionTool->AddItemToSel( image );
1323
1324 getViewControls()->SetCursorPosition( cursorPos, false );
1325 setCursor();
1326 }
1327 else
1328 {
1329 SCH_COMMIT commit( m_toolMgr );
1330 commit.Add( image, m_frame->GetScreen() );
1331 commit.Push( _( "Place Image" ) );
1332
1333 image = nullptr;
1335
1336 m_view->ClearPreview();
1337
1338 if( immediateMode )
1339 break;
1340 }
1341 }
1342 else if( evt->IsClick( BUT_RIGHT ) )
1343 {
1344 // Warp after context menu only if dragging...
1345 if( !image )
1346 m_toolMgr->VetoContextMenuMouseWarp();
1347
1348 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
1349 }
1350 else if( evt->IsAction( &ACTIONS::duplicate )
1351 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem )
1352 || evt->IsAction( &ACTIONS::paste ) )
1353 {
1354 if( image )
1355 {
1356 // This doesn't really make sense; we'll just end up dragging a stack of
1357 // objects so we ignore the duplicate and just carry on.
1358 wxBell();
1359 continue;
1360 }
1361
1362 // Exit. The duplicate/repeat/paste will run in its own loop.
1363 evt->SetPassEvent();
1364 break;
1365 }
1366 else if( image && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
1367 {
1368 image->SetPosition( cursorPos );
1369 m_view->ClearPreview();
1370 m_view->AddToPreview( image, false ); // Add, but not give ownership
1371 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
1372 m_frame->SetMsgPanel( image );
1373 }
1374 else if( image && evt->IsAction( &ACTIONS::doDelete ) )
1375 {
1376 cleanup();
1377 }
1378 else if( image && evt->IsAction( &ACTIONS::redo ) )
1379 {
1380 wxBell();
1381 }
1382 else
1383 {
1384 evt->SetPassEvent();
1385 }
1386
1387 // Enable autopanning and cursor capture only when there is an image to be placed
1388 getViewControls()->SetAutoPan( image != nullptr );
1389 getViewControls()->CaptureCursor( image != nullptr );
1390 }
1391
1392 getViewControls()->SetAutoPan( false );
1393 getViewControls()->CaptureCursor( false );
1394 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1395
1396 return 0;
1397}
1398
1399
1401{
1402 VECTOR2I cursorPos;
1403 KICAD_T type = aEvent.Parameter<KICAD_T>();
1406 SCH_ITEM* previewItem;
1407 bool loggedInfoBarError = false;
1408 wxString description;
1409 SCH_SCREEN* screen = m_frame->GetScreen();
1410 bool allowRepeat = false; // Set to true to allow new item repetition
1411
1412 if( m_inDrawingTool )
1413 return 0;
1414
1416
1417 if( type == SCH_JUNCTION_T && aEvent.HasPosition() )
1418 {
1419 SCH_SELECTION& selection = m_selectionTool->GetSelection();
1420 SCH_LINE* wire = dynamic_cast<SCH_LINE*>( selection.Front() );
1421
1422 if( wire )
1423 {
1424 SEG seg( wire->GetStartPoint(), wire->GetEndPoint() );
1425 VECTOR2I nearest = seg.NearestPoint( getViewControls()->GetCursorPosition() );
1426 getViewControls()->SetCrossHairCursorPosition( nearest, false );
1427 getViewControls()->WarpMouseCursor( getViewControls()->GetCursorPosition(), true );
1428 }
1429 }
1430
1431 switch( type )
1432 {
1433 case SCH_NO_CONNECT_T:
1434 previewItem = new SCH_NO_CONNECT( cursorPos );
1435 previewItem->SetParent( screen );
1436 description = _( "Add No Connect Flag" );
1437 allowRepeat = true;
1438 break;
1439
1440 case SCH_JUNCTION_T:
1441 previewItem = new SCH_JUNCTION( cursorPos );
1442 previewItem->SetParent( screen );
1443 description = _( "Add Junction" );
1444 break;
1445
1447 previewItem = new SCH_BUS_WIRE_ENTRY( cursorPos );
1448 previewItem->SetParent( screen );
1449 description = _( "Add Wire to Bus Entry" );
1450 allowRepeat = true;
1451 break;
1452
1453 default:
1454 wxASSERT_MSG( false, "Unknown item type in SCH_DRAWING_TOOLS::SingleClickPlace" );
1455 return 0;
1456 }
1457
1458 m_toolMgr->RunAction( ACTIONS::selectionClear );
1459
1460 cursorPos = aEvent.HasPosition() ? aEvent.Position() : controls->GetMousePosition();
1461
1462 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
1463 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
1464
1465 auto setCursor =
1466 [&]()
1467 {
1468 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
1469 };
1470
1471 Activate();
1472
1473 // Must be done after Activate() so that it gets set into the correct context
1474 getViewControls()->ShowCursor( true );
1475
1476 // Set initial cursor
1477 setCursor();
1478
1479 m_view->ClearPreview();
1480 m_view->AddToPreview( previewItem->Clone() );
1481
1482 // Prime the pump
1483 if( aEvent.HasPosition() && type != SCH_SHEET_PIN_T )
1484 m_toolMgr->PrimeTool( aEvent.Position() );
1485 else
1486 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1487
1488 // Main loop: keep receiving events
1489 while( TOOL_EVENT* evt = Wait() )
1490 {
1491 setCursor();
1492 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1493 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1494
1495 cursorPos = evt->IsPrime() ? evt->Position() : controls->GetMousePosition();
1496 cursorPos = grid.ResolveSnap( cursorPos, grid.GetItemGrid( previewItem ), nullptr ).position;
1497 controls->ForceCursorPosition( true, cursorPos );
1498
1499 if( evt->IsCancelInteractive() )
1500 {
1501 break;
1502 }
1503 else if( evt->IsActivate() )
1504 {
1505 if( evt->IsMoveTool() )
1506 {
1507 // Make sure we come back after the move tool runs
1508 frame()->PushTool( originalEvent );
1509 }
1510
1511 break;
1512 }
1513 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
1514 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
1515 {
1516 if( SCH_ITEM* existingItem = screen->GetItem( cursorPos, 0, type ) )
1517 {
1518 // No connects can be "toggled"/removed by clicking on them again
1519 // It helps with not having to fight pin selection ambiguity
1520 if( type == SCH_NO_CONNECT_T )
1521 {
1522 SCH_COMMIT commit( m_toolMgr );
1523 commit.Removed( existingItem, screen );
1524 m_frame->RemoveFromScreen( existingItem, screen );
1525 commit.Push( _( "Remove No Connect Flag" ) );
1526 }
1527 }
1528 else
1529 {
1530 if( type == SCH_JUNCTION_T )
1531 {
1532 if( !screen->IsExplicitJunctionAllowed( cursorPos ) )
1533 {
1534 m_frame->ShowInfoBarError( _( "Junction location contains no joinable wires and/or pins." ) );
1535 loggedInfoBarError = true;
1536 continue;
1537 }
1538 else if( loggedInfoBarError )
1539 {
1540 m_frame->GetInfoBar()->Dismiss();
1541 }
1542 }
1543
1544 if( type == SCH_JUNCTION_T )
1545 {
1546 SCH_COMMIT commit( m_toolMgr );
1548 lwbTool->AddJunction( &commit, screen, cursorPos );
1549
1550 m_frame->Schematic().CleanUp( &commit );
1551
1552 commit.Push( description );
1553 }
1554 else
1555 {
1556 SCH_ITEM* newItem = static_cast<SCH_ITEM*>( previewItem->Clone() );
1557 const_cast<KIID&>( newItem->m_Uuid ) = KIID();
1558 newItem->SetPosition( cursorPos );
1559 newItem->SetFlags( IS_NEW );
1560 m_frame->AddToScreen( newItem, screen );
1561
1562 if( allowRepeat )
1563 m_frame->SaveCopyForRepeatItem( newItem );
1564
1565 SCH_COMMIT commit( m_toolMgr );
1566 commit.Added( newItem, screen );
1567
1568 m_frame->Schematic().CleanUp( &commit );
1569
1570 commit.Push( description );
1571 }
1572 }
1573
1574 if( evt->IsDblClick( BUT_LEFT )
1575 || evt->IsAction( &ACTIONS::cursorDblClick )
1576 || type == SCH_SHEET_PIN_T ) // Finish tool.
1577 {
1578 break;
1579 }
1580 }
1581 else if( evt->IsClick( BUT_RIGHT ) )
1582 {
1583 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
1584 }
1585 else if( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() )
1586 {
1587 previewItem->SetPosition( cursorPos );
1588 m_view->ClearPreview();
1589 m_view->AddToPreview( previewItem->Clone() );
1590 m_frame->SetMsgPanel( previewItem );
1591 }
1592 else if( evt->Category() == TC_COMMAND )
1593 {
1594 if( ( type == SCH_BUS_WIRE_ENTRY_T ) && ( evt->IsAction( &SCH_ACTIONS::rotateCW )
1595 || evt->IsAction( &SCH_ACTIONS::rotateCCW )
1596 || evt->IsAction( &SCH_ACTIONS::mirrorV )
1597 || evt->IsAction( &SCH_ACTIONS::mirrorH ) ) )
1598 {
1599 SCH_BUS_ENTRY_BASE* busItem = static_cast<SCH_BUS_ENTRY_BASE*>( previewItem );
1600
1601 if( evt->IsAction( &SCH_ACTIONS::rotateCW ) )
1602 {
1603 busItem->Rotate( busItem->GetPosition(), false );
1604 }
1605 else if( evt->IsAction( &SCH_ACTIONS::rotateCCW ) )
1606 {
1607 busItem->Rotate( busItem->GetPosition(), true );
1608 }
1609 else if( evt->IsAction( &SCH_ACTIONS::mirrorV ) )
1610 {
1611 busItem->MirrorVertically( busItem->GetPosition().y );
1612 }
1613 else if( evt->IsAction( &SCH_ACTIONS::mirrorH ) )
1614 {
1615 busItem->MirrorHorizontally( busItem->GetPosition().x );
1616 }
1617
1618 m_view->ClearPreview();
1619 m_view->AddToPreview( previewItem->Clone() );
1620 }
1621 else if( evt->IsAction( &SCH_ACTIONS::properties ) )
1622 {
1623 switch( type )
1624 {
1626 {
1627 std::deque<SCH_ITEM*> strokeItems;
1628 strokeItems.push_back( previewItem );
1629
1630 DIALOG_WIRE_BUS_PROPERTIES dlg( m_frame, strokeItems );
1631
1633 [&]()
1634 {
1635 dlg.ShowModal();
1636 } );
1637
1638 break;
1639 }
1640
1641 case SCH_JUNCTION_T:
1642 {
1643 std::deque<SCH_JUNCTION*> junctions;
1644 junctions.push_back( static_cast<SCH_JUNCTION*>( previewItem ) );
1645
1646 DIALOG_JUNCTION_PROPS dlg( m_frame, junctions );
1647
1649 [&]()
1650 {
1651 dlg.ShowModal();
1652 } );
1653
1654 break;
1655 }
1656
1657 default:
1658 // Do nothing
1659 break;
1660 }
1661
1662 m_view->ClearPreview();
1663 m_view->AddToPreview( previewItem->Clone() );
1664 }
1665 else
1666 {
1667 evt->SetPassEvent();
1668 }
1669 }
1670 else
1671 {
1672 evt->SetPassEvent();
1673 }
1674 }
1675
1676 delete previewItem;
1677 m_view->ClearPreview();
1678
1679 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1680 controls->ForceCursorPosition( false );
1681
1682 return 0;
1683}
1684
1685
1687{
1688 for( SCH_ITEM* item : m_frame->GetScreen()->Items().Overlapping( SCH_LINE_T, aPosition ) )
1689 {
1690 SCH_LINE* line = static_cast<SCH_LINE*>( item );
1691
1692 if( line->GetEditFlags() & STRUCT_DELETED )
1693 continue;
1694
1695 if( line->IsWire() )
1696 return line;
1697 }
1698
1699 return nullptr;
1700}
1701
1702
1704{
1705 wxASSERT( aWire->IsWire() );
1706
1707 SCH_SHEET_PATH sheetPath = m_frame->GetCurrentSheet();
1708
1709 const auto labelDriverName = []( const auto& aConnection ) -> wxString
1710 {
1711 SCH_ITEM* driver = aConnection.Driver();
1712
1713 if( driver && driver->IsType( { SCH_LABEL_T, SCH_GLOBAL_LABEL_T } ) )
1714 return aConnection.LocalName();
1715
1716 return wxEmptyString;
1717 };
1718
1719 if( ADVANCED_CFG::GetCfg().m_ConnectivityEngine )
1720 {
1721 const auto connection = m_frame->Schematic().Connectivity().Connection( aWire->m_Uuid, sheetPath.PathRef() );
1722 return connection ? labelDriverName( *connection ) : wxString();
1723 }
1724
1725 const SCH_CONNECTION* wireConnection = aWire->Connection( &sheetPath );
1726 return wireConnection ? labelDriverName( *wireConnection ) : wxString();
1727}
1728
1729
1730bool SCH_DRAWING_TOOLS::createNewLabel( const VECTOR2I& aPosition, int aType,
1731 std::list<std::unique_ptr<SCH_LABEL_BASE>>& aLabelList )
1732{
1733 SCHEMATIC* schematic = getModel<SCHEMATIC>();
1734 SCHEMATIC_SETTINGS& settings = schematic->Settings();
1735 SCH_LABEL_BASE* labelItem = nullptr;
1736 SCH_GLOBALLABEL* globalLabel = nullptr;
1737 wxString netName;
1738
1739 switch( aType )
1740 {
1741 case LAYER_LOCLABEL:
1742 labelItem = new SCH_LABEL( aPosition );
1743
1744 if( SCH_LINE* wire = findWire( aPosition ) )
1745 netName = findWireLabelDriverName( wire );
1746
1747 break;
1748
1750 labelItem = new SCH_DIRECTIVE_LABEL( aPosition );
1751 labelItem->SetShape( m_lastNetClassFlagShape );
1752 labelItem->GetFields().emplace_back( labelItem, FIELD_T::USER, wxT( "Netclass" ) );
1753 labelItem->GetFields().emplace_back( labelItem, FIELD_T::USER, wxT( "Component Class" ) );
1754 labelItem->GetFields().back().SetItalic( true );
1755 labelItem->GetFields().back().SetVisible( true );
1756 break;
1757
1758 case LAYER_HIERLABEL:
1759 labelItem = new SCH_HIERLABEL( aPosition );
1760 labelItem->SetShape( m_lastGlobalLabelShape );
1762 break;
1763
1764 case LAYER_GLOBLABEL:
1765 globalLabel = new SCH_GLOBALLABEL( aPosition );
1766 globalLabel->SetShape( m_lastGlobalLabelShape );
1769 labelItem = globalLabel;
1770
1771 if( SCH_LINE* wire = findWire( aPosition ) )
1772 netName = findWireLabelDriverName( wire );
1773
1774 break;
1775
1776 default:
1777 wxFAIL_MSG( "SCH_DRAWING_TOOLS::createNewLabel() unknown label type" );
1778 return false;
1779 }
1780
1781 // The normal parent is the current screen for these labels, set by SCH_SCREEN::Append()
1782 // but it is also used during placement for SCH_HIERLABEL before beeing appended
1783 labelItem->SetParent( m_frame->GetScreen() );
1784
1785 labelItem->SetTextSize( VECTOR2I( settings.m_DefaultTextSize, settings.m_DefaultTextSize ) );
1786
1787 if( aType != LAYER_NETCLASS_REFS )
1788 {
1789 // Must be after SetTextSize()
1790 labelItem->SetBold( m_lastTextBold );
1791 labelItem->SetItalic( m_lastTextItalic );
1792 }
1793
1794 labelItem->SetSpinStyle( m_lastTextOrientation );
1795 labelItem->SetFlags( IS_NEW | IS_MOVING );
1796
1797 if( !netName.IsEmpty() )
1798 {
1799 // Auto-create from attached wire
1800 labelItem->SetText( netName );
1801 }
1802 else
1803 {
1804 DIALOG_LABEL_PROPERTIES dlg( m_frame, labelItem, true );
1805
1806 dlg.SetLabelList( &aLabelList );
1807
1808 // QuasiModal required for syntax help and Scintilla auto-complete
1809 if( dlg.ShowQuasiModal() != wxID_OK )
1810 {
1812 delete labelItem;
1813 return false;
1814 }
1815 }
1816
1817 if( aType != LAYER_NETCLASS_REFS )
1818 {
1819 m_lastTextBold = labelItem->IsBold();
1820 m_lastTextItalic = labelItem->IsItalic();
1821 }
1822
1823 m_lastTextOrientation = labelItem->GetSpinStyle();
1824
1825 if( aType == LAYER_GLOBLABEL || aType == LAYER_HIERLABEL )
1826 {
1827 m_lastGlobalLabelShape = labelItem->GetShape();
1829 }
1830 else if( aType == LAYER_NETCLASS_REFS )
1831 {
1832 m_lastNetClassFlagShape = labelItem->GetShape();
1833 }
1834
1835 if( aLabelList.empty() )
1836 aLabelList.push_back( std::unique_ptr<SCH_LABEL_BASE>( labelItem ) );
1837 else // DIALOG_LABEL_PROPERTIES already filled in aLabelList; labelItem is extraneous to needs
1838 delete labelItem;
1839
1840 return true;
1841}
1842
1843
1845{
1846 SCHEMATIC* schematic = getModel<SCHEMATIC>();
1847 SCHEMATIC_SETTINGS& settings = schematic->Settings();
1848 SCH_TEXT* textItem = nullptr;
1849
1850 textItem = new SCH_TEXT( aPosition );
1851 textItem->SetParent( schematic );
1852 textItem->SetTextSize( VECTOR2I( settings.m_DefaultTextSize, settings.m_DefaultTextSize ) );
1853 // Must be after SetTextSize()
1854 textItem->SetBold( m_lastTextBold );
1855 textItem->SetItalic( m_lastTextItalic );
1858 textItem->SetTextAngle( m_lastTextAngle );
1859 textItem->SetFlags( IS_NEW | IS_MOVING );
1860
1861 DIALOG_TEXT_PROPERTIES dlg( m_frame, textItem );
1862
1863 // QuasiModal required for syntax help and Scintilla auto-complete
1864 if( dlg.ShowQuasiModal() != wxID_OK )
1865 {
1866 delete textItem;
1867 return nullptr;
1868 }
1869
1870 m_lastTextBold = textItem->IsBold();
1871 m_lastTextItalic = textItem->IsItalic();
1872 m_lastTextHJustify = textItem->GetHorizJustify();
1873 m_lastTextVJustify = textItem->GetVertJustify();
1874 m_lastTextAngle = textItem->GetTextAngle();
1875 return textItem;
1876}
1877
1878
1880{
1881 SCHEMATIC_SETTINGS& settings = aSheet->Schematic()->Settings();
1882 SCH_SHEET_PIN* pin = new SCH_SHEET_PIN( aSheet );
1883
1884 pin->SetFlags( IS_NEW | IS_MOVING );
1885 pin->SetText( std::to_string( aSheet->GetPins().size() + 1 ) );
1886 pin->SetTextSize( VECTOR2I( settings.m_DefaultTextSize, settings.m_DefaultTextSize ) );
1887 pin->SetPosition( aPosition );
1888 pin->ClearSelected();
1889
1890 m_lastSheetPinType = pin->GetShape();
1891
1892 return pin;
1893}
1894
1895
1897 const VECTOR2I& aPosition,
1898 SCH_HIERLABEL* aLabel )
1899{
1900 auto pin = createNewSheetPin( aSheet, aPosition );
1901 pin->SetText( aLabel->GetText() );
1902 pin->SetShape( aLabel->GetShape() );
1903 return pin;
1904}
1905
1906
1908{
1909 SCH_ITEM* item = nullptr;
1912 bool ignorePrimePosition = false;
1913 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
1914 SCH_SHEET* sheet = nullptr;
1915 wxString description;
1916
1917 std::list<std::unique_ptr<SCH_LABEL_BASE>> itemsToPlace;
1918
1919 if( m_inDrawingTool )
1920 return 0;
1921
1923
1924 bool isText = aEvent.IsAction( &SCH_ACTIONS::placeSchematicText );
1925 bool isGlobalLabel = aEvent.IsAction( &SCH_ACTIONS::placeGlobalLabel );
1926 bool isHierLabel = aEvent.IsAction( &SCH_ACTIONS::placeHierLabel );
1927 bool isClassLabel = aEvent.IsAction( &SCH_ACTIONS::placeClassLabel );
1928 bool isNetLabel = aEvent.IsAction( &SCH_ACTIONS::placeLabel );
1929 bool isSheetPin = aEvent.IsAction( &SCH_ACTIONS::placeSheetPin );
1930
1931 GRID_HELPER_GRIDS snapGrid = isText ? GRID_TEXT : GRID_CONNECTABLE;
1932
1933 // If we have a selected sheet use it, otherwise try to get one under the cursor
1934 if( isSheetPin )
1935 sheet = dynamic_cast<SCH_SHEET*>( m_selectionTool->GetSelection().Front() );
1936
1937 m_toolMgr->RunAction( ACTIONS::selectionClear );
1938
1939 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
1940 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
1941
1942 auto setCursor =
1943 [&]()
1944 {
1945 if( item )
1946 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
1947 else if( isText )
1948 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
1949 else if( isGlobalLabel )
1950 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::LABEL_GLOBAL );
1951 else if( isNetLabel || isClassLabel )
1952 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::LABEL_NET );
1953 else if( isHierLabel )
1954 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::LABEL_HIER );
1955 else
1956 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1957 };
1958
1959 auto updatePreview =
1960 [&]()
1961 {
1962 m_view->ClearPreview();
1963 m_view->AddToPreview( item, false );
1964 item->RunOnChildren( [&]( SCH_ITEM* aChild )
1965 {
1966 m_view->AddToPreview( aChild, false );
1967 },
1969 m_frame->SetMsgPanel( item );
1970 };
1971
1972 auto cleanup =
1973 [&]()
1974 {
1975 m_toolMgr->RunAction( ACTIONS::selectionClear );
1976 m_view->ClearPreview();
1977 delete item;
1978 item = nullptr;
1979
1980 while( !itemsToPlace.empty() )
1981 itemsToPlace.erase( itemsToPlace.begin() );
1982 };
1983
1984 auto prepItemForPlacement =
1985 [&]( SCH_ITEM* aItem, const VECTOR2I& cursorPos )
1986 {
1987 item->SetPosition( cursorPos );
1988
1989 item->SetFlags( IS_NEW | IS_MOVING );
1990
1991 // Not placed yet, so pass a nullptr screen reference
1992 item->AutoplaceFields( nullptr, AUTOPLACE_AUTO );
1993
1994 updatePreview();
1995 m_selectionTool->ClearSelection( true );
1996 m_selectionTool->AddItemToSel( item );
1997 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1998
1999 // update the cursor so it looks correct before another event
2000 setCursor();
2001 };
2002
2003 Activate();
2004
2005 // Must be done after Activate() so that it gets set into the correct context
2006 controls->ShowCursor( true );
2007
2008 // Set initial cursor
2009 setCursor();
2010
2011 if( aEvent.HasPosition() )
2012 {
2013 m_toolMgr->PrimeTool( aEvent.Position() );
2014 }
2015 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate()
2016 && ( isText || isGlobalLabel || isHierLabel || isClassLabel || isNetLabel ) )
2017 {
2018 m_toolMgr->PrimeTool( { 0, 0 } );
2019 ignorePrimePosition = true;
2020 }
2021
2022 SCH_COMMIT commit( m_toolMgr );
2023
2024 // Main loop: keep receiving events
2025 while( TOOL_EVENT* evt = Wait() )
2026 {
2027 setCursor();
2028 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2029 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2030
2031 VECTOR2I cursorPos = controls->GetMousePosition();
2032 cursorPos = grid.ResolveSnap( cursorPos, snapGrid, item ).position;
2033 controls->ForceCursorPosition( true, cursorPos );
2034
2035 // The tool hotkey is interpreted as a click when drawing
2036 bool isSyntheticClick = item && evt->IsActivate() && evt->HasPosition() && evt->Matches( aEvent );
2037
2038 if( evt->IsCancelInteractive() || evt->IsAction( &ACTIONS::undo ) )
2039 {
2040 m_frame->GetInfoBar()->Dismiss();
2041
2042 if( item )
2043 cleanup();
2044 else
2045 break;
2046 }
2047 else if( evt->IsActivate() && !isSyntheticClick )
2048 {
2049 if( item && evt->IsMoveTool() )
2050 {
2051 // we're already moving our own item; ignore the move tool
2052 evt->SetPassEvent( false );
2053 continue;
2054 }
2055
2056 if( item )
2057 {
2058 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel item creation." ) );
2059 evt->SetPassEvent( false );
2060 continue;
2061 }
2062
2063 if( evt->IsPointEditor() )
2064 {
2065 // don't exit (the point editor runs in the background)
2066 continue;
2067 }
2068
2069 if( evt->IsMoveTool() )
2070 {
2071 // Make sure we come back after the move tool runs
2072 frame()->PushTool( originalEvent );
2073 }
2074
2075 break;
2076 }
2077 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
2078 || isSyntheticClick
2079 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
2080 {
2081 PLACE_NEXT:
2082 // First click creates...
2083 if( !item )
2084 {
2085 m_toolMgr->RunAction( ACTIONS::selectionClear );
2086
2087 if( isText )
2088 {
2089 item = createNewText( cursorPos );
2090 description = _( "Add Text" );
2091 }
2092 else if( isHierLabel )
2093 {
2094 if( m_dialogSyncSheetPin && m_dialogSyncSheetPin->GetPlacementTemplate() )
2095 {
2096 auto pin = static_cast<SCH_HIERLABEL*>( m_dialogSyncSheetPin->GetPlacementTemplate() );
2097 SCH_HIERLABEL* label = new SCH_HIERLABEL( cursorPos );
2098 SCHEMATIC* schematic = getModel<SCHEMATIC>();
2099 label->SetText( pin->GetText() );
2100 label->SetShape( pin->GetShape() );
2102 label->SetParent( schematic );
2103 label->SetBold( m_lastTextBold );
2104 label->SetItalic( m_lastTextItalic );
2106 label->SetTextSize( VECTOR2I( schematic->Settings().m_DefaultTextSize,
2107 schematic->Settings().m_DefaultTextSize ) );
2108 label->SetFlags( IS_NEW | IS_MOVING );
2109 itemsToPlace.push_back( std::unique_ptr<SCH_LABEL_BASE>( label ) );
2110 }
2111 else
2112 {
2113 createNewLabel( cursorPos, LAYER_HIERLABEL, itemsToPlace );
2114 }
2115
2116 description = _( "Add Hierarchical Label" );
2117 }
2118 else if( isNetLabel )
2119 {
2120 createNewLabel( cursorPos, LAYER_LOCLABEL, itemsToPlace );
2121 description = _( "Add Label" );
2122 }
2123 else if( isGlobalLabel )
2124 {
2125 createNewLabel( cursorPos, LAYER_GLOBLABEL, itemsToPlace );
2126 description = _( "Add Label" );
2127 }
2128 else if( isClassLabel )
2129 {
2130 createNewLabel( cursorPos, LAYER_NETCLASS_REFS, itemsToPlace );
2131 description = _( "Add Label" );
2132 }
2133 else if( isSheetPin )
2134 {
2135 EDA_ITEM* i = nullptr;
2136
2137 // If we didn't have a sheet selected, try to find one under the cursor
2138 if( !sheet && m_selectionTool->SelectPoint( cursorPos, { SCH_SHEET_T }, &i ) )
2139 sheet = dynamic_cast<SCH_SHEET*>( i );
2140
2141 if( !sheet )
2142 {
2143 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( m_frame );
2144 m_statusPopup->SetText( _( "Click over a sheet." ) );
2145 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
2146 m_statusPopup->PopupFor( 2000 );
2147 item = nullptr;
2148 }
2149 else
2150 {
2151 // User is using the 'Sync Sheet Pins' tool
2152 if( m_dialogSyncSheetPin && m_dialogSyncSheetPin->GetPlacementTemplate() )
2153 {
2154 item = createNewSheetPinFromLabel( sheet, cursorPos,
2155 m_dialogSyncSheetPin->GetPlacementTemplate() );
2156 }
2157 else
2158 {
2159 // User is using the 'Place Sheet Pins' tool
2160 SCH_HIERLABEL* label = importHierLabel( sheet );
2161
2162 if( !label )
2163 {
2164 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( m_frame );
2165 m_statusPopup->SetText( _( "No new hierarchical labels found." ) );
2166 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
2167 m_statusPopup->PopupFor( 2000 );
2168 item = nullptr;
2169 break;
2170 }
2171
2172 item = createNewSheetPinFromLabel( sheet, cursorPos, label );
2173 }
2174 }
2175
2176 description = _( "Add Sheet Pin" );
2177 }
2178
2179 // If we started with a hotkey which has a position then warp back to that.
2180 // Otherwise update to the current mouse position pinned inside the autoscroll
2181 // boundaries.
2182 if( evt->IsPrime() && !ignorePrimePosition )
2183 {
2184 cursorPos = grid.Align( evt->Position() );
2185 getViewControls()->WarpMouseCursor( cursorPos, true );
2186 }
2187 else
2188 {
2190 cursorPos = getViewControls()->GetMousePosition();
2191 cursorPos = grid.ResolveSnap( cursorPos, snapGrid, item ).position;
2192 }
2193
2194 if( !itemsToPlace.empty() )
2195 {
2196 item = itemsToPlace.front().release();
2197 itemsToPlace.pop_front();
2198 }
2199
2200 if( item )
2201 prepItemForPlacement( item, cursorPos );
2202
2203 if( m_frame->GetMoveWarpsCursor() )
2204 controls->SetCursorPosition( cursorPos, false );
2205
2206 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2207 }
2208 else // ... and second click places:
2209 {
2210 item->ClearFlags( IS_MOVING );
2211
2212 if( item->IsConnectable() )
2213 m_frame->AutoRotateItem( m_frame->GetScreen(), item );
2214
2215 if( isSheetPin && sheet )
2216 {
2217 // Sheet pins are owned by their parent sheet.
2218 commit.Modify( sheet, m_frame->GetScreen() );
2219 sheet->AddPin( (SCH_SHEET_PIN*) item );
2220 }
2221 else
2222 {
2223 m_frame->SaveCopyForRepeatItem( item );
2224 m_frame->AddToScreen( item, m_frame->GetScreen() );
2225 commit.Added( item, m_frame->GetScreen() );
2226 }
2227
2228 item->AutoplaceFields( m_frame->GetScreen(), AUTOPLACE_AUTO );
2229
2230 commit.Push( description );
2231
2232 m_view->ClearPreview();
2233
2234 if( m_dialogSyncSheetPin && m_dialogSyncSheetPin->GetPlacementTemplate() )
2235 {
2236 m_dialogSyncSheetPin->EndPlaceItem( item );
2237
2238 if( m_dialogSyncSheetPin->CanPlaceMore() )
2239 {
2240 item = nullptr;
2241 goto PLACE_NEXT;
2242 }
2243
2244 m_toolMgr->RunAction( ACTIONS::selectionClear );
2245 m_dialogSyncSheetPin->Show( true );
2246 break;
2247 }
2248
2249 item = nullptr;
2250
2251 if( isSheetPin && sheet )
2252 {
2253 SCH_HIERLABEL* label = importHierLabel( sheet );
2254
2255 if( !label )
2256 {
2257 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( m_frame );
2258 m_statusPopup->SetText( _( "No new hierarchical labels found." ) );
2259 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
2260 m_statusPopup->PopupFor( 2000 );
2261 break;
2262 }
2263
2264 item = createNewSheetPinFromLabel( sheet, cursorPos, label );
2265 }
2266 else if( !itemsToPlace.empty() )
2267 {
2268 item = itemsToPlace.front().release();
2269 itemsToPlace.pop_front();
2270 prepItemForPlacement( item, cursorPos );
2271 }
2272 }
2273 }
2274 else if( evt->IsClick( BUT_RIGHT ) )
2275 {
2276 // Warp after context menu only if dragging...
2277 if( !item )
2278 m_toolMgr->VetoContextMenuMouseWarp();
2279
2280 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
2281 }
2282 else if( item && evt->IsSelectionEvent() )
2283 {
2284 // This happens if our text was replaced out from under us by ConvertTextType()
2285 SCH_SELECTION& selection = m_selectionTool->GetSelection();
2286
2287 if( selection.GetSize() == 1 )
2288 {
2289 item = (SCH_ITEM*) selection.Front();
2290 updatePreview();
2291 }
2292 else
2293 {
2294 item = nullptr;
2295 }
2296 }
2297 else if( evt->IsAction( &ACTIONS::increment ) )
2298 {
2299 if( evt->HasParameter() )
2300 m_toolMgr->RunSynchronousAction( ACTIONS::increment, &commit, evt->Parameter<ACTIONS::INCREMENT>() );
2301 else
2302 m_toolMgr->RunSynchronousAction( ACTIONS::increment, &commit, ACTIONS::INCREMENT { 1, 0 } );
2303 }
2304 else if( evt->IsAction( &ACTIONS::duplicate )
2305 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem )
2306 || evt->IsAction( &ACTIONS::paste ) )
2307 {
2308 if( item )
2309 {
2310 wxBell();
2311 continue;
2312 }
2313
2314 // Exit. The duplicate/repeat/paste will run in its own loop.
2315 evt->SetPassEvent();
2316 break;
2317 }
2318 else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
2319 {
2320 item->CalcEdit( cursorPos );
2321 m_view->ClearPreview();
2322 m_view->AddToPreview( item->Clone() );
2323 m_frame->SetMsgPanel( item );
2324 }
2325 else if( item && evt->IsAction( &ACTIONS::doDelete ) )
2326 {
2327 cleanup();
2328 }
2329 else if( evt->IsAction( &ACTIONS::redo ) )
2330 {
2331 wxBell();
2332 }
2333 else if( item && ( evt->IsAction( &SCH_ACTIONS::toDLabel )
2334 || evt->IsAction( &SCH_ACTIONS::toGLabel )
2335 || evt->IsAction( &SCH_ACTIONS::toHLabel )
2336 || evt->IsAction( &SCH_ACTIONS::toLabel )
2337 || evt->IsAction( &SCH_ACTIONS::toText )
2338 || evt->IsAction( &SCH_ACTIONS::toTextBox ) ) )
2339 {
2340 wxBell();
2341 }
2342 else if( item && ( evt->IsAction( &SCH_ACTIONS::properties )
2343 || evt->IsAction( &SCH_ACTIONS::autoplaceFields )
2344 || evt->IsAction( &SCH_ACTIONS::rotateCW )
2345 || evt->IsAction( &SCH_ACTIONS::rotateCCW )
2346 || evt->IsAction( &SCH_ACTIONS::mirrorV )
2347 || evt->IsAction( &SCH_ACTIONS::mirrorH ) ) )
2348 {
2349 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2350 evt->SetPassEvent();
2351 }
2352 else
2353 {
2354 evt->SetPassEvent();
2355 }
2356
2357 // Enable autopanning and cursor capture only when there is an item to be placed
2358 controls->SetAutoPan( item != nullptr );
2359 controls->CaptureCursor( item != nullptr );
2360 }
2361
2362 controls->SetAutoPan( false );
2363 controls->CaptureCursor( false );
2364 controls->ForceCursorPosition( false );
2365 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2366
2367 if( m_dialogSyncSheetPin && m_dialogSyncSheetPin->CanPlaceMore() )
2368 {
2369 m_dialogSyncSheetPin->EndPlacement();
2370 m_dialogSyncSheetPin->Show( true );
2371 }
2372
2373 return 0;
2374}
2375
2376
2378{
2379 if( m_inDrawingTool )
2380 return 0;
2381
2383 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::RULE_AREA );
2384
2387 VECTOR2I cursorPos;
2388
2389 RULE_AREA_CREATE_HELPER ruleAreaTool( *getView(), m_frame, m_toolMgr );
2390 POLYGON_GEOM_MANAGER polyGeomMgr( ruleAreaTool );
2391 bool started = false;
2392
2393 // We might be running as the same shape in another co-routine. Make sure that one
2394 // gets whacked.
2395 m_toolMgr->DeactivateTool();
2396
2397 m_toolMgr->RunAction( ACTIONS::selectionClear );
2398
2399 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
2400 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
2401
2402 auto setCursor =
2403 [&]()
2404 {
2405 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2406 };
2407
2408 auto cleanup =
2409 [&]()
2410 {
2411 polyGeomMgr.Reset();
2412 started = false;
2413 getViewControls()->SetAutoPan( false );
2414 getViewControls()->CaptureCursor( false );
2415 m_toolMgr->RunAction( ACTIONS::selectionClear );
2416 };
2417
2418 Activate();
2419
2420 // Must be done after Activate() so that it gets set into the correct context
2421 getViewControls()->ShowCursor( true );
2422 //m_controls->ForceCursorPosition( false );
2423
2424 // Set initial cursor
2425 setCursor();
2426
2427 if( aEvent.HasPosition() )
2428 m_toolMgr->PrimeTool( aEvent.Position() );
2429
2430 // Main loop: keep receiving events
2431 while( TOOL_EVENT* evt = Wait() )
2432 {
2433 setCursor();
2434
2435 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2436 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2437
2438 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_CONNECTABLE );
2439 controls->ForceCursorPosition( true, cursorPos );
2440
2441 polyGeomMgr.SetLeaderMode( m_frame->eeconfig()->m_Drawing.line_mode == LINE_MODE_FREE ? LEADER_MODE::DIRECT
2443
2444 if( evt->IsCancelInteractive() )
2445 {
2446 if( started )
2447 {
2448 cleanup();
2449 }
2450 else
2451 {
2452 // We've handled the cancel event. Don't cancel other tools
2453 evt->SetPassEvent( false );
2454 break;
2455 }
2456 }
2457 else if( evt->IsActivate() )
2458 {
2459 if( started )
2460 cleanup();
2461
2462 if( evt->IsPointEditor() )
2463 {
2464 // don't exit (the point editor runs in the background)
2465 continue;
2466 }
2467
2468 if( evt->IsMoveTool() )
2469 {
2470 // Make sure we come back after the move tool runs
2471 frame()->PushTool( originalEvent );
2472 }
2473
2474 break;
2475 }
2476 else if( evt->IsClick( BUT_RIGHT ) )
2477 {
2478 if( !started )
2479 m_toolMgr->VetoContextMenuMouseWarp();
2480
2481 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
2482 }
2483 // events that lock in nodes
2484 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
2485 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick )
2486 || evt->IsAction( &SCH_ACTIONS::closeOutline ) )
2487 {
2488 // Check if it is double click / closing line (so we have to finish the zone)
2489 const bool endPolygon = evt->IsDblClick( BUT_LEFT )
2490 || evt->IsAction( &ACTIONS::cursorDblClick )
2491 || evt->IsAction( &SCH_ACTIONS::closeOutline )
2492 || polyGeomMgr.NewPointClosesOutline( cursorPos );
2493
2494 if( endPolygon )
2495 {
2496 polyGeomMgr.SetFinished();
2497 polyGeomMgr.Reset();
2498
2499 started = false;
2500 getViewControls()->SetAutoPan( false );
2501 getViewControls()->CaptureCursor( false );
2502 }
2503 // adding a corner
2504 else if( polyGeomMgr.AddPoint( cursorPos ) )
2505 {
2506 if( !started )
2507 {
2508 started = true;
2509
2510 getViewControls()->SetAutoPan( true );
2511 getViewControls()->CaptureCursor( true );
2512 }
2513 }
2514 }
2515 else if( started && ( evt->IsAction( &ACTIONS::deleteLastPoint )
2516 || evt->IsAction( &ACTIONS::doDelete )
2517 || evt->IsAction( &ACTIONS::undo ) ) )
2518 {
2519 if( std::optional<VECTOR2I> last = polyGeomMgr.DeleteLastCorner() )
2520 {
2521 cursorPos = last.value();
2522 getViewControls()->WarpMouseCursor( cursorPos, true );
2523 getViewControls()->ForceCursorPosition( true, cursorPos );
2524 polyGeomMgr.SetCursorPosition( cursorPos );
2525 }
2526 else
2527 {
2528 cleanup();
2529 }
2530 }
2531 else if( started && ( evt->IsMotion()
2532 || evt->IsAction( &ACTIONS::refreshPreview )
2533 || evt->IsDrag( BUT_LEFT ) ) )
2534 {
2535 polyGeomMgr.SetCursorPosition( cursorPos );
2536 }
2537 else if( evt->IsAction( &ACTIONS::duplicate )
2538 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem )
2539 || evt->IsAction( &ACTIONS::paste ) )
2540 {
2541 if( started )
2542 {
2543 wxBell();
2544 continue;
2545 }
2546
2547 // Exit. The duplicate/repeat/paste will run in its own loop.
2548 evt->SetPassEvent();
2549 break;
2550 }
2551 else
2552 {
2553 evt->SetPassEvent();
2554 }
2555
2556 } // end while
2557
2558 getViewControls()->SetAutoPan( false );
2559 getViewControls()->CaptureCursor( false );
2560 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2561 return 0;
2562}
2563
2564
2566{
2567 SCHEMATIC* schematic = getModel<SCHEMATIC>();
2568 SCH_TABLE* table = nullptr;
2569
2570 if( m_inDrawingTool )
2571 return 0;
2572
2574
2577 VECTOR2I cursorPos;
2578
2579 // We might be running as the same shape in another co-routine. Make sure that one
2580 // gets whacked.
2581 m_toolMgr->DeactivateTool();
2582
2583 m_toolMgr->RunAction( ACTIONS::selectionClear );
2584
2585 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
2586 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
2587
2588 auto setCursor =
2589 [&]()
2590 {
2591 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2592 };
2593
2594 auto cleanup =
2595 [&] ()
2596 {
2597 m_toolMgr->RunAction( ACTIONS::selectionClear );
2598 m_view->ClearPreview();
2599 delete table;
2600 table = nullptr;
2601 };
2602
2603 Activate();
2604
2605 // Must be done after Activate() so that it gets set into the correct context
2606 getViewControls()->ShowCursor( true );
2607
2608 // Set initial cursor
2609 setCursor();
2610
2611 if( aEvent.HasPosition() )
2612 m_toolMgr->PrimeTool( aEvent.Position() );
2613
2614 // Main loop: keep receiving events
2615 while( TOOL_EVENT* evt = Wait() )
2616 {
2617 setCursor();
2618 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2619 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2620
2621 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_GRAPHICS );
2622 controls->ForceCursorPosition( true, cursorPos );
2623
2624 // The tool hotkey is interpreted as a click when drawing
2625 bool isSyntheticClick = table && evt->IsActivate() && evt->HasPosition() && evt->Matches( aEvent );
2626
2627 if( evt->IsCancelInteractive() || ( table && evt->IsAction( &ACTIONS::undo ) ) )
2628 {
2629 if( table )
2630 cleanup();
2631 else
2632 break;
2633 }
2634 else if( evt->IsActivate() && !isSyntheticClick )
2635 {
2636 if( table && evt->IsMoveTool() )
2637 {
2638 // we're already drawing our own item; ignore the move tool
2639 evt->SetPassEvent( false );
2640 continue;
2641 }
2642
2643 if( table )
2644 cleanup();
2645
2646 if( evt->IsPointEditor() )
2647 {
2648 // don't exit (the point editor runs in the background)
2649 continue;
2650 }
2651
2652 if( evt->IsMoveTool() )
2653 {
2654 // Make sure we come back after the move tool runs
2655 frame()->PushTool( originalEvent );
2656 }
2657
2658 break;
2659 }
2660 else if( !table && ( evt->IsClick( BUT_LEFT )
2661 || evt->IsAction( &ACTIONS::cursorClick ) ) )
2662 {
2663 m_toolMgr->RunAction( ACTIONS::selectionClear );
2664
2665 table = new SCH_TABLE( 0 );
2666 table->SetColCount( 1 );
2667
2668 SCH_TABLECELL* tableCell = new SCH_TABLECELL();
2669 int defaultTextSize = schematic->Settings().m_DefaultTextSize;
2670
2671 tableCell->SetTextSize( VECTOR2I( defaultTextSize, defaultTextSize ) );
2672 table->AddCell( tableCell );
2673
2674 table->SetParent( schematic );
2675 table->SetFlags( IS_NEW );
2676 table->SetPosition( cursorPos );
2677
2678 m_view->ClearPreview();
2679 m_view->AddToPreview( table->Clone() );
2680 }
2681 else if( table && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
2682 || isSyntheticClick
2683 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick )
2684 || evt->IsAction( &SCH_ACTIONS::finishInteractive ) ) )
2685 {
2686 table->ClearEditFlags();
2687 table->SetFlags( IS_NEW );
2688 table->Normalize();
2689
2691
2692 // QuasiModal required for Scintilla auto-complete
2693 if( dlg.ShowQuasiModal() == wxID_OK )
2694 {
2695 SCH_COMMIT commit( m_toolMgr );
2696 commit.Add( table, m_frame->GetScreen() );
2697 commit.Push( _( "Draw Table" ) );
2698
2699 m_selectionTool->AddItemToSel( table );
2701 }
2702 else
2703 {
2704 delete table;
2705 }
2706
2707 table = nullptr;
2708 m_view->ClearPreview();
2709 }
2710 else if( table && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
2711 {
2712 VECTOR2I gridSize = grid.GetGridSize( grid.GetItemGrid( table ) );
2713 int fontSize = schematic->Settings().m_DefaultTextSize;
2714 VECTOR2I origin( table->GetPosition() );
2715 VECTOR2I requestedSize( cursorPos - origin );
2716
2717 int colCount = std::max( 1, requestedSize.x / ( fontSize * 15 ) );
2718 int rowCount = std::max( 1, requestedSize.y / ( fontSize * 2 ) );
2719
2720 VECTOR2I cellSize( std::max( gridSize.x * 5, requestedSize.x / colCount ),
2721 std::max( gridSize.y * 2, requestedSize.y / rowCount ) );
2722
2723 cellSize.x = KiROUND( (double) cellSize.x / gridSize.x ) * gridSize.x;
2724 cellSize.y = KiROUND( (double) cellSize.y / gridSize.y ) * gridSize.y;
2725
2726 table->ClearCells();
2727 table->SetColCount( colCount );
2728
2729 for( int col = 0; col < colCount; ++col )
2730 table->SetColWidth( col, cellSize.x );
2731
2732 for( int row = 0; row < rowCount; ++row )
2733 {
2734 table->SetRowHeight( row, cellSize.y );
2735
2736 for( int col = 0; col < colCount; ++col )
2737 {
2738 SCH_TABLECELL* cell = new SCH_TABLECELL();
2739 int defaultTextSize = schematic->Settings().m_DefaultTextSize;
2740
2741 cell->SetTextSize( VECTOR2I( defaultTextSize, defaultTextSize ) );
2742 cell->SetPosition( origin + VECTOR2I( col * cellSize.x, row * cellSize.y ) );
2743 cell->SetEnd( cell->GetPosition() + cellSize );
2744 table->AddCell( cell );
2745 }
2746 }
2747
2748 m_view->ClearPreview();
2749 m_view->AddToPreview( table->Clone() );
2750 m_frame->SetMsgPanel( table );
2751 }
2752 else if( !table && ( evt->IsDblClick( BUT_LEFT )
2753 || evt->IsAction( &ACTIONS::cursorDblClick ) ) )
2754 {
2755 m_toolMgr->RunAction( SCH_ACTIONS::properties );
2756 }
2757 else if( evt->IsClick( BUT_RIGHT ) )
2758 {
2759 // Warp after context menu only if dragging...
2760 if( !table )
2761 m_toolMgr->VetoContextMenuMouseWarp();
2762
2763 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
2764 }
2765 else if( evt->IsAction( &ACTIONS::duplicate )
2766 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem )
2767 || evt->IsAction( &ACTIONS::paste ) )
2768 {
2769 if( table )
2770 {
2771 wxBell();
2772 continue;
2773 }
2774
2775 // Exit. The duplicate/repeat/paste will run in its own loop.
2776 evt->SetPassEvent();
2777 break;
2778 }
2779 else if( table && evt->IsAction( &ACTIONS::redo ) )
2780 {
2781 wxBell();
2782 }
2783 else
2784 {
2785 evt->SetPassEvent();
2786 }
2787
2788 // Enable autopanning and cursor capture only when there is a shape being drawn
2789 getViewControls()->SetAutoPan( table != nullptr );
2790 getViewControls()->CaptureCursor( table != nullptr );
2791 }
2792
2793 getViewControls()->SetAutoPan( false );
2794 getViewControls()->CaptureCursor( false );
2795 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2796 return 0;
2797}
2798
2799
2801{
2802 bool isDrawSheetCopy = aEvent.IsAction( &SCH_ACTIONS::drawSheetFromFile );
2803 bool isDrawSheetFromDesignBlock = aEvent.IsAction( &SCH_ACTIONS::drawSheetFromDesignBlock );
2804
2805 std::unique_ptr<DESIGN_BLOCK> designBlock;
2806
2807 SCH_SHEET* sheet = nullptr;
2808 wxString filename;
2809 SCH_GROUP* sheetGroup = nullptr;
2810
2811 if( isDrawSheetCopy )
2812 {
2813 wxString* ptr = aEvent.Parameter<wxString*>();
2814 wxCHECK( ptr, 0 );
2815
2816 // We own the string if we're importing a sheet
2817 filename = *ptr;
2818 delete ptr;
2819 }
2820 else if( isDrawSheetFromDesignBlock )
2821 {
2822 designBlock.reset( aEvent.Parameter<DESIGN_BLOCK*>() );
2823 wxCHECK( designBlock, 0 );
2824 filename = designBlock->GetSchematicFile();
2825 }
2826
2827 if( ( isDrawSheetCopy || isDrawSheetFromDesignBlock ) && !wxFileExists( filename ) )
2828 {
2829 wxMessageBox( wxString::Format( _( "File '%s' does not exist." ), filename ) );
2830 return 0;
2831 }
2832
2833 if( m_inDrawingTool )
2834 return 0;
2835
2837
2838 EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
2839 SCHEMATIC_SETTINGS& schSettings = m_frame->Schematic().Settings();
2842 VECTOR2I cursorPos;
2843 bool startedWithDrag = false; // Track if initial sheet placement started with a drag
2844
2845 m_toolMgr->RunAction( ACTIONS::selectionClear );
2846
2847 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
2848 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
2849
2850 auto setCursor =
2851 [&]()
2852 {
2853 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2854 };
2855
2856 auto cleanup =
2857 [&] ()
2858 {
2859 m_toolMgr->RunAction( ACTIONS::selectionClear );
2860 m_view->ClearPreview();
2861 delete sheet;
2862 sheet = nullptr;
2863 };
2864
2865 Activate();
2866
2867 // Must be done after Activate() so that it gets set into the correct context
2868 getViewControls()->ShowCursor( true );
2869
2870 // Set initial cursor
2871 setCursor();
2872
2873 if( aEvent.HasPosition() && !( isDrawSheetCopy || isDrawSheetFromDesignBlock ) )
2874 m_toolMgr->PrimeTool( aEvent.Position() );
2875
2876 // Main loop: keep receiving events
2877 while( TOOL_EVENT* evt = Wait() )
2878 {
2879 setCursor();
2880 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2881 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2882
2883 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_GRAPHICS );
2884 controls->ForceCursorPosition( true, cursorPos );
2885
2886 // The tool hotkey is interpreted as a click when drawing
2887 bool isSyntheticClick = sheet && evt->IsActivate() && evt->HasPosition()
2888 && evt->Matches( aEvent );
2889
2890 if( evt->IsCancelInteractive() || ( sheet && evt->IsAction( &ACTIONS::undo ) ) )
2891 {
2892 m_frame->GetInfoBar()->Dismiss();
2893
2894 if( sheet )
2895 cleanup();
2896 else
2897 break;
2898 }
2899 else if( evt->IsActivate() && !isSyntheticClick )
2900 {
2901 if( sheet && evt->IsMoveTool() )
2902 {
2903 // we're already drawing our own item; ignore the move tool
2904 evt->SetPassEvent( false );
2905 continue;
2906 }
2907
2908 if( sheet )
2909 {
2910 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel sheet creation." ) );
2911 evt->SetPassEvent( false );
2912 continue;
2913 }
2914
2915 if( evt->IsPointEditor() )
2916 {
2917 // don't exit (the point editor runs in the background)
2918 continue;
2919 }
2920
2921 if( evt->IsMoveTool() )
2922 {
2923 // Make sure we come back after the move tool runs
2924 frame()->PushTool( originalEvent );
2925 }
2926
2927 break;
2928 }
2929 else if( !sheet && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
2930 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick )
2931 || evt->IsDrag( BUT_LEFT ) ) )
2932 {
2933 SCH_SELECTION& selection = m_selectionTool->GetSelection();
2934
2935 if( selection.Size() == 1
2936 && selection.Front()->Type() == SCH_SHEET_T
2937 && selection.Front()->GetBoundingBox().Contains( cursorPos ) )
2938 {
2939 if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorClick ) )
2940 {
2941 // sheet already selected
2942 continue;
2943 }
2944 else if( evt->IsDblClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
2945 {
2946 m_toolMgr->PostAction( SCH_ACTIONS::enterSheet );
2947 break;
2948 }
2949 }
2950
2951 m_toolMgr->RunAction( ACTIONS::selectionClear );
2952
2953 VECTOR2I sheetPos = evt->IsDrag( BUT_LEFT )
2954 ? grid.Align( evt->DragOrigin(), GRID_HELPER_GRIDS::GRID_GRAPHICS )
2955 : cursorPos;
2956
2957 // Remember whether this sheet was initiated with a drag so we can treat mouse-up as
2958 // the terminating (second) click.
2959 startedWithDrag = evt->IsDrag( BUT_LEFT );
2960
2961 sheet = new SCH_SHEET( m_frame->GetCurrentSheet().Last(), sheetPos );
2962 sheet->SetScreen( nullptr );
2963
2964 wxString ext = wxString( "." ) + FILEEXT::KiCadSchematicFileExtension;
2965
2966 if( isDrawSheetCopy )
2967 {
2968 wxFileName fn( filename );
2969
2970 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( fn.GetName() );
2971 sheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( fn.GetName() + ext );
2972 }
2973 else if( isDrawSheetFromDesignBlock )
2974 {
2975 wxString sn = UniqueSheetName( m_frame->GetScreen(), designBlock->GetLibId().GetLibItemName() );
2976 wxFileName fn( filename );
2977
2978 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( sn );
2979 sheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( fn.GetName() + ext );
2980
2981 std::vector<SCH_FIELD>& sheetFields = sheet->GetFields();
2982
2983 // Copy default fields into the sheet
2984 for( const auto& [fieldName, fieldValue] : designBlock->GetFields() )
2985 {
2986 sheetFields.emplace_back( sheet, FIELD_T::USER, fieldName );
2987 sheetFields.back().SetText( fieldValue );
2988 sheetFields.back().SetTextPos( sheet->GetPosition() );
2989 sheetFields.back().SetVisible( false );
2990 }
2991 }
2992 else
2993 {
2994 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( wxT( "Untitled Sheet" ) );
2995 sheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( wxT( "untitled" ) + ext );
2996 }
2997
2998 sheet->SetFlags( IS_NEW | IS_MOVING );
2999 sheet->SetBorderWidth( schIUScale.MilsToIU( cfg->m_Drawing.default_line_thickness ) );
3002 sizeSheet( sheet, cursorPos );
3003
3004 SCH_SHEET_LIST hierarchy = m_frame->Schematic().Hierarchy();
3005 SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
3006 instance.push_back( sheet );
3007 wxString pageNumber = hierarchy.GetNextPageNumber();
3008 instance.SetPageNumber( pageNumber );
3009
3010 m_view->ClearPreview();
3011 m_view->AddToPreview( sheet->Clone() );
3012 }
3013 else if( sheet && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
3014 || isSyntheticClick
3015 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick )
3016 || evt->IsAction( &ACTIONS::finishInteractive )
3017 || ( startedWithDrag && evt->IsMouseUp( BUT_LEFT ) ) ) )
3018 {
3019 getViewControls()->SetAutoPan( false );
3020 getViewControls()->CaptureCursor( false );
3021
3022 if( m_frame->EditSheetProperties( sheet, &m_frame->GetCurrentSheet(), nullptr, nullptr, nullptr,
3023 &filename ) )
3024 {
3025 m_view->ClearPreview();
3026
3027 if( ( isDrawSheetCopy || isDrawSheetFromDesignBlock ) && cfg->m_Drawing.auto_place_sheet_pins )
3029
3030 sheet->AutoplaceFields( m_frame->GetScreen(), AUTOPLACE_AUTO );
3031
3032 // Use the commit we were provided or make our own
3033 SCH_COMMIT tempCommit = SCH_COMMIT( m_toolMgr );
3034 SCH_COMMIT& c = evt->Commit() ? *( (SCH_COMMIT*) evt->Commit() ) : tempCommit;
3035
3036 // We need to manually add the sheet to the screen otherwise annotation will not be able to find
3037 // the sheet and its symbols to annotate.
3038 m_frame->AddToScreen( sheet );
3039 c.Added( sheet, m_frame->GetScreen() );
3040
3041 // Refresh the hierarchy so the new sheet and its symbols are found during annotation.
3042 // The cached hierarchy was built before this sheet was added.
3043 m_frame->Schematic().RefreshHierarchy();
3044
3045 if( cfg->m_AnnotatePanel.automatic && !( ( isDrawSheetCopy || isDrawSheetFromDesignBlock )
3047 {
3048 // Annotation will remove this from selection, but we add it back later
3049 m_selectionTool->AddItemToSel( sheet );
3050
3052
3053 m_frame->AnnotateSymbols( &c, ANNOTATE_SELECTION,
3055 (ANNOTATE_ALGO_T) schSettings.m_AnnotateMethod, true, /* recursive */
3056 schSettings.m_AnnotateStartNum, true, /* reset */
3057 false, /* regroup */
3058 false, /* repair */
3059 reporter );
3060 }
3061
3062 if( isDrawSheetFromDesignBlock && cfg->m_DesignBlockChooserPanel.place_as_group )
3063 {
3064 SCH_SCREEN* screen = m_frame->GetScreen();
3065
3066 sheetGroup = new SCH_GROUP( screen );
3067 sheetGroup->SetName( UniqueGroupName( screen, designBlock->GetLibId().GetLibItemName() ) );
3068 sheetGroup->SetDesignBlockLibId( designBlock->GetLibId() );
3069 c.Add( sheetGroup, screen );
3070 c.Modify( sheet, screen, RECURSE_MODE::NO_RECURSE );
3071 sheetGroup->AddItem( sheet );
3072 }
3073
3074 c.Push( isDrawSheetCopy ? "Import Sheet Copy" : "Draw Sheet" );
3075
3076 if( sheetGroup )
3077 m_selectionTool->AddItemToSel( sheetGroup );
3078 else
3079 m_selectionTool->AddItemToSel( sheet );
3080
3081 if( ( isDrawSheetCopy || isDrawSheetFromDesignBlock )
3083 {
3084 break;
3085 }
3086 }
3087 else
3088 {
3089 m_view->ClearPreview();
3090 delete sheet;
3091 }
3092
3093 sheet = nullptr;
3094 }
3095 else if( evt->IsAction( &ACTIONS::duplicate )
3096 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem )
3097 || evt->IsAction( &ACTIONS::paste ) )
3098 {
3099 if( sheet )
3100 {
3101 wxBell();
3102 continue;
3103 }
3104
3105 // Exit. The duplicate/repeat/paste will run in its own loop.
3106 evt->SetPassEvent();
3107 break;
3108 }
3109 else if( sheet && ( evt->IsAction( &ACTIONS::refreshPreview )
3110 || evt->IsMotion()
3111 || evt->IsDrag( BUT_LEFT ) ) )
3112 {
3113 sizeSheet( sheet, cursorPos );
3114 m_view->ClearPreview();
3115 m_view->AddToPreview( sheet->Clone() );
3116 m_frame->SetMsgPanel( sheet );
3117 }
3118 else if( evt->IsClick( BUT_RIGHT ) )
3119 {
3120 // Warp after context menu only if dragging...
3121 if( !sheet )
3122 m_toolMgr->VetoContextMenuMouseWarp();
3123
3124 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
3125 }
3126 else if( sheet && evt->IsAction( &ACTIONS::redo ) )
3127 {
3128 wxBell();
3129 }
3130 else
3131 {
3132 evt->SetPassEvent();
3133 }
3134
3135 // Enable autopanning and cursor capture only when there is a sheet to be placed
3136 getViewControls()->SetAutoPan( sheet != nullptr );
3137 getViewControls()->CaptureCursor( sheet != nullptr );
3138 }
3139
3140 getViewControls()->SetAutoPan( false );
3141 getViewControls()->CaptureCursor( false );
3142 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3143
3144 return 0;
3145}
3146
3147
3149{
3150 VECTOR2I pos = aSheet->GetPosition();
3151 VECTOR2I size = aPos - pos;
3153
3154 size.x = std::max( size.x, minSize.x );
3155 size.y = std::max( size.y, minSize.y );
3156
3157 VECTOR2I grid = m_frame->GetNearestGridPosition( pos + size );
3158 VECTOR2I gridSize = getView()->GetGAL()->GetGridSize();
3159
3160 // Keep the corner on-grid without letting inward rounding violate the minimum size.
3161 if( grid.x - pos.x < minSize.x )
3162 grid.x += gridSize.x;
3163
3164 if( grid.y - pos.y < minSize.y )
3165 grid.y += gridSize.y;
3166
3167 aSheet->Resize( VECTOR2I( grid.x - pos.x, grid.y - pos.y ) );
3168}
3169
3170
3171int SCH_DRAWING_TOOLS::doSyncSheetsPins( std::list<SCH_SHEET_PATH> sheetPaths, SCH_SHEET* aInitialSheet )
3172{
3173 if( !sheetPaths.size() )
3174 return 0;
3175
3176 m_dialogSyncSheetPin = std::make_unique<DIALOG_SYNC_SHEET_PINS>(
3177 m_frame, std::move( sheetPaths ),
3178 std::make_shared<SHEET_SYNCHRONIZATION_AGENT>(
3179 [&]( EDA_ITEM* aItem, SCH_SHEET_PATH aPath,
3181 {
3182 SCH_COMMIT commit( m_toolMgr );
3183
3184 if( auto pin = dynamic_cast<SCH_SHEET_PIN*>( aItem ) )
3185 {
3186 commit.Modify( pin->GetParent(), aPath.LastScreen() );
3187 aModify();
3188 commit.Push( _( "Modify Sheet Pin" ) );
3189 }
3190 else
3191 {
3192 commit.Modify( aItem, aPath.LastScreen() );
3193 aModify();
3194 commit.Push( _( "Modify Schematic Item" ) );
3195 }
3196
3197 // The push already updated the R-tree and republished connectivity
3198 updateItem( aItem, false );
3199 m_frame->OnModify();
3200 },
3201 [&]( EDA_ITEM* aItem, SCH_SHEET_PATH aPath )
3202 {
3203 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &aPath );
3204 SCH_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
3205 selectionTool->UnbrightenItem( aItem );
3206 selectionTool->AddItemToSel( aItem, true );
3207 m_toolMgr->RunAction( ACTIONS::doDelete );
3208 },
3209 [&]( SCH_SHEET* aItem, SCH_SHEET_PATH aPath,
3211 std::set<EDA_ITEM*> aTemplates )
3212 {
3213 switch( aOp )
3214 {
3216 {
3217 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
3218 m_dialogSyncSheetPin->Hide();
3219 m_dialogSyncSheetPin->PreparePlacementTemplate(
3221 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &aPath );
3223 break;
3224 }
3226 {
3227 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
3228 m_dialogSyncSheetPin->Hide();
3229 m_dialogSyncSheetPin->PreparePlacementTemplate(
3231 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &aPath );
3232 m_toolMgr->GetTool<SCH_SELECTION_TOOL>()->SyncSelection( {}, nullptr, { sheet } );
3234 break;
3235 }
3236 }
3237 },
3238 m_toolMgr, m_frame ),
3239 aInitialSheet );
3240 m_dialogSyncSheetPin->Show( true );
3241 return 0;
3242}
3243
3244
3246{
3247 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( m_selectionTool->GetSelection().Front() );
3248
3249 if( !sheet )
3250 {
3251 VECTOR2I cursorPos = getViewControls()->GetMousePosition();
3252
3253 if( EDA_ITEM* i = nullptr; static_cast<void>(m_selectionTool->SelectPoint( cursorPos, { SCH_SHEET_T }, &i ) ) , i != nullptr )
3254 {
3255 sheet = dynamic_cast<SCH_SHEET*>( i );
3256 }
3257 }
3258
3259 if ( sheet )
3260 {
3261 SCH_SHEET_PATH current = m_frame->GetCurrentSheet();
3262 current.push_back( sheet );
3263 return doSyncSheetsPins( { current } );
3264 }
3265
3266 return 0;
3267}
3268
3269
3271{
3272 if( m_inDrawingTool )
3273 return 0;
3274
3276
3277 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( m_selectionTool->GetSelection().Front() );
3278
3279 if( !sheet )
3280 return 0;
3281
3282 std::vector<SCH_HIERLABEL*> labels = GetUnplacedSheetPinLabels( *sheet );
3283
3284 if( labels.empty() )
3285 {
3286 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
3287
3288 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( m_frame );
3289 m_statusPopup->SetText( _( "No new hierarchical labels found." ) );
3290 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
3291 m_statusPopup->PopupFor( 2000 );
3292 m_toolMgr->RunAction( ACTIONS::selectionClear );
3293 m_view->ClearPreview();
3294 return 0;
3295 }
3296
3297 m_toolMgr->RunAction( ACTIONS::selectionClear );
3298
3299 SCH_COMMIT commit( m_toolMgr );
3300 commit.Modify( sheet, m_frame->GetScreen() );
3301
3302 AutoPlaceSheetPins( sheet, labels );
3303
3304 commit.Push( _( "Auto-place Sheet Pins" ) );
3305 return 0;
3306}
3307
3308
3310{
3311 static const std::function<void( std::list<SCH_SHEET_PATH>&, SCH_SCREEN*, std::set<SCH_SCREEN*>&,
3312 SCH_SHEET_PATH const& )> getSheetChildren =
3313 []( std::list<SCH_SHEET_PATH>& aPaths, SCH_SCREEN* aScene, std::set<SCH_SCREEN*>& aVisited,
3314 SCH_SHEET_PATH const& aCurPath )
3315 {
3316 if( ! aScene || aVisited.find(aScene) != aVisited.end() )
3317 return ;
3318
3319 std::vector<SCH_ITEM*> sheetChildren;
3320 aScene->GetSheets( &sheetChildren );
3321 aVisited.insert( aScene );
3322
3323 for( SCH_ITEM* child : sheetChildren )
3324 {
3325 SCH_SHEET_PATH cp = aCurPath;
3326 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( child );
3327 cp.push_back( sheet );
3328 aPaths.push_back( cp );
3329 getSheetChildren( aPaths, sheet->GetScreen(), aVisited, cp );
3330 }
3331 };
3332
3333 std::list<SCH_SHEET_PATH> sheetPaths;
3334 std::set<SCH_SCREEN*> visited;
3335
3336 // Build sheet paths for each top-level sheet (don't include virtual root in paths)
3337 std::vector<SCH_SHEET*> topLevelSheets = m_frame->Schematic().GetTopLevelSheets();
3338
3339 for( SCH_SHEET* topSheet : topLevelSheets )
3340 {
3341 if( topSheet && topSheet->GetScreen() )
3342 {
3343 SCH_SHEET_PATH current;
3344 current.push_back( topSheet );
3345 getSheetChildren( sheetPaths, topSheet->GetScreen(), visited, current );
3346 }
3347 }
3348
3349 if( sheetPaths.size() == 0 )
3350 {
3351 m_frame->ShowInfoBarMsg( _( "No sub schematic found in the current project" ) );
3352 return 0;
3353 }
3354
3355 // If a sheet is currently selected, pre-select its tab in the dialog
3356 SCH_SHEET* selectedSheet = dynamic_cast<SCH_SHEET*>( m_selectionTool->GetSelection().Front() );
3357
3358 return doSyncSheetsPins( std::move( sheetPaths ), selectedSheet );
3359}
3360
3362{
3363 if( !aSheet->GetScreen() )
3364 return nullptr;
3365
3366 std::vector<SCH_HIERLABEL*> labels;
3367
3368 for( EDA_ITEM* item : aSheet->GetScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
3369 {
3370 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
3371 labels.push_back( label );
3372 }
3373
3374 std::sort( labels.begin(), labels.end(),
3375 []( const SCH_HIERLABEL* label1, const SCH_HIERLABEL* label2 )
3376 {
3377 return StrNumCmp( label1->GetText(), label2->GetText(), true ) < 0;
3378 } );
3379
3380 for( SCH_HIERLABEL* label : labels )
3381 {
3382 if( !aSheet->HasPin( label->GetText() ) )
3383 return label;
3384 }
3385
3386 return nullptr;
3387}
3388
3389
3391{
3392 // clang-format off
3416 // clang-format on
3417}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
static TOOL_ACTION paste
Definition actions.h:76
static TOOL_ACTION deleteLastPoint
Definition actions.h:270
static TOOL_ACTION cursorDblClick
Definition actions.h:177
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION duplicate
Definition actions.h:80
static TOOL_ACTION activatePointEditor
Definition actions.h:267
static TOOL_ACTION doDelete
Definition actions.h:81
static TOOL_ACTION cursorClick
Definition actions.h:176
static TOOL_ACTION redo
Definition actions.h:72
static TOOL_ACTION increment
Definition actions.h:90
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION refreshPreview
Definition actions.h:155
static TOOL_ACTION finishInteractive
Definition actions.h:69
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
PANEL_DESIGN_BLOCK_CHOOSER m_DesignBlockChooserPanel
bool IsContentModified() const
Definition base_screen.h:56
void SetContentModified(bool aModified=true)
Definition base_screen.h:55
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
COMMIT & Added(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition commit.h:80
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
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
COMMIT & Removed(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been removed.
Definition commit.h:92
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.
LIB_ID GetSelectedLibId(int *aUnit=nullptr) const
DESIGN_BLOCK * GetDesignBlock(const LIB_ID &aLibId, bool aUseCacheLib, bool aShowErrorMsg, wxString *aErrorMsg=nullptr)
Load design block from design block library table.
void SetLabelList(std::list< std::unique_ptr< SCH_LABEL_BASE > > *aLabelList)
FIELDS_GRID_TABLE * GetFieldsGridTable()
int ShowModal() override
void SetDesignBlockLibId(const LIB_ID &aLibId)
Definition eda_group.h:101
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:58
void SetName(const wxString &aName)
Definition eda_group.h:76
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
virtual void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:349
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:270
EDA_ITEM_FLAGS GetEditFlags() const
Definition eda_item.h:170
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
const KIID m_Uuid
Definition eda_item.h:599
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:160
virtual bool Matches(const EDA_SEARCH_DATA &aSearchData, void *aAuxData) const
Compare the item against the search criteria in aSearchData.
Definition eda_item.h:484
virtual EDA_ITEM * Clone() const
Create a duplicate of this item with linked list members set to NULL.
Definition eda_item.cpp:278
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:153
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:329
bool IsItalic() const
Definition eda_text.h:200
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:512
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:378
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:239
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
virtual EDA_ANGLE GetTextAngle() const
Definition eda_text.h:178
void SetBold(bool aBold)
Set the text to be bold - this will also update the font if needed.
Definition eda_text.cpp:310
bool IsBold() const
Definition eda_text.h:215
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:242
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:268
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:290
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:370
PANEL_ANNOTATE m_AnnotatePanel
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
const VECTOR2D & GetGridSize() const
Return the grid size.
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
virtual void WarpMouseCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
If enabled (.
virtual void SetCrossHairCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true)=0
Move the graphic crosshair cursor to the requested position expressed in world coordinates.
virtual VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
virtual void SetCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true, bool aTriggeredByArrows=false, long aArrowCommand=0)=0
Move cursor to the requested position expressed in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
virtual void PinCursorInsideNonAutoscrollArea(bool aWarpMouseCursor)=0
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:209
Definition kiid.h:46
LEGACY_SYMBOL_LIB * GetCacheLibrary()
Object used to load, save, search, and otherwise manipulate symbol library files.
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
UTF8 Format() const
Definition lib_id.cpp:132
Define a library symbol object.
Definition lib_symbol.h:114
wxString GetDescription() const override
Get the Description field text value *‍/.
Definition lib_symbol.h:202
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:190
wxString GetKeyWords() const override
Definition lib_symbol.h:220
void SetGlobalPower()
bool IsPower() const override
void SetDescription(const wxString &aDescription)
Set the Description field text value.
void SetKeyWords(const wxString &aKeyWords)
bool IsLocalPower() const override
void SetLocalPower()
bool IsGlobalPower() const override
A singleton reporter that reports to nowhere.
Definition reporter.h:267
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:546
Class that handles the drawing of a polygon, including management of last corner deletion and drawing...
bool AddPoint(const VECTOR2I &aPt)
Lock in a polygon point.
void SetCursorPosition(const VECTOR2I &aPos)
Set the current cursor position.
bool NewPointClosesOutline(const VECTOR2I &aPt) const
std::optional< VECTOR2I > DeleteLastCorner()
Remove the last-added point from the polygon.
void SetFinished()
Mark the polygon finished and update the client.
void SetLeaderMode(LEADER_MODE aMode)
Set the leader mode to use when calculating the leader/returner lines.
void Reset()
Clear the manager state and start again.
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
static LEGACY_SYMBOL_LIBS * LegacySchLibs(PROJECT *aProject)
Returns the list of symbol libraries from a legacy (pre-5.x) design This is only used from the remapp...
An adjunct helper to the DRAWING_TOOL interactive tool, which handles incoming geometry changes from ...
These are loaded from Eeschema settings but then overwritten by the project settings.
std::shared_ptr< REFDES_TRACKER > m_refDesTracker
A list of previously used schematic reference designators.
Holds all the data relating to one schematic.
Definition schematic.h:149
SCHEMATIC_SETTINGS & Settings() const
bool Contains(const SCH_REFERENCE &aRef) const
Check if the schematic contains the specified reference.
static TOOL_ACTION rotateCCW
static TOOL_ACTION placeClassLabel
Definition sch_actions.h:85
static TOOL_ACTION placeSheetPin
Definition sch_actions.h:91
static TOOL_ACTION placeNextSymbolUnit
Definition sch_actions.h:73
static TOOL_ACTION editValue
static TOOL_ACTION setExcludeFromBOM
static TOOL_ACTION mirrorV
static TOOL_ACTION drawSheetFromFile
Definition sch_actions.h:89
static TOOL_ACTION placeGlobalLabel
Definition sch_actions.h:86
static TOOL_ACTION autoplaceFields
static TOOL_ACTION changeSymbol
static TOOL_ACTION syncAllSheetsPins
Definition sch_actions.h:97
static TOOL_ACTION closeOutline
static TOOL_ACTION drawSheet
Definition sch_actions.h:88
static TOOL_ACTION properties
static TOOL_ACTION editReference
static TOOL_ACTION leaveSheet
static TOOL_ACTION autoplaceAllSheetPins
Definition sch_actions.h:92
static TOOL_ACTION placeHierLabel
Definition sch_actions.h:87
static TOOL_ACTION placeLabel
Definition sch_actions.h:84
static TOOL_ACTION toText
static TOOL_ACTION placeBusWireEntry
Definition sch_actions.h:83
static TOOL_ACTION toHLabel
static TOOL_ACTION rotateCW
static TOOL_ACTION importSheet
Definition sch_actions.h:93
static TOOL_ACTION setExcludeFromSim
static TOOL_ACTION toLabel
static TOOL_ACTION placeJunction
Definition sch_actions.h:82
static TOOL_ACTION setDNP
static TOOL_ACTION drawRuleArea
static TOOL_ACTION placeSymbol
Definition sch_actions.h:72
static TOOL_ACTION placeImage
static TOOL_ACTION editWithLibEdit
static TOOL_ACTION toDLabel
static TOOL_ACTION setExcludeFromPosFiles
static TOOL_ACTION cycleBodyStyle
static TOOL_ACTION drawSheetFromDesignBlock
Definition sch_actions.h:90
static TOOL_ACTION mirrorH
static TOOL_ACTION placeDesignBlock
Definition sch_actions.h:75
static TOOL_ACTION drawTable
static TOOL_ACTION placeSchematicText
Definition sch_actions.h:98
static TOOL_ACTION toTextBox
static TOOL_ACTION changeSheet
static TOOL_ACTION enterSheet
static TOOL_ACTION editFootprint
static TOOL_ACTION repeatDrawItem
static TOOL_ACTION placeNoConnect
Definition sch_actions.h:81
static TOOL_ACTION toGLabel
static TOOL_ACTION setExcludeFromBoard
static TOOL_ACTION move
static TOOL_ACTION syncSheetPins
Definition sch_actions.h:95
static TOOL_ACTION placePower
Definition sch_actions.h:74
Object to handle a bitmap image that can be inserted in a schematic.
Definition sch_bitmap.h:36
Base class for a bus or wire entry.
VECTOR2I GetPosition() const override
void MirrorHorizontally(int aCenter) override
Mirror item horizontally about aCenter.
void MirrorVertically(int aCenter) override
Mirror item vertically about aCenter.
void Rotate(const VECTOR2I &aCenter, bool aRotateCCW) override
Rotate the item around aCenter 90 degrees in the clockwise direction.
Class for a wire to bus entry.
Commit for the schematic and symbol editors.
Definition sch_commit.h:54
void Revert() override
Revert the commit by restoring the modified items state.
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
int ImportSheet(const TOOL_EVENT &aEvent)
int doSyncSheetsPins(std::list< SCH_SHEET_PATH > aSheets, SCH_SHEET *aInitialSheet=nullptr)
SCH_TEXT * createNewText(const VECTOR2I &aPosition)
int PlaceNextSymbolUnit(const TOOL_EVENT &aEvent)
int DrawSheet(const TOOL_EVENT &aEvent)
bool createNewLabel(const VECTOR2I &aPosition, int aType, std::list< std::unique_ptr< SCH_LABEL_BASE > > &aLabelList)
SPIN_STYLE m_lastTextOrientation
int SyncSheetsPins(const TOOL_EVENT &aEvent)
std::vector< PICKED_SYMBOL > m_powerHistoryList
int SingleClickPlace(const TOOL_EVENT &aEvent)
SCH_LINE * findWire(const VECTOR2I &aPosition)
void sizeSheet(SCH_SHEET *aSheet, const VECTOR2I &aPos)
LABEL_FLAG_SHAPE m_lastGlobalLabelShape
LABEL_FLAG_SHAPE m_lastNetClassFlagShape
GR_TEXT_H_ALIGN_T m_lastTextHJustify
int DrawRuleArea(const TOOL_EVENT &aEvent)
std::unique_ptr< STATUS_TEXT_POPUP > m_statusPopup
int AutoPlaceAllSheetPins(const TOOL_EVENT &aEvent)
int TwoClickPlace(const TOOL_EVENT &aEvent)
SCH_SHEET_PIN * createNewSheetPin(SCH_SHEET *aSheet, const VECTOR2I &aPosition)
SCH_SHEET_PIN * createNewSheetPinFromLabel(SCH_SHEET *aSheet, const VECTOR2I &aPosition, SCH_HIERLABEL *aLabel)
int SyncAllSheetsPins(const TOOL_EVENT &aEvent)
wxString findWireLabelDriverName(SCH_LINE *aWire)
Gets the (global) label name driving this wire, if it is driven by a label.
int DrawTable(const TOOL_EVENT &aEvent)
GR_TEXT_V_ALIGN_T m_lastTextVJustify
LABEL_FLAG_SHAPE m_lastSheetPinType
MODE
The possible drawing modes of SCH_DRAWING_TOOLS.
std::unique_ptr< DIALOG_SYNC_SHEET_PINS > m_dialogSyncSheetPin
bool Init() override
Init() is called once upon a registration of the tool.
void setTransitions() override
Set up handlers for various events.
int PlaceSymbol(const TOOL_EVENT &aEvent)
int PlaceImage(const TOOL_EVENT &aEvent)
std::vector< PICKED_SYMBOL > m_symbolHistoryList
SCH_HIERLABEL * importHierLabel(SCH_SHEET *aSheet)
Try finding any hierlabel that does not have a sheet pin associated with it.
Schematic editor (Eeschema) main window.
void SetText(const wxString &aText) override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this label.
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:48
void SetSpinStyle(SPIN_STYLE aSpinStyle) override
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
virtual bool IsConnectable() const
Definition sch_item.h:545
virtual void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo)
Definition sch_item.h:676
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode)
Definition sch_item.h:678
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:302
virtual void CalcEdit(const VECTOR2I &aPosition)
Calculate the attributes of an item at aPosition when it is being edited.
Definition sch_item.h:485
virtual void SetUnit(int aUnit)
Definition sch_item.h:242
SCH_CONNECTION * Connection(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve the connection associated with this object in the given sheet.
Definition sch_item.cpp:580
bool IsType(const std::vector< KICAD_T > &aScanTypes) const override
Check whether the item is one of the listed types.
Definition sch_item.h:185
bool AutoRotateOnPlacement() const
autoRotateOnPlacement
SPIN_STYLE GetSpinStyle() const
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:179
LABEL_FLAG_SHAPE GetShape() const
Definition sch_label.h:178
void SetAutoRotateOnPlacement(bool autoRotate=true)
std::vector< SCH_FIELD > & GetFields()
Definition sch_label.h:210
virtual void SetSpinStyle(SPIN_STYLE aSpinStyle)
Tool responsible for drawing/placing items (symbols, wires, buses, labels, etc.)
int AddJunctionsIfNeeded(SCH_COMMIT *aCommit, SCH_SELECTION *aSelection)
Handle the addition of junctions to a selection of objects.
SCH_JUNCTION * AddJunction(SCH_COMMIT *aCommit, SCH_SCREEN *aScreen, const VECTOR2I &aPos)
int TrimOverLappingWires(SCH_COMMIT *aCommit, SCH_SELECTION *aSelection)
Logic to remove wires when overlapping correct items.
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:39
bool IsWire() const
Return true if the line is a wire.
VECTOR2I GetEndPoint() const
Definition sch_line.h:145
VECTOR2I GetStartPoint() const
Definition sch_line.h:136
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
void SortByReferenceOnly()
Sort the list of references by reference.
void ReannotateByOptions(ANNOTATE_ORDER_T aSortOption, ANNOTATE_ALGO_T aAlgoOption, int aStartNumber, const SCH_REFERENCE_LIST &aAdditionalRefs, bool aStartAtCurrent, SCH_SHEET_LIST *aHierarchy)
Forces reannotation of the provided references.
void SetRefDesTracker(std::shared_ptr< REFDES_TRACKER > aTracker)
void AddItem(const SCH_REFERENCE &aItem)
void UpdateAnnotation()
Update the symbol references for the schematic project (or the current sheet).
A helper to define a symbol's reference designator in a schematic.
bool AlwaysAnnotate() const
Verify the reference should always be automatically annotated.
void SetUnit(int aUnit)
wxString GetRef() const
int GetUnit() const
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
SCH_ITEM * GetItem(const VECTOR2I &aPosition, int aAccuracy=0, KICAD_T aType=SCH_LOCATE_ANY_T) const
Check aPosition within a distance of aAccuracy for items of type aFilter.
bool IsExplicitJunctionAllowed(const VECTOR2I &aPosition) const
Indicate that a junction dot may be placed at the given location.
int ClearSelection(const TOOL_EVENT &aEvent)
Clear current selection event handler.
void RebuildSelection()
Rebuild the selection from the EDA_ITEMs' selection flags.
SCH_SELECTION & GetSelection()
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:87
VECTOR2I GetPosition() const override
Definition sch_shape.h:86
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void SortByPageNumbers(bool aUpdateVirtualPageNums=true)
Sort the list of sheets by page number.
SCH_SHEET_LIST FindAllSheetsForScreen(const SCH_SCREEN *aScreen) const
Return a SCH_SHEET_LIST with a copy of all the SCH_SHEET_PATH using a particular screen.
wxString GetNextPageNumber() const
void GetSymbols(SCH_REFERENCE_LIST &aReferences, SYMBOL_FILTER aSymbolFilter, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
SCH_SCREEN * LastScreen()
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
const KIID_PATH & PathRef() const
Borrow the cached path until this sheet path is modified or destroyed.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void SetBorderColor(KIGFX::COLOR4D aColor)
Definition sch_sheet.h:154
void AddPin(SCH_SHEET_PIN *aSheetPin)
Add aSheetPin to the sheet.
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
bool Matches(const EDA_SEARCH_DATA &aSearchData, void *aAuxData) const override
Compare the item against the search criteria in aSearchData.
std::vector< SCH_FIELD > & GetFields()
Return a reference to the vector holding the sheet's fields.
Definition sch_sheet.h:91
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
void SetBackgroundColor(KIGFX::COLOR4D aColor)
Definition sch_sheet.h:157
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
VECTOR2I GetPosition() const override
Definition sch_sheet.h:506
bool HasPin(const wxString &aName) const
Check if the sheet already has a sheet pin named aName.
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
void SetBorderWidth(int aWidth)
Definition sch_sheet.h:151
void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo) override
void Resize(const VECTOR2I &aSize)
Resize this sheet to aSize and adjust all of the labels accordingly.
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:243
Schematic symbol object.
Definition sch_symbol.h:73
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:163
void SetUnitSelection(const SCH_SHEET_PATH *aSheet, int aUnitSelection)
Set the selected unit of this symbol on one sheet.
void updateItem(EDA_ITEM *aItem, bool aUpdateRTree) const
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
RAII class that sets an value at construction and resets it to the original value at destruction.
Definition seg.h:38
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:599
int AddItemsToSel(const TOOL_EVENT &aEvent)
int AddItemToSel(const TOOL_EVENT &aEvent)
void UnbrightenItem(EDA_ITEM *aItem)
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition selection.h:104
EDA_ITEM * Front() const
Definition selection.h:176
int Size() const
Returns the number of selected parts.
Definition selection.h:120
void SetReferencePoint(const VECTOR2I &aP)
Spin style for labels of all kinds on schematics.
Definition sch_label.h:38
An interface to the global shared library manager that is schematic-specific and linked to one projec...
Helper object to filter a list of libraries.
SCH_EDIT_FRAME * getModel() const
Definition tool_base.h:195
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 HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition tool_event.h:256
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:289
bool IsReactivate() const
Control whether the tool is first being pushed to the stack or being reactivated after a pause.
Definition tool_event.h:269
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:484
void RunMainStack(std::function< void()> aFunc)
void Go(int(SCH_EDIT_FRAME::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
std::unique_ptr< TOOL_MENU > m_menu
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
This file is part of the common library.
@ COMPONENT
Definition cursors.h:80
@ PLACE
Definition cursors.h:94
@ LABEL_GLOBAL
Definition cursors.h:78
@ MOVING
Definition cursors.h:44
@ LABEL_NET
Definition cursors.h:76
@ ARROW
Definition cursors.h:42
@ LABEL_HIER
Definition cursors.h:92
@ PENCIL
Definition cursors.h:48
const int minSize
Push and Shove router track width and via size dialog.
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
@ NO_RECURSE
Definition eda_item.h:52
#define IGNORE_PARENT_GROUP
Definition eda_item.h:55
#define IS_NEW
New item, just created.
#define STRUCT_DELETED
flag indication structures to be erased
#define ENDPOINT
ends. (Used to support dragging.)
#define IS_MOVING
Item being moved.
#define STARTPOINT
When a line is selected, these flags indicate which.
SCOPED_SET_RESET< EE_GRAPHIC_TOOL::MODE > SCOPED_DRAW_MODE
@ ID_POPUP_SCH_SELECT_UNIT
Definition eeschema_id.h:81
@ ID_POPUP_SCH_SELECT_BODY_STYLE
Definition eeschema_id.h:91
@ ID_POPUP_SCH_SELECT_BODY_STYLE_END
Definition eeschema_id.h:93
@ ID_POPUP_SCH_SELECT_UNIT_END
Definition eeschema_id.h:85
@ LINE_MODE_FREE
@ DEG45
45 Degree only
@ DIRECT
Unconstrained point-to-point.
GRID_HELPER_GRIDS
Definition grid_helper.h:55
@ GRID_TEXT
Definition grid_helper.h:62
@ GRID_GRAPHICS
Definition grid_helper.h:63
@ GRID_CONNECTABLE
Definition grid_helper.h:59
static const std::string KiCadSchematicFileExtension
static wxString ImageFileWildcard()
static wxString KiCadSchematicFileWildcard()
@ LAYER_HIERLABEL
Definition layer_ids.h:479
@ LAYER_GLOBLABEL
Definition layer_ids.h:478
@ LAYER_LOCLABEL
Definition layer_ids.h:477
@ LAYER_NETCLASS_REFS
Definition layer_ids.h:486
Definition for symbol library class.
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition wxgtk/ui.cpp:851
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:533
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
LIB_SYMBOL * SchGetLibSymbol(const LIB_ID &aLibId, SYMBOL_LIBRARY_ADAPTER *aLibMgr, LEGACY_SYMBOL_LIB *aCacheLib, wxWindow *aParent, bool aShowErrorMsg)
Load symbol from symbol library table.
static std::optional< VECTOR2I > designBlockAnchor(const std::vector< SCH_ITEM * > &aItems)
Pick the point a placed design block hangs from.
Class to handle a set of SCH_ITEMs.
@ AUTOPLACE_AUTO
Definition sch_item.h:75
std::vector< EDA_ITEM * > EDA_ITEMS
LABEL_FLAG_SHAPE
Label and flag shapes used with text objects.
Definition sch_label.h:97
@ F_ROUND
Definition sch_label.h:106
@ L_INPUT
Definition sch_label.h:98
ANNOTATE_ORDER_T
Schematic annotation order options.
@ ANNOTATE_SELECTION
Annotate the selection.
ANNOTATE_ALGO_T
Schematic annotation type options.
#define MIN_SHEET_HEIGHT
Definition sch_sheet.h:41
#define MIN_SHEET_WIDTH
Definition sch_sheet.h:40
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
@ SYMBOL_FILTER_ALL
wxString UniqueGroupName(SCH_SCREEN *aScreen, const wxString &aBaseName)
Return aBaseName, or aBaseName + smallest free integer if a group with that name already exists on aS...
bool IsUnannotatedUnitOccupied(const SCH_REFERENCE_LIST &aRefs, const wxString &aRef, const LIB_ID &aLibId, int aUnit)
Decide whether aUnit of an unannotated multi-unit symbol is already placed.
std::set< int > GetUnplacedUnitsForSymbol(const SCH_SYMBOL &aSym)
Get a list of unplaced (i.e.
void AutoPlaceSheetPins(SCH_SHEET *aSheet, const std::vector< SCH_HIERLABEL * > &aLabels)
Import and auto-place sheet pins for the given labels, growing the sheet as needed.
std::vector< SCH_ITEM * > FlattenGroups(const EDA_ITEMS &aItems)
Return the given items with the members of any group added, recursively.
std::vector< SCH_HIERLABEL * > GetUnplacedSheetPinLabels(const SCH_SHEET &aSheet)
Return one sheet hierarchical label per name that does not already have a sheet pin.
wxString UniqueSheetName(SCH_SCREEN *aScreen, const wxString &aBaseName)
Return aBaseName, or aBaseName + smallest free integer if a sheet with that name already exists on aS...
Increment event parameters.
Definition actions.h:328
bool PlaceAllUnits
Definition sch_screen.h:89
bool m_Reannotate
If a symbol is provide, reannotate it?
bool m_PlaceAllUnits
For a preselected multi-unit symbol, keep placing remaining units instead of exiting.
SCH_SYMBOL * m_Symbol
Provide a symbol to place.
int m_Unit
Unit number to place; 0 means next available unit.
SCH_SYMBOL * m_Symbol
Symbol used as reference for unit placement.
@ USER
The field ID hasn't been set yet; field is invalid.
@ INTERSHEET_REFS
Global label cross-reference page numbers.
std::string path
IbisParser parser & reporter
KIBIS_PIN * pin
int delta
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_CENTER
@ TA_CHOICE_MENU_CHOICE
Context menu choice.
Definition tool_event.h:94
@ MD_SHIFT
Definition tool_event.h:139
@ TC_COMMAND
Definition tool_event.h:53
@ BUT_LEFT
Definition tool_event.h:128
@ BUT_RIGHT
Definition tool_event.h:129
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ SCH_GROUP_T
Definition typeinfo.h:169
@ SCH_LINE_T
Definition typeinfo.h:159
@ SCH_NO_CONNECT_T
Definition typeinfo.h:156
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165
@ SCH_SHEET_PIN_T
Definition typeinfo.h:170
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:157
@ SCH_JUNCTION_T
Definition typeinfo.h:155
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
Definition of file extensions used in Kicad.