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