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