KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drawing_tool.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) 2014-2017 CERN
5 * Copyright (C) 2018-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include "drawing_tool.h"
27#include "geometry/shape_rect.h"
28
29#include <pgm_base.h>
31#include <pcbnew_settings.h>
33#include <dialogs/dialog_text_properties.h>
41#include <router/router_tool.h>
42#include <tool/tool_manager.h>
43#include <tools/pcb_actions.h>
49#include <view/view.h>
51#include <widgets/wx_infobar.h>
52#include <wx/filedlg.h>
53
54#include <bitmaps.h>
55#include <board.h>
56#include <board_commit.h>
58#include <confirm.h>
59#include <footprint.h>
60#include <macros.h>
61#include <painter.h>
62#include <pcb_edit_frame.h>
63#include <pcb_group.h>
64#include <pcb_bitmap.h>
65#include <pcb_text.h>
66#include <pcb_textbox.h>
67#include <pcb_dimension.h>
68#include <pcbnew_id.h>
70#include <scoped_set_reset.h>
71#include <string_utils.h>
72#include <zone.h>
73
74const unsigned int DRAWING_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
75
77
78
80{
81public:
83 ACTION_MENU( true )
84 {
85 SetIcon( BITMAPS::width_track_via );
86 SetTitle( _( "Select Via Size" ) );
87 }
88
89protected:
90 ACTION_MENU* create() const override
91 {
92 return new VIA_SIZE_MENU();
93 }
94
95 void update() override
96 {
99 bool useIndex = !bds.m_UseConnectedTrackWidth
100 && !bds.UseCustomTrackViaSize();
101 wxString msg;
102
103 Clear();
104
105 Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
106 _( "Specify custom track and via sizes" ), wxITEM_CHECK );
108
109 AppendSeparator();
110
111 for( unsigned i = 1; i < bds.m_ViasDimensionsList.size(); i++ )
112 {
114
115 if( via.m_Drill > 0 )
116 {
117 msg.Printf( _("Via %s, hole %s" ),
118 frame->MessageTextFromValue( via.m_Diameter ),
119 frame->MessageTextFromValue( via.m_Drill ) );
120 }
121 else
122 {
123 msg.Printf( _( "Via %s" ),
124 frame->MessageTextFromValue( via.m_Diameter ) );
125 }
126
127 int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
128 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
129 Check( menuIdx, useIndex && bds.GetViaSizeIndex() == i );
130 }
131 }
132
133 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
134 {
137 int id = aEvent.GetId();
138
139 // On Windows, this handler can be called with an event ID not existing in any
140 // menuitem, so only set flags when we have an ID match.
141
143 {
144 DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds );
145
146 if( sizeDlg.ShowModal() == wxID_OK )
147 {
148 bds.UseCustomTrackViaSize( true );
149 bds.m_UseConnectedTrackWidth = false;
150 }
151 }
153 {
154 bds.UseCustomTrackViaSize( false );
155 bds.m_UseConnectedTrackWidth = false;
157 }
158
160 }
161};
162
163
165 PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ),
166 m_view( nullptr ),
167 m_controls( nullptr ),
168 m_board( nullptr ),
169 m_frame( nullptr ),
170 m_mode( MODE::NONE ),
171 m_inDrawingTool( false ),
172 m_layer( UNDEFINED_LAYER ),
173 m_stroke( 1, PLOT_DASH_TYPE::DEFAULT, COLOR4D::UNSPECIFIED )
174{
175}
176
177
179{
180}
181
182
184{
185 auto haveHighlight =
186 [&]( const SELECTION& sel )
187 {
189
190 return !cfg->GetHighlightNetCodes().empty();
191 };
192
193 auto activeToolFunctor =
194 [this]( const SELECTION& aSel )
195 {
196 return m_mode != MODE::NONE;
197 };
198
199 // some interactive drawing tools can undo the last point
200 auto canUndoPoint =
201 [this]( const SELECTION& aSel )
202 {
203 return ( m_mode == MODE::ARC
204 || m_mode == MODE::ZONE
207 };
208
209 // functor for tools that can automatically close the outline
210 auto canCloseOutline =
211 [this]( const SELECTION& aSel )
212 {
213 return ( m_mode == MODE::ZONE
216 };
217
218 auto arcToolActive =
219 [this]( const SELECTION& aSel )
220 {
221 return m_mode == MODE::ARC;
222 };
223
224 auto viaToolActive =
225 [this]( const SELECTION& aSel )
226 {
227 return m_mode == MODE::VIA;
228 };
229
230 CONDITIONAL_MENU& ctxMenu = m_menu.GetMenu();
231
232 // cancel current tool goes in main context menu at the top if present
233 ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
234 ctxMenu.AddSeparator( 1 );
235
236 ctxMenu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 2 );
237 ctxMenu.AddSeparator( haveHighlight, 2 );
238
239 // tool-specific actions
240 ctxMenu.AddItem( PCB_ACTIONS::closeOutline, canCloseOutline, 200 );
241 ctxMenu.AddItem( PCB_ACTIONS::deleteLastPoint, canUndoPoint, 200 );
242 ctxMenu.AddItem( PCB_ACTIONS::arcPosture, arcToolActive, 200 );
243
245 ctxMenu.AddSeparator( 500 );
246
247 std::shared_ptr<VIA_SIZE_MENU> viaSizeMenu = std::make_shared<VIA_SIZE_MENU>();
248 viaSizeMenu->SetTool( this );
249 m_menu.RegisterSubMenu( viaSizeMenu );
250 ctxMenu.AddMenu( viaSizeMenu.get(), viaToolActive, 500 );
251
252 ctxMenu.AddSeparator( 500 );
253
254 // Type-specific sub-menus will be added for us by other tools
255 // For example, zone fill/unfill is provided by the PCB control tool
256
257 // Finally, add the standard zoom/grid items
258 getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( m_menu );
259
260 return true;
261}
262
263
265{
266 // Init variables used by every drawing tool
267 m_view = getView();
269 m_board = getModel<BOARD>();
270 m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
271
273}
274
275
277{
278 return m_mode;
279}
280
281
283{
284 if( m_frame )
285 {
286 SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
287 bool constrained;
288
290 constrained = mgr.GetAppSettings<PCBNEW_SETTINGS>()->m_Use45DegreeLimit;
291 else
292 constrained = mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>()->m_Use45Limit;
293
294 m_frame->DisplayConstraintsMsg( constrained ? _( "Constrain to H, V, 45" ) : wxString( "" ) );
295 }
296}
297
298
300{
302 return 0;
303
304 if( m_inDrawingTool )
305 return 0;
306
308
309 BOARD_ITEM* parent = m_frame->GetModel();
310 PCB_SHAPE* line = new PCB_SHAPE( parent );
311 BOARD_COMMIT commit( m_frame );
312 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
313 std::optional<VECTOR2D> startingPoint;
314
315 line->SetShape( SHAPE_T::SEGMENT );
316 line->SetFlags( IS_NEW );
317
318 if( aEvent.HasPosition() )
319 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
320
321 m_frame->PushTool( aEvent );
322 Activate();
323
324 while( drawShape( aEvent, &line, startingPoint ) )
325 {
326 if( line )
327 {
328 commit.Add( line );
329 commit.Push( _( "Draw a line segment" ) );
330 startingPoint = VECTOR2D( line->GetEnd() );
331 }
332 else
333 {
334 startingPoint = std::nullopt;
335 }
336
337 line = new PCB_SHAPE( parent );
338 line->SetShape( SHAPE_T::SEGMENT );
339 line->SetFlags( IS_NEW );
340 }
341
342 return 0;
343}
344
345
347{
349 return 0;
350
351 if( m_inDrawingTool )
352 return 0;
353
355
356 bool isTextBox = aEvent.IsAction( &PCB_ACTIONS::drawTextBox );
357 PCB_SHAPE* rect = nullptr;
358 BOARD_COMMIT commit( m_frame );
359 BOARD_ITEM* parent = m_frame->GetModel();
360 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::RECTANGLE );
361 std::optional<VECTOR2D> startingPoint;
362
363 rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
364 rect->SetShape( SHAPE_T::RECT );
365 rect->SetFilled( false );
366 rect->SetFlags( IS_NEW );
367
368 if( aEvent.HasPosition() )
369 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
370
371 m_frame->PushTool( aEvent );
372 Activate();
373
374 while( drawShape( aEvent, &rect, startingPoint ) )
375 {
376 if( rect )
377 {
378 bool cancelled = false;
379
380 if( PCB_TEXTBOX* textbox = dynamic_cast<PCB_TEXTBOX*>( rect ) )
381 cancelled = m_frame->ShowTextBoxPropertiesDialog( textbox ) != wxID_OK;
382
383 if( cancelled )
384 {
385 delete rect;
386 rect = nullptr;
387 }
388 else
389 {
390 rect->NormalizeRect();
391 commit.Add( rect );
392 commit.Push( isTextBox ? _( "Draw a text box" ) : _( "Draw a rectangle" ) );
393
395 }
396 }
397
398 rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
399 rect->SetShape( SHAPE_T::RECT );
400 rect->SetFilled( false );
401 rect->SetFlags( IS_NEW );
402 startingPoint = std::nullopt;
403 }
404
405 return 0;
406}
407
408
410{
412 return 0;
413
414 if( m_inDrawingTool )
415 return 0;
416
418
419 BOARD_ITEM* parent = m_frame->GetModel();
420 PCB_SHAPE* circle = new PCB_SHAPE( parent );
421 BOARD_COMMIT commit( m_frame );
422 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::CIRCLE );
423 std::optional<VECTOR2D> startingPoint;
424
425 circle->SetShape( SHAPE_T::CIRCLE );
426 circle->SetFilled( false );
427 circle->SetFlags( IS_NEW );
428
429 if( aEvent.HasPosition() )
430 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
431
432 m_frame->PushTool( aEvent );
433 Activate();
434
435 while( drawShape( aEvent, &circle, startingPoint ) )
436 {
437 if( circle )
438 {
439 commit.Add( circle );
440 commit.Push( _( "Draw a circle" ) );
441
443 }
444
445 circle = new PCB_SHAPE( parent );
446 circle->SetShape( SHAPE_T::CIRCLE );
447 circle->SetFilled( false );
448 circle->SetFlags( IS_NEW );
449
450 startingPoint = std::nullopt;
451 }
452
453 return 0;
454}
455
456
458{
460 return 0;
461
462 if( m_inDrawingTool )
463 return 0;
464
466
467 BOARD_ITEM* parent = m_frame->GetModel();
468 PCB_SHAPE* arc = new PCB_SHAPE( parent );
469 BOARD_COMMIT commit( m_frame );
470 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
471 std::optional<VECTOR2D> startingPoint;
472
473 arc->SetShape( SHAPE_T::ARC );
474 arc->SetFlags( IS_NEW );
475
476 m_frame->PushTool( aEvent );
477 Activate();
478
479 if( aEvent.HasPosition() )
480 startingPoint = aEvent.Position();
481
482 while( drawArc( aEvent, &arc, startingPoint ) )
483 {
484 if( arc )
485 {
486 commit.Add( arc );
487 commit.Push( _( "Draw an arc" ) );
488
490 }
491
492 arc = new PCB_SHAPE( parent );
493 arc->SetShape( SHAPE_T::ARC );
494 arc->SetFlags( IS_NEW );
495
496 startingPoint = std::nullopt;
497 }
498
499 return 0;
500}
501
502
504{
505 if( m_inDrawingTool )
506 return 0;
507
509
510 PCB_BITMAP* image = aEvent.Parameter<PCB_BITMAP*>();
511 bool immediateMode = image != nullptr;
513 bool ignorePrimePosition = false;
514 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
515
518 BOARD_COMMIT commit( m_frame );
519
521
522 // Add all the drawable symbols to preview
523 if( image )
524 {
525 image->SetPosition( cursorPos );
527 m_view->AddToPreview( image, false ); // Add, but not give ownership
528 }
529
530 m_frame->PushTool( aEvent );
531 auto setCursor =
532 [&]()
533 {
534 if( image )
535 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
536 else
537 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
538 };
539
540 auto cleanup =
541 [&] ()
542 {
546 delete image;
547 image = nullptr;
548 };
549
550 Activate();
551
552 // Must be done after Activate() so that it gets set into the correct context
553 getViewControls()->ShowCursor( true );
554
555 // Set initial cursor
556 setCursor();
557
558 // Prime the pump
559 if( image )
560 {
562 }
563 else if( aEvent.HasPosition() )
564 {
565 m_toolMgr->PrimeTool( aEvent.Position() );
566 }
567 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
568 {
569 m_toolMgr->PrimeTool( { 0, 0 } );
570 ignorePrimePosition = true;
571 }
572
573 // Main loop: keep receiving events
574 while( TOOL_EVENT* evt = Wait() )
575 {
576 setCursor();
577
578 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
579 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
580 cursorPos = GetClampedCoords(
581 grid.BestSnapAnchor( m_controls->GetMousePosition(), m_frame->GetActiveLayer() ),
583 m_controls->ForceCursorPosition( true, cursorPos );
584
585 if( evt->IsCancelInteractive() )
586 {
587 if( image )
588 {
589 cleanup();
590 }
591 else
592 {
593 m_frame->PopTool( aEvent );
594 break;
595 }
596
597 if( immediateMode )
598 {
599 m_frame->PopTool( aEvent );
600 break;
601 }
602 }
603 else if( evt->IsActivate() )
604 {
605 if( image && evt->IsMoveTool() )
606 {
607 // We're already moving our own item; ignore the move tool
608 evt->SetPassEvent( false );
609 continue;
610 }
611
612 if( image )
613 {
614 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel image creation." ) );
615 evt->SetPassEvent( false );
616 continue;
617 }
618
619 if( evt->IsMoveTool() )
620 {
621 // Leave ourselves on the stack so we come back after the move
622 break;
623 }
624 else
625 {
626 m_frame->PopTool( aEvent );
627 break;
628 }
629 }
630 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
631 {
632 if( !image )
633 {
635
636 wxFileDialog dlg( m_frame, _( "Choose Image" ), wxEmptyString, wxEmptyString,
637 _( "Image Files" ) + wxS( " " ) + wxImage::GetImageExtWildcard(),
638 wxFD_OPEN );
639
640 if( dlg.ShowModal() != wxID_OK )
641 continue;
642
643 // If we started with a hotkey which has a position then warp back to that.
644 // Otherwise update to the current mouse position pinned inside the autoscroll
645 // boundaries.
646 if( evt->IsPrime() && !ignorePrimePosition )
647 {
648 cursorPos = grid.Align( evt->Position() );
649 getViewControls()->WarpMouseCursor( cursorPos, true );
650 }
651 else
652 {
654 cursorPos = getViewControls()->GetMousePosition();
655 }
656
657 cursorPos = getViewControls()->GetMousePosition( true );
658
659 wxString fullFilename = dlg.GetPath();
660
661 if( wxFileExists( fullFilename ) )
662 image = new PCB_BITMAP( m_frame->GetModel(), cursorPos );
663
664 if( !image || !image->ReadImageFile( fullFilename ) )
665 {
666 wxMessageBox( _( "Could not load image from '%s'." ), fullFilename );
667 delete image;
668 image = nullptr;
669 continue;
670 }
671
672 image->SetFlags( IS_NEW | IS_MOVING );
673 image->SetLayer( m_frame->GetActiveLayer() );
674
676 m_view->AddToPreview( image, false ); // Add, but not give ownership
677 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
678 selectionTool->AddItemToSel( image, false );
679
680 getViewControls()->SetCursorPosition( cursorPos, false );
681 setCursor();
682 m_view->ShowPreview( true );
683 }
684 else
685 {
686 commit.Add( image );
687 commit.Push( _( "Place an image" ) );
688
690
691 image = nullptr;
693
695
696 if( immediateMode )
697 {
698 m_frame->PopTool( aEvent );
699 break;
700 }
701 }
702 }
703 else if( evt->IsClick( BUT_RIGHT ) )
704 {
705 // Warp after context menu only if dragging...
706 if( !image )
708
709 m_menu.ShowContextMenu( selectionTool->GetSelection() );
710 }
711 else if( image && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
712 {
713 image->SetPosition( cursorPos );
715 m_view->AddToPreview( image, false ); // Add, but not give ownership
716 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
717 }
718 else if( image && evt->IsAction( &ACTIONS::doDelete ) )
719 {
720 cleanup();
721 }
722 else if( image && ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
723 {
724 wxBell();
725 }
726 else
727 {
728 evt->SetPassEvent();
729 }
730
731 // Enable autopanning and cursor capture only when there is an image to be placed
732 getViewControls()->SetAutoPan( image != nullptr );
733 getViewControls()->CaptureCursor( image != nullptr );
734 }
735
736 getViewControls()->SetAutoPan( false );
737 getViewControls()->CaptureCursor( false );
738 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
739
740 return 0;
741}
742
743
745{
747 return 0;
748
749 if( m_inDrawingTool )
750 return 0;
751
753
754 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
755 PCB_TEXT* text = nullptr;
756 bool ignorePrimePosition = false;
758 BOARD_COMMIT commit( m_frame );
759 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
761
762 auto cleanup =
763 [&]()
764 {
767 m_controls->ShowCursor( true );
768 m_controls->SetAutoPan( false );
769 m_controls->CaptureCursor( false );
770 delete text;
771 text = nullptr;
772 };
773
774 auto setCursor =
775 [&]()
776 {
777 if( text )
778 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
779 else
780 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
781 };
782
784
785 m_frame->PushTool( aEvent );
786
787 Activate();
788 // Must be done after Activate() so that it gets set into the correct context
789 m_controls->ShowCursor( true );
791 // do not capture or auto-pan until we start placing some text
792 // Set initial cursor
793 setCursor();
794
795 if( aEvent.HasPosition() )
796 {
797 m_toolMgr->PrimeTool( aEvent.Position() );
798 }
799 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
800 {
801 m_toolMgr->PrimeTool( { 0, 0 } );
802 ignorePrimePosition = true;
803 }
804
805 // Main loop: keep receiving events
806 while( TOOL_EVENT* evt = Wait() )
807 {
808 setCursor();
809
810 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
811 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
812 VECTOR2I cursorPos = GetClampedCoords(
813 grid.BestSnapAnchor( m_controls->GetMousePosition(), m_frame->GetActiveLayer() ),
815 m_controls->ForceCursorPosition( true, cursorPos );
816
817 if( evt->IsCancelInteractive() )
818 {
819 if( text )
820 {
821 cleanup();
822 }
823 else
824 {
825 m_frame->PopTool( aEvent );
826 break;
827 }
828 }
829 else if( evt->IsActivate() )
830 {
831 if( text )
832 cleanup();
833
834 if( evt->IsMoveTool() )
835 {
836 // leave ourselves on the stack so we come back after the move
837 break;
838 }
839 else
840 {
841 m_frame->PopTool( aEvent );
842 break;
843 }
844 }
845 else if( evt->IsClick( BUT_RIGHT ) )
846 {
848 }
849 else if( evt->IsClick( BUT_LEFT ) )
850 {
851 bool placing = text != nullptr;
852
853 if( !text )
854 {
856
858
860 TEXT_ATTRIBUTES textAttrs;
861
862 textAttrs.m_Size = bds.GetTextSize( layer );
863 textAttrs.m_StrokeWidth = bds.GetTextThickness( layer );
864 InferBold( &textAttrs );
865 textAttrs.m_Italic = bds.GetTextItalic( layer );
866 textAttrs.m_KeepUpright = bds.GetTextUpright( layer );
867 textAttrs.m_Mirrored = IsBackLayer( layer );
868 textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
870
871 // Init the new item attributes
873 {
874 text = new PCB_TEXT( static_cast<FOOTPRINT*>( m_frame->GetModel() ),
876 }
877 else
878 {
879 text = new PCB_TEXT( m_frame->GetModel() );
880 }
881
882 text->SetLayer( layer );
883 text->SetAttributes( textAttrs );
884 text->SetTextPos( cursorPos );
885
886 DIALOG_TEXT_PROPERTIES textDialog( m_frame, text );
887 bool cancelled;
888
889 RunMainStack( [&]()
890 {
891 // QuasiModal required for Scintilla auto-complete
892 cancelled = !textDialog.ShowQuasiModal();
893 } );
894
895 if( cancelled || NoPrintableChars( text->GetText() ) )
896 {
897 delete text;
898 text = nullptr;
899 }
900 else if( text->GetTextPos() != cursorPos )
901 {
902 // If the user modified the location then go ahead and place it there.
903 // Otherwise we'll drag.
904 placing = true;
905 }
906
907 if( text )
908 {
909 if( !m_view->IsLayerVisible( text->GetLayer() ) )
910 {
911 m_frame->GetAppearancePanel()->SetLayerVisible( text->GetLayer(), true );
913 }
914
916 m_view->Update( &selection() );
917
918 // update the cursor so it looks correct before another event
919 setCursor();
920 }
921 }
922
923 if( placing )
924 {
925 text->ClearFlags();
927
928 commit.Add( text );
929 commit.Push( _( "Place a text" ) );
930
932
933 text = nullptr;
934 }
935
937
938 // If we started with a hotkey which has a position then warp back to that.
939 // Otherwise update to the current mouse position pinned inside the autoscroll
940 // boundaries.
941 if( evt->IsPrime() && !ignorePrimePosition )
942 {
943 cursorPos = evt->Position();
944 m_controls->WarpMouseCursor( cursorPos, true );
945 }
946 else
947 {
949 cursorPos = m_controls->GetMousePosition();
950 }
951
953
954 m_controls->ShowCursor( true );
955 m_controls->CaptureCursor( text != nullptr );
956 m_controls->SetAutoPan( text != nullptr );
957 }
958 else if( text && ( evt->IsMotion() || evt->IsAction( &PCB_ACTIONS::refreshPreview ) ) )
959 {
960 text->SetPosition( cursorPos );
961 selection().SetReferencePoint( cursorPos );
962 m_view->Update( &selection() );
963 }
964 else if( text && ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
965 {
966 wxBell();
967 }
968 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
969 {
970 if( text )
971 {
973 m_view->Update( &selection() );
974 frame()->SetMsgPanel( text );
975 }
976 else
977 {
978 evt->SetPassEvent();
979 }
980 }
981 else
982 {
983 evt->SetPassEvent();
984 }
985 }
986
987 m_controls->SetAutoPan( false );
988 m_controls->CaptureCursor( false );
990 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
991
992 if( selection().Empty() )
994
995 return 0;
996}
997
998
1000{
1001 const VECTOR2I lineVector{ aDim->GetEnd() - aDim->GetStart() };
1002
1003 aDim->SetEnd( aDim->GetStart() + GetVectorSnapped45( lineVector ) );
1004 aDim->Update();
1005}
1006
1007
1009{
1011 return 0;
1012
1013 if( m_inDrawingTool )
1014 return 0;
1015
1017
1018 enum DIMENSION_STEPS
1019 {
1020 SET_ORIGIN = 0,
1021 SET_END,
1022 SET_HEIGHT,
1023 FINISHED
1024 };
1025
1026 TOOL_EVENT originalEvent = aEvent;
1027 PCB_DIMENSION_BASE* dimension = nullptr;
1028 BOARD_COMMIT commit( m_frame );
1031 PCB_SELECTION preview; // A VIEW_GROUP that serves as a preview for the new item(s)
1032 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
1033 int step = SET_ORIGIN;
1035
1036 m_view->Add( &preview );
1037
1038 auto cleanup =
1039 [&]()
1040 {
1041 m_controls->SetAutoPan( false );
1042 m_controls->CaptureCursor( false );
1044
1045 preview.Clear();
1046 m_view->Update( &preview );
1047
1048 delete dimension;
1049 dimension = nullptr;
1050 step = SET_ORIGIN;
1051 };
1052
1053 auto setCursor =
1054 [&]()
1055 {
1056 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
1057 };
1058
1060
1061 m_frame->PushTool( aEvent );
1062
1063 Activate();
1064 // Must be done after Activate() so that it gets set into the correct context
1065 m_controls->ShowCursor( true );
1067 // Set initial cursor
1068 setCursor();
1069
1071
1072 if( aEvent.HasPosition() )
1073 m_toolMgr->PrimeTool( aEvent.Position() );
1074
1075 // Main loop: keep receiving events
1076 while( TOOL_EVENT* evt = Wait() )
1077 {
1078 if( step > SET_ORIGIN )
1079 frame()->SetMsgPanel( dimension );
1080
1081 setCursor();
1082
1083 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1084 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1085
1086 if( step == SET_HEIGHT && t != PCB_DIM_ORTHOGONAL_T )
1087 {
1088 if( dimension->GetStart().x != dimension->GetEnd().x
1089 && dimension->GetStart().y != dimension->GetEnd().y )
1090 {
1091 // Not cardinal. Grid snapping doesn't make sense for height.
1092 grid.SetUseGrid( false );
1093 }
1094 }
1095
1096 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
1097 cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, nullptr ), COORDS_PADDING );
1098
1099 m_controls->ForceCursorPosition( true, cursorPos );
1100
1101 if( evt->IsCancelInteractive() )
1102 {
1103 m_controls->SetAutoPan( false );
1104
1105 if( step != SET_ORIGIN ) // start from the beginning
1106 {
1107 cleanup();
1108 }
1109 else
1110 {
1111 m_frame->PopTool( aEvent );
1112 break;
1113 }
1114 }
1115 else if( evt->IsActivate() )
1116 {
1117 if( step != SET_ORIGIN )
1118 cleanup();
1119
1120 if( evt->IsPointEditor() )
1121 {
1122 // don't exit (the point editor runs in the background)
1123 }
1124 else if( evt->IsMoveTool() )
1125 {
1126 // leave ourselves on the stack so we come back after the move
1127 break;
1128 }
1129 else
1130 {
1131 m_frame->PopTool( aEvent );
1132 break;
1133 }
1134 }
1135 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
1136 {
1138 dimension->SetLineThickness( m_stroke.GetWidth() );
1139 m_view->Update( &preview );
1140 frame()->SetMsgPanel( dimension );
1141 }
1142 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
1143 {
1144 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
1145 {
1147 dimension->SetLineThickness( m_stroke.GetWidth() );
1148 m_view->Update( &preview );
1149 frame()->SetMsgPanel( dimension );
1150 }
1151 }
1152 else if( evt->IsClick( BUT_RIGHT ) )
1153 {
1155 }
1156 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1157 {
1158 switch( step )
1159 {
1160 case SET_ORIGIN:
1161 {
1163
1165
1166 // Init the new item attributes
1167 auto setMeasurementAttributes =
1168 [&]( PCB_DIMENSION_BASE* aDim )
1169 {
1170 aDim->SetUnitsMode( boardSettings.m_DimensionUnitsMode );
1171 aDim->SetUnitsFormat( boardSettings.m_DimensionUnitsFormat );
1172 aDim->SetPrecision( boardSettings.m_DimensionPrecision );
1173 aDim->SetSuppressZeroes( boardSettings.m_DimensionSuppressZeroes );
1174 aDim->SetTextPositionMode( boardSettings.m_DimensionTextPosition );
1175 aDim->SetKeepTextAligned( boardSettings.m_DimensionKeepTextAligned );
1176 };
1177
1178 if( originalEvent.IsAction( &PCB_ACTIONS::drawAlignedDimension ) )
1179 {
1180 dimension = new PCB_DIM_ALIGNED( m_frame->GetModel() );
1181 setMeasurementAttributes( dimension );
1182 }
1183 else if( originalEvent.IsAction( &PCB_ACTIONS::drawOrthogonalDimension ) )
1184 {
1185 dimension = new PCB_DIM_ORTHOGONAL( m_frame->GetModel() );
1186 setMeasurementAttributes( dimension );
1187 }
1188 else if( originalEvent.IsAction( &PCB_ACTIONS::drawCenterDimension ) )
1189 {
1190 dimension = new PCB_DIM_CENTER( m_frame->GetModel() );
1191 }
1192 else if( originalEvent.IsAction( &PCB_ACTIONS::drawRadialDimension ) )
1193 {
1194 dimension = new PCB_DIM_RADIAL( m_frame->GetModel() );
1195 setMeasurementAttributes( dimension );
1196 }
1197 else if( originalEvent.IsAction( &PCB_ACTIONS::drawLeader ) )
1198 {
1199 dimension = new PCB_DIM_LEADER( m_frame->GetModel() );
1200 dimension->SetTextPos( cursorPos );
1201 }
1202 else
1203 {
1204 wxFAIL_MSG( wxT( "Unhandled action in DRAWING_TOOL::DrawDimension" ) );
1205 }
1206
1207 t = dimension->Type();
1208
1209 dimension->SetLayer( layer );
1210 dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
1211 dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
1212 dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
1213 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1214 dimension->SetArrowLength( boardSettings.m_DimensionArrowLength );
1215 dimension->SetExtensionOffset( boardSettings.m_DimensionExtensionOffset );
1216 dimension->SetStart( cursorPos );
1217 dimension->SetEnd( cursorPos );
1218 dimension->Update();
1219
1220 if( !m_view->IsLayerVisible( layer ) )
1221 {
1222 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1224 }
1225
1226 preview.Add( dimension );
1227 frame()->SetMsgPanel( dimension );
1228
1229 m_controls->SetAutoPan( true );
1230 m_controls->CaptureCursor( true );
1231 break;
1232 }
1233
1234 case SET_END:
1235 // Dimensions that have origin and end in the same spot are not valid
1236 if( dimension->GetStart() == dimension->GetEnd() )
1237 {
1238 --step;
1239 break;
1240 }
1241
1242 if( t == PCB_DIM_CENTER_T || t == PCB_DIM_RADIAL_T || t == PCB_DIM_LEADER_T )
1243 {
1244 // No separate height step
1245 ++step;
1247 }
1248 else
1249 {
1250 break;
1251 }
1252
1253 case SET_HEIGHT:
1254 assert( dimension->GetStart() != dimension->GetEnd() );
1255 assert( dimension->GetLineThickness() > 0 );
1256
1257 preview.Remove( dimension );
1258
1259 commit.Add( dimension );
1260 commit.Push( _( "Draw a dimension" ) );
1261
1262 if( t == PCB_DIM_LEADER_T )
1263 {
1264 // Run the edit immediately to set the leader text
1265 m_toolMgr->RunAction( PCB_ACTIONS::properties, true, dimension );
1266 }
1267
1268 m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, dimension );
1269
1270 break;
1271 }
1272
1273 if( ++step >= FINISHED )
1274 {
1275 dimension = nullptr;
1276 step = SET_ORIGIN;
1277 m_controls->SetAutoPan( false );
1278 m_controls->CaptureCursor( false );
1279 }
1280 else if( evt->IsDblClick( BUT_LEFT ) )
1281 {
1283 }
1284 }
1285 else if( evt->IsMotion() )
1286 {
1287 switch( step )
1288 {
1289 case SET_END:
1290 dimension->SetEnd( cursorPos );
1291
1292 if( Is45Limited() || t == PCB_DIM_CENTER_T )
1293 constrainDimension( dimension );
1294
1295 if( t == PCB_DIM_ORTHOGONAL_T )
1296 {
1297 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1298
1299 BOX2I bounds( dimension->GetStart(),
1300 dimension->GetEnd() - dimension->GetStart() );
1301
1302 // Create a nice preview by measuring the longer dimension
1303 bool vert = bounds.GetWidth() < bounds.GetHeight();
1304
1305 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1307 }
1308 else if( t == PCB_DIM_RADIAL_T )
1309 {
1310 PCB_DIM_RADIAL* radialDim = static_cast<PCB_DIM_RADIAL*>( dimension );
1311 VECTOR2I textOffset( radialDim->GetArrowLength() * 10, 0 );
1312
1313 if( radialDim->GetEnd().x < radialDim->GetStart().x )
1314 textOffset = -textOffset;
1315
1316 radialDim->SetTextPos( radialDim->GetKnee() + textOffset );
1317 }
1318 else if( t == PCB_DIM_LEADER_T )
1319 {
1320 VECTOR2I textOffset( dimension->GetArrowLength() * 10, 0 );
1321
1322 if( dimension->GetEnd().x < dimension->GetStart().x )
1323 textOffset = -textOffset;
1324
1325 dimension->SetTextPos( dimension->GetEnd() + textOffset );
1326 }
1327
1328 dimension->Update();
1329 break;
1330
1331 case SET_HEIGHT:
1332 if( t == PCB_DIM_ALIGNED_T )
1333 {
1334 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dimension );
1335
1336 // Calculating the direction of travel perpendicular to the selected axis
1337 double angle = aligned->GetAngle() + ( M_PI / 2 );
1338
1339 VECTOR2I delta( (VECTOR2I) cursorPos - dimension->GetEnd() );
1340 double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
1341 aligned->SetHeight( height );
1342 aligned->Update();
1343 }
1344 else if( t == PCB_DIM_ORTHOGONAL_T )
1345 {
1346 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1347
1348 BOX2I bbox( dimension->GetStart(),
1349 dimension->GetEnd() - dimension->GetStart() );
1350 VECTOR2I direction( cursorPos - bbox.Centre() );
1351 bool vert;
1352
1353 // Only change the orientation when we move outside the bbox
1354 if( !bbox.Contains( cursorPos ) )
1355 {
1356 // If the dimension is horizontal or vertical, set correct orientation
1357 // otherwise, test if we're left/right of the bounding box or above/below it
1358 if( bbox.GetWidth() == 0 )
1359 vert = true;
1360 else if( bbox.GetHeight() == 0 )
1361 vert = false;
1362 else if( cursorPos.x > bbox.GetLeft() && cursorPos.x < bbox.GetRight() )
1363 vert = false;
1364 else if( cursorPos.y > bbox.GetTop() && cursorPos.y < bbox.GetBottom() )
1365 vert = true;
1366 else
1367 vert = std::abs( direction.y ) < std::abs( direction.x );
1368
1369 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1371 }
1372 else
1373 {
1374 vert = ortho->GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1375 }
1376
1377 VECTOR2I heightVector( cursorPos - dimension->GetStart() );
1378 ortho->SetHeight( vert ? heightVector.x : heightVector.y );
1379 ortho->Update();
1380 }
1381
1382 break;
1383 }
1384
1385 // Show a preview of the item
1386 m_view->Update( &preview );
1387 }
1388 else if( dimension && evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1389 {
1391
1392 if( !m_view->IsLayerVisible( layer ) )
1393 {
1394 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1396 }
1397
1398 dimension->SetLayer( layer );
1399 dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
1400 dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
1401 dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
1402 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1403 dimension->Update();
1404
1405 m_view->Update( &preview );
1406 frame()->SetMsgPanel( dimension );
1407 }
1408 else if( dimension && evt->IsAction( &PCB_ACTIONS::properties ) )
1409 {
1410 if( step == SET_END || step == SET_HEIGHT )
1411 {
1412 frame()->OnEditItemRequest( dimension );
1413 dimension->Update();
1414 frame()->SetMsgPanel( dimension );
1415 break;
1416 }
1417 else
1418 {
1419 wxBell();
1420 }
1421 }
1422 else if( dimension && ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
1423 {
1424 wxBell();
1425 }
1426 else
1427 {
1428 evt->SetPassEvent();
1429 }
1430 }
1431
1432 if( step != SET_ORIGIN )
1433 delete dimension;
1434
1435 m_controls->SetAutoPan( false );
1437 m_controls->CaptureCursor( false );
1438 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1439
1440 m_view->Remove( &preview );
1441
1442 if( selection().Empty() )
1444
1445 return 0;
1446}
1447
1448
1450{
1451 if( !m_frame->GetModel() )
1452 return 0;
1453
1454 if( m_inDrawingTool )
1455 return 0;
1456
1458
1459 // Note: PlaceImportedGraphics() will convert PCB_SHAPE_T and PCB_TEXT_T to footprint
1460 // items if needed
1462 int dlgResult = dlg.ShowModal();
1463
1464 std::list<std::unique_ptr<EDA_ITEM>>& list = dlg.GetImportedItems();
1465
1466 if( dlgResult != wxID_OK )
1467 return 0;
1468
1469 // Ensure the list is not empty:
1470 if( list.empty() )
1471 {
1472 wxMessageBox( _( "No graphic items found in file.") );
1473 return 0;
1474 }
1475
1477
1478 std::vector<BOARD_ITEM*> newItems; // all new items, including group
1479 std::vector<BOARD_ITEM*> selectedItems; // the group, or newItems if no group
1480 PCB_SELECTION preview;
1481 BOARD_COMMIT commit( m_frame );
1482 PCB_GROUP* group = nullptr;
1483 PCB_LAYER_ID layer = F_Cu;
1484
1485 if( dlg.ShouldGroupItems() )
1486 {
1489 else
1490 group = new PCB_GROUP( m_frame->GetBoard() );
1491
1492 newItems.push_back( group );
1493 selectedItems.push_back( group );
1494 preview.Add( group );
1495 }
1496
1497 for( std::unique_ptr<EDA_ITEM>& ptr : list )
1498 {
1499 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( ptr.get() );
1500
1501 newItems.push_back( item );
1502
1503 if( group )
1504 group->AddItem( item );
1505 else
1506 selectedItems.push_back( item );
1507
1508 preview.Add( item );
1509
1510 ptr.release();
1511 }
1512
1513 if( !dlg.IsPlacementInteractive() )
1514 {
1515 for( BOARD_ITEM* item : newItems )
1516 commit.Add( item );
1517
1518 commit.Push( _( "Place a DXF_SVG drawing" ) );
1519 return 0;
1520 }
1521
1522 layer = newItems.front()->GetLayer();
1523
1524 m_view->Add( &preview );
1525
1526 // Clear the current selection then select the drawings so that edit tools work on them
1528 m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &selectedItems );
1529
1530 m_frame->PushTool( aEvent );
1531
1532 auto setCursor =
1533 [&]()
1534 {
1535 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1536 };
1537
1538 Activate();
1539 // Must be done after Activate() so that it gets set into the correct context
1540 m_controls->ShowCursor( true );
1542 // Set initial cursor
1543 setCursor();
1544
1545 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
1547
1548 // Now move the new items to the current cursor position:
1549 VECTOR2I cursorPos = m_controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
1550 VECTOR2I delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
1551
1552 for( BOARD_ITEM* item : selectedItems )
1553 item->Move( delta );
1554
1555 m_view->Update( &preview );
1556
1557 // Main loop: keep receiving events
1558 while( TOOL_EVENT* evt = Wait() )
1559 {
1560 setCursor();
1561
1562 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1563 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1564 cursorPos = GetClampedCoords(
1565 grid.BestSnapAnchor( m_controls->GetMousePosition(), layer ),
1567 m_controls->ForceCursorPosition( true, cursorPos );
1568
1569 if( evt->IsCancelInteractive() || evt->IsActivate() )
1570 {
1572
1573 if( group )
1574 {
1575 preview.Remove( group );
1576 group->RemoveAll();
1577 }
1578
1579 for( BOARD_ITEM* item : newItems )
1580 delete item;
1581
1582 break;
1583 }
1584 else if( evt->IsMotion() )
1585 {
1586 delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
1587
1588 for( BOARD_ITEM* item : selectedItems )
1589 item->Move( delta );
1590
1591 m_view->Update( &preview );
1592 }
1593 else if( evt->IsClick( BUT_RIGHT ) )
1594 {
1596 }
1597 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1598 {
1599 // Place the imported drawings
1600 for( BOARD_ITEM* item : newItems )
1601 commit.Add( item );
1602
1603 commit.Push( _( "Place a DXF_SVG drawing" ) );
1604 break; // This is a one-shot command, not a tool
1605 }
1606 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
1607 {
1608 wxBell();
1609 }
1610 else
1611 {
1612 evt->SetPassEvent();
1613 }
1614 }
1615
1616 preview.Clear();
1617 m_view->Remove( &preview );
1618
1619 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1621
1622 m_frame->PopTool( aEvent );
1623
1624 return 0;
1625}
1626
1627
1629{
1630 // Make sense only in FP editor
1631 if( !m_isFootprintEditor )
1632 return 0;
1633
1634 if( !m_frame->GetModel() )
1635 return 0;
1636
1637 if( m_inDrawingTool )
1638 return 0;
1639
1641
1642 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
1644
1646
1647 m_frame->PushTool( aEvent );
1648
1649 auto setCursor =
1650 [&]()
1651 {
1652 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
1653 };
1654
1655 Activate();
1656 // Must be done after Activate() so that it gets set into the correct context
1657 m_controls->ShowCursor( true );
1658 m_controls->SetAutoPan( true );
1659 m_controls->CaptureCursor( false );
1661 // Set initial cursor
1662 setCursor();
1663
1664 while( TOOL_EVENT* evt = Wait() )
1665 {
1666 setCursor();
1667
1668 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1669 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1670 VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(),
1672 m_controls->ForceCursorPosition( true, cursorPos );
1673
1674 if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1675 {
1677 BOARD_COMMIT commit( m_frame );
1678 commit.Modify( footprint );
1679
1680 // set the new relative internal local coordinates of footprint items
1681 VECTOR2I moveVector = footprint->GetPosition() - cursorPos;
1682 footprint->MoveAnchorPosition( moveVector );
1683
1684 commit.Push( _( "Move the footprint reference anchor" ) );
1685
1686 // Usually, we do not need to change twice the anchor position,
1687 // so deselect the active tool
1688 m_frame->PopTool( aEvent );
1689 break;
1690 }
1691 else if( evt->IsClick( BUT_RIGHT ) )
1692 {
1694 }
1695 else if( evt->IsCancelInteractive() || evt->IsActivate() )
1696 {
1697 m_frame->PopTool( aEvent );
1698 break;
1699 }
1700 else
1701 {
1702 evt->SetPassEvent();
1703 }
1704 }
1705
1706 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1708
1709 return 0;
1710}
1711
1712
1714{
1715#define TOGGLE( a ) a = !a
1716
1717 SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
1718
1719 if( frame()->IsType( FRAME_PCB_EDITOR ) )
1721 else
1723
1725
1726 return 0;
1727
1728#undef TOGGLE
1729}
1730
1731
1736 PCB_SHAPE* aGraphic )
1737{
1738 if( !aMgr.IsReset() )
1739 {
1740 aGraphic->SetStart( aMgr.GetOrigin() );
1741 aGraphic->SetEnd( aMgr.GetEnd() );
1742 }
1743}
1744
1745
1746bool DRAWING_TOOL::drawShape( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
1747 std::optional<VECTOR2D> aStartingPoint )
1748{
1749 SHAPE_T shape = ( *aGraphic )->GetShape();
1750
1751 // Only three shapes are currently supported
1752 wxASSERT( shape == SHAPE_T::SEGMENT || shape == SHAPE_T::CIRCLE || shape == SHAPE_T::RECT );
1753
1755 EDA_UNITS userUnits = m_frame->GetUserUnits();
1757 PCB_SHAPE*& graphic = *aGraphic;
1758
1759 if( m_layer != m_frame->GetActiveLayer() )
1760 {
1763 m_stroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT );
1764 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
1765
1774 }
1775
1776 // geometric construction manager
1778
1779 // drawing assistant overlay
1780 // TODO: workaround because EDA_SHAPE_TYPE_T is not visible from commons.
1781 KIGFX::PREVIEW::GEOM_SHAPE geomShape( static_cast<KIGFX::PREVIEW::GEOM_SHAPE>( shape ) );
1782 KIGFX::PREVIEW::TWO_POINT_ASSISTANT twoPointAsst( twoPointManager, pcbIUScale, userUnits, geomShape );
1783
1784 // Add a VIEW_GROUP that serves as a preview for the new item
1785 PCB_SELECTION preview;
1786 m_view->Add( &preview );
1787 m_view->Add( &twoPointAsst );
1788
1789 bool started = false;
1790 bool cancelled = false;
1791 bool isLocalOriginSet = ( m_frame->GetScreen()->m_LocalOrigin != VECTOR2D( 0, 0 ) );
1792 VECTOR2I cursorPos = m_controls->GetMousePosition();
1793
1794 auto setCursor =
1795 [&]()
1796 {
1797 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1798 };
1799
1800 auto cleanup =
1801 [&]()
1802 {
1803 preview.Clear();
1804 m_view->Update( &preview );
1805 delete graphic;
1806 graphic = nullptr;
1807
1808 if( !isLocalOriginSet )
1810 };
1811
1812 m_controls->ShowCursor( true );
1814 // Set initial cursor
1815 setCursor();
1816
1818
1819 if( aStartingPoint )
1820 m_toolMgr->PrimeTool( *aStartingPoint );
1821
1822 // Main loop: keep receiving events
1823 while( TOOL_EVENT* evt = Wait() )
1824 {
1825 setCursor();
1826
1827 if( started )
1828 m_frame->SetMsgPanel( graphic );
1829
1830 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1831 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1832 cursorPos = GetClampedCoords(
1833 grid.BestSnapAnchor( m_controls->GetMousePosition(), m_layer ),
1835 m_controls->ForceCursorPosition( true, cursorPos );
1836
1837 if( evt->IsCancelInteractive() )
1838 {
1839 cleanup();
1840
1841 if( !started )
1842 {
1843 // We've handled the cancel event. Don't cancel other tools
1844 evt->SetPassEvent( false );
1845 m_frame->PopTool( aTool );
1846 cancelled = true;
1847 }
1848
1849 break;
1850 }
1851 else if( evt->IsActivate() )
1852 {
1853 if( evt->IsPointEditor() )
1854 {
1855 // don't exit (the point editor runs in the background)
1856 }
1857 else if( evt->IsMoveTool() )
1858 {
1859 cleanup();
1860 // leave ourselves on the stack so we come back after the move
1861 cancelled = true;
1862 break;
1863 }
1864 else
1865 {
1866 cleanup();
1867 m_frame->PopTool( aTool );
1868 cancelled = true;
1869 break;
1870 }
1871 }
1872 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1873 {
1874 if( m_layer != m_frame->GetActiveLayer() )
1875 {
1878 m_stroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT );
1879 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
1880
1889 }
1890
1891 if( graphic )
1892 {
1893 if( !m_view->IsLayerVisible( m_layer ) )
1894 {
1897 }
1898
1899 graphic->SetLayer( m_layer );
1900 graphic->SetStroke( m_stroke );
1901
1902 if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
1903 pcb_textbox->SetAttributes( m_textAttrs );
1904
1905 m_view->Update( &preview );
1906 frame()->SetMsgPanel( graphic );
1907 }
1908 else
1909 {
1910 evt->SetPassEvent();
1911 }
1912 }
1913 else if( evt->IsClick( BUT_RIGHT ) )
1914 {
1916 }
1917 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1918 {
1919 if( !graphic )
1920 break;
1921
1922 if( !started )
1923 {
1925
1926 if( aStartingPoint )
1927 {
1928 cursorPos = *aStartingPoint;
1929 aStartingPoint = std::nullopt;
1930 }
1931
1932 // Init the new item attributes
1933 if( graphic ) // always true, but Coverity can't seem to figure that out
1934 {
1935 graphic->SetShape( static_cast<SHAPE_T>( shape ) );
1936 graphic->SetFilled( false );
1937 graphic->SetStroke( m_stroke );
1938 graphic->SetLayer( m_layer );
1939 }
1940
1941 if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
1942 pcb_textbox->SetAttributes( m_textAttrs );
1943
1944 grid.SetSkipPoint( cursorPos );
1945
1946 twoPointManager.SetOrigin( cursorPos );
1947 twoPointManager.SetEnd( cursorPos );
1948
1949 if( !isLocalOriginSet )
1950 m_frame->GetScreen()->m_LocalOrigin = cursorPos;
1951
1952 preview.Add( graphic );
1953 frame()->SetMsgPanel( graphic );
1954 m_controls->SetAutoPan( true );
1955 m_controls->CaptureCursor( true );
1956
1957 if( !m_view->IsLayerVisible( m_layer ) )
1958 {
1961 }
1962
1963 updateSegmentFromGeometryMgr( twoPointManager, graphic );
1964
1965 started = true;
1966 }
1967 else if( shape == SHAPE_T::CIRCLE )
1968 {
1969 // No clever logic if drawing a circle
1970 preview.Clear();
1971 twoPointManager.Reset();
1972 break;
1973 }
1974 else
1975 {
1976 PCB_SHAPE* snapItem = dyn_cast<PCB_SHAPE*>( grid.GetSnapped() );
1977
1978 if( twoPointManager.GetOrigin() == twoPointManager.GetEnd()
1979 || ( evt->IsDblClick( BUT_LEFT ) && shape == SHAPE_T::SEGMENT )
1980 || snapItem )
1981 // User has clicked twice in the same spot
1982 // or clicked on the end of an existing segment (closing a path)
1983 {
1984 BOARD_COMMIT commit( m_frame );
1985
1986 // If the user clicks on an existing snap point from a drawsegment
1987 // we finish the segment as they are likely closing a path
1988 if( snapItem && ( shape == SHAPE_T::RECT || graphic->GetLength() > 0.0 ) )
1989 {
1990 commit.Add( graphic );
1991 commit.Push( _( "Draw a line segment" ) );
1992 m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, graphic );
1993 }
1994 else
1995 {
1996 delete graphic;
1997 }
1998
1999 graphic = nullptr;
2000 }
2001
2002 preview.Clear();
2003 twoPointManager.Reset();
2004 break;
2005 }
2006
2007 twoPointManager.SetEnd( GetClampedCoords( cursorPos ) );
2008 }
2009 else if( evt->IsMotion() )
2010 {
2011 VECTOR2I clampedCursorPos = cursorPos;
2012
2013 if( shape == SHAPE_T::CIRCLE || shape == SHAPE_T::ARC )
2014 {
2015 clampedCursorPos = getClampedRadiusEnd( twoPointManager.GetOrigin(), cursorPos );
2016 }
2017 else
2018 {
2019 clampedCursorPos = getClampedDifferenceEnd( twoPointManager.GetOrigin(),
2020 cursorPos );
2021 }
2022
2023 // 45 degree lines
2024 if( started && Is45Limited() )
2025 {
2026 const VECTOR2I lineVector( clampedCursorPos
2027 - VECTOR2I( twoPointManager.GetOrigin() ) );
2028
2029 // get a restricted 45/H/V line from the last fixed point to the cursor
2030 VECTOR2I newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECT ) );
2031 m_controls->ForceCursorPosition( true, VECTOR2I( twoPointManager.GetEnd() ) );
2032 twoPointManager.SetEnd( twoPointManager.GetOrigin() + newEnd );
2033 twoPointManager.SetAngleSnap( true );
2034 }
2035 else
2036 {
2037 twoPointManager.SetEnd( clampedCursorPos );
2038 twoPointManager.SetAngleSnap( false );
2039 }
2040
2041 updateSegmentFromGeometryMgr( twoPointManager, graphic );
2042 m_view->Update( &preview );
2043 m_view->Update( &twoPointAsst );
2044 }
2045 else if( graphic && evt->IsAction( &PCB_ACTIONS::incWidth ) )
2046 {
2048 graphic->SetStroke( m_stroke );
2049 m_view->Update( &preview );
2050 frame()->SetMsgPanel( graphic );
2051 }
2052 else if( graphic && evt->IsAction( &PCB_ACTIONS::decWidth ) )
2053 {
2054 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
2055 {
2057 graphic->SetStroke( m_stroke );
2058 m_view->Update( &preview );
2059 frame()->SetMsgPanel( graphic );
2060 }
2061 }
2062 else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
2063 {
2064 frame()->OnEditItemRequest( graphic );
2065 m_view->Update( &preview );
2066 frame()->SetMsgPanel( graphic );
2067 break;
2068 }
2069 else if( started && ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2070 {
2071 wxBell();
2072 }
2073 else if( evt->IsAction( &ACTIONS::resetLocalCoords ) )
2074 {
2075 isLocalOriginSet = true;
2076 evt->SetPassEvent();
2077 }
2078 else if( evt->IsAction( &ACTIONS::updateUnits ) )
2079 {
2080 if( frame()->GetUserUnits() != userUnits )
2081 {
2082 userUnits = frame()->GetUserUnits();
2083 twoPointAsst.SetUnits( userUnits );
2084 m_view->Update( &twoPointAsst );
2085 }
2086 evt->SetPassEvent();
2087 }
2088 else
2089 {
2090 evt->SetPassEvent();
2091 }
2092 }
2093
2094 if( !isLocalOriginSet ) // reset the relative coordinate if it was not set before
2096
2097 m_view->Remove( &twoPointAsst );
2098 m_view->Remove( &preview );
2099
2100 if( selection().Empty() )
2102
2103 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2104 m_controls->SetAutoPan( false );
2105 m_controls->CaptureCursor( false );
2107
2108 return !cancelled;
2109}
2110
2111
2116 PCB_SHAPE& aArc )
2117{
2118 VECTOR2I vec = aMgr.GetOrigin();
2119
2120 aArc.SetCenter( vec );
2121
2122 if( aMgr.GetSubtended() < ANGLE_0 )
2123 {
2124 vec = aMgr.GetStartRadiusEnd();
2125 aArc.SetStart( vec );
2126 vec = aMgr.GetEndRadiusEnd();
2127 aArc.SetEnd( vec );
2128 }
2129 else
2130 {
2131 vec = aMgr.GetEndRadiusEnd();
2132 aArc.SetStart( vec );
2133 vec = aMgr.GetStartRadiusEnd();
2134 aArc.SetEnd( vec );
2135 }
2136}
2137
2138
2139bool DRAWING_TOOL::drawArc( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
2140 std::optional<VECTOR2D> aStartingPoint )
2141{
2142 wxCHECK( aGraphic, false );
2143
2144 PCB_SHAPE*& graphic = *aGraphic;
2145
2146 wxCHECK( graphic, false );
2147
2148 if( m_layer != m_frame->GetActiveLayer() )
2149 {
2152 m_stroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT );
2153 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2154 }
2155
2156 // Arc geometric construction manager
2158
2159 // Arc drawing assistant overlay
2161
2162 // Add a VIEW_GROUP that serves as a preview for the new item
2163 PCB_SELECTION preview;
2164 m_view->Add( &preview );
2165 m_view->Add( &arcAsst );
2167
2168 auto setCursor =
2169 [&]()
2170 {
2171 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2172 };
2173
2174 auto cleanup =
2175 [&] ()
2176 {
2177 preview.Clear();
2178 delete *aGraphic;
2179 *aGraphic = nullptr;
2180 };
2181
2182 m_controls->ShowCursor( true );
2184 // Set initial cursor
2185 setCursor();
2186
2187 bool started = false;
2188 bool cancelled = false;
2189
2191
2192 if( aStartingPoint )
2193 m_toolMgr->PrimeTool( *aStartingPoint );
2194
2195 // Main loop: keep receiving events
2196 while( TOOL_EVENT* evt = Wait() )
2197 {
2198 if( started )
2199 m_frame->SetMsgPanel( graphic );
2200
2201 setCursor();
2202
2203 graphic->SetLayer( m_layer );
2204
2205 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2206 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2207 VECTOR2I cursorPos = GetClampedCoords(
2208 grid.BestSnapAnchor( m_controls->GetMousePosition(), graphic ), COORDS_PADDING );
2209 m_controls->ForceCursorPosition( true, cursorPos );
2210
2211 if( evt->IsCancelInteractive() )
2212 {
2213 cleanup();
2214
2215 if( !started )
2216 {
2217 // We've handled the cancel event. Don't cancel other tools
2218 evt->SetPassEvent( false );
2219 m_frame->PopTool( aTool );
2220 cancelled = true;
2221 }
2222
2223 break;
2224 }
2225 else if( evt->IsActivate() )
2226 {
2227 if( evt->IsPointEditor() )
2228 {
2229 // don't exit (the point editor runs in the background)
2230 }
2231 else if( evt->IsMoveTool() )
2232 {
2233 cleanup();
2234 // leave ourselves on the stack so we come back after the move
2235 cancelled = true;
2236 break;
2237 }
2238 else
2239 {
2240 cleanup();
2241 m_frame->PopTool( aTool );
2242 cancelled = true;
2243 break;
2244 }
2245 }
2246 else if( evt->IsClick( BUT_LEFT ) )
2247 {
2248 if( !started )
2249 {
2251
2252 m_controls->SetAutoPan( true );
2253 m_controls->CaptureCursor( true );
2254
2255 // Init the new item attributes
2256 // (non-geometric, those are handled by the manager)
2257 graphic->SetShape( SHAPE_T::ARC );
2258 graphic->SetStroke( m_stroke );
2259
2260 if( !m_view->IsLayerVisible( m_layer ) )
2261 {
2264 }
2265
2266 preview.Add( graphic );
2267 frame()->SetMsgPanel( graphic );
2268 started = true;
2269 }
2270
2271 arcManager.AddPoint( cursorPos, true );
2272 }
2273 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
2274 {
2275 arcManager.RemoveLastPoint();
2276 }
2277 else if( evt->IsMotion() )
2278 {
2279 // set angle snap
2280 arcManager.SetAngleSnap( Is45Limited() );
2281
2282 // update, but don't step the manager state
2283 arcManager.AddPoint( cursorPos, false );
2284 }
2285 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2286 {
2287 if( m_layer != m_frame->GetActiveLayer() )
2288 {
2291 m_stroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT );
2292 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2293 }
2294
2295 if( graphic )
2296 {
2297 if( !m_view->IsLayerVisible( m_layer ) )
2298 {
2301 }
2302
2303 graphic->SetLayer( m_layer );
2304 graphic->SetStroke( m_stroke );
2305 m_view->Update( &preview );
2306 frame()->SetMsgPanel( graphic );
2307 }
2308 else
2309 {
2310 evt->SetPassEvent();
2311 }
2312 }
2313 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
2314 {
2316 {
2317 graphic->SetArcAngleAndEnd( ANGLE_90 );
2318 frame()->OnEditItemRequest( graphic );
2319 m_view->Update( &preview );
2320 frame()->SetMsgPanel( graphic );
2321 break;
2322 }
2323 // Don't show the edit panel if we can't represent the arc with it
2324 else if( ( arcManager.GetStep() == KIGFX::PREVIEW::ARC_GEOM_MANAGER::SET_ANGLE )
2325 && ( arcManager.GetStartRadiusEnd() != arcManager.GetEndRadiusEnd() ) )
2326 {
2327 frame()->OnEditItemRequest( graphic );
2328 m_view->Update( &preview );
2329 frame()->SetMsgPanel( graphic );
2330 break;
2331 }
2332 else
2333 {
2334 evt->SetPassEvent();
2335 }
2336 }
2337 else if( evt->IsClick( BUT_RIGHT ) )
2338 {
2340 }
2341 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
2342 {
2344 graphic->SetStroke( m_stroke );
2345 m_view->Update( &preview );
2346 frame()->SetMsgPanel( graphic );
2347 }
2348 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
2349 {
2350 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
2351 {
2353 graphic->SetStroke( m_stroke );
2354 m_view->Update( &preview );
2355 frame()->SetMsgPanel( graphic );
2356 }
2357 }
2358 else if( evt->IsAction( &PCB_ACTIONS::arcPosture ) )
2359 {
2360 arcManager.ToggleClockwise();
2361 }
2362 else if( evt->IsAction( &ACTIONS::updateUnits ) )
2363 {
2364 arcAsst.SetUnits( frame()->GetUserUnits() );
2365 m_view->Update( &arcAsst );
2366 evt->SetPassEvent();
2367 }
2368 else if( started && ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2369 {
2370 wxBell();
2371 }
2372 else
2373 {
2374 evt->SetPassEvent();
2375 }
2376
2377 if( arcManager.IsComplete() )
2378 {
2379 break;
2380 }
2381 else if( arcManager.HasGeometryChanged() )
2382 {
2383 updateArcFromConstructionMgr( arcManager, *graphic );
2384 m_view->Update( &preview );
2385 m_view->Update( &arcAsst );
2386
2387 if( started )
2388 frame()->SetMsgPanel( graphic );
2389 else
2390 frame()->SetMsgPanel( board() );
2391 }
2392 }
2393
2394 preview.Remove( graphic );
2395 m_view->Remove( &arcAsst );
2396 m_view->Remove( &preview );
2397
2398 if( selection().Empty() )
2400
2401 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2402 m_controls->SetAutoPan( false );
2403 m_controls->CaptureCursor( false );
2405
2406 return !cancelled;
2407}
2408
2409
2411{
2412 bool clearSelection = false;
2413 *aZone = nullptr;
2414
2415 // not an action that needs a source zone
2416 if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON )
2417 return true;
2418
2420 const PCB_SELECTION& selection = selTool->GetSelection();
2421
2422 if( selection.Empty() )
2423 {
2424 clearSelection = true;
2426 }
2427
2428 // we want a single zone
2429 if( selection.Size() == 1 )
2430 *aZone = dyn_cast<ZONE*>( selection[0] );
2431
2432 // expected a zone, but didn't get one
2433 if( !*aZone )
2434 {
2435 if( clearSelection )
2437
2438 return false;
2439 }
2440
2441 return true;
2442}
2443
2445{
2447 return 0;
2448
2449 ZONE_MODE zoneMode = aEvent.Parameter<ZONE_MODE>();
2450 MODE drawMode = MODE::ZONE;
2451
2452 if( aEvent.IsAction( &PCB_ACTIONS::drawRuleArea ) )
2453 drawMode = MODE::KEEPOUT;
2454
2455 if( aEvent.IsAction( &PCB_ACTIONS::drawPolygon ) )
2456 drawMode = MODE::GRAPHIC_POLYGON;
2457
2458 SCOPED_DRAW_MODE scopedDrawMode( m_mode, drawMode );
2459
2460 // get a source zone, if we need one. We need it for:
2461 // ZONE_MODE::CUTOUT (adding a hole to the source zone)
2462 // ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
2463 ZONE* sourceZone = nullptr;
2464
2465 if( !getSourceZoneForAction( zoneMode, &sourceZone ) )
2466 return 0;
2467
2468 // Turn zones on if they are off, so that the created object will be visible after completion
2470
2472
2473 params.m_keepout = drawMode == MODE::KEEPOUT;
2474 params.m_mode = zoneMode;
2475 params.m_sourceZone = sourceZone;
2476
2477 if( zoneMode == ZONE_MODE::SIMILAR )
2478 params.m_layer = sourceZone->GetLayer();
2479 else
2480 params.m_layer = m_frame->GetActiveLayer();
2481
2482 ZONE_CREATE_HELPER zoneTool( *this, params );
2483 // the geometry manager which handles the zone geometry, and hands the calculated points
2484 // over to the zone creator tool
2485 POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
2486 bool started = false;
2488
2489 m_frame->PushTool( aEvent );
2490
2491 auto setCursor =
2492 [&]()
2493 {
2494 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2495 };
2496
2497 auto cleanup =
2498 [&] ()
2499 {
2500 polyGeomMgr.Reset();
2501 started = false;
2502 grid.ClearSkipPoint();
2503 m_controls->SetAutoPan( false );
2504 m_controls->CaptureCursor( false );
2505 };
2506
2507 Activate();
2508 // Must be done after Activate() so that it gets set into the correct context
2509 m_controls->ShowCursor( true );
2511 // Set initial cursor
2512 setCursor();
2513
2514 if( aEvent.HasPosition() )
2515 m_toolMgr->PrimeTool( aEvent.Position() );
2516
2517 // Main loop: keep receiving events
2518 while( TOOL_EVENT* evt = Wait() )
2519 {
2520 setCursor();
2521
2522 LSET layers( m_frame->GetActiveLayer() );
2523 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2524 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2525
2526 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
2527 cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, layers ), COORDS_PADDING );
2528
2529 m_controls->ForceCursorPosition( true, cursorPos );
2530
2533
2534 if( evt->IsCancelInteractive() )
2535 {
2536 if( polyGeomMgr.IsPolygonInProgress() )
2537 {
2538 cleanup();
2539 }
2540 else
2541 {
2542 // We've handled the cancel event. Don't cancel other tools
2543 evt->SetPassEvent( false );
2544 m_frame->PopTool( aEvent );
2545 break;
2546 }
2547 }
2548 else if( evt->IsActivate() )
2549 {
2550 if( polyGeomMgr.IsPolygonInProgress() )
2551 cleanup();
2552
2553 if( evt->IsPointEditor() )
2554 {
2555 // don't exit (the point editor runs in the background)
2556 }
2557 else if( evt->IsMoveTool() )
2558 {
2559 // leave ourselves on the stack so we come back after the move
2560 break;
2561 }
2562 else
2563 {
2564 m_frame->PopTool( aEvent );
2565 break;
2566 }
2567 }
2568 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2569 {
2570 if( zoneMode != ZONE_MODE::SIMILAR )
2571 params.m_layer = frame()->GetActiveLayer();
2572
2573 if( !m_view->IsLayerVisible( params.m_layer ) )
2574 {
2577 }
2578 }
2579 else if( evt->IsClick( BUT_RIGHT ) )
2580 {
2582 }
2583 // events that lock in nodes
2584 else if( evt->IsClick( BUT_LEFT )
2585 || evt->IsDblClick( BUT_LEFT )
2586 || evt->IsAction( &PCB_ACTIONS::closeOutline ) )
2587 {
2588 // Check if it is double click / closing line (so we have to finish the zone)
2589 const bool endPolygon = evt->IsDblClick( BUT_LEFT )
2590 || evt->IsAction( &PCB_ACTIONS::closeOutline )
2591 || polyGeomMgr.NewPointClosesOutline( cursorPos );
2592
2593 if( endPolygon )
2594 {
2595 polyGeomMgr.SetFinished();
2596 polyGeomMgr.Reset();
2597
2598 started = false;
2599 m_controls->SetAutoPan( false );
2600 m_controls->CaptureCursor( false );
2601 }
2602 // adding a corner
2603 else if( polyGeomMgr.AddPoint( cursorPos ) )
2604 {
2605 if( !started )
2606 {
2607 started = true;
2608
2609 m_controls->SetAutoPan( true );
2610 m_controls->CaptureCursor( true );
2611
2612 if( !m_view->IsLayerVisible( params.m_layer ) )
2613 {
2616 }
2617 }
2618 }
2619 }
2620 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
2621 {
2622 polyGeomMgr.DeleteLastCorner();
2623
2624 if( !polyGeomMgr.IsPolygonInProgress() )
2625 {
2626 // report finished as an empty shape
2627 polyGeomMgr.SetFinished();
2628
2629 // start again
2630 started = false;
2631 m_controls->SetAutoPan( false );
2632 m_controls->CaptureCursor( false );
2633 }
2634 }
2635 else if( polyGeomMgr.IsPolygonInProgress()
2636 && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
2637 {
2638 polyGeomMgr.SetCursorPosition( cursorPos );
2639 }
2640 else if( started && ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2641 {
2642 wxBell();
2643 }
2644 else if( started&& evt->IsAction( &PCB_ACTIONS::properties ) )
2645 {
2646 frame()->OnEditItemRequest( zoneTool.GetZone() );
2647 zoneTool.OnGeometryChange( polyGeomMgr );
2648 frame()->SetMsgPanel( zoneTool.GetZone() );
2649 }
2650 /*else if( evt->IsAction( &ACTIONS::updateUnits ) )
2651 {
2652 // If we ever have an assistant here that reports dimensions, we'll want to
2653 // update its units here....
2654 // zoneAsst.SetUnits( frame()->GetUserUnits() );
2655 // m_view->Update( &zoneAsst );
2656 evt->SetPassEvent();
2657 }*/
2658 else
2659 {
2660 evt->SetPassEvent();
2661 }
2662
2663 } // end while
2664
2665 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2667 controls()->SetAutoPan( false );
2668 m_controls->CaptureCursor( false );
2669 return 0;
2670}
2671
2672
2674{
2676 return 0;
2677
2678 struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
2679 {
2680 PCB_BASE_EDIT_FRAME* m_frame;
2681 PCB_GRID_HELPER m_gridHelper;
2682 std::shared_ptr<DRC_ENGINE> m_drcEngine;
2683 int m_drcEpsilon;
2684 int m_worstClearance;
2685 bool m_allowDRCViolations;
2686
2687 VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) :
2688 m_frame( aFrame ),
2689 m_gridHelper( aFrame->GetToolManager(), aFrame->GetMagneticItemsSettings() ),
2690 m_drcEngine( aFrame->GetBoard()->GetDesignSettings().m_DRCEngine ),
2691 m_drcEpsilon( aFrame->GetBoard()->GetDesignSettings().GetDRCEpsilon() ),
2692 m_worstClearance( 0 )
2693 {
2694 ROUTER_TOOL* router = m_frame->GetToolManager()->GetTool<ROUTER_TOOL>();
2695
2696 m_allowDRCViolations = router->Router()->Settings().AllowDRCViolations();
2697
2698 try
2699 {
2700 m_drcEngine->InitEngine( aFrame->GetDesignRulesPath() );
2701
2702 DRC_CONSTRAINT constraint;
2703
2704 if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, constraint ) )
2705 m_worstClearance = constraint.GetValue().Min();
2706
2707 if( m_drcEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, constraint ) )
2708 m_worstClearance = std::max( m_worstClearance, constraint.GetValue().Min() );
2709
2710 for( FOOTPRINT* footprint : aFrame->GetBoard()->Footprints() )
2711 {
2712 for( PAD* pad : footprint->Pads() )
2713 m_worstClearance = std::max( m_worstClearance, pad->GetLocalClearance() );
2714 }
2715 }
2716 catch( PARSE_ERROR& )
2717 {
2718 }
2719 }
2720
2721 virtual ~VIA_PLACER()
2722 {
2723 }
2724
2725 PCB_TRACK* findTrack( PCB_VIA* aVia )
2726 {
2727 const LSET lset = aVia->GetLayerSet();
2728 VECTOR2I position = aVia->GetPosition();
2729 BOX2I bbox = aVia->GetBoundingBox();
2730
2731 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
2732 KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
2733 std::vector<PCB_TRACK*> possible_tracks;
2734
2735 view->Query( bbox, items );
2736
2737 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
2738 {
2739 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
2740
2741 if( !( item->GetLayerSet() & lset ).any() )
2742 continue;
2743
2744 if( PCB_TRACK* track = dyn_cast<PCB_TRACK*>( item ) )
2745 {
2746 if( TestSegmentHit( position, track->GetStart(), track->GetEnd(),
2747 ( track->GetWidth() + aVia->GetWidth() ) / 2 ) )
2748 {
2749 possible_tracks.push_back( track );
2750 }
2751 }
2752 }
2753
2754 PCB_TRACK* return_track = nullptr;
2755 int min_d = std::numeric_limits<int>::max();
2756
2757 for( PCB_TRACK* track : possible_tracks )
2758 {
2759 SEG test( track->GetStart(), track->GetEnd() );
2760 int dist = ( test.NearestPoint( position ) - position ).EuclideanNorm();
2761
2762 if( dist < min_d )
2763 {
2764 min_d = dist;
2765 return_track = track;
2766 }
2767 }
2768
2769 return return_track;
2770 }
2771
2772 bool hasDRCViolation( PCB_VIA* aVia, BOARD_ITEM* aOther )
2773 {
2774 DRC_CONSTRAINT constraint;
2775 int clearance;
2776 BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aOther );
2777 ZONE* zone = dynamic_cast<ZONE*>( aOther );
2778
2779 if( zone && zone->GetIsRuleArea() )
2780 {
2781 if( zone->GetDoNotAllowVias() )
2782 return zone->Outline()->Collide( aVia->GetPosition(), aVia->GetWidth() / 2 );
2783
2784 return false;
2785 }
2786
2787 if( connectedItem )
2788 {
2789 int connectedItemNet = connectedItem->GetNetCode();
2790
2791 if( connectedItemNet == 0 || connectedItemNet == aVia->GetNetCode() )
2792 return false;
2793 }
2794
2795 for( PCB_LAYER_ID layer : aOther->GetLayerSet().Seq() )
2796 {
2797 // Bitmaps are "on" a copper layer but are not actually part of it
2798 if( !IsCopperLayer( layer ) || aOther->Type() == PCB_BITMAP_T )
2799 continue;
2800
2801 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aVia, aOther, layer );
2802 clearance = constraint.GetValue().Min();
2803
2804 if( clearance >= 0 )
2805 {
2806 std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( layer );
2807 std::shared_ptr<SHAPE> otherShape = aOther->GetEffectiveShape( layer );
2808
2809 if( viaShape->Collide( otherShape.get(), clearance - m_drcEpsilon ) )
2810 return true;
2811 }
2812 }
2813
2814 if( aOther->HasHole() )
2815 {
2816 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aVia, aOther,
2818 clearance = constraint.GetValue().Min();
2819
2820 if( clearance >= 0 )
2821 {
2822 std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( UNDEFINED_LAYER );
2823
2824 if( viaShape->Collide( aOther->GetEffectiveHoleShape().get(),
2825 clearance - m_drcEpsilon ) )
2826 {
2827 return true;
2828 }
2829 }
2830 }
2831
2832 return false;
2833 }
2834
2835 bool checkDRCViolation( PCB_VIA* aVia )
2836 {
2837 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
2838 std::set<BOARD_ITEM*> checkedItems;
2839 BOX2I bbox = aVia->GetBoundingBox();
2840
2841 bbox.Inflate( m_worstClearance );
2842 m_frame->GetCanvas()->GetView()->Query( bbox, items );
2843
2844 for( std::pair<KIGFX::VIEW_ITEM*, int> it : items )
2845 {
2846 BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( it.first );
2847
2848 if( !item )
2849 continue;
2850
2851 if( item->Type() == PCB_ZONE_T && !static_cast<ZONE*>( item )->GetIsRuleArea() )
2852 {
2853 continue; // stitching vias bind to zones, so ignore them
2854 }
2855 else if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_GROUP_T )
2856 {
2857 continue; // check against children, but not against footprint itself
2858 }
2859 else if( item->Type() == PCB_TEXT_T && !static_cast<PCB_TEXT*>( item )->IsVisible() )
2860 {
2861 continue; // ignore hidden items
2862 }
2863 else if( checkedItems.count( item ) )
2864 {
2865 continue; // already checked
2866 }
2867
2868 if( hasDRCViolation( aVia, item ) )
2869 return true;
2870
2871 checkedItems.insert( item );
2872 }
2873
2874 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( DISALLOW_CONSTRAINT, aVia, nullptr,
2876
2877 if( constraint.m_DisallowFlags && constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
2878 return true;
2879
2880 return false;
2881 }
2882
2883 PAD* findPad( PCB_VIA* aVia )
2884 {
2885 const VECTOR2I position = aVia->GetPosition();
2886 const LSET lset = aVia->GetLayerSet();
2887
2888 for( FOOTPRINT* fp : m_board->Footprints() )
2889 {
2890 for( PAD* pad : fp->Pads() )
2891 {
2892 if( pad->HitTest( position ) && ( pad->GetLayerSet() & lset ).any() )
2893 {
2894 if( pad->GetNetCode() > 0 )
2895 return pad;
2896 }
2897 }
2898 }
2899
2900 return nullptr;
2901 }
2902
2903 int findStitchedZoneNet( PCB_VIA* aVia )
2904 {
2905 const VECTOR2I position = aVia->GetPosition();
2906 const LSET lset = aVia->GetLayerSet();
2907
2908 // first take the net of the active layer
2909 if( lset.test( m_frame->GetActiveLayer() ) )
2910 {
2911 for( ZONE* z : m_board->Zones() )
2912 {
2913 if( z->IsOnLayer( m_frame->GetActiveLayer() ) )
2914 {
2915 if( z->HitTestFilledArea( m_frame->GetActiveLayer(), position ) )
2916 return z->GetNetCode();
2917 }
2918 }
2919 }
2920
2921 // none? take the topmost visible layer
2922 for( PCB_LAYER_ID layer : LSET( m_board->GetVisibleLayers() & lset ).Seq() )
2923 {
2924 for( ZONE* z : m_board->Zones() )
2925 {
2926 if( z->IsOnLayer( layer ) )
2927 {
2928 if( z->HitTestFilledArea( layer, position ) )
2929 return z->GetNetCode();
2930 }
2931 }
2932 }
2933
2934 return -1;
2935 }
2936
2937 void SnapItem( BOARD_ITEM *aItem ) override
2938 {
2939 m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
2940
2941 MAGNETIC_SETTINGS* settings = m_frame->GetMagneticItemsSettings();
2942 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
2943 VECTOR2I position = via->GetPosition();
2944
2945 if( settings->tracks == MAGNETIC_OPTIONS::CAPTURE_ALWAYS && m_gridHelper.GetSnap() )
2946 {
2947 PCB_TRACK* track = findTrack( via );
2948
2949 if( track )
2950 {
2951 SEG trackSeg( track->GetStart(), track->GetEnd() );
2952 VECTOR2I snap = m_gridHelper.AlignToSegment( position, trackSeg );
2953
2954 aItem->SetPosition( snap );
2955 }
2956 }
2957 else if( settings->pads == MAGNETIC_OPTIONS::CAPTURE_ALWAYS && m_gridHelper.GetSnap() )
2958 {
2959 PAD* pad = findPad( via );
2960
2961 if( pad )
2962 {
2963 aItem->SetPosition( pad->GetPosition() );
2964 }
2965 }
2966 }
2967
2968 bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
2969 {
2970 WX_INFOBAR* infobar = m_frame->GetInfoBar();
2971 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
2972 VECTOR2I viaPos = via->GetPosition();
2973 PCB_TRACK* track = findTrack( via );
2974 PAD* pad = findPad( via );
2975
2976 if( track )
2977 {
2978 via->SetNetCode( track->GetNetCode() );
2979 via->SetIsFree( false );
2980 }
2981 else if( pad )
2982 {
2983 via->SetNetCode( pad->GetNetCode() );
2984 via->SetIsFree( false );
2985 }
2986 else
2987 {
2988 via->SetNetCode( findStitchedZoneNet( via ) );
2989 via->SetIsFree( via->GetNetCode() > 0 );
2990 }
2991
2992 if( checkDRCViolation( via ) )
2993 {
2994 m_frame->ShowInfoBarError( _( "Via location violates DRC." ), true,
2996
2997 if( !m_allowDRCViolations )
2998 return false;
2999 }
3000 else
3001 {
3003 infobar->Dismiss();
3004 }
3005
3006 aCommit.Add( via );
3007
3008 // If the user explicitly disables snap (using shift), then don't break the tracks into
3009 // a chevron. This will prevent PNS from being able to connect the via and track but
3010 // it is explicitly requested by the user
3011 if( track && m_gridHelper.GetSnap() )
3012 {
3013 VECTOR2I trackStart = track->GetStart();
3014 VECTOR2I trackEnd = track->GetEnd();
3015 SEG trackSeg( trackStart, trackEnd );
3016
3017 OPT_VECTOR2I joint1;
3018 OPT_VECTOR2I joint2;
3019
3020 auto insertChevron =
3021 [&]()
3022 {
3023 if( ( trackStart - *joint1 ).SquaredEuclideanNorm()
3024 > ( trackStart - *joint2 ).SquaredEuclideanNorm() )
3025 {
3026 std::swap( joint1, joint2 );
3027 }
3028
3029 aCommit.Modify( track );
3030 track->SetStart( trackStart );
3031 track->SetEnd( *joint1 );
3032
3033 if( *joint1 != viaPos )
3034 {
3035 PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
3036 wxCHECK( newTrack, /* void */ );
3037 const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
3038
3039 newTrack->SetStart( *joint1 );
3040 newTrack->SetEnd( viaPos );
3041 aCommit.Add( newTrack );
3042 }
3043
3044 if( *joint2 != viaPos )
3045 {
3046 PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
3047 wxCHECK( newTrack, /* void */ );
3048 const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
3049
3050 newTrack->SetStart( viaPos );
3051 newTrack->SetEnd( *joint2 );
3052 aCommit.Add( newTrack );
3053 }
3054
3055 PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
3056 wxCHECK( newTrack, /* void */ );
3057 const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
3058
3059 newTrack->SetStart( *joint2 );
3060 newTrack->SetEnd( trackEnd );
3061 aCommit.Add( newTrack );
3062 };
3063
3064 if( viaPos == trackStart || viaPos == trackEnd )
3065 return true;
3066
3067 if( trackStart.x == trackEnd.x )
3068 {
3069 VECTOR2I splitPt = trackSeg.NearestPoint( viaPos );
3070
3071 if( splitPt.x != viaPos.x
3072 && abs( splitPt.x - viaPos.x ) < abs( splitPt.y - trackStart.y )
3073 && abs( splitPt.x - viaPos.x ) < abs( splitPt.y - trackEnd.y ) )
3074 {
3075 int offset = abs( splitPt.x - viaPos.x );
3076
3077 joint1 = VECTOR2I( splitPt.x, splitPt.y - offset );
3078 joint2 = VECTOR2I( splitPt.x, splitPt.y + offset );
3079
3080 insertChevron();
3081 return true;
3082 }
3083 }
3084 else if( trackStart.y == trackEnd.y )
3085 {
3086 VECTOR2I splitPt = trackSeg.NearestPoint( viaPos );
3087
3088 if( splitPt.y != viaPos.y
3089 && abs( trackStart.y - viaPos.y ) < abs( trackStart.x - viaPos.x )
3090 && abs( trackEnd.y - viaPos.y ) < abs( trackEnd.x - viaPos.x ) )
3091 {
3092 int offset = abs( splitPt.y - viaPos.y );
3093
3094 joint1 = VECTOR2I( splitPt.x - offset, splitPt.y );
3095 joint2 = VECTOR2I( splitPt.x + offset, splitPt.y );
3096
3097 insertChevron();
3098 return true;
3099 }
3100 }
3101 else if( abs( trackStart.y - trackEnd.y ) == abs( trackStart.x - trackEnd.x ) )
3102 {
3103 SEG horiz( VECTOR2I( -INT_MAX, viaPos.y ), VECTOR2I( INT_MAX, viaPos.y ) );
3104 SEG vert( VECTOR2I( viaPos.x, -INT_MAX ), VECTOR2I( viaPos.x, INT_MAX ) );
3105
3106 if( track->GetBoundingBox().Contains( viaPos ) )
3107 {
3108 joint1 = trackSeg.Intersect( horiz, true, true );
3109 joint2 = trackSeg.Intersect( vert, true, true );
3110
3111 if( !joint1 || !joint2 )
3112 return false;
3113
3114 insertChevron();
3115 return true;
3116 }
3117 }
3118
3119 aCommit.Modify( track );
3120 track->SetStart( trackStart );
3121 track->SetEnd( viaPos );
3122
3123 PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
3124 const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
3125
3126 newTrack->SetStart( viaPos );
3127 newTrack->SetEnd( trackEnd );
3128 aCommit.Add( newTrack );
3129 }
3130
3131 return true;
3132 }
3133
3134 std::unique_ptr<BOARD_ITEM> CreateItem() override
3135 {
3136 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
3137 PCB_VIA* via = new PCB_VIA( m_board );
3138
3139 via->SetNetCode( 0 );
3140 via->SetViaType( bds.m_CurrentViaType );
3141
3142 // for microvias, the size and hole will be changed later.
3143 via->SetWidth( bds.GetCurrentViaSize() );
3144 via->SetDrill( bds.GetCurrentViaDrill() );
3145
3146 // Usual via is from copper to component.
3147 // layer pair is B_Cu and F_Cu.
3148 via->SetLayerPair( B_Cu, F_Cu );
3149
3150 PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
3151 PCB_LAYER_ID last_layer;
3152
3153 // prepare switch to new active layer:
3154 if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
3155 last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
3156 else
3157 last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
3158
3159 // Adjust the actual via layer pair
3160 switch( via->GetViaType() )
3161 {
3162 case VIATYPE::BLIND_BURIED:
3163 via->SetLayerPair( first_layer, last_layer );
3164 break;
3165
3166 case VIATYPE::MICROVIA: // from external to the near neighbor inner layer
3167 {
3168 PCB_LAYER_ID last_inner_layer =
3169 ToLAYER_ID( ( m_board->GetCopperLayerCount() - 2 ) );
3170
3171 if( first_layer == B_Cu )
3172 last_layer = last_inner_layer;
3173 else if( first_layer == F_Cu )
3174 last_layer = In1_Cu;
3175 else if( first_layer == last_inner_layer )
3176 last_layer = B_Cu;
3177 else if( first_layer == In1_Cu )
3178 last_layer = F_Cu;
3179
3180 // else error: will be removed later
3181 via->SetLayerPair( first_layer, last_layer );
3182
3183 // Update diameter and hole size, which where set previously for normal vias
3184 NETCLASS* netClass = via->GetEffectiveNetClass();
3185
3186 via->SetWidth( netClass->GetuViaDiameter() );
3187 via->SetDrill( netClass->GetuViaDrill() );
3188 }
3189 break;
3190
3191 default:
3192 break;
3193 }
3194
3195 return std::unique_ptr<BOARD_ITEM>( via );
3196 }
3197 };
3198
3199 VIA_PLACER placer( frame() );
3200
3201 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
3202
3203 doInteractiveItemPlacement( aEvent, &placer, _( "Place via" ), IPO_REPEAT | IPO_SINGLE_CLICK );
3204
3205 return 0;
3206}
3207
3208
3210{
3211 assert( m_board );
3212 return m_board->GetDesignSettings().GetLineThickness( aLayer );
3213}
3214
3215
3216const unsigned int DRAWING_TOOL::WIDTH_STEP = pcbIUScale.mmToIU( 0.1 );
3217
3218
3220{
3221
3245}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
static TOOL_ACTION cancelInteractive
Definition: actions.h:63
static TOOL_ACTION updateUnits
Definition: actions.h:151
static TOOL_ACTION activatePointEditor
Definition: actions.h:173
static TOOL_ACTION doDelete
Definition: actions.h:73
static TOOL_ACTION cursorClick
Definition: actions.h:127
static TOOL_ACTION refreshPreview
Definition: actions.h:110
static TOOL_ACTION resetLocalCoords
Definition: actions.h:154
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:49
TOOL_MANAGER * getToolManager() const
void Clear()
Remove all the entries from the menu (as well as its title).
void SetTitle(const wxString &aTitle) override
Set title for the menu.
Definition: action_menu.cpp:87
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
Definition: action_menu.cpp:73
void SetLayerVisible(int aLayer, bool isVisible)
VECTOR2D m_LocalOrigin
Relative Screen cursor coordinate (on grid) in user units.
Definition: base_screen.h:90
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
Container for design settings for a BOARD object.
DIM_PRECISION m_DimensionPrecision
Number of digits after the decimal.
void UseCustomTrackViaSize(bool aEnabled)
Enables/disables custom track/via size settings.
VIATYPE m_CurrentViaType
(VIA_BLIND_BURIED, VIA_THROUGH, VIA_MICROVIA)
DIM_UNITS_FORMAT m_DimensionUnitsFormat
bool GetTextUpright(PCB_LAYER_ID aLayer) const
int GetTextThickness(PCB_LAYER_ID aLayer) const
Return the default text thickness from the layer class for the given layer.
bool GetTextItalic(PCB_LAYER_ID aLayer) const
std::shared_ptr< DRC_ENGINE > m_DRCEngine
void SetViaSizeIndex(unsigned aIndex)
Set the current via size list index to aIndex.
unsigned GetViaSizeIndex() const
VECTOR2I GetTextSize(PCB_LAYER_ID aLayer) const
Return the default text size from the layer class for the given layer.
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
DIM_TEXT_POSITION m_DimensionTextPosition
DIM_UNITS_MODE m_DimensionUnitsMode
std::vector< VIA_DIMENSION > m_ViasDimensionsList
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:71
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:230
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: board_item.cpp:220
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:201
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:230
virtual bool HasHole() const
Definition: board_item.h:141
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:405
FOOTPRINTS & Footprints()
Definition: board.h:312
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:728
coord_type GetTop() const
Definition: box2.h:194
coord_type GetHeight() const
Definition: box2.h:188
coord_type GetWidth() const
Definition: box2.h:187
bool Contains(const Vec &aPoint) const
Definition: box2.h:141
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:506
Vec Centre() const
Definition: box2.h:70
coord_type GetRight() const
Definition: box2.h:189
coord_type GetLeft() const
Definition: box2.h:193
coord_type GetBottom() const
Definition: box2.h:190
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:104
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition: commit.h:79
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.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
void AddCheckItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a checked menu entry to run a TOOL_ACTION on selected items.
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
std::list< std::unique_ptr< EDA_ITEM > > & GetImportedItems()
int ShowQuasiModal()
Implementing DIALOG_TRACK_VIA_SIZE_BASE.
TEXT_ATTRIBUTES m_textAttrs
Definition: drawing_tool.h:348
MODE GetDrawingMode() const
Return the current drawing mode of the DRAWING_TOOL or MODE::NONE if not currently in any drawing mod...
PCB_LAYER_ID m_layer
Definition: drawing_tool.h:346
int SetAnchor(const TOOL_EVENT &aEvent)
Place the footprint anchor (only in footprint editor).
int DrawDimension(const TOOL_EVENT &aEvent)
Start interactively drawing a dimension.
int DrawVia(const TOOL_EVENT &aEvent)
KIGFX::VIEW_CONTROLS * m_controls
Definition: drawing_tool.h:340
KIGFX::VIEW * m_view
Definition: drawing_tool.h:339
STROKE_PARAMS m_stroke
Definition: drawing_tool.h:347
int DrawZone(const TOOL_EVENT &aEvent)
Start interactively drawing a zone.
int DrawLine(const TOOL_EVENT &aEvent)
Start interactively drawing a line.
int DrawArc(const TOOL_EVENT &aEvent)
Start interactively drawing an arc.
VECTOR2I getClampedDifferenceEnd(const VECTOR2I &aOrigin, const VECTOR2I &aEnd)
Clamps the end vector to respect numeric limits of difference representation.
Definition: drawing_tool.h:284
int ToggleHV45Mode(const TOOL_EVENT &toolEvent)
Toggle the horizontal/vertical/45-degree constraint for drawing tools.
int PlaceCharacteristics(const TOOL_EVENT &aEvent)
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
int PlaceImportedGraphics(const TOOL_EVENT &aEvent)
Place a drawing imported from a DXF or SVG file in footprint editor.
int PlaceStackup(const TOOL_EVENT &aEvent)
VECTOR2I getClampedRadiusEnd(const VECTOR2I &aOrigin, const VECTOR2I &aEnd)
Clamps the end vector to respect numeric limits of radius representation.
Definition: drawing_tool.h:314
bool m_inDrawingTool
Definition: drawing_tool.h:344
static const unsigned int WIDTH_STEP
Definition: drawing_tool.h:350
static const unsigned int COORDS_PADDING
Definition: drawing_tool.h:351
bool drawArc(const TOOL_EVENT &aTool, PCB_SHAPE **aGraphic, std::optional< VECTOR2D > aStartingPoint)
Start drawing an arc.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
int PlaceImage(const TOOL_EVENT &aEvent)
Display a dialog that allows one to select and image then decide where to place the image in the edit...
int DrawCircle(const TOOL_EVENT &aEvent)
Start interactively drawing a circle.
BOARD * m_board
Definition: drawing_tool.h:341
int DrawRectangle(const TOOL_EVENT &aEvent)
Start interactively drawing a rectangle.
bool Init() override
Init() is called once upon a registration of the tool.
void constrainDimension(PCB_DIMENSION_BASE *aDim)
Force the dimension lime to be drawn on multiple of 45 degrees.
int PlaceText(const TOOL_EVENT &aEvent)
Display a dialog that allows one to input text and its settings and then lets the user decide where t...
PCB_BASE_EDIT_FRAME * m_frame
Definition: drawing_tool.h:342
void UpdateStatusBar() const
bool getSourceZoneForAction(ZONE_MODE aMode, ZONE **aZone)
Draw a polygon, that is added as a zone or a keepout area.
int getSegmentWidth(PCB_LAYER_ID aLayer) const
bool drawShape(const TOOL_EVENT &aTool, PCB_SHAPE **aGraphic, std::optional< VECTOR2D > aStartingPoint)
Start drawing a selected shape (i.e.
int m_DisallowFlags
Definition: drc_rule.h:173
SEVERITY GetSeverity() const
Definition: drc_rule.h:162
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:141
void ShowInfoBarMsg(const wxString &aMsg, bool aShowCloseButton=false)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an info icon on the left of...
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, WX_INFOBAR::MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
bool IsType(FRAME_T aType) const
WX_INFOBAR * GetInfoBar()
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
void DisplayConstraintsMsg(const wxString &msg)
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
virtual void SetPosition(const VECTOR2I &aPos)
Definition: eda_item.h:233
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:123
const KIID m_Uuid
Definition: eda_item.h:475
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:470
void SetFilled(bool aFlag)
Definition: eda_shape.h:95
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:145
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:124
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:112
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:149
double GetLength() const
Return the length of the track using the hypotenuse calculation.
Definition: eda_shape.cpp:110
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:596
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:383
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:197
void SetTextSize(const VECTOR2I &aNewSize)
Definition: eda_text.cpp:359
void SetItalic(bool aItalic)
Definition: eda_text.cpp:213
void MoveAnchorPosition(const VECTOR2I &aMoveVector)
Move the reference point of the footprint.
Definition: footprint.cpp:1683
VECTOR2I GetPosition() const override
Definition: footprint.h:190
bool GetSnap() const
Definition: grid_helper.h:66
void SetSnap(bool aSnap)
Definition: grid_helper.h:65
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
Represents an assistant draw when interactively drawing an arc on a canvas.
Definition: arc_assistant.h:39
void SetUnits(EDA_UNITS aUnits)
Definition: arc_assistant.h:70
Manage the construction of a circular arc though sequential setting of critical points: center,...
VECTOR2I GetOrigin() const
< Get the center point of the arc (valid when state > SET_ORIGIN)
ARC_STEPS GetStep() const
Get the current step the manager is on (useful when drawing something depends on the current state)
void ToggleClockwise()
Set angle snapping (for the next point)
VECTOR2I GetStartRadiusEnd() const
Get the coordinates of the arc end point.
VECTOR2I GetEndRadiusEnd() const
Get the radius of the arc (valid if step >= SET_START)
@ SET_ANGLE
Waiting to lock in the arc end point.
@ SET_START
Waiting to lock in the arc start point.
void AddPoint(const VECTOR2I &aPt, bool aLockIn)
Add a point to the construction manager.
void RemoveLastPoint()
Undo the last point, and move the manager back to the previous step.
Represents an assistant draw when interactively drawing a line or circle on a canvas.
Represent a very simple geometry manager for items that have a start and end point.
void SetOrigin(const VECTOR2I &aOrigin)
< Set the origin of the ruler (the fixed end)
void Reset()
Reset the manager to the initial state.
void SetEnd(const VECTOR2I &aEnd)
Set the current end of the rectangle (the end that moves with the cursor.
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const std::set< int > & GetHighlightNetCodes() const
Return the netcode of currently highlighted net.
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 (.
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
void ShowPreview(bool aShow=true)
Definition: view.cpp:1666
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:314
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:351
virtual int Query(const BOX2I &aRect, std::vector< LAYER_ITEM_PAIR > &aResult) const
Find all visible items that touch or are within the rectangle aRect.
Definition: view.cpp:422
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: view.cpp:1608
void AddToPreview(EDA_ITEM *aItem, bool aTakeOwnership=true)
Definition: view.cpp:1652
void ClearPreview()
Definition: view.cpp:1630
void RecacheAllItems()
Rebuild GAL display lists.
Definition: view.cpp:1401
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition: view.h:73
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:410
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:213
Definition: kiid.h:48
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:536
static LSET AllLayersMask()
Definition: lset.cpp:808
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
T Min() const
Definition: minoptmax.h:33
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:47
int GetuViaDrill() const
Definition: netclass.h:92
int GetuViaDiameter() const
Definition: netclass.h:88
Definition: pad.h:59
static TOOL_ACTION deleteLastPoint
Definition: pcb_actions.h:193
static TOOL_ACTION toggleHV45Mode
Definition: pcb_actions.h:467
static TOOL_ACTION drawRuleArea
Definition: pcb_actions.h:185
static TOOL_ACTION placeText
Definition: pcb_actions.h:176
static TOOL_ACTION drawOrthogonalDimension
Definition: pcb_actions.h:181
static TOOL_ACTION drawRectangle
Definition: pcb_actions.h:172
static TOOL_ACTION setAnchor
Definition: pcb_actions.h:192
static TOOL_ACTION drawCircle
Definition: pcb_actions.h:173
static TOOL_ACTION placeImage
Definition: pcb_actions.h:175
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition: pcb_actions.h:56
static TOOL_ACTION trackViaSizeChanged
Definition: pcb_actions.h:338
static TOOL_ACTION layerChanged
Definition: pcb_actions.h:328
static TOOL_ACTION drawTextBox
Definition: pcb_actions.h:177
static TOOL_ACTION drawZoneCutout
Definition: pcb_actions.h:186
static TOOL_ACTION drawPolygon
Definition: pcb_actions.h:171
static TOOL_ACTION drawRadialDimension
Definition: pcb_actions.h:180
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:149
static TOOL_ACTION drawLeader
Definition: pcb_actions.h:182
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:59
static TOOL_ACTION placeCharacteristics
Definition: pcb_actions.h:188
static TOOL_ACTION incWidth
Increase width of currently drawn line.
Definition: pcb_actions.h:197
static TOOL_ACTION clearHighlight
Definition: pcb_actions.h:498
static TOOL_ACTION placeImportedGraphics
Definition: pcb_actions.h:191
static TOOL_ACTION drawVia
Definition: pcb_actions.h:184
static TOOL_ACTION drawArc
Definition: pcb_actions.h:174
static TOOL_ACTION drawSimilarZone
Definition: pcb_actions.h:187
static TOOL_ACTION decWidth
Decrease width of currently drawn line.
Definition: pcb_actions.h:200
static TOOL_ACTION drawCenterDimension
Definition: pcb_actions.h:179
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: pcb_actions.h:62
static TOOL_ACTION arcPosture
Switch posture when drawing arc.
Definition: pcb_actions.h:203
static TOOL_ACTION closeOutline
Definition: pcb_actions.h:194
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: pcb_actions.h:66
static TOOL_ACTION drawLine
Definition: pcb_actions.h:170
static TOOL_ACTION placeStackup
Definition: pcb_actions.h:189
static TOOL_ACTION drawAlignedDimension
Definition: pcb_actions.h:178
static TOOL_ACTION drawZone
Definition: pcb_actions.h:183
Common, abstract interface for edit frames.
wxString GetDesignRulesPath()
Return the absolute path to the design rules file for the currently-loaded board.
int ShowTextBoxPropertiesDialog(PCB_TEXTBOX *aTextBox)
virtual void OnEditItemRequest(BOARD_ITEM *aItem)=0
Install the corresponding dialog editor for the given item.
APPEARANCE_CONTROLS * GetAppearancePanel()
void SetObjectVisible(GAL_LAYER_ID aLayer, bool aVisible=true)
virtual PCB_LAYER_ID GetActiveLayer() const
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
BOARD * GetBoard() const
virtual BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Returns the BOARD_DESIGN_SETTINGS for the open project.
virtual BOARD_ITEM_CONTAINER * GetModel() const =0
Object to handle a bitmap image that can be inserted in a PCB.
Definition: pcb_bitmap.h:42
Abstract dimension API.
void Update()
Update the dimension's cached text and geometry.
int GetLineThickness() const
void SetExtensionOffset(int aOffset)
void SetLineThickness(int aWidth)
void SetArrowLength(int aLength)
virtual const VECTOR2I & GetStart() const
The dimension's origin is the first feature point for the dimension.
virtual void SetEnd(const VECTOR2I &aPoint)
virtual void SetStart(const VECTOR2I &aPoint)
int GetArrowLength() const
virtual const VECTOR2I & GetEnd() const
For better understanding of the points that make a dimension:
double GetAngle() const
Return the angle of the crossbar.
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
Mark the center of a circle or arc with a cross shape.
A leader is a dimension-like object pointing to a specific point.
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
A radial dimension indicates either the radius or diameter of an arc or circle.
VECTOR2I GetKnee() const
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
The main frame for Pcbnew.
VECTOR2I AlignToSegment(const VECTOR2I &aPoint, const SEG &aSeg)
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:51
PCB_LAYER_ID m_Route_Layer_TOP
Definition: pcb_screen.h:43
PCB_LAYER_ID m_Route_Layer_BOTTOM
Definition: pcb_screen.h:44
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
EDA_ITEM * GetTopLeftItem(bool aFootprintsOnly=false) const override
void NormalizeRect()
Definition: pcb_shape.cpp:171
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:72
@ TEXT_is_DIVERS
Definition: pcb_text.h:52
virtual bool Is45Limited() const
Should the tool use its 45° mode option?
PCB_BASE_EDIT_FRAME * frame() const
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
@ IPO_SINGLE_CLICK
Create an item immediately on placement starting, otherwise show the pencil cursor until the item is ...
@ IPO_REPEAT
Allow repeat placement of the item.
bool m_isFootprintEditor
void doInteractiveItemPlacement(const TOOL_EVENT &aTool, INTERACTIVE_PLACER_BASE *aPlacer, const wxString &aCommitMessage, int aOptions=IPO_ROTATE|IPO_FLIP|IPO_REPEAT)
Helper function for performing a common interactive idiom: wait for a left click, place an item there...
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
int GetWidth() const
Definition: pcb_track.h:107
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:109
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:112
virtual EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: pcb_track.cpp:61
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: pcb_track.cpp:267
const VECTOR2I & GetStart() const
Definition: pcb_track.h:113
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:110
VECTOR2I GetPosition() const override
Definition: pcb_track.h:446
std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: pcb_track.cpp:1194
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_track.cpp:505
ROUTING_SETTINGS & Settings()
Definition: pns_router.h:189
ROUTER * Router() const
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.
@ DIRECT
Unconstrained point-to-point.
void SetCursorPosition(const VECTOR2I &aPos)
Set the current cursor position.
void DeleteLastCorner()
Remove the last-added point from the polygon.
bool NewPointClosesOutline(const VECTOR2I &aPt) const
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.
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:269
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
Definition: seg.cpp:196
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
int AddItemToSel(const TOOL_EVENT &aEvent)
virtual void Add(EDA_ITEM *aItem)
Definition: selection.cpp:42
virtual void Remove(EDA_ITEM *aItem)
Definition: selection.cpp:60
virtual void Clear() override
Remove all the stored items from the group.
Definition: selection.h:92
int Size() const
Returns the number of selected parts.
Definition: selection.h:115
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.h:260
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:109
T * GetAppSettings()
Returns a handle to the a given settings by type If the settings have already been loaded,...
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
int GetWidth() const
Definition: stroke_params.h:98
void SetWidth(int aWidth)
Definition: stroke_params.h:99
void SetColor(const KIGFX::COLOR4D &aColor)
void SetPlotStyle(PLOT_DASH_TYPE aPlotStyle)
bool m_KeepUpright
If true, keep rotation angle between -90...90 degrees for readability.
GR_TEXT_H_ALIGN_T m_Halign
GR_TEXT_V_ALIGN_T m_Valign
virtual void PopTool(const TOOL_EVENT &aEvent)
Pops a tool from the stack.
virtual void PushTool(const TOOL_EVENT &aEvent)
NB: the definition of "tool" is different at the user level.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:54
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:216
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
Generic, UI-independent tool event.
Definition: tool_event.h:156
bool HasPosition() const
Definition: tool_event.h:243
bool DisableGridSnapping() const
Definition: tool_event.h:344
T Parameter() const
Return a non-standard parameter assigned to the event.
Definition: tool_event.h:442
const VECTOR2D Position() const
Returns the point where dragging has started.
Definition: tool_event.h:266
bool IsReactivate() const
Definition: tool_event.h:255
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
Definition: tool_event.cpp:81
void RunMainStack(std::function< void()> aFunc)
Call a function using the main stack.
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_MENU m_menu
The functions below are not yet implemented - their interface may change.
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:142
void PrimeTool(const VECTOR2D &aPosition)
"Prime" a tool by sending a cursor left-click event with the mouse position set to the passed in posi...
TOOLS_HOLDER * GetToolHolder() const
Definition: tool_manager.h:296
void VetoContextMenuMouseWarp()
Disable mouse warping after the current context menu is closed.
Definition: tool_manager.h:424
KIGFX::VIEW * GetView() const
Definition: tool_manager.h:285
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
void RegisterSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Store a submenu of this menu model.
Definition: tool_menu.cpp:50
void ShowContextMenu(SELECTION &aSelection)
Helper function to set and immediately show a CONDITIONAL_MENU in concert with the given SELECTION.
Definition: tool_menu.cpp:57
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A lower-precision version of StringFromValue().
EDA_UNITS GetUserUnits() const
void update() override
Update menu state stub.
OPT_TOOL_EVENT eventHandler(const wxMenuEvent &aEvent) override
Event handler stub.
ACTION_MENU * create() const override
< Return an instance of this class. It has to be overridden in inheriting classes.
A modified version of the wxInfoBar class that allows us to:
Definition: wx_infobar.h:75
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
Definition: wx_infobar.cpp:187
MESSAGE_TYPE GetMessageType() const
Definition: wx_infobar.h:100
An adjunct helper to the DRAWING_TOOL interactive tool, which handles incoming geometry changes from ...
void OnGeometryChange(const POLYGON_GEOM_MANAGER &aMgr) override
Called when the polygon is complete.
ZONE * GetZone() const
static bool IsZoneFillAction(const TOOL_EVENT *aEvent)
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:710
bool GetDoNotAllowVias() const
Definition: zone.h:712
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: zone.cpp:248
SHAPE_POLY_SET * Outline()
Definition: zone.h:325
This file is part of the common library.
#define TOGGLE(a)
static void updateArcFromConstructionMgr(const KIGFX::PREVIEW::ARC_GEOM_MANAGER &aMgr, PCB_SHAPE &aArc)
Update an arc PCB_SHAPE from the current state of an Arc Geometry Manager.
static void updateSegmentFromGeometryMgr(const KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER &aMgr, PCB_SHAPE *aGraphic)
Update a PCB_SHAPE from the current state of a #TWO_POINT_GEOMETRY_MANAGER.
@ DISALLOW_CONSTRAINT
Definition: drc_rule.h:62
@ CLEARANCE_CONSTRAINT
Definition: drc_rule.h:47
@ HOLE_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:48
#define _(s)
static constexpr EDA_ANGLE & ANGLE_90
Definition: eda_angle.h:431
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:429
#define IS_NEW
New item, just created.
#define IS_MOVING
Item being moved.
SHAPE_T
Definition: eda_shape.h:41
EDA_UNITS
Definition: eda_units.h:43
@ FRAME_PCB_EDITOR
Definition: frame_type.h:40
a few functions useful in geometry calculations.
VECTOR2< T > GetVectorSnapped45(const VECTOR2< T > &aVec, bool only45=false)
Snap a vector onto the nearest 0, 45 or 90 degree line.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamps a vector to values that can be negated, respecting numeric limits of coordinates data type wit...
void InferBold(TEXT_ATTRIBUTES *aAttrs)
Definition: gr_text.h:88
@ NONE
Definition: kibis.h:53
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:928
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:831
@ LAYER_ZONES
Control for copper zone opacity/visibility (color ignored)
Definition: layer_ids.h:231
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ B_Cu
Definition: layer_ids.h:95
@ UNDEFINED_LAYER
Definition: layer_ids.h:60
@ In1_Cu
Definition: layer_ids.h:65
@ F_Cu
Definition: layer_ids.h:64
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:932
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:418
ZONE_MODE
Definition: pcb_actions.h:34
Class to handle a set of BOARD_ITEMs.
@ ID_POPUP_PCB_SELECT_CUSTOM_WIDTH
Definition: pcbnew_id.h:26
@ ID_POPUP_PCB_SELECT_VIASIZE1
Definition: pcbnew_id.h:45
@ ID_POPUP_PCB_SELECT_VIASIZE16
Definition: pcbnew_id.h:60
int GetUserUnits()
Return the currently selected user unit value for the interface.
see class PGM_BASE
Class that computes missing connections on a PCB.
@ RPT_SEVERITY_IGNORE
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:115
bool NoPrintableChars(const wxString &aString)
Return true if the string is empty or contains only whitespace.
PLOT_DASH_TYPE
Dashed line types.
Definition: stroke_params.h:48
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
MAGNETIC_OPTIONS tracks
MAGNETIC_OPTIONS pads
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:119
Container to handle a stock of specific vias each with unique diameter and drill sizes in the BOARD c...
Parameters used to fully describe a zone creation process.
ZONE_MODE m_mode
Zone settings source (for similar and cutout zones)
bool m_keepout
< Should create a keepout zone?
ZONE * m_sourceZone
Zone leader mode.
PCB_LAYER_ID m_layer
The zone mode to operate in.
constexpr int delta
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_TOP
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition: tool_event.h:557
@ MD_SHIFT
Definition: tool_event.h:138
@ BUT_LEFT
Definition: tool_event.h:127
@ BUT_RIGHT
Definition: tool_event.h:128
bool TestSegmentHit(const VECTOR2I &aRefPoint, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aDist)
Test if aRefPoint is with aDistance on the line defined by aStart and aEnd.
Definition: trigo.cpp:129
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:129
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:78
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:101
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:98
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:99
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:106
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:103
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:90
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:97
@ PCB_BITMAP_T
class PCB_BITMAP, bitmap on a layer
Definition: typeinfo.h:89
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition: typeinfo.h:96
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:100
VECTOR2< double > VECTOR2D
Definition: vector2d.h:587
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588