KiCad PCB EDA Suite
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-2022 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 <fp_shape.h>
61#include <fp_textbox.h>
62#include <macros.h>
63#include <painter.h>
64#include <pcb_edit_frame.h>
65#include <pcb_group.h>
66#include <pcb_bitmap.h>
67#include <pcb_text.h>
68#include <pcb_textbox.h>
69#include <pcb_dimension.h>
70#include <pcbnew_id.h>
72#include <scoped_set_reset.h>
73#include <status_popup.h>
74#include <string_utils.h>
75#include <zone.h>
76
77const unsigned int DRAWING_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
78
80
81
83{
84public:
86 ACTION_MENU( true )
87 {
89 SetTitle( _( "Select Via Size" ) );
90 }
91
92protected:
93 ACTION_MENU* create() const override
94 {
95 return new VIA_SIZE_MENU();
96 }
97
98 void update() override
99 {
102 bool useIndex = !bds.m_UseConnectedTrackWidth
103 && !bds.UseCustomTrackViaSize();
104 wxString msg;
105
106 Clear();
107
108 Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
109 _( "Specify custom track and via sizes" ), wxITEM_CHECK );
111
112 AppendSeparator();
113
114 for( unsigned i = 1; i < bds.m_ViasDimensionsList.size(); i++ )
115 {
117
118 if( via.m_Drill > 0 )
119 {
120 msg.Printf( _("Via %s, hole %s" ),
121 frame->MessageTextFromValue( via.m_Diameter ),
122 frame->MessageTextFromValue( via.m_Drill ) );
123 }
124 else
125 {
126 msg.Printf( _( "Via %s" ),
127 frame->MessageTextFromValue( via.m_Diameter ) );
128 }
129
130 int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
131 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
132 Check( menuIdx, useIndex && bds.GetViaSizeIndex() == i );
133 }
134 }
135
136 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
137 {
140 int id = aEvent.GetId();
141
142 // On Windows, this handler can be called with an event ID not existing in any
143 // menuitem, so only set flags when we have an ID match.
144
146 {
147 DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds );
148
149 if( sizeDlg.ShowModal() == wxID_OK )
150 {
151 bds.UseCustomTrackViaSize( true );
152 bds.m_UseConnectedTrackWidth = false;
153 }
154 }
156 {
157 bds.UseCustomTrackViaSize( false );
158 bds.m_UseConnectedTrackWidth = false;
160 }
161
163 }
164};
165
166
168 PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ),
169 m_view( nullptr ),
170 m_controls( nullptr ),
171 m_board( nullptr ),
172 m_frame( nullptr ),
173 m_mode( MODE::NONE ),
174 m_inDrawingTool( false ),
175 m_layer( UNDEFINED_LAYER ),
176 m_stroke( 1, PLOT_DASH_TYPE::DEFAULT, COLOR4D::UNSPECIFIED )
177{
178}
179
180
182{
183}
184
185
187{
188 auto haveHighlight =
189 [&]( const SELECTION& sel )
190 {
192
193 return !cfg->GetHighlightNetCodes().empty();
194 };
195
196 auto activeToolFunctor =
197 [this]( const SELECTION& aSel )
198 {
199 return m_mode != MODE::NONE;
200 };
201
202 // some interactive drawing tools can undo the last point
203 auto canUndoPoint =
204 [this]( const SELECTION& aSel )
205 {
206 return ( m_mode == MODE::ARC
207 || m_mode == MODE::ZONE
210 };
211
212 // functor for tools that can automatically close the outline
213 auto canCloseOutline =
214 [this]( const SELECTION& aSel )
215 {
216 return ( m_mode == MODE::ZONE
219 };
220
221 auto arcToolActive =
222 [this]( const SELECTION& aSel )
223 {
224 return m_mode == MODE::ARC;
225 };
226
227 auto viaToolActive =
228 [this]( const SELECTION& aSel )
229 {
230 return m_mode == MODE::VIA;
231 };
232
233 CONDITIONAL_MENU& ctxMenu = m_menu.GetMenu();
234
235 // cancel current tool goes in main context menu at the top if present
236 ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
237 ctxMenu.AddSeparator( 1 );
238
239 ctxMenu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 2 );
240 ctxMenu.AddSeparator( haveHighlight, 2 );
241
242 // tool-specific actions
243 ctxMenu.AddItem( PCB_ACTIONS::closeOutline, canCloseOutline, 200 );
244 ctxMenu.AddItem( PCB_ACTIONS::deleteLastPoint, canUndoPoint, 200 );
245 ctxMenu.AddItem( PCB_ACTIONS::arcPosture, arcToolActive, 200 );
246
248 ctxMenu.AddSeparator( 500 );
249
250 std::shared_ptr<VIA_SIZE_MENU> viaSizeMenu = std::make_shared<VIA_SIZE_MENU>();
251 viaSizeMenu->SetTool( this );
252 m_menu.RegisterSubMenu( viaSizeMenu );
253 ctxMenu.AddMenu( viaSizeMenu.get(), viaToolActive, 500 );
254
255 ctxMenu.AddSeparator( 500 );
256
257 // Type-specific sub-menus will be added for us by other tools
258 // For example, zone fill/unfill is provided by the PCB control tool
259
260 // Finally, add the standard zoom/grid items
261 getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( m_menu );
262
263 return true;
264}
265
266
268{
269 // Init variables used by every drawing tool
270 m_view = getView();
272 m_board = getModel<BOARD>();
273 m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
274
276}
277
278
280{
281 return m_mode;
282}
283
284
286{
287 if( m_frame )
288 {
289 SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
290 bool constrained;
291
293 constrained = mgr.GetAppSettings<PCBNEW_SETTINGS>()->m_Use45DegreeLimit;
294 else
295 constrained = mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>()->m_Use45Limit;
296
297 m_frame->DisplayConstraintsMsg( constrained ? _( "Constrain to H, V, 45" ) : wxT( "" ) );
298 }
299}
300
301
303{
305 return 0;
306
307 if( m_inDrawingTool )
308 return 0;
309
311
312 FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
313 PCB_SHAPE* line = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
314 BOARD_COMMIT commit( m_frame );
315 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
316 std::optional<VECTOR2D> startingPoint;
317
318 line->SetShape( SHAPE_T::SEGMENT );
319 line->SetFlags( IS_NEW );
320
321 if( aEvent.HasPosition() )
322 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
323
324 m_frame->PushTool( aEvent );
325 Activate();
326
327 while( drawShape( aEvent, &line, startingPoint ) )
328 {
329 if( line )
330 {
332 static_cast<FP_SHAPE*>( line )->SetLocalCoord();
333
334 commit.Add( line );
335 commit.Push( _( "Draw a line segment" ) );
336 startingPoint = VECTOR2D( line->GetEnd() );
337 }
338 else
339 {
340 startingPoint = std::nullopt;
341 }
342
343 line = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
344 line->SetShape( SHAPE_T::SEGMENT );
345 line->SetFlags( IS_NEW );
346 }
347
348 return 0;
349}
350
351
353{
355 return 0;
356
357 if( m_inDrawingTool )
358 return 0;
359
361
362 bool isTextBox = aEvent.IsAction( &PCB_ACTIONS::drawTextBox );
363 PCB_SHAPE* rect = nullptr;
364 BOARD_COMMIT commit( m_frame );
365 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::RECTANGLE );
366 std::optional<VECTOR2D> startingPoint;
367
368 auto makeNew =
369 [&]() -> PCB_SHAPE*
370 {
372 {
373 FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
374
375 if( isTextBox )
376 return new FP_TEXTBOX( parentFootprint );
377 else
378 return new FP_SHAPE( parentFootprint );
379 }
380 else
381 {
382 if( isTextBox )
383 return new PCB_TEXTBOX( m_frame->GetModel() );
384 else
385 return new PCB_SHAPE();
386 }
387 };
388
389 rect = makeNew();
390 rect->SetShape( SHAPE_T::RECT );
391 rect->SetFilled( false );
392 rect->SetFlags( IS_NEW );
393
394 if( aEvent.HasPosition() )
395 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
396
397 m_frame->PushTool( aEvent );
398 Activate();
399
400 while( drawShape( aEvent, &rect, startingPoint ) )
401 {
402 if( rect )
403 {
405 static_cast<FP_SHAPE*>( rect )->SetLocalCoord();
406
407 if( isTextBox && m_frame->ShowTextBoxPropertiesDialog( rect ) != wxID_OK )
408 {
409 delete rect;
410 rect = nullptr;
411 }
412 else
413 {
414 rect->NormalizeRect();
415 commit.Add( rect );
416 commit.Push( isTextBox ? _( "Draw a text box" ) : _( "Draw a rectangle" ) );
417
419 }
420 }
421
422 rect = makeNew();
423 rect->SetShape( SHAPE_T::RECT );
424 rect->SetFilled( false );
425 rect->SetFlags( IS_NEW );
426 startingPoint = std::nullopt;
427 }
428
429 return 0;
430}
431
432
434{
436 return 0;
437
438 if( m_inDrawingTool )
439 return 0;
440
442
443 FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
444 PCB_SHAPE* circle = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
445 BOARD_COMMIT commit( m_frame );
446 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::CIRCLE );
447 std::optional<VECTOR2D> startingPoint;
448
449 circle->SetShape( SHAPE_T::CIRCLE );
450 circle->SetFilled( false );
451 circle->SetFlags( IS_NEW );
452
453 if( aEvent.HasPosition() )
454 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
455
456 m_frame->PushTool( aEvent );
457 Activate();
458
459 while( drawShape( aEvent, &circle, startingPoint ) )
460 {
461 if( circle )
462 {
464 static_cast<FP_SHAPE*>( circle )->SetLocalCoord();
465
466 commit.Add( circle );
467 commit.Push( _( "Draw a circle" ) );
468
470 }
471
472 circle = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
473 circle->SetShape( SHAPE_T::CIRCLE );
474 circle->SetFilled( false );
475 circle->SetFlags( IS_NEW );
476
477 startingPoint = std::nullopt;
478 }
479
480 return 0;
481}
482
483
485{
487 return 0;
488
489 if( m_inDrawingTool )
490 return 0;
491
493
494 FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
495 PCB_SHAPE* arc = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
496 BOARD_COMMIT commit( m_frame );
497 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
498 std::optional<VECTOR2D> startingPoint;
499
500 arc->SetShape( SHAPE_T::ARC );
501 arc->SetFlags( IS_NEW );
502
503 m_frame->PushTool( aEvent );
504 Activate();
505
506 if( aEvent.HasPosition() )
507 startingPoint = aEvent.Position();
508
509 while( drawArc( aEvent, &arc, startingPoint ) )
510 {
511 if( arc )
512 {
514 static_cast<FP_SHAPE*>( arc )->SetLocalCoord();
515
516 commit.Add( arc );
517 commit.Push( _( "Draw an arc" ) );
518
520 }
521
522 arc = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
523 arc->SetShape( SHAPE_T::ARC );
524 arc->SetFlags( IS_NEW );
525
526 startingPoint = std::nullopt;
527 }
528
529 return 0;
530}
531
532
534{
535 if( m_inDrawingTool )
536 return 0;
537
539
540 PCB_BITMAP* image = aEvent.Parameter<PCB_BITMAP*>();
541 bool immediateMode = image != nullptr;
543 bool ignorePrimePosition = false;
544 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
545
548 BOARD_COMMIT commit( m_frame );
549
551
552 // Add all the drawable symbols to preview
553 if( image )
554 {
555 image->SetPosition( cursorPos );
557 m_view->AddToPreview( image, false ); // Add, but not give ownership
558 }
559
560 m_frame->PushTool( aEvent );
561 auto setCursor =
562 [&]()
563 {
564 if( image )
566 else
568 };
569
570 auto cleanup =
571 [&] ()
572 {
575 delete image;
576 image = nullptr;
577 };
578
579 Activate();
580
581 // Must be done after Activate() so that it gets set into the correct context
582 getViewControls()->ShowCursor( true );
583
584 // Set initial cursor
585 setCursor();
586
587 // Prime the pump
588 if( image )
589 {
591 }
592 else if( aEvent.HasPosition() )
593 {
594 m_toolMgr->PrimeTool( aEvent.Position() );
595 }
596 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
597 {
598 m_toolMgr->PrimeTool( { 0, 0 } );
599 ignorePrimePosition = true;
600 }
601
602 // Main loop: keep receiving events
603 while( TOOL_EVENT* evt = Wait() )
604 {
605 setCursor();
606
607 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
608 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
609 cursorPos = GetClampedCoords(
610 grid.BestSnapAnchor( m_controls->GetMousePosition(), m_frame->GetActiveLayer() ),
612 m_controls->ForceCursorPosition( true, cursorPos );
613
614 if( evt->IsCancelInteractive() )
615 {
616 if( image )
617 {
618 cleanup();
619 }
620 else
621 {
622 m_frame->PopTool( aEvent );
623 break;
624 }
625
626 if( immediateMode )
627 {
628 m_frame->PopTool( aEvent );
629 break;
630 }
631 }
632 else if( evt->IsActivate() )
633 {
634 if( image && evt->IsMoveTool() )
635 {
636 // We're already moving our own item; ignore the move tool
637 evt->SetPassEvent( false );
638 continue;
639 }
640
641 if( image )
642 {
643 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel image creation." ) );
644 evt->SetPassEvent( false );
645 continue;
646 }
647
648 if( evt->IsMoveTool() )
649 {
650 // Leave ourselves on the stack so we come back after the move
651 break;
652 }
653 else
654 {
655 m_frame->PopTool( aEvent );
656 break;
657 }
658 }
659 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
660 {
661 if( !image )
662 {
664
665 wxFileDialog dlg( m_frame, _( "Choose Image" ), wxEmptyString, wxEmptyString,
666 _( "Image Files" ) + wxS( " " ) + wxImage::GetImageExtWildcard(),
667 wxFD_OPEN );
668
669 if( dlg.ShowModal() != wxID_OK )
670 continue;
671
672 // If we started with a hotkey which has a position then warp back to that.
673 // Otherwise update to the current mouse position pinned inside the autoscroll
674 // boundaries.
675 if( evt->IsPrime() && !ignorePrimePosition )
676 {
677 cursorPos = grid.Align( evt->Position() );
678 getViewControls()->WarpMouseCursor( cursorPos, true );
679 }
680 else
681 {
683 cursorPos = getViewControls()->GetMousePosition();
684 }
685
686 cursorPos = getViewControls()->GetMousePosition( true );
687
688 wxString fullFilename = dlg.GetPath();
689
690 if( wxFileExists( fullFilename ) )
691 image = new PCB_BITMAP( m_frame->GetModel(), cursorPos );
692
693 if( !image || !image->ReadImageFile( fullFilename ) )
694 {
695 wxMessageBox( _( "Could not load image from '%s'." ), fullFilename );
696 delete image;
697 image = nullptr;
698 continue;
699 }
700
701 image->SetFlags( IS_NEW | IS_MOVING );
702 image->SetLayer( m_frame->GetActiveLayer() );
703
705 m_view->AddToPreview( image, false ); // Add, but not give ownership
706 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
707 selectionTool->AddItemToSel( image, false );
708
709 getViewControls()->SetCursorPosition( cursorPos, false );
710 setCursor();
711 m_view->ShowPreview( true );
712 }
713 else
714 {
715 commit.Add( image );
716 commit.Push( _( "Place an image" ) );
717
719
720 image = nullptr;
722
724
725 if( immediateMode )
726 {
727 m_frame->PopTool( aEvent );
728 break;
729 }
730 }
731 }
732 else if( evt->IsClick( BUT_RIGHT ) )
733 {
734 // Warp after context menu only if dragging...
735 if( !image )
737
738 m_menu.ShowContextMenu( selectionTool->GetSelection() );
739 }
740 else if( image && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
741 {
742 image->SetPosition( cursorPos );
744 m_view->AddToPreview( image, false ); // Add, but not give ownership
745 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
746 }
747 else if( image && evt->IsAction( &ACTIONS::doDelete ) )
748 {
749 cleanup();
750 }
751 else if( image && ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
752 {
753 wxBell();
754 }
755 else
756 {
757 evt->SetPassEvent();
758 }
759
760 // Enable autopanning and cursor capture only when there is an image to be placed
761 getViewControls()->SetAutoPan( image != nullptr );
762 getViewControls()->CaptureCursor( image != nullptr );
763 }
764
765 getViewControls()->SetAutoPan( false );
766 getViewControls()->CaptureCursor( false );
768
769 return 0;
770}
771
772
774{
776 return 0;
777
778 if( m_inDrawingTool )
779 return 0;
780
782
783 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
784 BOARD_ITEM* text = nullptr;
785 bool ignorePrimePosition = false;
787 BOARD_COMMIT commit( m_frame );
788 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
790
791 auto cleanup =
792 [&]()
793 {
796 m_controls->ShowCursor( true );
797 m_controls->SetAutoPan( false );
798 m_controls->CaptureCursor( false );
799 delete text;
800 text = nullptr;
801 };
802
803 auto setCursor =
804 [&]()
805 {
806 if( text )
808 else
810 };
811
813
814 m_frame->PushTool( aEvent );
815
816 Activate();
817 // Must be done after Activate() so that it gets set into the correct context
818 m_controls->ShowCursor( true );
820 // do not capture or auto-pan until we start placing some text
821 // Set initial cursor
822 setCursor();
823
824 if( aEvent.HasPosition() )
825 {
826 m_toolMgr->PrimeTool( aEvent.Position() );
827 }
828 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
829 {
830 m_toolMgr->PrimeTool( { 0, 0 } );
831 ignorePrimePosition = true;
832 }
833
834 // Main loop: keep receiving events
835 while( TOOL_EVENT* evt = Wait() )
836 {
837 setCursor();
838
839 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
840 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
841 VECTOR2I cursorPos = GetClampedCoords(
842 grid.BestSnapAnchor( m_controls->GetMousePosition(), m_frame->GetActiveLayer() ),
844 m_controls->ForceCursorPosition( true, cursorPos );
845
846 if( evt->IsCancelInteractive() )
847 {
848 if( text )
849 {
850 cleanup();
851 }
852 else
853 {
854 m_frame->PopTool( aEvent );
855 break;
856 }
857 }
858 else if( evt->IsActivate() )
859 {
860 if( text )
861 cleanup();
862
863 if( evt->IsMoveTool() )
864 {
865 // leave ourselves on the stack so we come back after the move
866 break;
867 }
868 else
869 {
870 m_frame->PopTool( aEvent );
871 break;
872 }
873 }
874 else if( evt->IsClick( BUT_RIGHT ) )
875 {
877 }
878 else if( evt->IsClick( BUT_LEFT ) )
879 {
880 bool placing = text != nullptr;
881
882 if( !text )
883 {
885
887
889 TEXT_ATTRIBUTES textAttrs;
890
891 textAttrs.m_Size = bds.GetTextSize( layer );
892 textAttrs.m_StrokeWidth = bds.GetTextThickness( layer );
893 InferBold( &textAttrs );
894 textAttrs.m_Italic = bds.GetTextItalic( layer );
895 textAttrs.m_KeepUpright = bds.GetTextUpright( layer );
896 textAttrs.m_Mirrored = IsBackLayer( layer );
897 textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
899
900 // Init the new item attributes
902 {
903 FP_TEXT* fpText = new FP_TEXT( (FOOTPRINT*) m_frame->GetModel() );
904
905 fpText->SetLayer( layer );
906 fpText->SetAttributes( textAttrs );
907 fpText->SetTextPos( cursorPos );
908
909 text = fpText;
910
911 DIALOG_TEXT_PROPERTIES textDialog( m_frame, fpText );
912 bool cancelled;
913
914 RunMainStack( [&]()
915 {
916 cancelled = !textDialog.ShowModal();
917 } );
918
919 if( cancelled || NoPrintableChars( fpText->GetText() ) )
920 {
921 delete text;
922 text = nullptr;
923 }
924 else if( fpText->GetTextPos() != cursorPos )
925 {
926 // If the user modified the location then go ahead and place it there.
927 // Otherwise we'll drag.
928 placing = true;
929 }
930 }
931 else
932 {
933 PCB_TEXT* pcbText = new PCB_TEXT( m_frame->GetModel() );
934 pcbText->SetFlags( IS_NEW );
935
936 pcbText->SetLayer( layer );
937 pcbText->SetAttributes( textAttrs );
938 pcbText->SetTextPos( cursorPos );
939
940 RunMainStack( [&]()
941 {
943 } );
944
945 if( NoPrintableChars( pcbText->GetText() ) )
946 delete pcbText;
947 else
948 text = pcbText;
949 }
950
951 if( text )
952 {
953 if( !m_view->IsLayerVisible( text->GetLayer() ) )
954 {
955 m_frame->GetAppearancePanel()->SetLayerVisible( text->GetLayer(), true );
957 }
958
960 m_view->Update( &selection() );
961
962 // update the cursor so it looks correct before another event
963 setCursor();
964 }
965 }
966
967 if( placing )
968 {
969 text->ClearFlags();
971
972 commit.Add( text );
973 commit.Push( _( "Place a text" ) );
974
976
977 text = nullptr;
978 }
979
981
982 // If we started with a hotkey which has a position then warp back to that.
983 // Otherwise update to the current mouse position pinned inside the autoscroll
984 // boundaries.
985 if( evt->IsPrime() && !ignorePrimePosition )
986 {
987 cursorPos = evt->Position();
988 m_controls->WarpMouseCursor( cursorPos, true );
989 }
990 else
991 {
993 cursorPos = m_controls->GetMousePosition();
994 }
995
997
998 m_controls->ShowCursor( true );
999 m_controls->CaptureCursor( text != nullptr );
1000 m_controls->SetAutoPan( text != nullptr );
1001 }
1002 else if( text && ( evt->IsMotion() || evt->IsAction( &PCB_ACTIONS::refreshPreview ) ) )
1003 {
1004 text->SetPosition( cursorPos );
1005 selection().SetReferencePoint( cursorPos );
1006 m_view->Update( &selection() );
1007 }
1008 else if( text && ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
1009 {
1010 wxBell();
1011 }
1012 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
1013 {
1014 if( text )
1015 {
1017 m_view->Update( &selection() );
1018 frame()->SetMsgPanel( text );
1019 }
1020 else
1021 {
1022 evt->SetPassEvent();
1023 }
1024 }
1025 else
1026 {
1027 evt->SetPassEvent();
1028 }
1029 }
1030
1031 m_controls->SetAutoPan( false );
1032 m_controls->CaptureCursor( false );
1035
1036 if( selection().Empty() )
1038
1039 return 0;
1040}
1041
1042
1044{
1045 const VECTOR2I lineVector{ aDim->GetEnd() - aDim->GetStart() };
1046
1047 aDim->SetEnd( aDim->GetStart() + GetVectorSnapped45( lineVector ) );
1048 aDim->Update();
1049}
1050
1051
1053{
1055 return 0;
1056
1057 if( m_inDrawingTool )
1058 return 0;
1059
1061
1062 enum DIMENSION_STEPS
1063 {
1064 SET_ORIGIN = 0,
1065 SET_END,
1066 SET_HEIGHT,
1067 FINISHED
1068 };
1069
1070 TOOL_EVENT originalEvent = aEvent;
1071 PCB_DIMENSION_BASE* dimension = nullptr;
1072 BOARD_COMMIT commit( m_frame );
1075 PCB_SELECTION preview; // A VIEW_GROUP that serves as a preview for the new item(s)
1076 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
1077 int step = SET_ORIGIN;
1079
1080 m_view->Add( &preview );
1081
1082 auto cleanup =
1083 [&]()
1084 {
1085 m_controls->SetAutoPan( false );
1086 m_controls->CaptureCursor( false );
1088
1089 preview.Clear();
1090 m_view->Update( &preview );
1091
1092 delete dimension;
1093 dimension = nullptr;
1094 step = SET_ORIGIN;
1095 };
1096
1097 auto setCursor =
1098 [&]()
1099 {
1101 };
1102
1104
1105 m_frame->PushTool( aEvent );
1106
1107 Activate();
1108 // Must be done after Activate() so that it gets set into the correct context
1109 m_controls->ShowCursor( true );
1111 // Set initial cursor
1112 setCursor();
1113
1115
1116 if( aEvent.HasPosition() )
1117 m_toolMgr->PrimeTool( aEvent.Position() );
1118
1119 // Main loop: keep receiving events
1120 while( TOOL_EVENT* evt = Wait() )
1121 {
1122 if( step > SET_ORIGIN )
1123 frame()->SetMsgPanel( dimension );
1124
1125 setCursor();
1126
1127 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1128 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1129
1130 if( step == SET_HEIGHT )
1131 {
1132 if( dimension->GetStart().x != dimension->GetEnd().x
1133 && dimension->GetStart().y != dimension->GetEnd().y )
1134 {
1135 // Not cardinal. Grid snapping doesn't make sense for height.
1136 grid.SetUseGrid( false );
1137 }
1138 }
1139
1140 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
1141 cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, nullptr ), COORDS_PADDING );
1142
1143 m_controls->ForceCursorPosition( true, cursorPos );
1144
1145 if( evt->IsCancelInteractive() )
1146 {
1147 m_controls->SetAutoPan( false );
1148
1149 if( step != SET_ORIGIN ) // start from the beginning
1150 {
1151 cleanup();
1152 }
1153 else
1154 {
1155 m_frame->PopTool( aEvent );
1156 break;
1157 }
1158 }
1159 else if( evt->IsActivate() )
1160 {
1161 if( step != SET_ORIGIN )
1162 cleanup();
1163
1164 if( evt->IsPointEditor() )
1165 {
1166 // don't exit (the point editor runs in the background)
1167 }
1168 else if( evt->IsMoveTool() )
1169 {
1170 // leave ourselves on the stack so we come back after the move
1171 break;
1172 }
1173 else
1174 {
1175 m_frame->PopTool( aEvent );
1176 break;
1177 }
1178 }
1179 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
1180 {
1182 dimension->SetLineThickness( m_stroke.GetWidth() );
1183 m_view->Update( &preview );
1184 frame()->SetMsgPanel( dimension );
1185 }
1186 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
1187 {
1188 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
1189 {
1191 dimension->SetLineThickness( m_stroke.GetWidth() );
1192 m_view->Update( &preview );
1193 frame()->SetMsgPanel( dimension );
1194 }
1195 }
1196 else if( evt->IsClick( BUT_RIGHT ) )
1197 {
1199 }
1200 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1201 {
1202 switch( step )
1203 {
1204 case SET_ORIGIN:
1205 {
1207
1209
1210 // Init the new item attributes
1211 auto setMeasurementAttributes =
1212 [&]( PCB_DIMENSION_BASE* aDim )
1213 {
1214 aDim->SetUnitsMode( boardSettings.m_DimensionUnitsMode );
1215 aDim->SetUnitsFormat( boardSettings.m_DimensionUnitsFormat );
1216 aDim->SetPrecision( boardSettings.m_DimensionPrecision );
1217 aDim->SetSuppressZeroes( boardSettings.m_DimensionSuppressZeroes );
1218 aDim->SetTextPositionMode( boardSettings.m_DimensionTextPosition );
1219 aDim->SetKeepTextAligned( boardSettings.m_DimensionKeepTextAligned );
1220
1221 if( boardSettings.m_DimensionUnitsMode == DIM_UNITS_MODE::AUTOMATIC )
1222 aDim->SetUnits( m_frame->GetUserUnits() );
1223 };
1224
1225 if( originalEvent.IsAction( &PCB_ACTIONS::drawAlignedDimension ) )
1226 {
1227 dimension = new PCB_DIM_ALIGNED( m_frame->GetModel(),
1230 setMeasurementAttributes( dimension );
1231 }
1232 else if( originalEvent.IsAction( &PCB_ACTIONS::drawOrthogonalDimension ) )
1233 {
1235 setMeasurementAttributes( dimension );
1236 }
1237 else if( originalEvent.IsAction( &PCB_ACTIONS::drawCenterDimension ) )
1238 {
1239 dimension = new PCB_DIM_CENTER( m_frame->GetModel(), m_isFootprintEditor );
1240 }
1241 else if( originalEvent.IsAction( &PCB_ACTIONS::drawRadialDimension ) )
1242 {
1243 dimension = new PCB_DIM_RADIAL( m_frame->GetModel(), m_isFootprintEditor );
1244 setMeasurementAttributes( dimension );
1245 }
1246 else if( originalEvent.IsAction( &PCB_ACTIONS::drawLeader ) )
1247 {
1248 dimension = new PCB_DIM_LEADER( m_frame->GetModel(), m_isFootprintEditor );
1249 dimension->Text().SetPosition( cursorPos );
1250 }
1251 else
1252 {
1253 wxFAIL_MSG( wxT( "Unhandled action in DRAWING_TOOL::DrawDimension" ) );
1254 }
1255
1256 t = dimension->Type();
1257
1258 dimension->SetLayer( layer );
1259 dimension->Text().SetTextSize( boardSettings.GetTextSize( layer ) );
1260 dimension->Text().SetTextThickness( boardSettings.GetTextThickness( layer ) );
1261 dimension->Text().SetItalic( boardSettings.GetTextItalic( layer ) );
1262 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1263 dimension->SetArrowLength( boardSettings.m_DimensionArrowLength );
1264 dimension->SetExtensionOffset( boardSettings.m_DimensionExtensionOffset );
1265 dimension->SetStart( cursorPos );
1266 dimension->SetEnd( cursorPos );
1267 dimension->Update();
1268
1269 if( !m_view->IsLayerVisible( layer ) )
1270 {
1271 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1273 }
1274
1275 preview.Add( dimension );
1276 frame()->SetMsgPanel( dimension );
1277
1278 m_controls->SetAutoPan( true );
1279 m_controls->CaptureCursor( true );
1280 break;
1281 }
1282
1283 case SET_END:
1284 // Dimensions that have origin and end in the same spot are not valid
1285 if( dimension->GetStart() == dimension->GetEnd() )
1286 {
1287 --step;
1288 break;
1289 }
1290
1291 if( t == PCB_DIM_CENTER_T || t == PCB_DIM_RADIAL_T || t == PCB_DIM_LEADER_T
1293 {
1294 // No separate height step
1295 ++step;
1297 }
1298 else
1299 {
1300 break;
1301 }
1302
1303 case SET_HEIGHT:
1304 assert( dimension->GetStart() != dimension->GetEnd() );
1305 assert( dimension->GetLineThickness() > 0 );
1306
1307 preview.Remove( dimension );
1308
1309 commit.Add( dimension );
1310 commit.Push( _( "Draw a dimension" ) );
1311
1312 if( t == PCB_DIM_LEADER_T || t == PCB_FP_DIM_LEADER_T )
1313 {
1314 // Run the edit immediately to set the leader text
1315 m_toolMgr->RunAction( PCB_ACTIONS::properties, true, dimension );
1316 }
1317
1318 m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, dimension );
1319
1320 break;
1321 }
1322
1323 if( ++step >= FINISHED )
1324 {
1325 dimension = nullptr;
1326 step = SET_ORIGIN;
1327 m_controls->SetAutoPan( false );
1328 m_controls->CaptureCursor( false );
1329 }
1330 else if( evt->IsDblClick( BUT_LEFT ) )
1331 {
1333 }
1334 }
1335 else if( evt->IsMotion() )
1336 {
1337 switch( step )
1338 {
1339 case SET_END:
1340 dimension->SetEnd( cursorPos );
1341
1342 if( Is45Limited() || t == PCB_DIM_CENTER_T || t == PCB_FP_DIM_CENTER_T )
1343 constrainDimension( dimension );
1344
1346 {
1347 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1348
1349 BOX2I bounds( dimension->GetStart(),
1350 dimension->GetEnd() - dimension->GetStart() );
1351
1352 // Create a nice preview by measuring the longer dimension
1353 bool vert = bounds.GetWidth() < bounds.GetHeight();
1354
1355 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1357 }
1358 else if( t == PCB_DIM_RADIAL_T || t == PCB_FP_DIM_RADIAL_T )
1359 {
1360 PCB_DIM_RADIAL* radialDim = static_cast<PCB_DIM_RADIAL*>( dimension );
1361 VECTOR2I textOffset( radialDim->GetArrowLength() * 10, 0 );
1362
1363 if( radialDim->GetEnd().x < radialDim->GetStart().x )
1364 textOffset = -textOffset;
1365
1366 radialDim->Text().SetPosition( radialDim->GetKnee() + textOffset );
1367 }
1368 else if( t == PCB_DIM_LEADER_T || t == PCB_FP_DIM_LEADER_T )
1369 {
1370 VECTOR2I textOffset( dimension->GetArrowLength() * 10, 0 );
1371
1372 if( dimension->GetEnd().x < dimension->GetStart().x )
1373 textOffset = -textOffset;
1374
1375 dimension->Text().SetPosition( dimension->GetEnd() + textOffset );
1376 }
1377
1378 dimension->Update();
1379 break;
1380
1381 case SET_HEIGHT:
1382 if( t == PCB_DIM_ALIGNED_T || t == PCB_FP_DIM_ALIGNED_T )
1383 {
1384 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dimension );
1385
1386 // Calculating the direction of travel perpendicular to the selected axis
1387 double angle = aligned->GetAngle() + ( M_PI / 2 );
1388
1389 VECTOR2I delta( (VECTOR2I) cursorPos - dimension->GetEnd() );
1390 double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
1391 aligned->SetHeight( height );
1392 aligned->Update();
1393 }
1394 else if( t == PCB_DIM_ORTHOGONAL_T || t == PCB_FP_DIM_ORTHOGONAL_T )
1395 {
1396 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1397
1398 BOX2I bbox( dimension->GetStart(),
1399 dimension->GetEnd() - dimension->GetStart() );
1400 VECTOR2I direction( cursorPos - bbox.Centre() );
1401 bool vert;
1402
1403 // Only change the orientation when we move outside the bbox
1404 if( !bbox.Contains( cursorPos ) )
1405 {
1406 // If the dimension is horizontal or vertical, set correct orientation
1407 // otherwise, test if we're left/right of the bounding box or above/below it
1408 if( bbox.GetWidth() == 0 )
1409 vert = true;
1410 else if( bbox.GetHeight() == 0 )
1411 vert = false;
1412 else if( cursorPos.x > bbox.GetLeft() && cursorPos.x < bbox.GetRight() )
1413 vert = false;
1414 else if( cursorPos.y > bbox.GetTop() && cursorPos.y < bbox.GetBottom() )
1415 vert = true;
1416 else
1417 vert = std::abs( direction.y ) < std::abs( direction.x );
1418
1419 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1421 }
1422 else
1423 {
1424 vert = ortho->GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1425 }
1426
1427 VECTOR2I heightVector( cursorPos - dimension->GetStart() );
1428 ortho->SetHeight( vert ? heightVector.x : heightVector.y );
1429 ortho->Update();
1430 }
1431
1432 break;
1433 }
1434
1435 // Show a preview of the item
1436 m_view->Update( &preview );
1437 }
1438 else if( dimension && evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1439 {
1441
1442 if( !m_view->IsLayerVisible( layer ) )
1443 {
1444 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1446 }
1447
1448 dimension->SetLayer( layer );
1449 dimension->Text().SetTextSize( boardSettings.GetTextSize( layer ) );
1450 dimension->Text().SetTextThickness( boardSettings.GetTextThickness( layer ) );
1451 dimension->Text().SetItalic( boardSettings.GetTextItalic( layer ) );
1452 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1453 dimension->Update();
1454
1455 m_view->Update( &preview );
1456 frame()->SetMsgPanel( dimension );
1457 }
1458 else if( dimension && evt->IsAction( &PCB_ACTIONS::properties ) )
1459 {
1460 if( step == SET_END || step == SET_HEIGHT )
1461 {
1462 frame()->OnEditItemRequest( dimension );
1463 dimension->Update();
1464 frame()->SetMsgPanel( dimension );
1465 break;
1466 }
1467 else
1468 {
1469 wxBell();
1470 }
1471 }
1472 else if( dimension && ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
1473 {
1474 wxBell();
1475 }
1476 else
1477 {
1478 evt->SetPassEvent();
1479 }
1480 }
1481
1482 if( step != SET_ORIGIN )
1483 delete dimension;
1484
1485 m_controls->SetAutoPan( false );
1487 m_controls->CaptureCursor( false );
1489
1490 m_view->Remove( &preview );
1491
1492 if( selection().Empty() )
1494
1495 return 0;
1496}
1497
1498
1500{
1501 if( !m_frame->GetModel() )
1502 return 0;
1503
1504 if( m_inDrawingTool )
1505 return 0;
1506
1508
1509 // Note: PlaceImportedGraphics() will convert PCB_SHAPE_T and PCB_TEXT_T to footprint
1510 // items if needed
1512 int dlgResult = dlg.ShowModal();
1513
1514 std::list<std::unique_ptr<EDA_ITEM>>& list = dlg.GetImportedItems();
1515
1516 if( dlgResult != wxID_OK )
1517 return 0;
1518
1519 // Ensure the list is not empty:
1520 if( list.empty() )
1521 {
1522 wxMessageBox( _( "No graphic items found in file.") );
1523 return 0;
1524 }
1525
1527
1528 std::vector<BOARD_ITEM*> newItems; // all new items, including group
1529 std::vector<BOARD_ITEM*> selectedItems; // the group, or newItems if no group
1530 PCB_SELECTION preview;
1531 BOARD_COMMIT commit( m_frame );
1532 PCB_GROUP* group = nullptr;
1533 PCB_LAYER_ID layer = F_Cu;
1534
1535 if( dlg.ShouldGroupItems() )
1536 {
1539 else
1540 group = new PCB_GROUP( m_frame->GetBoard() );
1541
1542 newItems.push_back( group );
1543 selectedItems.push_back( group );
1544 preview.Add( group );
1545 }
1546
1547 for( std::unique_ptr<EDA_ITEM>& ptr : list )
1548 {
1549 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( ptr.get() );
1550
1552 wxASSERT( item->Type() == PCB_FP_SHAPE_T || item->Type() == PCB_FP_TEXT_T );
1553 else
1554 wxASSERT( item->Type() == PCB_SHAPE_T || item->Type() == PCB_TEXT_T );
1555
1556 newItems.push_back( item );
1557
1558 if( group )
1559 group->AddItem( item );
1560 else
1561 selectedItems.push_back( item );
1562
1563 preview.Add( item );
1564
1565 ptr.release();
1566 }
1567
1568 if( !dlg.IsPlacementInteractive() )
1569 {
1570 for( BOARD_ITEM* item : newItems )
1571 commit.Add( item );
1572
1573 commit.Push( _( "Place a DXF_SVG drawing" ) );
1574 return 0;
1575 }
1576
1577 layer = newItems.front()->GetLayer();
1578
1579 m_view->Add( &preview );
1580
1581 // Clear the current selection then select the drawings so that edit tools work on them
1583 m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &selectedItems );
1584
1585 m_frame->PushTool( aEvent );
1586
1587 auto setCursor =
1588 [&]()
1589 {
1591 };
1592
1593 Activate();
1594 // Must be done after Activate() so that it gets set into the correct context
1595 m_controls->ShowCursor( true );
1597 // Set initial cursor
1598 setCursor();
1599
1600 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
1602
1603 // Now move the new items to the current cursor position:
1604 VECTOR2I cursorPos = m_controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
1605 VECTOR2I delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
1606
1607 for( BOARD_ITEM* item : selectedItems )
1608 item->Move( delta );
1609
1610 m_view->Update( &preview );
1611
1612 // Main loop: keep receiving events
1613 while( TOOL_EVENT* evt = Wait() )
1614 {
1615 setCursor();
1616
1617 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1618 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1619 cursorPos = GetClampedCoords(
1620 grid.BestSnapAnchor( m_controls->GetMousePosition(), layer ),
1622 m_controls->ForceCursorPosition( true, cursorPos );
1623
1624 if( evt->IsCancelInteractive() || evt->IsActivate() )
1625 {
1627
1628 if( group )
1629 {
1630 preview.Remove( group );
1631 group->RemoveAll();
1632 }
1633
1634 for( BOARD_ITEM* item : newItems )
1635 delete item;
1636
1637 break;
1638 }
1639 else if( evt->IsMotion() )
1640 {
1641 delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
1642
1643 for( BOARD_ITEM* item : selectedItems )
1644 item->Move( delta );
1645
1646 m_view->Update( &preview );
1647 }
1648 else if( evt->IsClick( BUT_RIGHT ) )
1649 {
1651 }
1652 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1653 {
1654 // Place the imported drawings
1655 for( BOARD_ITEM* item : newItems )
1656 commit.Add( item );
1657
1658 commit.Push( _( "Place a DXF_SVG drawing" ) );
1659 break; // This is a one-shot command, not a tool
1660 }
1661 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
1662 {
1663 wxBell();
1664 }
1665 else
1666 {
1667 evt->SetPassEvent();
1668 }
1669 }
1670
1671 preview.Clear();
1672 m_view->Remove( &preview );
1673
1676
1677 m_frame->PopTool( aEvent );
1678
1679 return 0;
1680}
1681
1682
1684{
1685 wxASSERT( m_isFootprintEditor );
1686
1687 if( !m_frame->GetModel() )
1688 return 0;
1689
1690 if( m_inDrawingTool )
1691 return 0;
1692
1694
1695 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
1697
1699
1700 m_frame->PushTool( aEvent );
1701
1702 auto setCursor =
1703 [&]()
1704 {
1706 };
1707
1708 Activate();
1709 // Must be done after Activate() so that it gets set into the correct context
1710 m_controls->ShowCursor( true );
1711 m_controls->SetAutoPan( true );
1712 m_controls->CaptureCursor( false );
1714 // Set initial cursor
1715 setCursor();
1716
1717 while( TOOL_EVENT* evt = Wait() )
1718 {
1719 setCursor();
1720
1721 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1722 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1723 VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(),
1725 m_controls->ForceCursorPosition( true, cursorPos );
1726
1727 if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1728 {
1730 BOARD_COMMIT commit( m_frame );
1731 commit.Modify( footprint );
1732
1733 // set the new relative internal local coordinates of footprint items
1734 VECTOR2I moveVector = footprint->GetPosition() - cursorPos;
1735 footprint->MoveAnchorPosition( moveVector );
1736
1737 commit.Push( _( "Move the footprint reference anchor" ) );
1738
1739 // Usually, we do not need to change twice the anchor position,
1740 // so deselect the active tool
1741 m_frame->PopTool( aEvent );
1742 break;
1743 }
1744 else if( evt->IsClick( BUT_RIGHT ) )
1745 {
1747 }
1748 else if( evt->IsCancelInteractive() || evt->IsActivate() )
1749 {
1750 m_frame->PopTool( aEvent );
1751 break;
1752 }
1753 else
1754 {
1755 evt->SetPassEvent();
1756 }
1757 }
1758
1761
1762 return 0;
1763}
1764
1765
1767{
1768#define TOGGLE( a ) a = !a
1769
1770 SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
1771
1772 if( frame()->IsType( FRAME_PCB_EDITOR ) )
1774 else
1776
1778
1779 return 0;
1780
1781#undef TOGGLE
1782}
1783
1784
1789 PCB_SHAPE* aGraphic )
1790{
1791 if( !aMgr.IsReset() )
1792 {
1793 aGraphic->SetStart( aMgr.GetOrigin() );
1794 aGraphic->SetEnd( aMgr.GetEnd() );
1795 }
1796}
1797
1798
1799bool DRAWING_TOOL::drawShape( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
1800 std::optional<VECTOR2D> aStartingPoint )
1801{
1802 SHAPE_T shape = ( *aGraphic )->GetShape();
1803
1804 // Only three shapes are currently supported
1805 wxASSERT( shape == SHAPE_T::SEGMENT || shape == SHAPE_T::CIRCLE || shape == SHAPE_T::RECT );
1806
1808 EDA_UNITS userUnits = m_frame->GetUserUnits();
1810 PCB_SHAPE*& graphic = *aGraphic;
1811
1812 if( m_layer != m_frame->GetActiveLayer() )
1813 {
1817 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
1818
1827 }
1828
1829 // geometric construction manager
1831
1832 // drawing assistant overlay
1833 // TODO: workaround because EDA_SHAPE_TYPE_T is not visible from commons.
1834 KIGFX::PREVIEW::GEOM_SHAPE geomShape( static_cast<KIGFX::PREVIEW::GEOM_SHAPE>( shape ) );
1835 KIGFX::PREVIEW::TWO_POINT_ASSISTANT twoPointAsst( twoPointManager, pcbIUScale, userUnits, geomShape );
1836
1837 // Add a VIEW_GROUP that serves as a preview for the new item
1838 PCB_SELECTION preview;
1839 m_view->Add( &preview );
1840 m_view->Add( &twoPointAsst );
1841
1842 bool started = false;
1843 bool cancelled = false;
1844 bool isLocalOriginSet = ( m_frame->GetScreen()->m_LocalOrigin != VECTOR2D( 0, 0 ) );
1845 VECTOR2I cursorPos = m_controls->GetMousePosition();
1846
1847 auto setCursor =
1848 [&]()
1849 {
1851 };
1852
1853 auto cleanup =
1854 [&]()
1855 {
1856 preview.Clear();
1857 m_view->Update( &preview );
1858 delete graphic;
1859 graphic = nullptr;
1860
1861 if( !isLocalOriginSet )
1863 };
1864
1865 m_controls->ShowCursor( true );
1867 // Set initial cursor
1868 setCursor();
1869
1871
1872 if( aStartingPoint )
1873 m_toolMgr->PrimeTool( *aStartingPoint );
1874
1875 // Main loop: keep receiving events
1876 while( TOOL_EVENT* evt = Wait() )
1877 {
1878 setCursor();
1879
1880 if( started )
1881 m_frame->SetMsgPanel( graphic );
1882
1883 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1884 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1885 cursorPos = GetClampedCoords(
1886 grid.BestSnapAnchor( m_controls->GetMousePosition(), m_layer ),
1888 m_controls->ForceCursorPosition( true, cursorPos );
1889
1890 if( evt->IsCancelInteractive() )
1891 {
1892 cleanup();
1893
1894 if( !started )
1895 {
1896 // We've handled the cancel event. Don't cancel other tools
1897 evt->SetPassEvent( false );
1898 m_frame->PopTool( aTool );
1899 cancelled = true;
1900 }
1901
1902 break;
1903 }
1904 else if( evt->IsActivate() )
1905 {
1906 if( evt->IsPointEditor() )
1907 {
1908 // don't exit (the point editor runs in the background)
1909 }
1910 else if( evt->IsMoveTool() )
1911 {
1912 cleanup();
1913 // leave ourselves on the stack so we come back after the move
1914 cancelled = true;
1915 break;
1916 }
1917 else
1918 {
1919 cleanup();
1920 m_frame->PopTool( aTool );
1921 cancelled = true;
1922 break;
1923 }
1924 }
1925 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1926 {
1927 if( m_layer != m_frame->GetActiveLayer() )
1928 {
1932 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
1933
1942 }
1943
1944 if( graphic )
1945 {
1946 if( !m_view->IsLayerVisible( m_layer ) )
1947 {
1950 }
1951
1952 graphic->SetLayer( m_layer );
1953 graphic->SetStroke( m_stroke );
1954
1955 if( FP_TEXTBOX* fp_textbox = dynamic_cast<FP_TEXTBOX*>( graphic ) )
1956 fp_textbox->SetAttributes( m_textAttrs );
1957 else if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
1958 pcb_textbox->SetAttributes( m_textAttrs );
1959
1960 m_view->Update( &preview );
1961 frame()->SetMsgPanel( graphic );
1962 }
1963 else
1964 {
1965 evt->SetPassEvent();
1966 }
1967 }
1968 else if( evt->IsClick( BUT_RIGHT ) )
1969 {
1971 }
1972 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1973 {
1974 if( !graphic )
1975 break;
1976
1977 if( !started )
1978 {
1980
1981 if( aStartingPoint )
1982 {
1983 cursorPos = *aStartingPoint;
1984 aStartingPoint = std::nullopt;
1985 }
1986
1987 // Init the new item attributes
1988 if( graphic ) // always true, but Coverity can't seem to figure that out
1989 {
1990 graphic->SetShape( static_cast<SHAPE_T>( shape ) );
1991 graphic->SetFilled( false );
1992 graphic->SetStroke( m_stroke );
1993 graphic->SetLayer( m_layer );
1994 }
1995
1996 if( FP_TEXTBOX* fp_textbox = dynamic_cast<FP_TEXTBOX*>( graphic ) )
1997 fp_textbox->SetAttributes( m_textAttrs );
1998 else if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
1999 pcb_textbox->SetAttributes( m_textAttrs );
2000
2001 grid.SetSkipPoint( cursorPos );
2002
2003 twoPointManager.SetOrigin( cursorPos );
2004 twoPointManager.SetEnd( cursorPos );
2005
2006 if( !isLocalOriginSet )
2007 m_frame->GetScreen()->m_LocalOrigin = cursorPos;
2008
2009 preview.Add( graphic );
2010 frame()->SetMsgPanel( graphic );
2011 m_controls->SetAutoPan( true );
2012 m_controls->CaptureCursor( true );
2013
2014 if( !m_view->IsLayerVisible( m_layer ) )
2015 {
2018 }
2019
2020 updateSegmentFromGeometryMgr( twoPointManager, graphic );
2021
2022 started = true;
2023 }
2024 else if( shape == SHAPE_T::CIRCLE )
2025 {
2026 // No clever logic if drawing a circle
2027 preview.Clear();
2028 twoPointManager.Reset();
2029 break;
2030 }
2031 else
2032 {
2033 PCB_SHAPE* snapItem = dyn_cast<PCB_SHAPE*>( grid.GetSnapped() );
2034
2035 if( twoPointManager.GetOrigin() == twoPointManager.GetEnd()
2036 || ( evt->IsDblClick( BUT_LEFT ) && shape == SHAPE_T::SEGMENT )
2037 || snapItem )
2038 // User has clicked twice in the same spot
2039 // or clicked on the end of an existing segment (closing a path)
2040 {
2041 BOARD_COMMIT commit( m_frame );
2042
2043 // If the user clicks on an existing snap point from a drawsegment
2044 // we finish the segment as they are likely closing a path
2045 if( snapItem && ( shape == SHAPE_T::RECT || graphic->GetLength() > 0.0 ) )
2046 {
2047 commit.Add( graphic );
2048 commit.Push( _( "Draw a line segment" ) );
2049 m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, graphic );
2050 }
2051 else
2052 {
2053 delete graphic;
2054 }
2055
2056 graphic = nullptr;
2057 }
2058
2059 preview.Clear();
2060 twoPointManager.Reset();
2061 break;
2062 }
2063
2064 twoPointManager.SetEnd( GetClampedCoords( cursorPos ) );
2065 }
2066 else if( evt->IsMotion() )
2067 {
2068 VECTOR2I clampedCursorPos = cursorPos;
2069
2070 if( shape == SHAPE_T::CIRCLE || shape == SHAPE_T::ARC )
2071 {
2072 clampedCursorPos = getClampedRadiusEnd( twoPointManager.GetOrigin(), cursorPos );
2073 }
2074 else
2075 {
2076 clampedCursorPos = getClampedDifferenceEnd( twoPointManager.GetOrigin(),
2077 cursorPos );
2078 }
2079
2080 // 45 degree lines
2081 if( started && Is45Limited() )
2082 {
2083 const VECTOR2I lineVector( clampedCursorPos
2084 - VECTOR2I( twoPointManager.GetOrigin() ) );
2085
2086 // get a restricted 45/H/V line from the last fixed point to the cursor
2087 VECTOR2I newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECT ) );
2088 m_controls->ForceCursorPosition( true, VECTOR2I( twoPointManager.GetEnd() ) );
2089 twoPointManager.SetEnd( twoPointManager.GetOrigin() + newEnd );
2090 twoPointManager.SetAngleSnap( true );
2091 }
2092 else
2093 {
2094 twoPointManager.SetEnd( clampedCursorPos );
2095 twoPointManager.SetAngleSnap( false );
2096 }
2097
2098 updateSegmentFromGeometryMgr( twoPointManager, graphic );
2099 m_view->Update( &preview );
2100 m_view->Update( &twoPointAsst );
2101 }
2102 else if( graphic && evt->IsAction( &PCB_ACTIONS::incWidth ) )
2103 {
2105 graphic->SetStroke( m_stroke );
2106 m_view->Update( &preview );
2107 frame()->SetMsgPanel( graphic );
2108 }
2109 else if( graphic && evt->IsAction( &PCB_ACTIONS::decWidth ) )
2110 {
2111 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
2112 {
2114 graphic->SetStroke( m_stroke );
2115 m_view->Update( &preview );
2116 frame()->SetMsgPanel( graphic );
2117 }
2118 }
2119 else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
2120 {
2121 frame()->OnEditItemRequest( graphic );
2122 m_view->Update( &preview );
2123 frame()->SetMsgPanel( graphic );
2124 break;
2125 }
2126 else if( started && ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2127 {
2128 wxBell();
2129 }
2130 else if( evt->IsAction( &ACTIONS::resetLocalCoords ) )
2131 {
2132 isLocalOriginSet = true;
2133 evt->SetPassEvent();
2134 }
2135 else if( evt->IsAction( &ACTIONS::updateUnits ) )
2136 {
2137 if( frame()->GetUserUnits() != userUnits )
2138 {
2139 userUnits = frame()->GetUserUnits();
2140 twoPointAsst.SetUnits( userUnits );
2141 m_view->Update( &twoPointAsst );
2142 }
2143 evt->SetPassEvent();
2144 }
2145 else
2146 {
2147 evt->SetPassEvent();
2148 }
2149 }
2150
2151 if( !isLocalOriginSet ) // reset the relative coordinate if it was not set before
2153
2154 m_view->Remove( &twoPointAsst );
2155 m_view->Remove( &preview );
2156
2157 if( selection().Empty() )
2159
2161 m_controls->SetAutoPan( false );
2162 m_controls->CaptureCursor( false );
2164
2165 return !cancelled;
2166}
2167
2168
2173 PCB_SHAPE& aArc )
2174{
2175 VECTOR2I vec = aMgr.GetOrigin();
2176
2177 aArc.SetCenter( vec );
2178
2179 if( aMgr.GetSubtended() < ANGLE_0 )
2180 {
2181 vec = aMgr.GetStartRadiusEnd();
2182 aArc.SetStart( vec );
2183 vec = aMgr.GetEndRadiusEnd();
2184 aArc.SetEnd( vec );
2185 }
2186 else
2187 {
2188 vec = aMgr.GetEndRadiusEnd();
2189 aArc.SetStart( vec );
2190 vec = aMgr.GetStartRadiusEnd();
2191 aArc.SetEnd( vec );
2192 }
2193}
2194
2195
2196bool DRAWING_TOOL::drawArc( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
2197 std::optional<VECTOR2D> aStartingPoint )
2198{
2199 wxCHECK( aGraphic, false );
2200
2201 PCB_SHAPE*& graphic = *aGraphic;
2202
2203 wxCHECK( graphic, false );
2204
2205 if( m_layer != m_frame->GetActiveLayer() )
2206 {
2210 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2211 }
2212
2213 // Arc geometric construction manager
2215
2216 // Arc drawing assistant overlay
2218
2219 // Add a VIEW_GROUP that serves as a preview for the new item
2220 PCB_SELECTION preview;
2221 m_view->Add( &preview );
2222 m_view->Add( &arcAsst );
2224
2225 auto setCursor =
2226 [&]()
2227 {
2229 };
2230
2231 auto cleanup =
2232 [&] ()
2233 {
2234 preview.Clear();
2235 delete *aGraphic;
2236 *aGraphic = nullptr;
2237 };
2238
2239 m_controls->ShowCursor( true );
2241 // Set initial cursor
2242 setCursor();
2243
2244 bool started = false;
2245 bool cancelled = false;
2246
2248
2249 if( aStartingPoint )
2250 m_toolMgr->PrimeTool( *aStartingPoint );
2251
2252 // Main loop: keep receiving events
2253 while( TOOL_EVENT* evt = Wait() )
2254 {
2255 if( started )
2256 m_frame->SetMsgPanel( graphic );
2257
2258 setCursor();
2259
2260 graphic->SetLayer( m_layer );
2261
2262 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2263 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2264 VECTOR2I cursorPos = GetClampedCoords(
2265 grid.BestSnapAnchor( m_controls->GetMousePosition(), graphic ), COORDS_PADDING );
2266 m_controls->ForceCursorPosition( true, cursorPos );
2267
2268 if( evt->IsCancelInteractive() )
2269 {
2270 cleanup();
2271
2272 if( !started )
2273 {
2274 // We've handled the cancel event. Don't cancel other tools
2275 evt->SetPassEvent( false );
2276 m_frame->PopTool( aTool );
2277 cancelled = true;
2278 }
2279
2280 break;
2281 }
2282 else if( evt->IsActivate() )
2283 {
2284 if( evt->IsPointEditor() )
2285 {
2286 // don't exit (the point editor runs in the background)
2287 }
2288 else if( evt->IsMoveTool() )
2289 {
2290 cleanup();
2291 // leave ourselves on the stack so we come back after the move
2292 cancelled = true;
2293 break;
2294 }
2295 else
2296 {
2297 cleanup();
2298 m_frame->PopTool( aTool );
2299 cancelled = true;
2300 break;
2301 }
2302 }
2303 else if( evt->IsClick( BUT_LEFT ) )
2304 {
2305 if( !started )
2306 {
2308
2309 m_controls->SetAutoPan( true );
2310 m_controls->CaptureCursor( true );
2311
2312 // Init the new item attributes
2313 // (non-geometric, those are handled by the manager)
2314 graphic->SetShape( SHAPE_T::ARC );
2315 graphic->SetStroke( m_stroke );
2316
2317 if( !m_view->IsLayerVisible( m_layer ) )
2318 {
2321 }
2322
2323 preview.Add( graphic );
2324 frame()->SetMsgPanel( graphic );
2325 started = true;
2326 }
2327
2328 arcManager.AddPoint( cursorPos, true );
2329 }
2330 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
2331 {
2332 arcManager.RemoveLastPoint();
2333 }
2334 else if( evt->IsMotion() )
2335 {
2336 // set angle snap
2337 arcManager.SetAngleSnap( Is45Limited() );
2338
2339 // update, but don't step the manager state
2340 arcManager.AddPoint( cursorPos, false );
2341 }
2342 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2343 {
2344 if( m_layer != m_frame->GetActiveLayer() )
2345 {
2349 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2350 }
2351
2352 if( graphic )
2353 {
2354 if( !m_view->IsLayerVisible( m_layer ) )
2355 {
2358 }
2359
2360 graphic->SetLayer( m_layer );
2361 graphic->SetStroke( m_stroke );
2362 m_view->Update( &preview );
2363 frame()->SetMsgPanel( graphic );
2364 }
2365 else
2366 {
2367 evt->SetPassEvent();
2368 }
2369 }
2370 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
2371 {
2373 {
2374 graphic->SetArcAngleAndEnd( ANGLE_90 );
2375 frame()->OnEditItemRequest( graphic );
2376 m_view->Update( &preview );
2377 frame()->SetMsgPanel( graphic );
2378 break;
2379 }
2380 // Don't show the edit panel if we can't represent the arc with it
2381 else if( ( arcManager.GetStep() == KIGFX::PREVIEW::ARC_GEOM_MANAGER::SET_ANGLE )
2382 && ( arcManager.GetStartRadiusEnd() != arcManager.GetEndRadiusEnd() ) )
2383 {
2384 frame()->OnEditItemRequest( graphic );
2385 m_view->Update( &preview );
2386 frame()->SetMsgPanel( graphic );
2387 break;
2388 }
2389 else
2390 {
2391 evt->SetPassEvent();
2392 }
2393 }
2394 else if( evt->IsClick( BUT_RIGHT ) )
2395 {
2397 }
2398 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
2399 {
2401 graphic->SetStroke( m_stroke );
2402 m_view->Update( &preview );
2403 frame()->SetMsgPanel( graphic );
2404 }
2405 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
2406 {
2407 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
2408 {
2410 graphic->SetStroke( m_stroke );
2411 m_view->Update( &preview );
2412 frame()->SetMsgPanel( graphic );
2413 }
2414 }
2415 else if( evt->IsAction( &PCB_ACTIONS::arcPosture ) )
2416 {
2417 arcManager.ToggleClockwise();
2418 }
2419 else if( evt->IsAction( &ACTIONS::updateUnits ) )
2420 {
2421 arcAsst.SetUnits( frame()->GetUserUnits() );
2422 m_view->Update( &arcAsst );
2423 evt->SetPassEvent();
2424 }
2425 else if( started && ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2426 {
2427 wxBell();
2428 }
2429 else
2430 {
2431 evt->SetPassEvent();
2432 }
2433
2434 if( arcManager.IsComplete() )
2435 {
2436 break;
2437 }
2438 else if( arcManager.HasGeometryChanged() )
2439 {
2440 updateArcFromConstructionMgr( arcManager, *graphic );
2441 m_view->Update( &preview );
2442 m_view->Update( &arcAsst );
2443
2444 if( started )
2445 frame()->SetMsgPanel( graphic );
2446 else
2447 frame()->SetMsgPanel( board() );
2448 }
2449 }
2450
2451 preview.Remove( graphic );
2452 m_view->Remove( &arcAsst );
2453 m_view->Remove( &preview );
2454
2455 if( selection().Empty() )
2457
2459 m_controls->SetAutoPan( false );
2460 m_controls->CaptureCursor( false );
2462
2463 return !cancelled;
2464}
2465
2466
2468{
2469 bool clearSelection = false;
2470 *aZone = nullptr;
2471
2472 // not an action that needs a source zone
2473 if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON )
2474 return true;
2475
2477 const PCB_SELECTION& selection = selTool->GetSelection();
2478
2479 if( selection.Empty() )
2480 {
2481 clearSelection = true;
2483 }
2484
2485 // we want a single zone
2486 if( selection.Size() == 1 )
2487 *aZone = dyn_cast<ZONE*>( selection[0] );
2488
2489 // expected a zone, but didn't get one
2490 if( !*aZone )
2491 {
2492 if( clearSelection )
2494
2495 return false;
2496 }
2497
2498 return true;
2499}
2500
2502{
2504 return 0;
2505
2506 ZONE_MODE zoneMode = aEvent.Parameter<ZONE_MODE>();
2507 MODE drawMode = MODE::ZONE;
2508
2509 if( aEvent.IsAction( &PCB_ACTIONS::drawRuleArea ) )
2510 drawMode = MODE::KEEPOUT;
2511
2512 if( aEvent.IsAction( &PCB_ACTIONS::drawPolygon ) )
2513 drawMode = MODE::GRAPHIC_POLYGON;
2514
2515 SCOPED_DRAW_MODE scopedDrawMode( m_mode, drawMode );
2516
2517 // get a source zone, if we need one. We need it for:
2518 // ZONE_MODE::CUTOUT (adding a hole to the source zone)
2519 // ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
2520 ZONE* sourceZone = nullptr;
2521
2522 if( !getSourceZoneForAction( zoneMode, &sourceZone ) )
2523 return 0;
2524
2525 // Turn zones on if they are off, so that the created object will be visible after completion
2527
2529
2530 params.m_keepout = drawMode == MODE::KEEPOUT;
2531 params.m_mode = zoneMode;
2532 params.m_sourceZone = sourceZone;
2533
2534 if( zoneMode == ZONE_MODE::SIMILAR )
2535 params.m_layer = sourceZone->GetLayer();
2536 else
2537 params.m_layer = m_frame->GetActiveLayer();
2538
2539 ZONE_CREATE_HELPER zoneTool( *this, params );
2540 // the geometry manager which handles the zone geometry, and hands the calculated points
2541 // over to the zone creator tool
2542 POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
2543 bool started = false;
2545
2546 m_frame->PushTool( aEvent );
2547
2548 auto setCursor =
2549 [&]()
2550 {
2552 };
2553
2554 auto cleanup =
2555 [&] ()
2556 {
2557 polyGeomMgr.Reset();
2558 started = false;
2559 grid.ClearSkipPoint();
2560 m_controls->SetAutoPan( false );
2561 m_controls->CaptureCursor( false );
2562 };
2563
2564 Activate();
2565 // Must be done after Activate() so that it gets set into the correct context
2566 m_controls->ShowCursor( true );
2568 // Set initial cursor
2569 setCursor();
2570
2571 if( aEvent.HasPosition() )
2572 m_toolMgr->PrimeTool( aEvent.Position() );
2573
2574 // Main loop: keep receiving events
2575 while( TOOL_EVENT* evt = Wait() )
2576 {
2577 setCursor();
2578
2579 LSET layers( m_frame->GetActiveLayer() );
2580 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2581 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2582
2583 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
2584 cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, layers ), COORDS_PADDING );
2585
2586 m_controls->ForceCursorPosition( true, cursorPos );
2587
2590
2591 if( evt->IsCancelInteractive() )
2592 {
2593 if( polyGeomMgr.IsPolygonInProgress() )
2594 {
2595 cleanup();
2596 }
2597 else
2598 {
2599 // We've handled the cancel event. Don't cancel other tools
2600 evt->SetPassEvent( false );
2601 m_frame->PopTool( aEvent );
2602 break;
2603 }
2604 }
2605 else if( evt->IsActivate() )
2606 {
2607 if( polyGeomMgr.IsPolygonInProgress() )
2608 cleanup();
2609
2610 if( evt->IsPointEditor() )
2611 {
2612 // don't exit (the point editor runs in the background)
2613 }
2614 else if( evt->IsMoveTool() )
2615 {
2616 // leave ourselves on the stack so we come back after the move
2617 break;
2618 }
2619 else
2620 {
2621 m_frame->PopTool( aEvent );
2622 break;
2623 }
2624 }
2625 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2626 {
2627 if( zoneMode != ZONE_MODE::SIMILAR )
2628 params.m_layer = frame()->GetActiveLayer();
2629
2630 if( !m_view->IsLayerVisible( params.m_layer ) )
2631 {
2634 }
2635 }
2636 else if( evt->IsClick( BUT_RIGHT ) )
2637 {
2639 }
2640 // events that lock in nodes
2641 else if( evt->IsClick( BUT_LEFT )
2642 || evt->IsDblClick( BUT_LEFT )
2643 || evt->IsAction( &PCB_ACTIONS::closeOutline ) )
2644 {
2645 // Check if it is double click / closing line (so we have to finish the zone)
2646 const bool endPolygon = evt->IsDblClick( BUT_LEFT )
2647 || evt->IsAction( &PCB_ACTIONS::closeOutline )
2648 || polyGeomMgr.NewPointClosesOutline( cursorPos );
2649
2650 if( endPolygon )
2651 {
2652 polyGeomMgr.SetFinished();
2653 polyGeomMgr.Reset();
2654
2655 started = false;
2656 m_controls->SetAutoPan( false );
2657 m_controls->CaptureCursor( false );
2658 }
2659 // adding a corner
2660 else if( polyGeomMgr.AddPoint( cursorPos ) )
2661 {
2662 if( !started )
2663 {
2664 started = true;
2665
2666 m_controls->SetAutoPan( true );
2667 m_controls->CaptureCursor( true );
2668
2669 if( !m_view->IsLayerVisible( params.m_layer ) )
2670 {
2673 }
2674 }
2675 }
2676 }
2677 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
2678 {
2679 polyGeomMgr.DeleteLastCorner();
2680
2681 if( !polyGeomMgr.IsPolygonInProgress() )
2682 {
2683 // report finished as an empty shape
2684 polyGeomMgr.SetFinished();
2685
2686 // start again
2687 started = false;
2688 m_controls->SetAutoPan( false );
2689 m_controls->CaptureCursor( false );
2690 }
2691 }
2692 else if( polyGeomMgr.IsPolygonInProgress()
2693 && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
2694 {
2695 polyGeomMgr.SetCursorPosition( cursorPos );
2696 }
2697 else if( started && ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2698 {
2699 wxBell();
2700 }
2701 else if( started&& evt->IsAction( &PCB_ACTIONS::properties ) )
2702 {
2703 frame()->OnEditItemRequest( zoneTool.GetZone() );
2704 zoneTool.OnGeometryChange( polyGeomMgr );
2705 frame()->SetMsgPanel( zoneTool.GetZone() );
2706 }
2707 /*else if( evt->IsAction( &ACTIONS::updateUnits ) )
2708 {
2709 // If we ever have an assistant here that reports dimensions, we'll want to
2710 // update its units here....
2711 // zoneAsst.SetUnits( frame()->GetUserUnits() );
2712 // m_view->Update( &zoneAsst );
2713 evt->SetPassEvent();
2714 }*/
2715 else
2716 {
2717 evt->SetPassEvent();
2718 }
2719
2720 } // end while
2721
2724 controls()->SetAutoPan( false );
2725 m_controls->CaptureCursor( false );
2726 return 0;
2727}
2728
2729
2731{
2733 return 0;
2734
2735 struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
2736 {
2738 PCB_GRID_HELPER m_gridHelper;
2739 std::shared_ptr<DRC_ENGINE> m_drcEngine;
2740 int m_drcEpsilon;
2741 int m_worstClearance;
2742 bool m_allowDRCViolations;
2743
2744 VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) :
2745 m_frame( aFrame ),
2746 m_gridHelper( aFrame->GetToolManager(), aFrame->GetMagneticItemsSettings() ),
2747 m_drcEngine( aFrame->GetBoard()->GetDesignSettings().m_DRCEngine ),
2748 m_drcEpsilon( aFrame->GetBoard()->GetDesignSettings().GetDRCEpsilon() ),
2749 m_worstClearance( 0 )
2750 {
2752
2753 m_allowDRCViolations = router->Router()->Settings().AllowDRCViolations();
2754
2755 try
2756 {
2757 m_drcEngine->InitEngine( aFrame->GetDesignRulesPath() );
2758
2759 DRC_CONSTRAINT constraint;
2760
2761 if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, constraint ) )
2762 m_worstClearance = constraint.GetValue().Min();
2763
2764 if( m_drcEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, constraint ) )
2765 m_worstClearance = std::max( m_worstClearance, constraint.GetValue().Min() );
2766
2767 for( FOOTPRINT* footprint : aFrame->GetBoard()->Footprints() )
2768 {
2769 for( PAD* pad : footprint->Pads() )
2770 m_worstClearance = std::max( m_worstClearance, pad->GetLocalClearance() );
2771 }
2772 }
2773 catch( PARSE_ERROR& )
2774 {
2775 }
2776 }
2777
2778 virtual ~VIA_PLACER()
2779 {
2780 }
2781
2782 PCB_TRACK* findTrack( PCB_VIA* aVia )
2783 {
2784 const LSET lset = aVia->GetLayerSet();
2785 VECTOR2I position = aVia->GetPosition();
2786 BOX2I bbox = aVia->GetBoundingBox();
2787
2788 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
2790 std::vector<PCB_TRACK*> possible_tracks;
2791
2792 view->Query( bbox, items );
2793
2794 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
2795 {
2796 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
2797
2798 if( !( item->GetLayerSet() & lset ).any() )
2799 continue;
2800
2801 if( PCB_TRACK* track = dyn_cast<PCB_TRACK*>( item ) )
2802 {
2803 if( TestSegmentHit( position, track->GetStart(), track->GetEnd(),
2804 ( track->GetWidth() + aVia->GetWidth() ) / 2 ) )
2805 {
2806 possible_tracks.push_back( track );
2807 }
2808 }
2809 }
2810
2811 PCB_TRACK* return_track = nullptr;
2812 int min_d = std::numeric_limits<int>::max();
2813
2814 for( PCB_TRACK* track : possible_tracks )
2815 {
2816 SEG test( track->GetStart(), track->GetEnd() );
2817 int dist = ( test.NearestPoint( position ) - position ).EuclideanNorm();
2818
2819 if( dist < min_d )
2820 {
2821 min_d = dist;
2822 return_track = track;
2823 }
2824 }
2825
2826 return return_track;
2827 }
2828
2829 bool hasDRCViolation( PCB_VIA* aVia, BOARD_ITEM* aOther )
2830 {
2831 DRC_CONSTRAINT constraint;
2832 int clearance;
2833 BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aOther );
2834 ZONE* zone = dynamic_cast<ZONE*>( aOther );
2835
2836 if( zone && zone->GetIsRuleArea() )
2837 {
2838 if( zone->GetDoNotAllowVias() )
2839 return zone->Outline()->Collide( aVia->GetPosition(), aVia->GetWidth() / 2 );
2840
2841 return false;
2842 }
2843
2844 if( connectedItem )
2845 {
2846 int connectedItemNet = connectedItem->GetNetCode();
2847
2848 if( connectedItemNet == 0 || connectedItemNet == aVia->GetNetCode() )
2849 return false;
2850 }
2851
2852 for( PCB_LAYER_ID layer : aOther->GetLayerSet().Seq() )
2853 {
2854 if( !IsCopperLayer( layer ) )
2855 continue;
2856
2857 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aVia, aOther, layer );
2858 clearance = constraint.GetValue().Min();
2859
2860 if( clearance >= 0 )
2861 {
2862 std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( layer );
2863 std::shared_ptr<SHAPE> otherShape = aOther->GetEffectiveShape( layer );
2864
2865 if( viaShape->Collide( otherShape.get(), clearance - m_drcEpsilon ) )
2866 return true;
2867 }
2868 }
2869
2870 if( aOther->HasHole() )
2871 {
2872 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aVia, aOther,
2874 clearance = constraint.GetValue().Min();
2875
2876 if( clearance >= 0 )
2877 {
2878 std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( UNDEFINED_LAYER );
2879
2880 if( viaShape->Collide( aOther->GetEffectiveHoleShape().get(),
2881 clearance - m_drcEpsilon ) )
2882 {
2883 return true;
2884 }
2885 }
2886 }
2887
2888 return false;
2889 }
2890
2891 bool checkDRCViolation( PCB_VIA* aVia )
2892 {
2893 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
2894 std::set<BOARD_ITEM*> checkedItems;
2895 BOX2I bbox = aVia->GetBoundingBox();
2896
2897 bbox.Inflate( m_worstClearance );
2898 m_frame->GetCanvas()->GetView()->Query( bbox, items );
2899
2900 for( std::pair<KIGFX::VIEW_ITEM*, int> it : items )
2901 {
2902 BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( it.first );
2903
2904 if( !item )
2905 continue;
2906
2907 if( ( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2908 && !static_cast<ZONE*>( item )->GetIsRuleArea() )
2909 {
2910 continue; // stitching vias bind to zones, so ignore them
2911 }
2912 else if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_GROUP_T )
2913 {
2914 continue; // check against children, but not against footprint itself
2915 }
2916 else if( item->Type() == PCB_FP_TEXT_T
2917 && !static_cast<FP_TEXT*>( item )->IsVisible() )
2918 {
2919 continue; // ignore hidden items
2920 }
2921 else if( checkedItems.count( item ) )
2922 {
2923 continue; // already checked
2924 }
2925
2926 if( hasDRCViolation( aVia, item ) )
2927 return true;
2928
2929 checkedItems.insert( item );
2930 }
2931
2932 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( DISALLOW_CONSTRAINT, aVia, nullptr,
2934
2935 if( constraint.m_DisallowFlags && constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
2936 return true;
2937
2938 return false;
2939 }
2940
2941 PAD* findPad( PCB_VIA* aVia )
2942 {
2943 const VECTOR2I position = aVia->GetPosition();
2944 const LSET lset = aVia->GetLayerSet();
2945
2946 for( FOOTPRINT* fp : m_board->Footprints() )
2947 {
2948 for( PAD* pad : fp->Pads() )
2949 {
2950 if( pad->HitTest( position ) && ( pad->GetLayerSet() & lset ).any() )
2951 {
2952 if( pad->GetNetCode() > 0 )
2953 return pad;
2954 }
2955 }
2956 }
2957
2958 return nullptr;
2959 }
2960
2961 int findStitchedZoneNet( PCB_VIA* aVia )
2962 {
2963 const VECTOR2I position = aVia->GetPosition();
2964 const LSET lset = aVia->GetLayerSet();
2965
2966 // first take the net of the active layer
2967 if( lset.test( m_frame->GetActiveLayer() ) )
2968 {
2969 for( ZONE* z : m_board->Zones() )
2970 {
2971 if( z->IsOnLayer( m_frame->GetActiveLayer() ) )
2972 {
2973 if( z->HitTestFilledArea( m_frame->GetActiveLayer(), position ) )
2974 return z->GetNetCode();
2975 }
2976 }
2977 }
2978
2979 // none? take the topmost visible layer
2980 for( PCB_LAYER_ID layer : LSET( m_board->GetVisibleLayers() & lset ).Seq() )
2981 {
2982 for( ZONE* z : m_board->Zones() )
2983 {
2984 if( z->IsOnLayer( layer ) )
2985 {
2986 if( z->HitTestFilledArea( layer, position ) )
2987 return z->GetNetCode();
2988 }
2989 }
2990 }
2991
2992 return -1;
2993 }
2994
2995 void SnapItem( BOARD_ITEM *aItem ) override
2996 {
2997 m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
2998
3000 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
3001 VECTOR2I position = via->GetPosition();
3002
3003 if( settings->tracks == MAGNETIC_OPTIONS::CAPTURE_ALWAYS && m_gridHelper.GetSnap() )
3004 {
3005 PCB_TRACK* track = findTrack( via );
3006
3007 if( track )
3008 {
3009 SEG trackSeg( track->GetStart(), track->GetEnd() );
3010 VECTOR2I snap = m_gridHelper.AlignToSegment( position, trackSeg );
3011
3012 aItem->SetPosition( snap );
3013 }
3014 }
3015 else if( settings->pads == MAGNETIC_OPTIONS::CAPTURE_ALWAYS && m_gridHelper.GetSnap() )
3016 {
3017 PAD* pad = findPad( via );
3018
3019 if( pad )
3020 {
3021 aItem->SetPosition( pad->GetPosition() );
3022 }
3023 }
3024 }
3025
3026 bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
3027 {
3028 WX_INFOBAR* infobar = m_frame->GetInfoBar();
3029 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
3030 VECTOR2I viaPos = via->GetPosition();
3031 PCB_TRACK* track = findTrack( via );
3032 PAD* pad = findPad( via );
3033
3034 if( track )
3035 {
3036 via->SetNetCode( track->GetNetCode() );
3037 via->SetIsFree( false );
3038 }
3039 else if( pad )
3040 {
3041 via->SetNetCode( pad->GetNetCode() );
3042 via->SetIsFree( false );
3043 }
3044 else
3045 {
3046 via->SetNetCode( findStitchedZoneNet( via ) );
3047 via->SetIsFree( via->GetNetCode() > 0 );
3048 }
3049
3050 if( checkDRCViolation( via ) )
3051 {
3052 m_frame->ShowInfoBarError( _( "Via location violates DRC." ), true,
3054
3055 if( !m_allowDRCViolations )
3056 return false;
3057 }
3058 else
3059 {
3061 infobar->Dismiss();
3062 }
3063
3064 aCommit.Add( via );
3065
3066 // If the user explicitly disables snap (using shift), then don't break the tracks into
3067 // a chevron. This will prevent PNS from being able to connect the via and track but
3068 // it is explicitly requested by the user
3069 if( track && m_gridHelper.GetSnap() )
3070 {
3071 VECTOR2I trackStart = track->GetStart();
3072 VECTOR2I trackEnd = track->GetEnd();
3073 SEG trackSeg( trackStart, trackEnd );
3074
3075 OPT_VECTOR2I joint1;
3076 OPT_VECTOR2I joint2;
3077
3078 auto insertChevron =
3079 [&]()
3080 {
3081 if( ( trackStart - *joint1 ).SquaredEuclideanNorm()
3082 > ( trackStart - *joint2 ).SquaredEuclideanNorm() )
3083 {
3084 std::swap( joint1, joint2 );
3085 }
3086
3087 aCommit.Modify( track );
3088 track->SetStart( trackStart );
3089 track->SetEnd( *joint1 );
3090
3091 PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
3092 wxCHECK( newTrack, /* void */ );
3093 const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
3094
3095 newTrack->SetStart( *joint1 );
3096 newTrack->SetEnd( viaPos );
3097 aCommit.Add( newTrack );
3098
3099 newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
3100 wxCHECK( newTrack, /* void */ );
3101 const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
3102
3103 newTrack->SetStart( viaPos );
3104 newTrack->SetEnd( *joint2 );
3105 aCommit.Add( newTrack );
3106
3107 newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
3108 wxCHECK( newTrack, /* void */ );
3109 const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
3110
3111 newTrack->SetStart( *joint2 );
3112 newTrack->SetEnd( trackEnd );
3113 aCommit.Add( newTrack );
3114 };
3115
3116 if( viaPos == trackStart || viaPos == trackEnd )
3117 return true;
3118
3119 if( trackStart.x == trackEnd.x )
3120 {
3121 VECTOR2I splitPt = trackSeg.NearestPoint( viaPos );
3122
3123 if( splitPt.x != viaPos.x
3124 && abs( splitPt.x - viaPos.x ) < abs( splitPt.y - trackStart.y )
3125 && abs( splitPt.x - viaPos.x ) < abs( splitPt.y - trackEnd.y ) )
3126 {
3127 int offset = abs( splitPt.x - viaPos.x );
3128
3129 joint1 = VECTOR2I( splitPt.x, splitPt.y - offset );
3130 joint2 = VECTOR2I( splitPt.x, splitPt.y + offset );
3131
3132 insertChevron();
3133 return true;
3134 }
3135 }
3136 else if( trackStart.y == trackEnd.y )
3137 {
3138 VECTOR2I splitPt = trackSeg.NearestPoint( viaPos );
3139
3140 if( splitPt.y != viaPos.y
3141 && abs( trackStart.y - viaPos.y ) < abs( trackStart.x - viaPos.x )
3142 && abs( trackEnd.y - viaPos.y ) < abs( trackEnd.x - viaPos.x ) )
3143 {
3144 int offset = abs( splitPt.y - viaPos.y );
3145
3146 joint1 = VECTOR2I( splitPt.x - offset, splitPt.y );
3147 joint2 = VECTOR2I( splitPt.x + offset, splitPt.y );
3148
3149 insertChevron();
3150 return true;
3151 }
3152 }
3153 else if( abs( trackStart.y - trackEnd.y ) == abs( trackStart.x - trackEnd.x ) )
3154 {
3155 SEG horiz( VECTOR2I( -INT_MAX, viaPos.y ), VECTOR2I( INT_MAX, viaPos.y ) );
3156 SEG vert( VECTOR2I( viaPos.x, -INT_MAX ), VECTOR2I( viaPos.x, INT_MAX ) );
3157
3158 if( track->GetBoundingBox().Contains( viaPos ) )
3159 {
3160 joint1 = trackSeg.Intersect( horiz, true, true );
3161 joint2 = trackSeg.Intersect( vert, true, true );
3162
3163 if( !joint1 || !joint2 )
3164 return false;
3165
3166 insertChevron();
3167 return true;
3168 }
3169 }
3170
3171 aCommit.Modify( track );
3172 track->SetStart( trackStart );
3173 track->SetEnd( viaPos );
3174
3175 PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
3176 const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
3177
3178 newTrack->SetStart( viaPos );
3179 newTrack->SetEnd( trackEnd );
3180 aCommit.Add( newTrack );
3181 }
3182
3183 return true;
3184 }
3185
3186 std::unique_ptr<BOARD_ITEM> CreateItem() override
3187 {
3189 PCB_VIA* via = new PCB_VIA( m_board );
3190
3191 via->SetNetCode( 0 );
3192 via->SetViaType( bds.m_CurrentViaType );
3193
3194 // for microvias, the size and hole will be changed later.
3195 via->SetWidth( bds.GetCurrentViaSize() );
3196 via->SetDrill( bds.GetCurrentViaDrill() );
3197
3198 // Usual via is from copper to component.
3199 // layer pair is B_Cu and F_Cu.
3200 via->SetLayerPair( B_Cu, F_Cu );
3201
3202 PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
3203 PCB_LAYER_ID last_layer;
3204
3205 // prepare switch to new active layer:
3206 if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
3207 last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
3208 else
3209 last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
3210
3211 // Adjust the actual via layer pair
3212 switch( via->GetViaType() )
3213 {
3215 via->SetLayerPair( first_layer, last_layer );
3216 break;
3217
3218 case VIATYPE::MICROVIA: // from external to the near neighbor inner layer
3219 {
3220 PCB_LAYER_ID last_inner_layer =
3222
3223 if( first_layer == B_Cu )
3224 last_layer = last_inner_layer;
3225 else if( first_layer == F_Cu )
3226 last_layer = In1_Cu;
3227 else if( first_layer == last_inner_layer )
3228 last_layer = B_Cu;
3229 else if( first_layer == In1_Cu )
3230 last_layer = F_Cu;
3231
3232 // else error: will be removed later
3233 via->SetLayerPair( first_layer, last_layer );
3234
3235 // Update diameter and hole size, which where set previously for normal vias
3236 NETCLASS* netClass = via->GetEffectiveNetClass();
3237
3238 via->SetWidth( netClass->GetuViaDiameter() );
3239 via->SetDrill( netClass->GetuViaDrill() );
3240 }
3241 break;
3242
3243 default:
3244 break;
3245 }
3246
3247 return std::unique_ptr<BOARD_ITEM>( via );
3248 }
3249 };
3250
3251 VIA_PLACER placer( frame() );
3252
3253 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
3254
3255 doInteractiveItemPlacement( aEvent, &placer, _( "Place via" ), IPO_REPEAT | IPO_SINGLE_CLICK );
3256
3257 return 0;
3258}
3259
3260
3262{
3263 assert( m_board );
3264 return m_board->GetDesignSettings().GetLineThickness( aLayer );
3265}
3266
3267
3268const unsigned int DRAWING_TOOL::WIDTH_STEP = pcbIUScale.mmToIU( 0.1 );
3269
3270
3272{
3273
3297}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
@ width_track_via
static TOOL_ACTION cancelInteractive
Definition: actions.h:63
static TOOL_ACTION updateUnits
Definition: actions.h:150
static TOOL_ACTION activatePointEditor
Definition: actions.h:172
static TOOL_ACTION doDelete
Definition: actions.h:73
static TOOL_ACTION cursorClick
Definition: actions.h:126
static TOOL_ACTION refreshPreview
Definition: actions.h:109
static TOOL_ACTION resetLocalCoords
Definition: actions.h:153
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.
wxSize GetTextSize(PCB_LAYER_ID aLayer) const
Return the default text size from the layer class for the given layer.
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.
int m_DimensionPrecision
Number of digits after the decimal.
unsigned GetViaSizeIndex() const
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:58
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:214
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:219
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:185
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:229
virtual bool HasHole() const
Definition: board_item.h:128
LSET GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:583
ZONES & Zones()
Definition: board.h:313
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:397
FOOTPRINTS & Footprints()
Definition: board.h:307
int GetCopperLayerCount() const
Definition: board.cpp:545
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:686
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)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
COMMIT & Add(EDA_ITEM *aItem)
Notify observers that aItem has been added.
Definition: commit.h:78
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
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()
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 mo...
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
Update the board display after modifying it by a python script (note: it is automatically called by a...
virtual void SetPosition(const VECTOR2I &aPos)
Definition: eda_item.h:250
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:139
const KIID m_Uuid
Definition: eda_item.h:492
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
const VECTOR2I & GetTextPos() const
Definition: eda_text.h:208
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:87
void SetAttributes(const EDA_TEXT &aSrc)
Set the text attributes from another instance.
Definition: eda_text.cpp:264
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:371
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:185
void SetTextSize(const VECTOR2I &aNewSize)
Definition: eda_text.cpp:347
void SetItalic(bool aItalic)
Definition: eda_text.cpp:201
void MoveAnchorPosition(const VECTOR2I &aMoveVector)
Move the reference point of the footprint.
Definition: footprint.cpp:1729
PADS & Pads()
Definition: footprint.h:170
VECTOR2I GetPosition() const override
Definition: footprint.h:188
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:104
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:1649
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:316
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:349
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:425
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:1591
void AddToPreview(EDA_ITEM *aItem, bool aTakeOwnership=true)
Definition: view.cpp:1635
void ClearPreview()
Definition: view.cpp:1613
void RecacheAllItems()
Rebuild GAL display lists.
Definition: view.cpp:1384
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:530
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:466
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:496
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.
int ShowTextBoxPropertiesDialog(BOARD_ITEM *aText)
wxString GetDesignRulesPath()
Return the absolute path to the design rules file for the currently-loaded board.
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)
void ShowTextPropertiesDialog(BOARD_ITEM *aText)
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.
Definition: pcb_dimension.h:96
void Update()
Update the dimension's cached text and geometry.
int GetLineThickness() const
void SetExtensionOffset(int aOffset)
PCB_TEXT & Text()
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
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
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:175
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:72
virtual void SetPosition(const VECTOR2I &aPos) override
Definition: pcb_text.h:81
KIGFX::PCB_VIEW * view() const
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:269
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:441
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:1198
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_track.cpp:508
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:261
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:188
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(bool aLoadNow=true)
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:215
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:88
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:175
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:57
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:697
bool GetDoNotAllowVias() const
Definition: zone.h:699
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: zone.cpp:239
SHAPE_POLY_SET * Outline()
Definition: zone.h:312
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:425
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:423
#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:81
@ NONE
Definition: kibis.h:53
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:922
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:825
@ 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
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:412
ZONE_MODE
Definition: pcb_actions.h:34
@ SIMILAR
Add a new zone with the same settings as an existing one.
@ ADD
Add a new zone/keepout with fresh settings.
@ BLIND_BURIED
@ 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:111
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_FP_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:95
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:110
@ PCB_FP_SHAPE_T
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:94
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:107
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:108
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:115
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:112
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:90
@ PCB_FP_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:97
@ PCB_FP_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:99
@ PCB_FP_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:96
@ 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:106
@ PCB_FP_ZONE_T
class ZONE, managed by a footprint
Definition: typeinfo.h:100
@ PCB_FP_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:98
@ PCB_FP_TEXT_T
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition: typeinfo.h:105
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:109
VECTOR2< double > VECTOR2D
Definition: vector2d.h:617
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618