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