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