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