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#include <set>
28
29#include <kiplatform/ui.h>
30#include <optional>
31#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>
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 || evt->IsAction( &ACTIONS::paste ) )
554 {
555 if( symbol )
556 {
557 wxBell();
558 continue;
559 }
560
561 // Exit. The duplicate/repeat/paste will run in its own loop.
562 m_frame->PopTool( aEvent );
563 evt->SetPassEvent();
564 break;
565 }
566 else if( symbol && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
567 {
568 symbol->SetPosition( cursorPos );
569 m_view->ClearPreview();
570 m_view->AddToPreview( symbol, false ); // Add, but not give ownership
571 m_frame->SetMsgPanel( symbol );
572 }
573 else if( symbol && evt->IsAction( &ACTIONS::doDelete ) )
574 {
575 cleanup();
576 }
577 else if( symbol && ( evt->IsAction( &ACTIONS::redo )
578 || evt->IsAction( &SCH_ACTIONS::editWithLibEdit )
579 || evt->IsAction( &SCH_ACTIONS::changeSymbol ) ) )
580 {
581 wxBell();
582 }
583 else if( symbol && ( evt->IsAction( &SCH_ACTIONS::properties )
584 || evt->IsAction( &SCH_ACTIONS::editReference )
585 || evt->IsAction( &SCH_ACTIONS::editValue )
586 || evt->IsAction( &SCH_ACTIONS::editFootprint )
587 || evt->IsAction( &SCH_ACTIONS::autoplaceFields )
588 || evt->IsAction( &SCH_ACTIONS::cycleBodyStyle )
589 || evt->IsAction( &SCH_ACTIONS::setExcludeFromBOM )
590 || evt->IsAction( &SCH_ACTIONS::setExcludeFromBoard )
591 || evt->IsAction( &SCH_ACTIONS::setExcludeFromSimulation )
592 || evt->IsAction( &SCH_ACTIONS::setDNP )
593 || evt->IsAction( &SCH_ACTIONS::rotateCW )
594 || evt->IsAction( &SCH_ACTIONS::rotateCCW )
595 || evt->IsAction( &SCH_ACTIONS::mirrorV )
596 || evt->IsAction( &SCH_ACTIONS::mirrorH ) ) )
597 {
598 m_toolMgr->PostAction( ACTIONS::refreshPreview );
599 evt->SetPassEvent();
600 }
601 else
602 {
603 evt->SetPassEvent();
604 }
605
606 // Enable autopanning and cursor capture only when there is a symbol to be placed
607 getViewControls()->SetAutoPan( symbol != nullptr );
608 getViewControls()->CaptureCursor( symbol != nullptr );
609 }
610
611 getViewControls()->SetAutoPan( false );
612 getViewControls()->CaptureCursor( false );
613 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
614
615 return 0;
616}
617
618
620{
623 SCH_SYMBOL* symbol = params.m_Symbol;
624 int requestedUnit = params.m_Unit;
625
626 // TODO: get from selection
627 if( !symbol )
628 {
629 static const std::vector<KICAD_T> symbolTypes = { SCH_SYMBOL_T };
630 SCH_SELECTION& selection = m_selectionTool->RequestSelection( symbolTypes );
631
632 if( selection.Size() != 1 )
633 {
634 m_frame->ShowInfoBarMsg( _( "Select a single symbol to place the next unit." ) );
635 return 0;
636 }
637
638 wxCHECK( selection.Front()->Type() == SCH_SYMBOL_T, 0 );
639 symbol = static_cast<SCH_SYMBOL*>( selection.Front() );
640 }
641
642 if( !symbol )
643 return 0;
644
645 if( !symbol->IsMultiUnit() )
646 {
647 m_frame->ShowInfoBarMsg( _( "This symbol has only one unit." ) );
648 return 0;
649 }
650
651 const std::set<int> missingUnits = GetUnplacedUnitsForSymbol( *symbol );
652
653 if( missingUnits.empty() )
654 {
655 m_frame->ShowInfoBarMsg( _( "All units of this symbol are already placed." ) );
656 return 0;
657 }
658
659 int nextMissing;
660
661 if( requestedUnit > 0 )
662 {
663 if( missingUnits.count( requestedUnit ) == 0 )
664 {
665 m_frame->ShowInfoBarMsg( _( "Requested unit already placed." ) );
666 return 0;
667 }
668
669 nextMissing = requestedUnit;
670 }
671 else
672 {
673 // Find the lowest unit number that is missing
674 nextMissing = *std::min_element( missingUnits.begin(), missingUnits.end() );
675 }
676
677 std::unique_ptr<SCH_SYMBOL> newSymbol = std::make_unique<SCH_SYMBOL>( *symbol );
678 const SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
679
680 newSymbol->SetUnitSelection( &sheetPath, nextMissing );
681 newSymbol->SetUnit( nextMissing );
682 newSymbol->SetRefProp( symbol->GetRef( &sheetPath, false ) );
683
684 // Post the new symbol - don't reannotate it - we set the reference ourselves
686 SCH_ACTIONS::PLACE_SYMBOL_PARAMS{ newSymbol.release(), false } );
687 return 0;
688}
689
690
692{
693 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
694 EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
695 SCHEMATIC_SETTINGS& schSettings = m_frame->Schematic().Settings();
696 SCH_SCREEN* screen = m_frame->GetScreen();
697 SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
698
701 VECTOR2I cursorPos;
702
703 if( !cfg || !common_settings )
704 return 0;
705
706 if( m_inDrawingTool )
707 return 0;
708
709 bool placingDesignBlock = aEvent.IsAction( &SCH_ACTIONS::placeDesignBlock );
710
711 std::unique_ptr<DESIGN_BLOCK> designBlock;
712 wxString sheetFileName = wxEmptyString;
713 int suffix = 1;
714
715 if( placingDesignBlock )
716 {
717 SCH_DESIGN_BLOCK_PANE* designBlockPane = m_frame->GetDesignBlockPane();
718
719 if( designBlockPane->GetSelectedLibId().IsValid() )
720 {
721 designBlock.reset( designBlockPane->GetDesignBlock( designBlockPane->GetSelectedLibId(),
722 true, true ) );
723
724 if( !designBlock )
725 return 0;
726
727 sheetFileName = designBlock->GetSchematicFile();
728 }
729 }
730 else
731 {
732 wxString* importSourceFile = aEvent.Parameter<wxString*>();
733
734 if( importSourceFile != nullptr )
735 sheetFileName = *importSourceFile;
736 }
737
738 auto setCursor =
739 [&]()
740 {
741 m_frame->GetCanvas()->SetCurrentCursor( designBlock ? KICURSOR::MOVING
743 };
744
745 auto placeSheetContents =
746 [&]()
747 {
748 SCH_COMMIT commit( m_toolMgr );
749 SCH_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
750
751 EDA_ITEMS newItems;
752 bool keepAnnotations = cfg->m_DesignBlockChooserPanel.keep_annotations;
753 bool placeAsGroup = cfg->m_DesignBlockChooserPanel.place_as_group;
754 bool repeatPlacement = cfg->m_DesignBlockChooserPanel.repeated_placement;
755
756 selectionTool->ClearSelection();
757
758 // Mark all existing items on the screen so we don't select them after appending
759 for( EDA_ITEM* item : screen->Items() )
760 item->SetFlags( SKIP_STRUCT );
761
762 if( !m_frame->LoadSheetFromFile( sheetPath.Last(), &sheetPath, sheetFileName, true,
763 placingDesignBlock ) )
764 {
765 return false;
766 }
767
768 m_frame->SetSheetNumberAndCount();
769
770 m_frame->SyncView();
771 m_frame->OnModify();
772 m_frame->HardRedraw(); // Full reinit of the current screen and the display.
773
774 SCH_GROUP* group = nullptr;
775
776 if( placeAsGroup )
777 {
778 group = new SCH_GROUP( screen );
779
780 if( designBlock )
781 {
782 group->SetName( designBlock->GetLibId().GetLibItemName() );
783 group->SetDesignBlockLibId( designBlock->GetLibId() );
784 }
785 else
786 {
787 group->SetName( wxFileName( sheetFileName ).GetName() );
788 }
789
790 if( repeatPlacement )
791 group->SetName( group->GetName() + wxString::Format( "%d", suffix++ ) );
792 }
793
794 // Select all new items
795 for( EDA_ITEM* item : screen->Items() )
796 {
797 if( !item->HasFlag( SKIP_STRUCT ) )
798 {
799 if( item->Type() == SCH_SYMBOL_T && !keepAnnotations )
800 static_cast<SCH_SYMBOL*>( item )->ClearAnnotation( &sheetPath, false );
801
802 if( item->Type() == SCH_LINE_T )
803 item->SetFlags( STARTPOINT | ENDPOINT );
804
805 if( placeAsGroup )
806 group->AddItem( item );
807
808 commit.Added( item, screen );
809 newItems.emplace_back( item );
810 }
811 else
812 {
813 item->ClearFlags( SKIP_STRUCT );
814 }
815 }
816
817 if( placeAsGroup )
818 {
819 commit.Add( group, screen );
820 selectionTool->AddItemToSel( group );
821 }
822 else
823 {
824 selectionTool->AddItemsToSel( &newItems, true );
825 }
826
827 cursorPos = grid.Align( controls->GetMousePosition(),
828 grid.GetSelectionGrid( selectionTool->GetSelection() ) );
829 controls->ForceCursorPosition( true, cursorPos );
830
831 // Move everything to our current mouse position now
832 // that we have a selection to get a reference point
833 VECTOR2I anchorPos = selectionTool->GetSelection().GetReferencePoint();
834 VECTOR2I delta = cursorPos - anchorPos;
835
836 // Will all be SCH_ITEMs as these were pulled from the screen->Items()
837 for( EDA_ITEM* item : newItems )
838 static_cast<SCH_ITEM*>( item )->Move( delta );
839
840 if( !keepAnnotations )
841 {
842 if( cfg->m_AnnotatePanel.automatic )
843 {
844 NULL_REPORTER reporter;
845 m_frame->AnnotateSymbols( &commit, ANNOTATE_SELECTION,
847 (ANNOTATE_ALGO_T) schSettings.m_AnnotateMethod, true /* recursive */,
848 schSettings.m_AnnotateStartNum, false, false, reporter );
849 }
850
851 // Annotation will clear selection, so we need to restore it
852 for( EDA_ITEM* item : newItems )
853 {
854 if( item->Type() == SCH_LINE_T )
855 item->SetFlags( STARTPOINT | ENDPOINT );
856 }
857
858 selectionTool->AddItemsToSel( &newItems, true );
859 }
860
861 // Start moving selection, cancel undoes the insertion
862 bool placed = m_toolMgr->RunSynchronousAction( SCH_ACTIONS::move, &commit );
863
864 // Update our cursor position to the new location in case we're placing repeated copies
865 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_CONNECTABLE );
866
867 if( placed )
868 {
869 commit.Push( placingDesignBlock ? _( "Add Design Block" )
870 : _( "Import Schematic Sheet Content" ) );
871 }
872 else
873 {
874 commit.Revert();
875 }
876
877 m_frame->UpdateHierarchyNavigator();
878
879 return placed;
880 };
881
882 // Whether we are placing the sheet as a sheet, or as its contents, we need to get a filename
883 // if we weren't provided one
884 if( sheetFileName.IsEmpty() )
885 {
886 wxString path;
887 wxString file;
888
889 if (!placingDesignBlock)
890 {
891 if( sheetFileName.IsEmpty() )
892 {
893 path = wxPathOnly( m_frame->Prj().GetProjectFullName() );
894 file = wxEmptyString;
895 }
896 else
897 {
898 path = wxPathOnly( sheetFileName );
899 file = wxFileName( sheetFileName ).GetFullName();
900 }
901
902 // Open file chooser dialog even if we have been provided a file so the user
903 // can select the options they want
904 wxFileDialog dlg( m_frame, _( "Choose Schematic" ), path, file,
905 FILEEXT::KiCadSchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
906
907 FILEDLG_IMPORT_SHEET_CONTENTS dlgHook( cfg );
908 dlg.SetCustomizeHook( dlgHook );
909
910 if( dlg.ShowModal() == wxID_CANCEL )
911 return 0;
912
913 sheetFileName = dlg.GetPath();
914
915 m_frame->GetDesignBlockPane()->UpdateCheckboxes();
916 }
917
918 if( sheetFileName.IsEmpty() )
919 return 0;
920 }
921
922 // If we're placing sheet contents, we don't even want to run our tool loop, just add the items
923 // to the canvas and run the move tool
925 {
926 while( placeSheetContents() && cfg->m_DesignBlockChooserPanel.repeated_placement )
927 {}
928
930 m_view->ClearPreview();
931 return 0;
932 }
933
934 // We're placing a sheet as a sheet, we need to run a small tool loop to get the starting
935 // coordinate of the sheet drawing
936 m_frame->PushTool( aEvent );
937
938 Activate();
939
940 // Must be done after Activate() so that it gets set into the correct context
941 getViewControls()->ShowCursor( true );
942
943 // Set initial cursor
944 setCursor();
945
946 if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
947 m_toolMgr->PrimeTool( { 0, 0 } );
948
949 // Main loop: keep receiving events
950 while( TOOL_EVENT* evt = Wait() )
951 {
952 setCursor();
953 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
954 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
955
956 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_CONNECTABLE );
957 controls->ForceCursorPosition( true, cursorPos );
958
959 // The tool hotkey is interpreted as a click when drawing
960 bool isSyntheticClick = designBlock && evt->IsActivate() && evt->HasPosition() && evt->Matches( aEvent );
961
962 if( evt->IsCancelInteractive() || ( designBlock && evt->IsAction( &ACTIONS::undo ) ) )
963 {
964 m_frame->GetInfoBar()->Dismiss();
965 break;
966 }
967 else if( evt->IsActivate() && !isSyntheticClick )
968 {
969 m_frame->GetInfoBar()->Dismiss();
970 break;
971 }
972 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
973 || isSyntheticClick
974 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
975 {
976 if( placingDesignBlock )
977 {
978 // drawSheet must delete designBlock
979 m_toolMgr->PostAction( SCH_ACTIONS::drawSheetFromDesignBlock, designBlock.release() );
980 }
981 else
982 {
983 // drawSheet must delete sheetFileName
984 m_toolMgr->PostAction( SCH_ACTIONS::drawSheetFromFile, new wxString( sheetFileName ) );
985 }
986
987 break;
988 }
989 else if( evt->IsClick( BUT_RIGHT ) )
990 {
991 // Warp after context menu only if dragging...
992 if( !designBlock )
993 m_toolMgr->VetoContextMenuMouseWarp();
994
995 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
996 }
997 else if( evt->IsAction( &ACTIONS::duplicate )
998 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem ) )
999 {
1000 wxBell();
1001 }
1002 else
1003 {
1004 evt->SetPassEvent();
1005 }
1006 }
1007
1008 m_frame->PopTool( aEvent );
1009 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1010
1011 return 0;
1012}
1013
1014
1016{
1017 SCH_BITMAP* image = aEvent.Parameter<SCH_BITMAP*>();
1018 bool immediateMode = image != nullptr;
1019 bool ignorePrimePosition = false;
1020 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
1021
1022 if( m_inDrawingTool )
1023 return 0;
1024
1026
1029 VECTOR2I cursorPos;
1030
1031 m_toolMgr->RunAction( ACTIONS::selectionClear );
1032
1033 // Add all the drawable symbols to preview
1034 if( image )
1035 {
1036 image->SetPosition( getViewControls()->GetCursorPosition() );
1037 m_view->ClearPreview();
1038 m_view->AddToPreview( image, false ); // Add, but not give ownership
1039 }
1040
1041 m_frame->PushTool( aEvent );
1042
1043 auto setCursor =
1044 [&]()
1045 {
1046 if( image )
1047 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1048 else
1049 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1050 };
1051
1052 auto cleanup =
1053 [&] ()
1054 {
1055 m_toolMgr->RunAction( ACTIONS::selectionClear );
1056 m_view->ClearPreview();
1057 m_view->RecacheAllItems();
1058 delete image;
1059 image = nullptr;
1060 };
1061
1062 Activate();
1063
1064 // Must be done after Activate() so that it gets set into the correct context
1065 getViewControls()->ShowCursor( true );
1066
1067 // Set initial cursor
1068 setCursor();
1069
1070 // Prime the pump
1071 if( image )
1072 {
1073 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1074 }
1075 else if( aEvent.HasPosition() )
1076 {
1077 m_toolMgr->PrimeTool( aEvent.Position() );
1078 }
1079 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
1080 {
1081 m_toolMgr->PrimeTool( { 0, 0 } );
1082 ignorePrimePosition = true;
1083 }
1084
1085 // Main loop: keep receiving events
1086 while( TOOL_EVENT* evt = Wait() )
1087 {
1088 setCursor();
1089 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1090 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1091
1092 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_GRAPHICS );
1093 controls->ForceCursorPosition( true, cursorPos );
1094
1095 // The tool hotkey is interpreted as a click when drawing
1096 bool isSyntheticClick = image && evt->IsActivate() && evt->HasPosition() && evt->Matches( aEvent );
1097
1098 if( evt->IsCancelInteractive() || ( image && evt->IsAction( &ACTIONS::undo ) ) )
1099 {
1100 m_frame->GetInfoBar()->Dismiss();
1101
1102 if( image )
1103 {
1104 cleanup();
1105 }
1106 else
1107 {
1108 m_frame->PopTool( aEvent );
1109 break;
1110 }
1111
1112 if( immediateMode )
1113 {
1114 m_frame->PopTool( aEvent );
1115 break;
1116 }
1117 }
1118 else if( evt->IsActivate() && !isSyntheticClick )
1119 {
1120 if( image && evt->IsMoveTool() )
1121 {
1122 // we're already moving our own item; ignore the move tool
1123 evt->SetPassEvent( false );
1124 continue;
1125 }
1126
1127 if( image )
1128 {
1129 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel image creation." ) );
1130 evt->SetPassEvent( false );
1131 continue;
1132 }
1133
1134 if( evt->IsMoveTool() )
1135 {
1136 // leave ourselves on the stack so we come back after the move
1137 break;
1138 }
1139 else
1140 {
1141 m_frame->PopTool( aEvent );
1142 break;
1143 }
1144 }
1145 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
1146 || isSyntheticClick
1147 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
1148 {
1149 if( !image )
1150 {
1151 m_toolMgr->RunAction( ACTIONS::selectionClear );
1152
1153 wxFileDialog dlg( m_frame, _( "Choose Image" ), m_mruPath, wxEmptyString,
1154 _( "Image Files" ) + wxS( " " ) + wxImage::GetImageExtWildcard(), wxFD_OPEN );
1155
1156 bool cancelled = false;
1157
1159 [&]()
1160 {
1161 cancelled = dlg.ShowModal() != wxID_OK;
1162 } );
1163
1164 if( cancelled )
1165 continue;
1166
1167 // If we started with a hotkey which has a position then warp back to that.
1168 // Otherwise update to the current mouse position pinned inside the autoscroll
1169 // boundaries.
1170 if( evt->IsPrime() && !ignorePrimePosition )
1171 {
1172 cursorPos = grid.Align( evt->Position() );
1173 getViewControls()->WarpMouseCursor( cursorPos, true );
1174 }
1175 else
1176 {
1178 cursorPos = getViewControls()->GetMousePosition();
1179 }
1180
1181 wxString fullFilename = dlg.GetPath();
1182 m_mruPath = wxPathOnly( fullFilename );
1183
1184 if( wxFileExists( fullFilename ) )
1185 image = new SCH_BITMAP( cursorPos );
1186
1187 if( !image || !image->GetReferenceImage().ReadImageFile( fullFilename ) )
1188 {
1189 wxMessageBox( wxString::Format( _( "Could not load image from '%s'." ), fullFilename ) );
1190 delete image;
1191 image = nullptr;
1192 continue;
1193 }
1194
1195 image->SetFlags( IS_NEW | IS_MOVING );
1196
1197 m_frame->SaveCopyForRepeatItem( image );
1198
1199 m_view->ClearPreview();
1200 m_view->AddToPreview( image, false ); // Add, but not give ownership
1201 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
1202
1203 m_selectionTool->AddItemToSel( image );
1204
1205 getViewControls()->SetCursorPosition( cursorPos, false );
1206 setCursor();
1207 }
1208 else
1209 {
1210 SCH_COMMIT commit( m_toolMgr );
1211 commit.Add( image, m_frame->GetScreen() );
1212 commit.Push( _( "Place Image" ) );
1213
1214 image = nullptr;
1216
1217 m_view->ClearPreview();
1218
1219 if( immediateMode )
1220 {
1221 m_frame->PopTool( aEvent );
1222 break;
1223 }
1224 }
1225 }
1226 else if( evt->IsClick( BUT_RIGHT ) )
1227 {
1228 // Warp after context menu only if dragging...
1229 if( !image )
1230 m_toolMgr->VetoContextMenuMouseWarp();
1231
1232 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
1233 }
1234 else if( evt->IsAction( &ACTIONS::duplicate )
1235 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem )
1236 || evt->IsAction( &ACTIONS::paste ) )
1237 {
1238 if( image )
1239 {
1240 // This doesn't really make sense; we'll just end up dragging a stack of
1241 // objects so we ignore the duplicate and just carry on.
1242 wxBell();
1243 continue;
1244 }
1245
1246 // Exit. The duplicate/repeat/paste will run in its own loop.
1247 m_frame->PopTool( aEvent );
1248 evt->SetPassEvent();
1249 break;
1250 }
1251 else if( image && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
1252 {
1253 image->SetPosition( cursorPos );
1254 m_view->ClearPreview();
1255 m_view->AddToPreview( image, false ); // Add, but not give ownership
1256 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
1257 m_frame->SetMsgPanel( image );
1258 }
1259 else if( image && evt->IsAction( &ACTIONS::doDelete ) )
1260 {
1261 cleanup();
1262 }
1263 else if( image && evt->IsAction( &ACTIONS::redo ) )
1264 {
1265 wxBell();
1266 }
1267 else
1268 {
1269 evt->SetPassEvent();
1270 }
1271
1272 // Enable autopanning and cursor capture only when there is an image to be placed
1273 getViewControls()->SetAutoPan( image != nullptr );
1274 getViewControls()->CaptureCursor( image != nullptr );
1275 }
1276
1277 getViewControls()->SetAutoPan( false );
1278 getViewControls()->CaptureCursor( false );
1279 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1280
1281 return 0;
1282}
1283
1284
1286{
1287 if( m_inDrawingTool )
1288 return 0;
1289
1291
1292 // Note: PlaceImportedGraphics() will convert PCB_SHAPE_T and PCB_TEXT_T to footprint
1293 // items if needed
1295
1296 // Set filename on drag-and-drop
1297 if( aEvent.HasParameter() )
1298 dlg.SetFilenameOverride( *aEvent.Parameter<wxString*>() );
1299
1300 int dlgResult = dlg.ShowModal();
1301
1302 std::list<std::unique_ptr<EDA_ITEM>>& list = dlg.GetImportedItems();
1303
1304 if( dlgResult != wxID_OK )
1305 return 0;
1306
1307 // Ensure the list is not empty:
1308 if( list.empty() )
1309 {
1310 wxMessageBox( _( "No graphic items found in file." ) );
1311 return 0;
1312 }
1313
1315
1317 std::vector<SCH_ITEM*> newItems; // all new items, including group
1318 std::vector<SCH_ITEM*> selectedItems; // the group, or newItems if no group
1319 SCH_SELECTION preview;
1320 SCH_COMMIT commit( m_toolMgr );
1321
1322 for( std::unique_ptr<EDA_ITEM>& ptr : list )
1323 {
1324 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( ptr.get() );
1325 wxCHECK2_MSG( item, continue, wxString::Format( "Bad item type: ", ptr->Type() ) );
1326
1327 newItems.push_back( item );
1328 selectedItems.push_back( item );
1329 preview.Add( item );
1330
1331 ptr.release();
1332 }
1333
1334 if( !dlg.IsPlacementInteractive() )
1335 {
1336 // Place the imported drawings
1337 for( SCH_ITEM* item : newItems )
1338 commit.Add(item, m_frame->GetScreen());
1339
1340 commit.Push( _( "Import Graphic" ) );
1341 return 0;
1342 }
1343
1344 m_view->Add( &preview );
1345
1346 // Clear the current selection then select the drawings so that edit tools work on them
1347 m_toolMgr->RunAction( ACTIONS::selectionClear );
1348
1349 EDA_ITEMS selItems( selectedItems.begin(), selectedItems.end() );
1350 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &selItems );
1351
1352 m_frame->PushTool( aEvent );
1353
1354 auto setCursor =
1355 [&]()
1356 {
1357 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1358 };
1359
1360 Activate();
1361 // Must be done after Activate() so that it gets set into the correct context
1362 controls->ShowCursor( true );
1363 controls->ForceCursorPosition( false );
1364 // Set initial cursor
1365 setCursor();
1366
1367 //SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
1369
1370 // Now move the new items to the current cursor position:
1371 VECTOR2I cursorPos = controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
1372 VECTOR2I delta = cursorPos;
1373 VECTOR2I currentOffset;
1374
1375 for( SCH_ITEM* item : selectedItems )
1376 item->Move( delta );
1377
1378 currentOffset += delta;
1379
1380 m_view->Update( &preview );
1381
1382 // Main loop: keep receiving events
1383 while( TOOL_EVENT* evt = Wait() )
1384 {
1385 setCursor();
1386
1387 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1388 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1389
1390 cursorPos = grid.Align( controls->GetMousePosition(), GRID_GRAPHICS );
1391 controls->ForceCursorPosition( true, cursorPos );
1392
1393 if( evt->IsCancelInteractive() || evt->IsActivate() )
1394 {
1395 m_toolMgr->RunAction( ACTIONS::selectionClear );
1396
1397 for( SCH_ITEM* item : newItems )
1398 delete item;
1399
1400 break;
1401 }
1402 else if( evt->IsMotion() )
1403 {
1404 delta = cursorPos - currentOffset;
1405
1406 for( SCH_ITEM* item : selectedItems )
1407 item->Move( delta );
1408
1409 currentOffset += delta;
1410
1411 m_view->Update( &preview );
1412 }
1413 else if( evt->IsClick( BUT_RIGHT ) )
1414 {
1415 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
1416 }
1417 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
1418 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
1419 {
1420 // Place the imported drawings
1421 for( SCH_ITEM* item : newItems )
1422 commit.Add( item, m_frame->GetScreen() );
1423
1424 commit.Push( _( "Import Graphic" ) );
1425 break; // This is a one-shot command, not a tool
1426 }
1427 else
1428 {
1429 evt->SetPassEvent();
1430 }
1431 }
1432
1433 preview.Clear();
1434 m_view->Remove( &preview );
1435
1436 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1437 controls->ForceCursorPosition( false );
1438
1439 m_frame->PopTool( aEvent );
1440
1441 return 0;
1442}
1443
1444
1446{
1447 VECTOR2I cursorPos;
1448 KICAD_T type = aEvent.Parameter<KICAD_T>();
1451 SCH_ITEM* previewItem;
1452 bool loggedInfoBarError = false;
1453 wxString description;
1454 SCH_SCREEN* screen = m_frame->GetScreen();
1455 bool allowRepeat = false; // Set to true to allow new item repetition
1456
1457 if( m_inDrawingTool )
1458 return 0;
1459
1461
1462 if( type == SCH_JUNCTION_T && aEvent.HasPosition() )
1463 {
1464 SCH_SELECTION& selection = m_selectionTool->GetSelection();
1465 SCH_LINE* wire = dynamic_cast<SCH_LINE*>( selection.Front() );
1466
1467 if( wire )
1468 {
1469 SEG seg( wire->GetStartPoint(), wire->GetEndPoint() );
1470 VECTOR2I nearest = seg.NearestPoint( getViewControls()->GetCursorPosition() );
1471 getViewControls()->SetCrossHairCursorPosition( nearest, false );
1472 getViewControls()->WarpMouseCursor( getViewControls()->GetCursorPosition(), true );
1473 }
1474 }
1475
1476 switch( type )
1477 {
1478 case SCH_NO_CONNECT_T:
1479 previewItem = new SCH_NO_CONNECT( cursorPos );
1480 previewItem->SetParent( screen );
1481 description = _( "Add No Connect Flag" );
1482 allowRepeat = true;
1483 break;
1484
1485 case SCH_JUNCTION_T:
1486 previewItem = new SCH_JUNCTION( cursorPos );
1487 previewItem->SetParent( screen );
1488 description = _( "Add Junction" );
1489 break;
1490
1492 previewItem = new SCH_BUS_WIRE_ENTRY( cursorPos );
1493 previewItem->SetParent( screen );
1494 description = _( "Add Wire to Bus Entry" );
1495 allowRepeat = true;
1496 break;
1497
1498 default:
1499 wxASSERT_MSG( false, "Unknown item type in SCH_DRAWING_TOOLS::SingleClickPlace" );
1500 return 0;
1501 }
1502
1503 m_toolMgr->RunAction( ACTIONS::selectionClear );
1504
1505 cursorPos = aEvent.HasPosition() ? aEvent.Position() : controls->GetMousePosition();
1506
1507 m_frame->PushTool( aEvent );
1508
1509 auto setCursor =
1510 [&]()
1511 {
1512 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
1513 };
1514
1515 Activate();
1516
1517 // Must be done after Activate() so that it gets set into the correct context
1518 getViewControls()->ShowCursor( true );
1519
1520 // Set initial cursor
1521 setCursor();
1522
1523 m_view->ClearPreview();
1524 m_view->AddToPreview( previewItem->Clone() );
1525
1526 // Prime the pump
1527 if( aEvent.HasPosition() && type != SCH_SHEET_PIN_T )
1528 m_toolMgr->PrimeTool( aEvent.Position() );
1529 else
1530 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1531
1532 // Main loop: keep receiving events
1533 while( TOOL_EVENT* evt = Wait() )
1534 {
1535 setCursor();
1536 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1537 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1538
1539 cursorPos = evt->IsPrime() ? evt->Position() : controls->GetMousePosition();
1540 cursorPos = grid.BestSnapAnchor( cursorPos, grid.GetItemGrid( previewItem ), nullptr );
1541 controls->ForceCursorPosition( true, cursorPos );
1542
1543 if( evt->IsCancelInteractive() )
1544 {
1545 m_frame->PopTool( aEvent );
1546 break;
1547 }
1548 else if( evt->IsActivate() )
1549 {
1550 if( evt->IsMoveTool() )
1551 {
1552 // leave ourselves on the stack so we come back after the move
1553 break;
1554 }
1555 else
1556 {
1557 m_frame->PopTool( aEvent );
1558 break;
1559 }
1560 }
1561 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
1562 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
1563 {
1564 if( !screen->GetItem( cursorPos, 0, type ) )
1565 {
1566 if( type == SCH_JUNCTION_T )
1567 {
1568 if( !screen->IsExplicitJunctionAllowed( cursorPos ) )
1569 {
1570 m_frame->ShowInfoBarError( _( "Junction location contains no joinable wires and/or pins." ) );
1571 loggedInfoBarError = true;
1572 continue;
1573 }
1574 else if( loggedInfoBarError )
1575 {
1576 m_frame->GetInfoBar()->Dismiss();
1577 }
1578 }
1579
1580 if( type == SCH_JUNCTION_T )
1581 {
1582 SCH_COMMIT commit( m_toolMgr );
1583 SCH_LINE_WIRE_BUS_TOOL* lwbTool =
1585 lwbTool->AddJunction( &commit, screen, cursorPos );
1586
1587 m_frame->Schematic().CleanUp( &commit );
1588
1589 commit.Push( description );
1590 }
1591 else
1592 {
1593 SCH_ITEM* newItem = static_cast<SCH_ITEM*>( previewItem->Clone() );
1594 const_cast<KIID&>( newItem->m_Uuid ) = KIID();
1595 newItem->SetPosition( cursorPos );
1596 newItem->SetFlags( IS_NEW );
1597 m_frame->AddToScreen( newItem, screen );
1598
1599 if( allowRepeat )
1600 m_frame->SaveCopyForRepeatItem( newItem );
1601
1602 SCH_COMMIT commit( m_toolMgr );
1603 commit.Added( newItem, screen );
1604
1605 m_frame->Schematic().CleanUp( &commit );
1606
1607 commit.Push( description );
1608 }
1609 }
1610
1611 if( evt->IsDblClick( BUT_LEFT ) || type == SCH_SHEET_PIN_T ) // Finish tool.
1612 {
1613 m_frame->PopTool( aEvent );
1614 break;
1615 }
1616 }
1617 else if( evt->IsClick( BUT_RIGHT ) )
1618 {
1619 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
1620 }
1621 else if( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() )
1622 {
1623 previewItem->SetPosition( cursorPos );
1624 m_view->ClearPreview();
1625 m_view->AddToPreview( previewItem->Clone() );
1626 m_frame->SetMsgPanel( previewItem );
1627 }
1628 else if( evt->Category() == TC_COMMAND )
1629 {
1630 if( ( type == SCH_BUS_WIRE_ENTRY_T ) && ( evt->IsAction( &SCH_ACTIONS::rotateCW )
1631 || evt->IsAction( &SCH_ACTIONS::rotateCCW )
1632 || evt->IsAction( &SCH_ACTIONS::mirrorV )
1633 || evt->IsAction( &SCH_ACTIONS::mirrorH ) ) )
1634 {
1635 SCH_BUS_ENTRY_BASE* busItem = static_cast<SCH_BUS_ENTRY_BASE*>( previewItem );
1636
1637 if( evt->IsAction( &SCH_ACTIONS::rotateCW ) )
1638 {
1639 busItem->Rotate( busItem->GetPosition(), false );
1640 }
1641 else if( evt->IsAction( &SCH_ACTIONS::rotateCCW ) )
1642 {
1643 busItem->Rotate( busItem->GetPosition(), true );
1644 }
1645 else if( evt->IsAction( &SCH_ACTIONS::mirrorV ) )
1646 {
1647 busItem->MirrorVertically( busItem->GetPosition().y );
1648 }
1649 else if( evt->IsAction( &SCH_ACTIONS::mirrorH ) )
1650 {
1651 busItem->MirrorHorizontally( busItem->GetPosition().x );
1652 }
1653
1654 m_view->ClearPreview();
1655 m_view->AddToPreview( previewItem->Clone() );
1656 }
1657 else if( evt->IsAction( &SCH_ACTIONS::properties ) )
1658 {
1659 switch( type )
1660 {
1662 {
1663 std::deque<SCH_ITEM*> strokeItems;
1664 strokeItems.push_back( previewItem );
1665
1666 DIALOG_WIRE_BUS_PROPERTIES dlg( m_frame, strokeItems );
1667
1669 [&]()
1670 {
1671 dlg.ShowModal();
1672 } );
1673
1674 break;
1675 }
1676
1677 case SCH_JUNCTION_T:
1678 {
1679 std::deque<SCH_JUNCTION*> junctions;
1680 junctions.push_back( static_cast<SCH_JUNCTION*>( previewItem ) );
1681
1682 DIALOG_JUNCTION_PROPS dlg( m_frame, junctions );
1683
1685 [&]()
1686 {
1687 dlg.ShowModal();
1688 } );
1689
1690 break;
1691 }
1692
1693 default:
1694 // Do nothing
1695 break;
1696 }
1697
1698 m_view->ClearPreview();
1699 m_view->AddToPreview( previewItem->Clone() );
1700 }
1701 else
1702 {
1703 evt->SetPassEvent();
1704 }
1705 }
1706 else
1707 {
1708 evt->SetPassEvent();
1709 }
1710 }
1711
1712 delete previewItem;
1713 m_view->ClearPreview();
1714
1715 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1716 controls->ForceCursorPosition( false );
1717
1718 return 0;
1719}
1720
1721
1723{
1724 for( SCH_ITEM* item : m_frame->GetScreen()->Items().Overlapping( SCH_LINE_T, aPosition ) )
1725 {
1726 SCH_LINE* line = static_cast<SCH_LINE*>( item );
1727
1728 if( line->GetEditFlags() & STRUCT_DELETED )
1729 continue;
1730
1731 if( line->IsWire() )
1732 return line;
1733 }
1734
1735 return nullptr;
1736}
1737
1738
1740{
1741 wxASSERT( aWire->IsWire() );
1742
1743 SCH_SHEET_PATH sheetPath = m_frame->GetCurrentSheet();
1744
1745 if( SCH_CONNECTION* wireConnection = aWire->Connection( &sheetPath ) )
1746 {
1747 SCH_ITEM* wireDriver = wireConnection->Driver();
1748
1749 if( wireDriver && wireDriver->IsType( { SCH_LABEL_T, SCH_GLOBAL_LABEL_T } ) )
1750 return wireConnection->LocalName();
1751 }
1752
1753 return wxEmptyString;
1754}
1755
1756
1757bool SCH_DRAWING_TOOLS::createNewLabel( const VECTOR2I& aPosition, int aType,
1758 std::list<std::unique_ptr<SCH_LABEL_BASE>>& aLabelList )
1759{
1760 SCHEMATIC* schematic = getModel<SCHEMATIC>();
1761 SCHEMATIC_SETTINGS& settings = schematic->Settings();
1762 SCH_LABEL_BASE* labelItem = nullptr;
1763 SCH_GLOBALLABEL* globalLabel = nullptr;
1764 wxString netName;
1765
1766 switch( aType )
1767 {
1768 case LAYER_LOCLABEL:
1769 labelItem = new SCH_LABEL( aPosition );
1770
1771 if( SCH_LINE* wire = findWire( aPosition ) )
1772 netName = findWireLabelDriverName( wire );
1773
1774 break;
1775
1777 labelItem = new SCH_DIRECTIVE_LABEL( aPosition );
1778 labelItem->SetShape( m_lastNetClassFlagShape );
1779 labelItem->GetFields().emplace_back( labelItem, FIELD_T::USER, wxT( "Netclass" ) );
1780 labelItem->GetFields().emplace_back( labelItem, FIELD_T::USER, wxT( "Component Class" ) );
1781 labelItem->GetFields().back().SetItalic( true );
1782 labelItem->GetFields().back().SetVisible( true );
1783 break;
1784
1785 case LAYER_HIERLABEL:
1786 labelItem = new SCH_HIERLABEL( aPosition );
1787 labelItem->SetShape( m_lastGlobalLabelShape );
1789 break;
1790
1791 case LAYER_GLOBLABEL:
1792 globalLabel = new SCH_GLOBALLABEL( aPosition );
1793 globalLabel->SetShape( m_lastGlobalLabelShape );
1796 labelItem = globalLabel;
1797
1798 if( SCH_LINE* wire = findWire( aPosition ) )
1799 netName = findWireLabelDriverName( wire );
1800
1801 break;
1802
1803 default:
1804 wxFAIL_MSG( "SCH_DRAWING_TOOLS::createNewLabel() unknown label type" );
1805 return false;
1806 }
1807
1808 // The normal parent is the current screen for these labels, set by SCH_SCREEN::Append()
1809 // but it is also used during placement for SCH_HIERLABEL before beeing appended
1810 labelItem->SetParent( m_frame->GetScreen() );
1811
1812 labelItem->SetTextSize( VECTOR2I( settings.m_DefaultTextSize, settings.m_DefaultTextSize ) );
1813
1814 if( aType != LAYER_NETCLASS_REFS )
1815 {
1816 // Must be after SetTextSize()
1817 labelItem->SetBold( m_lastTextBold );
1818 labelItem->SetItalic( m_lastTextItalic );
1819 }
1820
1821 labelItem->SetSpinStyle( m_lastTextOrientation );
1822 labelItem->SetFlags( IS_NEW | IS_MOVING );
1823
1824 if( !netName.IsEmpty() )
1825 {
1826 // Auto-create from attached wire
1827 labelItem->SetText( netName );
1828 }
1829 else
1830 {
1831 DIALOG_LABEL_PROPERTIES dlg( m_frame, labelItem, true );
1832
1833 dlg.SetLabelList( &aLabelList );
1834
1835 // QuasiModal required for syntax help and Scintilla auto-complete
1836 if( dlg.ShowQuasiModal() != wxID_OK )
1837 {
1839 delete labelItem;
1840 return false;
1841 }
1842 }
1843
1844 if( aType != LAYER_NETCLASS_REFS )
1845 {
1846 m_lastTextBold = labelItem->IsBold();
1847 m_lastTextItalic = labelItem->IsItalic();
1848 }
1849
1850 m_lastTextOrientation = labelItem->GetSpinStyle();
1851
1852 if( aType == LAYER_GLOBLABEL || aType == LAYER_HIERLABEL )
1853 {
1854 m_lastGlobalLabelShape = labelItem->GetShape();
1856 }
1857 else if( aType == LAYER_NETCLASS_REFS )
1858 {
1859 m_lastNetClassFlagShape = labelItem->GetShape();
1860 }
1861
1862 if( aLabelList.empty() )
1863 aLabelList.push_back( std::unique_ptr<SCH_LABEL_BASE>( labelItem ) );
1864 else // DIALOG_LABEL_PROPERTIES already filled in aLabelList; labelItem is extraneous to needs
1865 delete labelItem;
1866
1867 return true;
1868}
1869
1870
1872{
1873 SCHEMATIC* schematic = getModel<SCHEMATIC>();
1874 SCHEMATIC_SETTINGS& settings = schematic->Settings();
1875 SCH_TEXT* textItem = nullptr;
1876
1877 textItem = new SCH_TEXT( aPosition );
1878 textItem->SetParent( schematic );
1879 textItem->SetTextSize( VECTOR2I( settings.m_DefaultTextSize, settings.m_DefaultTextSize ) );
1880 // Must be after SetTextSize()
1881 textItem->SetBold( m_lastTextBold );
1882 textItem->SetItalic( m_lastTextItalic );
1885 textItem->SetTextAngle( m_lastTextAngle );
1886 textItem->SetFlags( IS_NEW | IS_MOVING );
1887
1888 DIALOG_TEXT_PROPERTIES dlg( m_frame, textItem );
1889
1890 // QuasiModal required for syntax help and Scintilla auto-complete
1891 if( dlg.ShowQuasiModal() != wxID_OK )
1892 {
1893 delete textItem;
1894 return nullptr;
1895 }
1896
1897 m_lastTextBold = textItem->IsBold();
1898 m_lastTextItalic = textItem->IsItalic();
1899 m_lastTextHJustify = textItem->GetHorizJustify();
1900 m_lastTextVJustify = textItem->GetVertJustify();
1901 m_lastTextAngle = textItem->GetTextAngle();
1902 return textItem;
1903}
1904
1905
1907{
1908 SCHEMATIC_SETTINGS& settings = aSheet->Schematic()->Settings();
1909 SCH_SHEET_PIN* pin = new SCH_SHEET_PIN( aSheet );
1910
1911 pin->SetFlags( IS_NEW | IS_MOVING );
1912 pin->SetText( std::to_string( aSheet->GetPins().size() + 1 ) );
1913 pin->SetTextSize( VECTOR2I( settings.m_DefaultTextSize, settings.m_DefaultTextSize ) );
1914 pin->SetPosition( aPosition );
1915 pin->ClearSelected();
1916
1917 m_lastSheetPinType = pin->GetShape();
1918
1919 return pin;
1920}
1921
1922
1924 const VECTOR2I& aPosition,
1925 SCH_HIERLABEL* aLabel )
1926{
1927 auto pin = createNewSheetPin( aSheet, aPosition );
1928 pin->SetText( aLabel->GetText() );
1929 pin->SetShape( aLabel->GetShape() );
1930 return pin;
1931}
1932
1933
1935{
1936 SCH_ITEM* item = nullptr;
1939 bool ignorePrimePosition = false;
1940 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
1941 SCH_SHEET* sheet = nullptr;
1942 wxString description;
1943
1944 if( m_inDrawingTool )
1945 return 0;
1946
1948
1949 bool isText = aEvent.IsAction( &SCH_ACTIONS::placeSchematicText );
1950 bool isGlobalLabel = aEvent.IsAction( &SCH_ACTIONS::placeGlobalLabel );
1951 bool isHierLabel = aEvent.IsAction( &SCH_ACTIONS::placeHierLabel );
1952 bool isClassLabel = aEvent.IsAction( &SCH_ACTIONS::placeClassLabel );
1953 bool isNetLabel = aEvent.IsAction( &SCH_ACTIONS::placeLabel );
1954 bool isSheetPin = aEvent.IsAction( &SCH_ACTIONS::placeSheetPin );
1955
1956 GRID_HELPER_GRIDS snapGrid = isText ? GRID_TEXT : GRID_CONNECTABLE;
1957
1958 // If we have a selected sheet use it, otherwise try to get one under the cursor
1959 if( isSheetPin )
1960 sheet = dynamic_cast<SCH_SHEET*>( m_selectionTool->GetSelection().Front() );
1961
1962 m_toolMgr->RunAction( ACTIONS::selectionClear );
1963
1964 m_frame->PushTool( aEvent );
1965
1966 auto setCursor =
1967 [&]()
1968 {
1969 if( item )
1970 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
1971 else if( isText )
1972 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
1973 else if( isGlobalLabel )
1974 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::LABEL_GLOBAL );
1975 else if( isNetLabel )
1976 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::LABEL_NET );
1977 else if( isClassLabel )
1978 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::LABEL_NET ); // JEY TODO: netclass directive cursor
1979 else if( isHierLabel )
1980 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::LABEL_HIER );
1981 else
1982 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1983 };
1984
1985 auto updatePreview =
1986 [&]()
1987 {
1988 m_view->ClearPreview();
1989 m_view->AddToPreview( item, false );
1990 item->RunOnChildren( [&]( SCH_ITEM* aChild )
1991 {
1992 m_view->AddToPreview( aChild, false );
1993 },
1995 m_frame->SetMsgPanel( item );
1996 };
1997
1998 auto cleanup =
1999 [&]()
2000 {
2001 m_toolMgr->RunAction( ACTIONS::selectionClear );
2002 m_view->ClearPreview();
2003 delete item;
2004 item = nullptr;
2005 };
2006
2007 auto prepItemForPlacement =
2008 [&]( SCH_ITEM* aItem, const VECTOR2I& cursorPos )
2009 {
2010 item->SetPosition( cursorPos );
2011
2012 item->SetFlags( IS_NEW | IS_MOVING );
2013
2014 // Not placed yet, so pass a nullptr screen reference
2015 item->AutoplaceFields( nullptr, AUTOPLACE_AUTO );
2016
2017 updatePreview();
2018 m_selectionTool->ClearSelection( true );
2019 m_selectionTool->AddItemToSel( item );
2020 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2021
2022 // update the cursor so it looks correct before another event
2023 setCursor();
2024 };
2025
2026 Activate();
2027
2028 // Must be done after Activate() so that it gets set into the correct context
2029 controls->ShowCursor( true );
2030
2031 // Set initial cursor
2032 setCursor();
2033
2034 if( aEvent.HasPosition() )
2035 {
2036 m_toolMgr->PrimeTool( aEvent.Position() );
2037 }
2038 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate()
2039 && ( isText || isGlobalLabel || isHierLabel || isClassLabel || isNetLabel ) )
2040 {
2041 m_toolMgr->PrimeTool( { 0, 0 } );
2042 ignorePrimePosition = true;
2043 }
2044
2045 SCH_COMMIT commit( m_toolMgr );
2046 std::list<std::unique_ptr<SCH_LABEL_BASE>> itemsToPlace;
2047
2048 // Main loop: keep receiving events
2049 while( TOOL_EVENT* evt = Wait() )
2050 {
2051 setCursor();
2052 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2053 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2054
2055 VECTOR2I cursorPos = controls->GetMousePosition();
2056 cursorPos = grid.BestSnapAnchor( cursorPos, snapGrid, item );
2057 controls->ForceCursorPosition( true, cursorPos );
2058
2059 // The tool hotkey is interpreted as a click when drawing
2060 bool isSyntheticClick = item && evt->IsActivate() && evt->HasPosition() && evt->Matches( aEvent );
2061
2062 if( evt->IsCancelInteractive() || evt->IsAction( &ACTIONS::undo ) )
2063 {
2064 m_frame->GetInfoBar()->Dismiss();
2065
2066 if( item )
2067 {
2068 cleanup();
2069 }
2070 else
2071 {
2072 m_frame->PopTool( aEvent );
2073 break;
2074 }
2075 }
2076 else if( evt->IsActivate() && !isSyntheticClick )
2077 {
2078 if( item && evt->IsMoveTool() )
2079 {
2080 // we're already moving our own item; ignore the move tool
2081 evt->SetPassEvent( false );
2082 continue;
2083 }
2084
2085 if( item )
2086 {
2087 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel item creation." ) );
2088 evt->SetPassEvent( false );
2089 continue;
2090 }
2091
2092 if( evt->IsPointEditor() )
2093 {
2094 // don't exit (the point editor runs in the background)
2095 }
2096 else if( evt->IsMoveTool() )
2097 {
2098 // leave ourselves on the stack so we come back after the move
2099 break;
2100 }
2101 else
2102 {
2103 m_frame->PopTool( aEvent );
2104 break;
2105 }
2106 }
2107 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
2108 || isSyntheticClick
2109 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
2110 {
2111 PLACE_NEXT:
2112 // First click creates...
2113 if( !item )
2114 {
2115 m_toolMgr->RunAction( ACTIONS::selectionClear );
2116
2117 if( isText )
2118 {
2119 item = createNewText( cursorPos );
2120 description = _( "Add Text" );
2121 }
2122 else if( isHierLabel )
2123 {
2124 if( m_dialogSyncSheetPin && m_dialogSyncSheetPin->GetPlacementTemplate() )
2125 {
2126 auto pin = static_cast<SCH_HIERLABEL*>( m_dialogSyncSheetPin->GetPlacementTemplate() );
2127 SCH_HIERLABEL* label = new SCH_HIERLABEL( cursorPos );
2128 SCHEMATIC* schematic = getModel<SCHEMATIC>();
2129 label->SetText( pin->GetText() );
2130 label->SetShape( pin->GetShape() );
2132 label->SetParent( schematic );
2133 label->SetBold( m_lastTextBold );
2134 label->SetItalic( m_lastTextItalic );
2136 label->SetTextSize( VECTOR2I( schematic->Settings().m_DefaultTextSize,
2137 schematic->Settings().m_DefaultTextSize ) );
2138 label->SetFlags( IS_NEW | IS_MOVING );
2139 itemsToPlace.push_back( std::unique_ptr<SCH_LABEL_BASE>( label ) );
2140 }
2141 else
2142 {
2143 createNewLabel( cursorPos, LAYER_HIERLABEL, itemsToPlace );
2144 }
2145
2146 description = _( "Add Hierarchical Label" );
2147 }
2148 else if( isNetLabel )
2149 {
2150 createNewLabel( cursorPos, LAYER_LOCLABEL, itemsToPlace );
2151 description = _( "Add Label" );
2152 }
2153 else if( isGlobalLabel )
2154 {
2155 createNewLabel( cursorPos, LAYER_GLOBLABEL, itemsToPlace );
2156 description = _( "Add Label" );
2157 }
2158 else if( isClassLabel )
2159 {
2160 createNewLabel( cursorPos, LAYER_NETCLASS_REFS, itemsToPlace );
2161 description = _( "Add Label" );
2162 }
2163 else if( isSheetPin )
2164 {
2165 EDA_ITEM* i = nullptr;
2166
2167 // If we didn't have a sheet selected, try to find one under the cursor
2168 if( !sheet && m_selectionTool->SelectPoint( cursorPos, { SCH_SHEET_T }, &i ) )
2169 sheet = dynamic_cast<SCH_SHEET*>( i );
2170
2171 if( !sheet )
2172 {
2173 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( m_frame );
2174 m_statusPopup->SetText( _( "Click over a sheet." ) );
2176 + wxPoint( 20, 20 ) );
2177 m_statusPopup->PopupFor( 2000 );
2178 item = nullptr;
2179 }
2180 else
2181 {
2182 // User is using the 'Sync Sheet Pins' tool
2183 if( m_dialogSyncSheetPin && m_dialogSyncSheetPin->GetPlacementTemplate() )
2184 {
2186 sheet, cursorPos,
2187 static_cast<SCH_HIERLABEL*>( m_dialogSyncSheetPin->GetPlacementTemplate() ) );
2188 }
2189 else
2190 {
2191 // User is using the 'Place Sheet Pins' tool
2192 SCH_HIERLABEL* label = importHierLabel( sheet );
2193
2194 if( !label )
2195 {
2196 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( m_frame );
2197 m_statusPopup->SetText( _( "No new hierarchical labels found." ) );
2198 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
2199 m_statusPopup->PopupFor( 2000 );
2200 item = nullptr;
2201
2202 m_frame->PopTool( aEvent );
2203 break;
2204 }
2205
2206 item = createNewSheetPinFromLabel( sheet, cursorPos, label );
2207 }
2208 }
2209
2210 description = _( "Add Sheet Pin" );
2211 }
2212
2213 // If we started with a hotkey which has a position then warp back to that.
2214 // Otherwise update to the current mouse position pinned inside the autoscroll
2215 // boundaries.
2216 if( evt->IsPrime() && !ignorePrimePosition )
2217 {
2218 cursorPos = grid.Align( evt->Position() );
2219 getViewControls()->WarpMouseCursor( cursorPos, true );
2220 }
2221 else
2222 {
2224 cursorPos = getViewControls()->GetMousePosition();
2225 cursorPos = grid.BestSnapAnchor( cursorPos, snapGrid, item );
2226 }
2227
2228 if( !itemsToPlace.empty() )
2229 {
2230 item = itemsToPlace.front().release();
2231 itemsToPlace.pop_front();
2232 }
2233
2234 if( item )
2235 prepItemForPlacement( item, cursorPos );
2236
2237 if( m_frame->GetMoveWarpsCursor() )
2238 controls->SetCursorPosition( cursorPos, false );
2239
2240 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2241 }
2242 else // ... and second click places:
2243 {
2244 item->ClearFlags( IS_MOVING );
2245
2246 if( item->IsConnectable() )
2247 m_frame->AutoRotateItem( m_frame->GetScreen(), item );
2248
2249 if( isSheetPin && sheet )
2250 {
2251 // Sheet pins are owned by their parent sheet.
2252 commit.Modify( sheet, m_frame->GetScreen() );
2253 sheet->AddPin( (SCH_SHEET_PIN*) item );
2254 }
2255 else
2256 {
2257 m_frame->SaveCopyForRepeatItem( item );
2258 m_frame->AddToScreen( item, m_frame->GetScreen() );
2259 commit.Added( item, m_frame->GetScreen() );
2260 }
2261
2262 item->AutoplaceFields( m_frame->GetScreen(), AUTOPLACE_AUTO );
2263
2264 commit.Push( description );
2265
2266 m_view->ClearPreview();
2267
2268 if( m_dialogSyncSheetPin && m_dialogSyncSheetPin->GetPlacementTemplate() )
2269 {
2270 m_dialogSyncSheetPin->EndPlaceItem( item );
2271
2272 if( m_dialogSyncSheetPin->CanPlaceMore() )
2273 {
2274 item = nullptr;
2275 goto PLACE_NEXT;
2276 }
2277
2278 m_frame->PopTool( aEvent );
2279 m_toolMgr->RunAction( ACTIONS::selectionClear );
2280 m_dialogSyncSheetPin->Show( true );
2281 break;
2282 }
2283
2284 item = nullptr;
2285
2286 if( isSheetPin && sheet )
2287 {
2288 SCH_HIERLABEL* label = importHierLabel( sheet );
2289
2290 if( !label )
2291 {
2292 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( m_frame );
2293 m_statusPopup->SetText( _( "No new hierarchical labels found." ) );
2294 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
2295 m_statusPopup->PopupFor( 2000 );
2296
2297 m_frame->PopTool( aEvent );
2298 break;
2299 }
2300
2301 item = createNewSheetPinFromLabel( sheet, cursorPos, label );
2302 }
2303 else if( !itemsToPlace.empty() )
2304 {
2305 item = itemsToPlace.front().release();
2306 itemsToPlace.pop_front();
2307 prepItemForPlacement( item, cursorPos );
2308 }
2309 }
2310 }
2311 else if( evt->IsClick( BUT_RIGHT ) )
2312 {
2313 // Warp after context menu only if dragging...
2314 if( !item )
2315 m_toolMgr->VetoContextMenuMouseWarp();
2316
2317 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
2318 }
2319 else if( item && evt->IsSelectionEvent() )
2320 {
2321 // This happens if our text was replaced out from under us by ConvertTextType()
2322 SCH_SELECTION& selection = m_selectionTool->GetSelection();
2323
2324 if( selection.GetSize() == 1 )
2325 {
2326 item = (SCH_ITEM*) selection.Front();
2327 updatePreview();
2328 }
2329 else
2330 {
2331 item = nullptr;
2332 }
2333 }
2334 else if( evt->IsAction( &ACTIONS::increment ) )
2335 {
2336 if( evt->HasParameter() )
2337 m_toolMgr->RunSynchronousAction( ACTIONS::increment, &commit, evt->Parameter<ACTIONS::INCREMENT>() );
2338 else
2339 m_toolMgr->RunSynchronousAction( ACTIONS::increment, &commit, ACTIONS::INCREMENT { 1, 0 } );
2340 }
2341 else if( evt->IsAction( &ACTIONS::duplicate )
2342 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem )
2343 || evt->IsAction( &ACTIONS::paste ) )
2344 {
2345 if( item )
2346 {
2347 wxBell();
2348 continue;
2349 }
2350
2351 // Exit. The duplicate/repeat/paste will run in its own loop.
2352 m_frame->PopTool( aEvent );
2353 evt->SetPassEvent();
2354 break;
2355 }
2356 else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
2357 {
2358 item->SetPosition( cursorPos );
2359
2360 // Not placed yet, so pass a nullptr screen reference
2361 item->AutoplaceFields( nullptr, AUTOPLACE_AUTO );
2362
2363 updatePreview();
2364 }
2365 else if( item && evt->IsAction( &ACTIONS::doDelete ) )
2366 {
2367 cleanup();
2368 }
2369 else if( evt->IsAction( &ACTIONS::redo ) )
2370 {
2371 wxBell();
2372 }
2373 else if( item && ( evt->IsAction( &SCH_ACTIONS::toDLabel )
2374 || evt->IsAction( &SCH_ACTIONS::toGLabel )
2375 || evt->IsAction( &SCH_ACTIONS::toHLabel )
2376 || evt->IsAction( &SCH_ACTIONS::toLabel )
2377 || evt->IsAction( &SCH_ACTIONS::toText )
2378 || evt->IsAction( &SCH_ACTIONS::toTextBox ) ) )
2379 {
2380 wxBell();
2381 }
2382 else if( item && ( evt->IsAction( &SCH_ACTIONS::properties )
2383 || evt->IsAction( &SCH_ACTIONS::autoplaceFields )
2384 || evt->IsAction( &SCH_ACTIONS::rotateCW )
2385 || evt->IsAction( &SCH_ACTIONS::rotateCCW )
2386 || evt->IsAction( &SCH_ACTIONS::mirrorV )
2387 || evt->IsAction( &SCH_ACTIONS::mirrorH ) ) )
2388 {
2389 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2390 evt->SetPassEvent();
2391 }
2392 else
2393 {
2394 evt->SetPassEvent();
2395 }
2396
2397 // Enable autopanning and cursor capture only when there is an item to be placed
2398 controls->SetAutoPan( item != nullptr );
2399 controls->CaptureCursor( item != nullptr );
2400 }
2401
2402 controls->SetAutoPan( false );
2403 controls->CaptureCursor( false );
2404 controls->ForceCursorPosition( false );
2405 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2406
2407 if( m_dialogSyncSheetPin && m_dialogSyncSheetPin->CanPlaceMore() )
2408 {
2409 m_dialogSyncSheetPin->EndPlacement();
2410 m_dialogSyncSheetPin->Show( true );
2411 }
2412
2413 return 0;
2414}
2415
2416
2418{
2419 SCHEMATIC* schematic = getModel<SCHEMATIC>();
2420 SCHEMATIC_SETTINGS& sch_settings = schematic->Settings();
2421 SCH_SHAPE* item = nullptr;
2422 bool isTextBox = aEvent.IsAction( &SCH_ACTIONS::drawTextBox );
2423 SHAPE_T type = aEvent.Parameter<SHAPE_T>();
2424 wxString description;
2425
2426 if( m_inDrawingTool )
2427 return 0;
2428
2430
2433 VECTOR2I cursorPos;
2434
2435 // We might be running as the same shape in another co-routine. Make sure that one
2436 // gets whacked.
2437 m_toolMgr->DeactivateTool();
2438
2439 m_toolMgr->RunAction( ACTIONS::selectionClear );
2440
2441 m_frame->PushTool( aEvent );
2442
2443 auto setCursor =
2444 [&]()
2445 {
2446 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2447 };
2448
2449 auto cleanup =
2450 [&] ()
2451 {
2452 m_toolMgr->RunAction( ACTIONS::selectionClear );
2453 m_view->ClearPreview();
2454 delete item;
2455 item = nullptr;
2456 };
2457
2458 Activate();
2459
2460 // Must be done after Activate() so that it gets set into the correct context
2461 getViewControls()->ShowCursor( true );
2462
2463 // Set initial cursor
2464 setCursor();
2465
2466 if( aEvent.HasPosition() )
2467 m_toolMgr->PrimeTool( aEvent.Position() );
2468
2469 // Main loop: keep receiving events
2470 while( TOOL_EVENT* evt = Wait() )
2471 {
2472 setCursor();
2473 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2474 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2475
2476 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_GRAPHICS );
2477 controls->ForceCursorPosition( true, cursorPos );
2478
2479 // The tool hotkey is interpreted as a click when drawing
2480 bool isSyntheticClick = item && evt->IsActivate() && evt->HasPosition() && evt->Matches( aEvent );
2481
2482 if( evt->IsCancelInteractive() || ( item && evt->IsAction( &ACTIONS::undo ) ) )
2483 {
2484 if( item )
2485 {
2486 cleanup();
2487 }
2488 else
2489 {
2490 m_frame->PopTool( aEvent );
2491 break;
2492 }
2493 }
2494 else if( evt->IsActivate() && !isSyntheticClick )
2495 {
2496 if( item && evt->IsMoveTool() )
2497 {
2498 // we're already drawing our own item; ignore the move tool
2499 evt->SetPassEvent( false );
2500 continue;
2501 }
2502
2503 if( item )
2504 cleanup();
2505
2506 if( evt->IsPointEditor() )
2507 {
2508 // don't exit (the point editor runs in the background)
2509 }
2510 else if( evt->IsMoveTool() )
2511 {
2512 // leave ourselves on the stack so we come back after the move
2513 break;
2514 }
2515 else
2516 {
2517 m_frame->PopTool( aEvent );
2518 break;
2519 }
2520 }
2521 else if( !item && ( evt->IsClick( BUT_LEFT )
2522 || evt->IsAction( &ACTIONS::cursorClick ) ) )
2523 {
2524 m_toolMgr->RunAction( ACTIONS::selectionClear );
2525
2526 if( isTextBox )
2527 {
2529
2530 textbox->SetTextSize( VECTOR2I( sch_settings.m_DefaultTextSize,
2531 sch_settings.m_DefaultTextSize ) );
2532
2533 // Must come after SetTextSize()
2534 textbox->SetBold( m_lastTextBold );
2535 textbox->SetItalic( m_lastTextItalic );
2536
2537 textbox->SetTextAngle( m_lastTextboxAngle );
2540 textbox->SetStroke( m_lastTextboxStroke );
2542 textbox->SetParent( schematic );
2543
2544 item = textbox;
2545 description = _( "Add Text Box" );
2546 }
2547 else
2548 {
2549 item = new SCH_SHAPE( type, LAYER_NOTES, 0, m_lastFillStyle );
2550
2551 item->SetStroke( m_lastStroke );
2553 item->SetParent( schematic );
2554 description = wxString::Format( _( "Add %s" ), item->GetFriendlyName() );
2555 }
2556
2557 item->SetFlags( IS_NEW );
2558 item->BeginEdit( cursorPos );
2559
2560 m_view->ClearPreview();
2561 m_view->AddToPreview( item->Clone() );
2562 }
2563 else if( item && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
2564 || isSyntheticClick
2565 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick )
2566 || evt->IsAction( &ACTIONS::finishInteractive ) ) )
2567 {
2568 bool finished = false;
2569
2570 if( evt->IsDblClick( BUT_LEFT )
2571 || evt->IsAction( &ACTIONS::cursorDblClick )
2572 || evt->IsAction( &ACTIONS::finishInteractive ) )
2573 {
2574 finished = true;
2575 }
2576 else
2577 {
2578 finished = !item->ContinueEdit( cursorPos );
2579 }
2580
2581 if( finished )
2582 {
2583 item->EndEdit();
2584 item->ClearEditFlags();
2585 item->SetFlags( IS_NEW );
2586
2587 if( isTextBox )
2588 {
2589 SCH_TEXTBOX* textbox = static_cast<SCH_TEXTBOX*>( item );
2590 DIALOG_TEXT_PROPERTIES dlg( m_frame, textbox );
2591
2592 getViewControls()->SetAutoPan( false );
2593 getViewControls()->CaptureCursor( false );
2594
2595 // QuasiModal required for syntax help and Scintilla auto-complete
2596 if( dlg.ShowQuasiModal() != wxID_OK )
2597 {
2598 cleanup();
2599 continue;
2600 }
2601
2602 m_lastTextBold = textbox->IsBold();
2603 m_lastTextItalic = textbox->IsItalic();
2604 m_lastTextboxAngle = textbox->GetTextAngle();
2607 m_lastTextboxStroke = textbox->GetStroke();
2610 }
2611 else
2612 {
2613 m_lastStroke = item->GetStroke();
2614 m_lastFillStyle = item->GetFillMode();
2615 m_lastFillColor = item->GetFillColor();
2616 }
2617
2618 SCH_COMMIT commit( m_toolMgr );
2619 commit.Add( item, m_frame->GetScreen() );
2620 commit.Push( wxString::Format( _( "Draw %s" ), item->GetClass() ) );
2621
2622 m_selectionTool->AddItemToSel( item );
2623 item = nullptr;
2624
2625 m_view->ClearPreview();
2627 }
2628 }
2629 else if( evt->IsAction( &ACTIONS::duplicate )
2630 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem )
2631 || evt->IsAction( &ACTIONS::paste ) )
2632 {
2633 if( item )
2634 {
2635 wxBell();
2636 continue;
2637 }
2638
2639 // Exit. The duplicate/repeat/paste will run in its own loop.
2640 m_frame->PopTool( aEvent );
2641 evt->SetPassEvent();
2642 break;
2643 }
2644 else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
2645 {
2646 item->CalcEdit( cursorPos );
2647 m_view->ClearPreview();
2648 m_view->AddToPreview( item->Clone() );
2649 m_frame->SetMsgPanel( item );
2650 }
2651 else if( evt->IsDblClick( BUT_LEFT ) && !item )
2652 {
2653 m_toolMgr->RunAction( SCH_ACTIONS::properties );
2654 }
2655 else if( evt->IsClick( BUT_RIGHT ) )
2656 {
2657 // Warp after context menu only if dragging...
2658 if( !item )
2659 m_toolMgr->VetoContextMenuMouseWarp();
2660
2661 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
2662 }
2663 else if( item && evt->IsAction( &ACTIONS::redo ) )
2664 {
2665 wxBell();
2666 }
2667 else
2668 {
2669 evt->SetPassEvent();
2670 }
2671
2672 // Enable autopanning and cursor capture only when there is a shape being drawn
2673 getViewControls()->SetAutoPan( item != nullptr );
2674 getViewControls()->CaptureCursor( item != nullptr );
2675 }
2676
2677 getViewControls()->SetAutoPan( false );
2678 getViewControls()->CaptureCursor( false );
2679 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2680 return 0;
2681}
2682
2683
2685{
2686 if( m_inDrawingTool )
2687 return 0;
2688
2690 SCOPED_SET_RESET<bool> scopedDrawMode( m_drawingRuleArea, true );
2691
2694 VECTOR2I cursorPos;
2695
2696 RULE_AREA_CREATE_HELPER ruleAreaTool( *getView(), m_frame, m_toolMgr );
2697 POLYGON_GEOM_MANAGER polyGeomMgr( ruleAreaTool );
2698 bool started = false;
2699
2700 // We might be running as the same shape in another co-routine. Make sure that one
2701 // gets whacked.
2702 m_toolMgr->DeactivateTool();
2703
2704 m_toolMgr->RunAction( ACTIONS::selectionClear );
2705
2706 m_frame->PushTool( aEvent );
2707
2708 auto setCursor =
2709 [&]()
2710 {
2711 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2712 };
2713
2714 auto cleanup =
2715 [&]()
2716 {
2717 polyGeomMgr.Reset();
2718 started = false;
2719 getViewControls()->SetAutoPan( false );
2720 getViewControls()->CaptureCursor( false );
2721 m_toolMgr->RunAction( ACTIONS::selectionClear );
2722 };
2723
2724 Activate();
2725
2726 // Must be done after Activate() so that it gets set into the correct context
2727 getViewControls()->ShowCursor( true );
2728 //m_controls->ForceCursorPosition( false );
2729
2730 // Set initial cursor
2731 setCursor();
2732
2733 if( aEvent.HasPosition() )
2734 m_toolMgr->PrimeTool( aEvent.Position() );
2735
2736 // Main loop: keep receiving events
2737 while( TOOL_EVENT* evt = Wait() )
2738 {
2739 setCursor();
2740
2741 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2742 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2743
2744 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_CONNECTABLE );
2745 controls->ForceCursorPosition( true, cursorPos );
2746
2747 polyGeomMgr.SetLeaderMode( m_frame->eeconfig()->m_Drawing.line_mode == LINE_MODE_FREE ? LEADER_MODE::DIRECT
2749
2750 if( evt->IsCancelInteractive() )
2751 {
2752 if( started )
2753 {
2754 cleanup();
2755 }
2756 else
2757 {
2758 m_frame->PopTool( aEvent );
2759
2760 // We've handled the cancel event. Don't cancel other tools
2761 evt->SetPassEvent( false );
2762 break;
2763 }
2764 }
2765 else if( evt->IsActivate() )
2766 {
2767 if( started )
2768 cleanup();
2769
2770 if( evt->IsPointEditor() )
2771 {
2772 // don't exit (the point editor runs in the background)
2773 }
2774 else if( evt->IsMoveTool() )
2775 {
2776 // leave ourselves on the stack so we come back after the move
2777 break;
2778 }
2779 else
2780 {
2781 m_frame->PopTool( aEvent );
2782 break;
2783 }
2784 }
2785 else if( evt->IsClick( BUT_RIGHT ) )
2786 {
2787 if( !started )
2788 m_toolMgr->VetoContextMenuMouseWarp();
2789
2790 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
2791 }
2792 // events that lock in nodes
2793 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
2794 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick )
2795 || evt->IsAction( &SCH_ACTIONS::closeOutline ) )
2796 {
2797 // Check if it is double click / closing line (so we have to finish the zone)
2798 const bool endPolygon = evt->IsDblClick( BUT_LEFT )
2799 || evt->IsAction( &ACTIONS::cursorDblClick )
2800 || evt->IsAction( &SCH_ACTIONS::closeOutline )
2801 || polyGeomMgr.NewPointClosesOutline( cursorPos );
2802
2803 if( endPolygon )
2804 {
2805 polyGeomMgr.SetFinished();
2806 polyGeomMgr.Reset();
2807
2808 started = false;
2809 getViewControls()->SetAutoPan( false );
2810 getViewControls()->CaptureCursor( false );
2811 }
2812 // adding a corner
2813 else if( polyGeomMgr.AddPoint( cursorPos ) )
2814 {
2815 if( !started )
2816 {
2817 started = true;
2818
2819 getViewControls()->SetAutoPan( true );
2820 getViewControls()->CaptureCursor( true );
2821 }
2822 }
2823 }
2824 else if( started && ( evt->IsAction( &SCH_ACTIONS::deleteLastPoint )
2825 || evt->IsAction( &ACTIONS::doDelete )
2826 || evt->IsAction( &ACTIONS::undo ) ) )
2827 {
2828 if( std::optional<VECTOR2I> last = polyGeomMgr.DeleteLastCorner() )
2829 {
2830 cursorPos = last.value();
2831 getViewControls()->WarpMouseCursor( cursorPos, true );
2832 getViewControls()->ForceCursorPosition( true, cursorPos );
2833 polyGeomMgr.SetCursorPosition( cursorPos );
2834 }
2835 else
2836 {
2837 cleanup();
2838 }
2839 }
2840 else if( started && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
2841 {
2842 polyGeomMgr.SetCursorPosition( cursorPos );
2843 }
2844 else if( evt->IsAction( &ACTIONS::duplicate )
2845 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem )
2846 || evt->IsAction( &ACTIONS::paste ) )
2847 {
2848 if( started )
2849 {
2850 wxBell();
2851 continue;
2852 }
2853
2854 // Exit. The duplicate/repeat/paste will run in its own loop.
2855 m_frame->PopTool( aEvent );
2856 evt->SetPassEvent();
2857 break;
2858 }
2859 else
2860 {
2861 evt->SetPassEvent();
2862 }
2863
2864 } // end while
2865
2866 getViewControls()->SetAutoPan( false );
2867 getViewControls()->CaptureCursor( false );
2868 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2869 return 0;
2870}
2871
2872
2874{
2875 SCHEMATIC* schematic = getModel<SCHEMATIC>();
2876 SCH_TABLE* table = nullptr;
2877
2878 if( m_inDrawingTool )
2879 return 0;
2880
2882
2885 VECTOR2I cursorPos;
2886
2887 // We might be running as the same shape in another co-routine. Make sure that one
2888 // gets whacked.
2889 m_toolMgr->DeactivateTool();
2890
2891 m_toolMgr->RunAction( ACTIONS::selectionClear );
2892
2893 m_frame->PushTool( aEvent );
2894
2895 auto setCursor =
2896 [&]()
2897 {
2898 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2899 };
2900
2901 auto cleanup =
2902 [&] ()
2903 {
2904 m_toolMgr->RunAction( ACTIONS::selectionClear );
2905 m_view->ClearPreview();
2906 delete table;
2907 table = nullptr;
2908 };
2909
2910 Activate();
2911
2912 // Must be done after Activate() so that it gets set into the correct context
2913 getViewControls()->ShowCursor( true );
2914
2915 // Set initial cursor
2916 setCursor();
2917
2918 if( aEvent.HasPosition() )
2919 m_toolMgr->PrimeTool( aEvent.Position() );
2920
2921 // Main loop: keep receiving events
2922 while( TOOL_EVENT* evt = Wait() )
2923 {
2924 setCursor();
2925 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2926 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2927
2928 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_GRAPHICS );
2929 controls->ForceCursorPosition( true, cursorPos );
2930
2931 // The tool hotkey is interpreted as a click when drawing
2932 bool isSyntheticClick = table && evt->IsActivate() && evt->HasPosition() && evt->Matches( aEvent );
2933
2934 if( evt->IsCancelInteractive() || ( table && evt->IsAction( &ACTIONS::undo ) ) )
2935 {
2936 if( table )
2937 {
2938 cleanup();
2939 }
2940 else
2941 {
2942 m_frame->PopTool( aEvent );
2943 break;
2944 }
2945 }
2946 else if( evt->IsActivate() && !isSyntheticClick )
2947 {
2948 if( table && evt->IsMoveTool() )
2949 {
2950 // we're already drawing our own item; ignore the move tool
2951 evt->SetPassEvent( false );
2952 continue;
2953 }
2954
2955 if( table )
2956 cleanup();
2957
2958 if( evt->IsPointEditor() )
2959 {
2960 // don't exit (the point editor runs in the background)
2961 }
2962 else if( evt->IsMoveTool() )
2963 {
2964 // leave ourselves on the stack so we come back after the move
2965 break;
2966 }
2967 else
2968 {
2969 m_frame->PopTool( aEvent );
2970 break;
2971 }
2972 }
2973 else if( !table && ( evt->IsClick( BUT_LEFT )
2974 || evt->IsAction( &ACTIONS::cursorClick ) ) )
2975 {
2976 m_toolMgr->RunAction( ACTIONS::selectionClear );
2977
2978 table = new SCH_TABLE( 0 );
2979 table->SetColCount( 1 );
2980
2981 SCH_TABLECELL* tableCell = new SCH_TABLECELL();
2982 int defaultTextSize = schematic->Settings().m_DefaultTextSize;
2983
2984 tableCell->SetTextSize( VECTOR2I( defaultTextSize, defaultTextSize ) );
2985 table->AddCell( tableCell );
2986
2987 table->SetParent( schematic );
2988 table->SetFlags( IS_NEW );
2989 table->SetPosition( cursorPos );
2990
2991 m_view->ClearPreview();
2992 m_view->AddToPreview( table->Clone() );
2993 }
2994 else if( table && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
2995 || isSyntheticClick
2996 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick )
2997 || evt->IsAction( &SCH_ACTIONS::finishInteractive ) ) )
2998 {
2999 table->ClearEditFlags();
3000 table->SetFlags( IS_NEW );
3001 table->Normalize();
3002
3004
3005 // QuasiModal required for Scintilla auto-complete
3006 if( dlg.ShowQuasiModal() == wxID_OK )
3007 {
3008 SCH_COMMIT commit( m_toolMgr );
3009 commit.Add( table, m_frame->GetScreen() );
3010 commit.Push( _( "Draw Table" ) );
3011
3012 m_selectionTool->AddItemToSel( table );
3014 }
3015 else
3016 {
3017 delete table;
3018 }
3019
3020 table = nullptr;
3021 m_view->ClearPreview();
3022 }
3023 else if( table && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
3024 {
3025 VECTOR2I gridSize = grid.GetGridSize( grid.GetItemGrid( table ) );
3026 int fontSize = schematic->Settings().m_DefaultTextSize;
3027 VECTOR2I origin( table->GetPosition() );
3028 VECTOR2I requestedSize( cursorPos - origin );
3029
3030 int colCount = std::max( 1, requestedSize.x / ( fontSize * 15 ) );
3031 int rowCount = std::max( 1, requestedSize.y / ( fontSize * 2 ) );
3032
3033 VECTOR2I cellSize( std::max( gridSize.x * 5, requestedSize.x / colCount ),
3034 std::max( gridSize.y * 2, requestedSize.y / rowCount ) );
3035
3036 cellSize.x = KiROUND( (double) cellSize.x / gridSize.x ) * gridSize.x;
3037 cellSize.y = KiROUND( (double) cellSize.y / gridSize.y ) * gridSize.y;
3038
3039 table->ClearCells();
3040 table->SetColCount( colCount );
3041
3042 for( int col = 0; col < colCount; ++col )
3043 table->SetColWidth( col, cellSize.x );
3044
3045 for( int row = 0; row < rowCount; ++row )
3046 {
3047 table->SetRowHeight( row, cellSize.y );
3048
3049 for( int col = 0; col < colCount; ++col )
3050 {
3051 SCH_TABLECELL* cell = new SCH_TABLECELL();
3052 int defaultTextSize = schematic->Settings().m_DefaultTextSize;
3053
3054 cell->SetTextSize( VECTOR2I( defaultTextSize, defaultTextSize ) );
3055 cell->SetPosition( origin + VECTOR2I( col * cellSize.x, row * cellSize.y ) );
3056 cell->SetEnd( cell->GetPosition() + cellSize );
3057 table->AddCell( cell );
3058 }
3059 }
3060
3061 m_view->ClearPreview();
3062 m_view->AddToPreview( table->Clone() );
3063 m_frame->SetMsgPanel( table );
3064 }
3065 else if( evt->IsDblClick( BUT_LEFT ) && !table )
3066 {
3067 m_toolMgr->RunAction( SCH_ACTIONS::properties );
3068 }
3069 else if( evt->IsClick( BUT_RIGHT ) )
3070 {
3071 // Warp after context menu only if dragging...
3072 if( !table )
3073 m_toolMgr->VetoContextMenuMouseWarp();
3074
3075 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
3076 }
3077 else if( evt->IsAction( &ACTIONS::duplicate )
3078 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem )
3079 || evt->IsAction( &ACTIONS::paste ) )
3080 {
3081 if( table )
3082 {
3083 wxBell();
3084 continue;
3085 }
3086
3087 // Exit. The duplicate/repeat/paste will run in its own loop.
3088 m_frame->PopTool( aEvent );
3089 evt->SetPassEvent();
3090 break;
3091 }
3092 else if( table && evt->IsAction( &ACTIONS::redo ) )
3093 {
3094 wxBell();
3095 }
3096 else
3097 {
3098 evt->SetPassEvent();
3099 }
3100
3101 // Enable autopanning and cursor capture only when there is a shape being drawn
3102 getViewControls()->SetAutoPan( table != nullptr );
3103 getViewControls()->CaptureCursor( table != nullptr );
3104 }
3105
3106 getViewControls()->SetAutoPan( false );
3107 getViewControls()->CaptureCursor( false );
3108 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3109 return 0;
3110}
3111
3112
3114{
3115 bool isDrawSheetCopy = aEvent.IsAction( &SCH_ACTIONS::drawSheetFromFile );
3116 bool isDrawSheetFromDesignBlock = aEvent.IsAction( &SCH_ACTIONS::drawSheetFromDesignBlock );
3117
3118 std::unique_ptr<DESIGN_BLOCK> designBlock;
3119
3120 SCH_SHEET* sheet = nullptr;
3121 wxString filename;
3122
3123 if( isDrawSheetCopy )
3124 {
3125 wxString* ptr = aEvent.Parameter<wxString*>();
3126 wxCHECK( ptr, 0 );
3127
3128 // We own the string if we're importing a sheet
3129 filename = *ptr;
3130 delete ptr;
3131 }
3132 else if( isDrawSheetFromDesignBlock )
3133 {
3134 designBlock.reset( aEvent.Parameter<DESIGN_BLOCK*>() );
3135 wxCHECK( designBlock, 0 );
3136 filename = designBlock->GetSchematicFile();
3137 }
3138
3139 if( ( isDrawSheetCopy || isDrawSheetFromDesignBlock ) && !wxFileExists( filename ) )
3140 {
3141 wxMessageBox( wxString::Format( _( "File '%s' does not exist." ), filename ) );
3142 return 0;
3143 }
3144
3145 if( m_inDrawingTool )
3146 return 0;
3147
3149
3150 EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
3151 SCHEMATIC_SETTINGS& schSettings = m_frame->Schematic().Settings();
3154 VECTOR2I cursorPos;
3155 bool startedWithDrag = false; // Track if initial sheet placement started with a drag
3156
3157 m_toolMgr->RunAction( ACTIONS::selectionClear );
3158
3159 m_frame->PushTool( aEvent );
3160
3161 auto setCursor =
3162 [&]()
3163 {
3164 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
3165 };
3166
3167 auto cleanup =
3168 [&] ()
3169 {
3170 m_toolMgr->RunAction( ACTIONS::selectionClear );
3171 m_view->ClearPreview();
3172 delete sheet;
3173 sheet = nullptr;
3174 };
3175
3176 Activate();
3177
3178 // Must be done after Activate() so that it gets set into the correct context
3179 getViewControls()->ShowCursor( true );
3180
3181 // Set initial cursor
3182 setCursor();
3183
3184 if( aEvent.HasPosition() && !( isDrawSheetCopy || isDrawSheetFromDesignBlock ) )
3185 m_toolMgr->PrimeTool( aEvent.Position() );
3186
3187 // Main loop: keep receiving events
3188 while( TOOL_EVENT* evt = Wait() )
3189 {
3190 setCursor();
3191 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
3192 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
3193
3194 cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_GRAPHICS );
3195 controls->ForceCursorPosition( true, cursorPos );
3196
3197 // The tool hotkey is interpreted as a click when drawing
3198 bool isSyntheticClick = sheet && evt->IsActivate() && evt->HasPosition()
3199 && evt->Matches( aEvent );
3200
3201 if( evt->IsCancelInteractive() || ( sheet && evt->IsAction( &ACTIONS::undo ) ) )
3202 {
3203 m_frame->GetInfoBar()->Dismiss();
3204
3205 if( sheet )
3206 {
3207 cleanup();
3208 }
3209 else
3210 {
3211 m_frame->PopTool( aEvent );
3212 break;
3213 }
3214 }
3215 else if( evt->IsActivate() && !isSyntheticClick )
3216 {
3217 if( sheet && evt->IsMoveTool() )
3218 {
3219 // we're already drawing our own item; ignore the move tool
3220 evt->SetPassEvent( false );
3221 continue;
3222 }
3223
3224 if( sheet )
3225 {
3226 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel sheet creation." ) );
3227 evt->SetPassEvent( false );
3228 continue;
3229 }
3230
3231 if( evt->IsPointEditor() )
3232 {
3233 // don't exit (the point editor runs in the background)
3234 }
3235 else if( evt->IsMoveTool() )
3236 {
3237 // leave ourselves on the stack so we come back after the move
3238 break;
3239 }
3240 else
3241 {
3242 m_frame->PopTool( aEvent );
3243 break;
3244 }
3245 }
3246 else if( !sheet && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
3247 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick )
3248 || evt->IsDrag( BUT_LEFT ) ) )
3249 {
3250 SCH_SELECTION& selection = m_selectionTool->GetSelection();
3251
3252 if( selection.Size() == 1
3253 && selection.Front()->Type() == SCH_SHEET_T
3254 && selection.Front()->GetBoundingBox().Contains( cursorPos ) )
3255 {
3256 if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorClick ) )
3257 {
3258 // sheet already selected
3259 continue;
3260 }
3261 else if( evt->IsDblClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
3262 {
3263 m_toolMgr->PostAction( SCH_ACTIONS::enterSheet );
3264 m_frame->PopTool( aEvent );
3265 break;
3266 }
3267 }
3268
3269 m_toolMgr->RunAction( ACTIONS::selectionClear );
3270
3271 VECTOR2I sheetPos = evt->IsDrag( BUT_LEFT ) ?
3272 grid.Align( evt->DragOrigin(), GRID_HELPER_GRIDS::GRID_GRAPHICS ) :
3273 cursorPos;
3274
3275 // Remember whether this sheet was initiated with a drag so we can treat mouse-up as
3276 // the terminating (second) click.
3277 startedWithDrag = evt->IsDrag( BUT_LEFT );
3278
3279 sheet = new SCH_SHEET( m_frame->GetCurrentSheet().Last(), sheetPos );
3280 sheet->SetScreen( nullptr );
3281
3282 wxString ext = wxString( "." ) + FILEEXT::KiCadSchematicFileExtension;
3283
3284 if( isDrawSheetCopy )
3285 {
3286 wxFileName fn( filename );
3287
3288 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( fn.GetName() );
3289 sheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( fn.GetName() + ext );
3290 }
3291 else if( isDrawSheetFromDesignBlock )
3292 {
3293 wxFileName fn( filename );
3294
3295 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( designBlock->GetLibId().GetLibItemName() );
3296 sheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( fn.GetName() + ext );
3297
3298 std::vector<SCH_FIELD>& sheetFields = sheet->GetFields();
3299
3300 // Copy default fields into the sheet
3301 for( const auto& [fieldName, fieldValue] : designBlock->GetFields() )
3302 {
3303 sheetFields.emplace_back( sheet, FIELD_T::USER, fieldName );
3304 sheetFields.back().SetText( fieldValue );
3305 sheetFields.back().SetVisible( false );
3306 }
3307 }
3308 else
3309 {
3310 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( wxT( "Untitled Sheet" ) );
3311 sheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( wxT( "untitled" ) + ext );
3312 }
3313
3314 sheet->SetFlags( IS_NEW | IS_MOVING );
3315 sheet->SetBorderWidth( schIUScale.MilsToIU( cfg->m_Drawing.default_line_thickness ) );
3318 sizeSheet( sheet, cursorPos );
3319
3320 SCH_SHEET_LIST hierarchy = m_frame->Schematic().Hierarchy();
3321 SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
3322 instance.push_back( sheet );
3323 wxString pageNumber;
3324
3325 // Find the next available page number by checking all existing page numbers
3326 std::set<int> usedPageNumbers;
3327
3328 for( const SCH_SHEET_PATH& path : hierarchy )
3329 {
3330 wxString existingPageNum = path.GetPageNumber();
3331 long pageNum = 0;
3332
3333 if( existingPageNum.ToLong( &pageNum ) && pageNum > 0 )
3334 usedPageNumbers.insert( static_cast<int>( pageNum ) );
3335 }
3336
3337 // Find the first available number starting from 1
3338 int nextAvailable = 1;
3339
3340 while( usedPageNumbers.count( nextAvailable ) > 0 )
3341 nextAvailable++;
3342
3343 pageNumber.Printf( wxT( "%d" ), nextAvailable );
3344 instance.SetPageNumber( pageNumber );
3345
3346 m_view->ClearPreview();
3347 m_view->AddToPreview( sheet->Clone() );
3348 }
3349 else if( sheet && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
3350 || isSyntheticClick
3351 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick )
3352 || evt->IsAction( &ACTIONS::finishInteractive )
3353 || ( startedWithDrag && evt->IsMouseUp( BUT_LEFT ) ) ) )
3354 {
3355 getViewControls()->SetAutoPan( false );
3356 getViewControls()->CaptureCursor( false );
3357
3358 if( m_frame->EditSheetProperties( static_cast<SCH_SHEET*>( sheet ), &m_frame->GetCurrentSheet(),
3359 nullptr, nullptr, nullptr, &filename ) )
3360 {
3361 m_view->ClearPreview();
3362
3363 sheet->AutoplaceFields( m_frame->GetScreen(), AUTOPLACE_AUTO );
3364
3365 // Use the commit we were provided or make our own
3366 SCH_COMMIT tempCommit = SCH_COMMIT( m_toolMgr );
3367 SCH_COMMIT& c = evt->Commit() ? *( (SCH_COMMIT*) evt->Commit() ) : tempCommit;
3368
3369 // We need to manually add the sheet to the screen otherwise annotation will not be able to find
3370 // the sheet and its symbols to annotate.
3371 m_frame->AddToScreen( sheet );
3372 c.Added( sheet, m_frame->GetScreen() );
3373
3374 // This convoluted logic means we always annotate unless we are drawing a copy/design block
3375 // and the user has explicitly requested we keep the annotations via checkbox
3376
3377 if( cfg->m_AnnotatePanel.automatic
3378 && !( ( isDrawSheetCopy || isDrawSheetFromDesignBlock )
3380 {
3381 // Annotation will remove this from selection, but we add it back later
3382 m_selectionTool->AddItemToSel( sheet );
3383
3384 NULL_REPORTER reporter;
3385 m_frame->AnnotateSymbols( &c,
3388 (ANNOTATE_ALGO_T) schSettings.m_AnnotateMethod,
3389 true, /* recursive */
3390 schSettings.m_AnnotateStartNum,
3391 true, /* reset */
3392 false, /* repair */
3393 reporter );
3394 }
3395
3396 c.Push( isDrawSheetCopy ? "Import Sheet Copy" : "Draw Sheet" );
3397
3398 m_selectionTool->AddItemToSel( sheet );
3399 }
3400 else
3401 {
3402 m_view->ClearPreview();
3403 delete sheet;
3404 }
3405
3406 sheet = nullptr;
3407 }
3408 else if( evt->IsAction( &ACTIONS::duplicate )
3409 || evt->IsAction( &SCH_ACTIONS::repeatDrawItem )
3410 || evt->IsAction( &ACTIONS::paste ) )
3411 {
3412 if( sheet )
3413 {
3414 wxBell();
3415 continue;
3416 }
3417
3418 // Exit. The duplicate/repeat/paste will run in its own loop.
3419 m_frame->PopTool( aEvent );
3420 evt->SetPassEvent();
3421 break;
3422 }
3423 else if( sheet && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion()
3424 || evt->IsDrag( BUT_LEFT ) ) )
3425 {
3426 sizeSheet( sheet, cursorPos );
3427 m_view->ClearPreview();
3428 m_view->AddToPreview( sheet->Clone() );
3429 m_frame->SetMsgPanel( sheet );
3430 }
3431 else if( evt->IsClick( BUT_RIGHT ) )
3432 {
3433 // Warp after context menu only if dragging...
3434 if( !sheet )
3435 m_toolMgr->VetoContextMenuMouseWarp();
3436
3437 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
3438 }
3439 else if( sheet && evt->IsAction( &ACTIONS::redo ) )
3440 {
3441 wxBell();
3442 }
3443 else
3444 {
3445 evt->SetPassEvent();
3446 }
3447
3448 // Enable autopanning and cursor capture only when there is a sheet to be placed
3449 getViewControls()->SetAutoPan( sheet != nullptr );
3450 getViewControls()->CaptureCursor( sheet != nullptr );
3451 }
3452
3453 getViewControls()->SetAutoPan( false );
3454 getViewControls()->CaptureCursor( false );
3455 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3456
3457 return 0;
3458}
3459
3460
3462{
3463 VECTOR2I pos = aSheet->GetPosition();
3464 VECTOR2I size = aPos - pos;
3465
3466 size.x = std::max( size.x, schIUScale.MilsToIU( MIN_SHEET_WIDTH ) );
3467 size.y = std::max( size.y, schIUScale.MilsToIU( MIN_SHEET_HEIGHT ) );
3468
3469 VECTOR2I grid = m_frame->GetNearestGridPosition( pos + size );
3470 aSheet->Resize( VECTOR2I( grid.x - pos.x, grid.y - pos.y ) );
3471}
3472
3473
3474int SCH_DRAWING_TOOLS::doSyncSheetsPins( std::list<SCH_SHEET_PATH> sheetPaths )
3475{
3476 if( !sheetPaths.size() )
3477 return 0;
3478
3479 m_dialogSyncSheetPin = std::make_unique<DIALOG_SYNC_SHEET_PINS>(
3480 m_frame, std::move( sheetPaths ),
3481 std::make_shared<SHEET_SYNCHRONIZATION_AGENT>(
3482 [&]( EDA_ITEM* aItem, SCH_SHEET_PATH aPath,
3484 {
3485 SCH_COMMIT commit( m_toolMgr );
3486
3487 if( auto pin = dynamic_cast<SCH_SHEET_PIN*>( aItem ) )
3488 {
3489 commit.Modify( pin->GetParent(), aPath.LastScreen() );
3490 aModify();
3491 commit.Push( _( "Modify sheet pin" ) );
3492 }
3493 else
3494 {
3495 commit.Modify( aItem, aPath.LastScreen() );
3496 aModify();
3497 commit.Push( _( "Modify schematic item" ) );
3498 }
3499
3500 updateItem( aItem, true );
3501 m_frame->OnModify();
3502 },
3503 [&]( EDA_ITEM* aItem, SCH_SHEET_PATH aPath )
3504 {
3505 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &aPath );
3506 SCH_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
3507 selectionTool->UnbrightenItem( aItem );
3508 selectionTool->AddItemToSel( aItem, true );
3509 m_toolMgr->RunAction( ACTIONS::doDelete );
3510 },
3511 [&]( SCH_SHEET* aItem, SCH_SHEET_PATH aPath,
3513 std::set<EDA_ITEM*> aTemplates )
3514 {
3515 switch( aOp )
3516 {
3518 {
3519 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
3520 m_dialogSyncSheetPin->Hide();
3521 m_dialogSyncSheetPin->PreparePlacementTemplate(
3523 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &aPath );
3525 break;
3526 }
3528 {
3529 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
3530 m_dialogSyncSheetPin->Hide();
3531 m_dialogSyncSheetPin->PreparePlacementTemplate(
3533 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &aPath );
3534 m_toolMgr->GetTool<SCH_SELECTION_TOOL>()->SyncSelection( {}, nullptr, { sheet } );
3536 break;
3537 }
3538 }
3539 },
3540 m_toolMgr, m_frame ) );
3541 m_dialogSyncSheetPin->Show( true );
3542 return 0;
3543}
3544
3545
3547{
3548 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( m_selectionTool->GetSelection().Front() );
3549
3550 if( !sheet )
3551 {
3552 VECTOR2I cursorPos = getViewControls()->GetMousePosition();
3553
3554 if( EDA_ITEM* i = nullptr; static_cast<void>(m_selectionTool->SelectPoint( cursorPos, { SCH_SHEET_T }, &i ) ) , i != nullptr )
3555 {
3556 sheet = dynamic_cast<SCH_SHEET*>( i );
3557 }
3558 }
3559
3560 if ( sheet )
3561 {
3562 SCH_SHEET_PATH current = m_frame->GetCurrentSheet();
3563 current.push_back( sheet );
3564 return doSyncSheetsPins( { current } );
3565 }
3566
3567 return 0;
3568}
3569
3570
3572{
3573 if( m_inDrawingTool )
3574 return 0;
3575
3577
3578 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( m_selectionTool->GetSelection().Front() );
3579
3580 if( !sheet )
3581 return 0;
3582
3583 std::vector<SCH_HIERLABEL*> labels = importHierLabels( sheet );
3584
3585 if( labels.empty() )
3586 {
3587 m_frame->PushTool( aEvent );
3588 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( m_frame );
3589 m_statusPopup->SetText( _( "No new hierarchical labels found." ) );
3590 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
3591 m_statusPopup->PopupFor( 2000 );
3592 m_frame->PopTool( aEvent );
3593 m_toolMgr->RunAction( ACTIONS::selectionClear );
3594 m_view->ClearPreview();
3595 return 0;
3596 }
3597
3598 m_toolMgr->RunAction( ACTIONS::selectionClear );
3599
3600 SCH_COMMIT commit( m_toolMgr );
3601 BOX2I bbox = sheet->GetBoundingBox();
3602 VECTOR2I cursorPos = bbox.GetPosition();
3603 SCH_ITEM* lastPlacedLabel = nullptr;
3604
3605 auto calculatePositionForLabel =
3606 [&]( const SCH_ITEM* lastLabel, const SCH_HIERLABEL* currentLabel ) -> VECTOR2I
3607 {
3608 if( !lastLabel )
3609 return cursorPos;
3610
3611 int lastX = lastLabel->GetPosition().x;
3612 int lastY = lastLabel->GetPosition().y;
3613 int lastWidth = lastLabel->GetBoundingBox().GetWidth();
3614 int lastHeight = lastLabel->GetBoundingBox().GetHeight();
3615
3616 int currentWidth = currentLabel->GetBoundingBox().GetWidth();
3617 int currentHeight = currentLabel->GetBoundingBox().GetHeight();
3618
3619 // If there is enough space, place the label to the right of the last placed label
3620 if( ( lastX + lastWidth + currentWidth ) <= ( bbox.GetPosition().x + bbox.GetSize().x ) )
3621 return { lastX + lastWidth, lastY };
3622
3623 // If not enough space to the right, move to the next row if vertical space allows
3624 if( ( lastY + lastHeight + currentHeight ) <= ( bbox.GetPosition().y + bbox.GetSize().y ) )
3625 return { bbox.GetPosition().x, lastY + lastHeight };
3626
3627 return cursorPos;
3628 };
3629
3630 for( SCH_HIERLABEL* label : labels )
3631 {
3632 if( !lastPlacedLabel )
3633 {
3634 std::vector<SCH_SHEET_PIN*> existingPins = sheet->GetPins();
3635
3636 if( !existingPins.empty() )
3637 {
3638 std::sort( existingPins.begin(), existingPins.end(),
3639 []( const SCH_ITEM* a, const SCH_ITEM* b )
3640 {
3641 return ( a->GetPosition().x < b->GetPosition().x )
3642 || ( a->GetPosition().x == b->GetPosition().x
3643 && a->GetPosition().y < b->GetPosition().y );
3644 } );
3645
3646 lastPlacedLabel = existingPins.back();
3647 }
3648 }
3649
3650 cursorPos = calculatePositionForLabel( lastPlacedLabel, label );
3651 SCH_ITEM* item = createNewSheetPinFromLabel( sheet, cursorPos, label );
3652
3653 if( item )
3654 {
3655 item->SetFlags( IS_NEW | IS_MOVING );
3656 item->AutoplaceFields( nullptr, AUTOPLACE_AUTO );
3657 item->ClearFlags( IS_MOVING );
3658
3659 if( item->IsConnectable() )
3660 m_frame->AutoRotateItem( m_frame->GetScreen(), item );
3661
3662 commit.Modify( sheet, m_frame->GetScreen() );
3663
3664 sheet->AddPin( static_cast<SCH_SHEET_PIN*>( item ) );
3665 item->AutoplaceFields( m_frame->GetScreen(), AUTOPLACE_AUTO );
3666
3667 commit.Push( _( "Add Sheet Pin" ) );
3668
3669 lastPlacedLabel = item;
3670 }
3671 }
3672
3673 return 0;
3674}
3675
3676
3678{
3679 static const std::function<void( std::list<SCH_SHEET_PATH>&, SCH_SCREEN*, std::set<SCH_SCREEN*>&,
3680 SCH_SHEET_PATH const& )> getSheetChildren =
3681 []( std::list<SCH_SHEET_PATH>& aPaths, SCH_SCREEN* aScene, std::set<SCH_SCREEN*>& aVisited,
3682 SCH_SHEET_PATH const& aCurPath )
3683 {
3684 if( ! aScene || aVisited.find(aScene) != aVisited.end() )
3685 return ;
3686
3687 std::vector<SCH_ITEM*> sheetChildren;
3688 aScene->GetSheets( &sheetChildren );
3689 aVisited.insert( aScene );
3690
3691 for( SCH_ITEM* child : sheetChildren )
3692 {
3693 SCH_SHEET_PATH cp = aCurPath;
3694 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( child );
3695 cp.push_back( sheet );
3696 aPaths.push_back( cp );
3697 getSheetChildren( aPaths, sheet->GetScreen(), aVisited, cp );
3698 }
3699 };
3700
3701 std::list<SCH_SHEET_PATH> sheetPaths;
3702 std::set<SCH_SCREEN*> visited;
3703
3704 // Build sheet paths for each top-level sheet (don't include virtual root in paths)
3705 std::vector<SCH_SHEET*> topLevelSheets = m_frame->Schematic().GetTopLevelSheets();
3706
3707 for( SCH_SHEET* topSheet : topLevelSheets )
3708 {
3709 if( topSheet && topSheet->GetScreen() )
3710 {
3711 SCH_SHEET_PATH current;
3712 current.push_back( topSheet );
3713 getSheetChildren( sheetPaths, topSheet->GetScreen(), visited, current );
3714 }
3715 }
3716
3717 if( sheetPaths.size() == 0 )
3718 {
3719 m_frame->ShowInfoBarMsg( _( "No sub schematic found in the current project" ) );
3720 return 0;
3721 }
3722
3723
3724 return doSyncSheetsPins( std::move( sheetPaths ) );
3725}
3726
3728{
3729 if( !aSheet->GetScreen() )
3730 return nullptr;
3731
3732 std::vector<SCH_HIERLABEL*> labels;
3733
3734 for( EDA_ITEM* item : aSheet->GetScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
3735 {
3736 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
3737 labels.push_back( label );
3738 }
3739
3740 std::sort( labels.begin(), labels.end(),
3741 []( const SCH_HIERLABEL* label1, const SCH_HIERLABEL* label2 )
3742 {
3743 return StrNumCmp( label1->GetText(), label2->GetText(), true ) < 0;
3744 } );
3745
3746 for( SCH_HIERLABEL* label : labels )
3747 {
3748 if( !aSheet->HasPin( label->GetText() ) )
3749 return label;
3750 }
3751
3752 return nullptr;
3753}
3754
3755
3756std::vector<SCH_HIERLABEL*> SCH_DRAWING_TOOLS::importHierLabels( SCH_SHEET* aSheet )
3757{
3758 if( !aSheet->GetScreen() )
3759 return {};
3760
3761 std::vector<SCH_HIERLABEL*> labels;
3762
3763 for( EDA_ITEM* item : aSheet->GetScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
3764 {
3765 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
3766
3767 if( !aSheet->HasPin( label->GetText() ) )
3768 labels.push_back( label );
3769 }
3770
3771 return labels;
3772}
3773
3774
3776{
3777 // clang-format off
3808 // clang-format on
3809}
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 paste
Definition actions.h:80
static TOOL_ACTION cancelInteractive
Definition actions.h:72
static TOOL_ACTION cursorDblClick
Definition actions.h:181
static TOOL_ACTION undo
Definition actions.h:75
static TOOL_ACTION duplicate
Definition actions.h:84
static TOOL_ACTION activatePointEditor
Definition actions.h:271
static TOOL_ACTION doDelete
Definition actions.h:85
static TOOL_ACTION cursorClick
Definition actions.h:180
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:224
static TOOL_ACTION refreshPreview
Definition actions.h:159
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:232
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:169
const EDA_ANGLE & GetTextAngle() const
Definition eda_text.h:147
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:540
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:98
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:425
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:200
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:394
void SetBold(bool aBold)
Set the text to be bold - this will also update the font if needed.
Definition eda_text.cpp:343
bool IsBold() const
Definition eda_text.h:184
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:203
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:278
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:307
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:315
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:417
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:105
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
LEGACY_SYMBOL_LIB * GetCacheLibrary()
Object used to load, save, search, and otherwise manipulate symbol library files.
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:83
wxString GetDescription() const override
Definition lib_symbol.h:168
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:153
wxString GetKeyWords() const override
Definition lib_symbol.h:183
void SetGlobalPower()
void SetDescription(const wxString &aDescription)
Gets the Description field text value *‍/.
Definition lib_symbol.h:162
void SetKeyWords(const wxString &aKeyWords)
Definition lib_symbol.h:181
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:537
Class that handles the drawing of a polygon, including management of last corner deletion and drawing...
bool AddPoint(const VECTOR2I &aPt)
Lock in a polygon point.
void SetCursorPosition(const VECTOR2I &aPos)
Set the current cursor position.
bool NewPointClosesOutline(const VECTOR2I &aPt) const
std::optional< VECTOR2I > DeleteLastCorner()
Remove the last-added point from the polygon.
void SetFinished()
Mark the polygon finished and update the client.
void SetLeaderMode(LEADER_MODE aMode)
Set the leader mode to use when calculating the leader/returner lines.
void Reset()
Clear the manager state and start again.
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
static LEGACY_SYMBOL_LIBS * LegacySchLibs(PROJECT *aProject)
Returns the list of symbol libraries from a legacy (pre-5.x) design This is only used from the remapp...
An adjunct helper to the DRAWING_TOOL interactive tool, which handles incoming geometry changes from ...
These are loaded from Eeschema settings but then overwritten by the project settings.
std::shared_ptr< REFDES_TRACKER > m_refDesTracker
A list of previously used schematic reference designators.
Holds all the data relating to one schematic.
Definition schematic.h: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:507
virtual void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo)
Definition sch_item.h:609
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode)
Definition sch_item.h:611
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:244
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:352
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:988
VECTOR2I GetEndPoint() const
Definition sch_line.h:148
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:463
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:198
Schematic symbol object.
Definition sch_symbol.h:76
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:165
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:601
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
An interface to the global shared library manager that is schematic-specific and linked to one projec...
Helper object to filter a list of libraries.
SCH_EDIT_FRAME * getModel() const
Definition tool_base.h: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 RunMainStack(std::function< void()> aFunc)
void Go(int(SCH_EDIT_FRAME::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
std::unique_ptr< TOOL_MENU > m_menu
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
This file is part of the common library.
@ COMPONENT
Definition cursors.h:84
@ PLACE
Definition cursors.h:98
@ LABEL_GLOBAL
Definition cursors.h:82
@ MOVING
Definition cursors.h:48
@ LABEL_NET
Definition cursors.h:80
@ ARROW
Definition cursors.h:46
@ LABEL_HIER
Definition cursors.h:96
@ PENCIL
Definition cursors.h:52
#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:44
@ GRID_TEXT
Definition grid_helper.h:51
@ GRID_GRAPHICS
Definition grid_helper.h:52
@ GRID_CONNECTABLE
Definition grid_helper.h:48
static const std::string KiCadSchematicFileExtension
static wxString KiCadSchematicFileWildcard()
@ LAYER_HIERLABEL
Definition layer_ids.h:457
@ LAYER_GLOBLABEL
Definition layer_ids.h:456
@ LAYER_NOTES
Definition layer_ids.h:467
@ LAYER_LOCLABEL
Definition layer_ids.h:455
@ LAYER_NETCLASS_REFS
Definition layer_ids.h:464
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:946
see class PGM_BASE
LIB_SYMBOL * SchGetLibSymbol(const LIB_ID &aLibId, SYMBOL_LIBRARY_ADAPTER *aLibMgr, LEGACY_SYMBOL_LIB *aCacheLib, wxWindow *aParent, bool aShowErrorMsg)
Load symbol from symbol library table.
Class to handle a set of SCH_ITEMs.
@ AUTOPLACE_AUTO
Definition sch_item.h: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
@ USER
The field ID hasn't been set yet; field is invalid.
@ INTERSHEET_REFS
Global label cross-reference page numbers.
std::string path
KIBIS_PIN * pin
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
@ MD_SHIFT
Definition tool_event.h:143
@ TC_COMMAND
Definition tool_event.h:57
@ 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:167
@ SCH_NO_CONNECT_T
Definition typeinfo.h:164
@ SCH_SYMBOL_T
Definition typeinfo.h:176
@ SCH_SHEET_T
Definition typeinfo.h:179
@ SCH_HIER_LABEL_T
Definition typeinfo.h:173
@ SCH_SHEET_PIN_T
Definition typeinfo.h:178
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:165
@ SCH_JUNCTION_T
Definition typeinfo.h:163
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695