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