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