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