KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drawing_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2014-2017 CERN
5 * Copyright (C) 2018-2024 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#include "dialog_table_properties.h"
29
30#include <pgm_base.h>
32#include <pcbnew_settings.h>
34#include <dialogs/dialog_text_properties.h>
45#include <router/router_tool.h>
46#include <status_popup.h>
47#include <tool/tool_manager.h>
48#include <tools/pcb_actions.h>
54#include <view/view.h>
56#include <widgets/wx_infobar.h>
57#include <wx/filedlg.h>
58#include <wx/msgdlg.h>
59
60#include <bitmaps.h>
61#include <board.h>
62#include <board_commit.h>
64#include <confirm.h>
65#include <footprint.h>
66#include <macros.h>
67#include <gal/painter.h>
68#include <pcb_edit_frame.h>
69#include <pcb_group.h>
70#include <pcb_reference_image.h>
71#include <pcb_text.h>
72#include <pcb_textbox.h>
73#include <pcb_table.h>
74#include <pcb_tablecell.h>
75#include <pcb_dimension.h>
76#include <pcbnew_id.h>
77#include <scoped_set_reset.h>
78#include <string_utils.h>
79#include <zone.h>
80#include <fix_board_shape.h>
81#include <view/view_controls.h>
82
83const unsigned int DRAWING_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
84
86
87
89{
90public:
92 ACTION_MENU( true )
93 {
94 SetIcon( BITMAPS::width_track_via );
95 SetTitle( _( "Select Via Size" ) );
96 }
97
98protected:
99 ACTION_MENU* create() const override
100 {
101 return new VIA_SIZE_MENU();
102 }
103
104 void update() override
105 {
108 bool useIndex = !bds.m_UseConnectedTrackWidth
109 && !bds.UseCustomTrackViaSize();
110 wxString msg;
111
112 Clear();
113
114 Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
115 _( "Specify custom track and via sizes" ), wxITEM_CHECK );
117
118 AppendSeparator();
119
120 for( unsigned i = 1; i < bds.m_ViasDimensionsList.size(); i++ )
121 {
123
124 if( via.m_Drill > 0 )
125 {
126 msg.Printf( _("Via %s, hole %s" ),
127 frame->MessageTextFromValue( via.m_Diameter ),
128 frame->MessageTextFromValue( via.m_Drill ) );
129 }
130 else
131 {
132 msg.Printf( _( "Via %s" ),
133 frame->MessageTextFromValue( via.m_Diameter ) );
134 }
135
136 int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
137 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
138 Check( menuIdx, useIndex && bds.GetViaSizeIndex() == i );
139 }
140 }
141
142 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
143 {
146 int id = aEvent.GetId();
147
148 // On Windows, this handler can be called with an event ID not existing in any
149 // menuitem, so only set flags when we have an ID match.
150
152 {
153 DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds );
154
155 if( sizeDlg.ShowModal() == wxID_OK )
156 {
157 bds.UseCustomTrackViaSize( true );
158 bds.m_UseConnectedTrackWidth = false;
159 }
160 }
162 {
163 bds.UseCustomTrackViaSize( false );
164 bds.m_UseConnectedTrackWidth = false;
166 }
167
169 }
170};
171
172
174 PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ),
175 m_view( nullptr ),
176 m_controls( nullptr ),
177 m_board( nullptr ),
178 m_frame( nullptr ),
179 m_mode( MODE::NONE ),
180 m_inDrawingTool( false ),
181 m_layer( UNDEFINED_LAYER ),
182 m_stroke( 1, LINE_STYLE::DEFAULT, COLOR4D::UNSPECIFIED ),
183 m_pickerItem( nullptr ),
184 m_tuningPattern( nullptr )
185{
186}
187
188
190{
191}
192
193
195{
196 auto haveHighlight =
197 [&]( const SELECTION& sel )
198 {
200
201 return !cfg->GetHighlightNetCodes().empty();
202 };
203
204 auto activeToolFunctor =
205 [this]( const SELECTION& aSel )
206 {
207 return m_mode != MODE::NONE;
208 };
209
210 // some interactive drawing tools can undo the last point
211 auto canUndoPoint =
212 [this]( const SELECTION& aSel )
213 {
214 return ( m_mode == MODE::ARC
215 || m_mode == MODE::ZONE
218 || m_mode == MODE::BEZIER
219 || m_mode == MODE::LINE );
220 };
221
222 // functor for tools that can automatically close the outline
223 auto canCloseOutline =
224 [this]( const SELECTION& aSel )
225 {
226 return ( m_mode == MODE::ZONE
229 };
230
231 auto arcToolActive =
232 [this]( const SELECTION& aSel )
233 {
234 return m_mode == MODE::ARC;
235 };
236
237 auto viaToolActive =
238 [this]( const SELECTION& aSel )
239 {
240 return m_mode == MODE::VIA;
241 };
242
243 auto tuningToolActive =
244 [this]( const SELECTION& aSel )
245 {
246 return m_mode == MODE::TUNING;
247 };
248
249 auto dimensionToolActive =
250 [this]( const SELECTION& aSel )
251 {
252 return m_mode == MODE::DIMENSION;
253 };
254
255 CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
256
257 // cancel current tool goes in main context menu at the top if present
258 ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
259 ctxMenu.AddSeparator( 1 );
260
261 ctxMenu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 2 );
262 ctxMenu.AddSeparator( haveHighlight, 2 );
263
264 // tool-specific actions
265 ctxMenu.AddItem( PCB_ACTIONS::closeOutline, canCloseOutline, 200 );
266 ctxMenu.AddItem( PCB_ACTIONS::deleteLastPoint, canUndoPoint, 200 );
267 ctxMenu.AddItem( PCB_ACTIONS::arcPosture, arcToolActive, 200 );
268 ctxMenu.AddItem( PCB_ACTIONS::spacingIncrease, tuningToolActive, 200 );
269 ctxMenu.AddItem( PCB_ACTIONS::spacingDecrease, tuningToolActive, 200 );
270 ctxMenu.AddItem( PCB_ACTIONS::amplIncrease, tuningToolActive, 200 );
271 ctxMenu.AddItem( PCB_ACTIONS::amplDecrease, tuningToolActive, 200 );
272 ctxMenu.AddItem( PCB_ACTIONS::lengthTunerSettings, tuningToolActive, 200 );
273 ctxMenu.AddItem( PCB_ACTIONS::changeDimensionArrows, dimensionToolActive, 200 );
274
275 ctxMenu.AddCheckItem( PCB_ACTIONS::toggleHV45Mode, !tuningToolActive, 250 );
276 ctxMenu.AddSeparator( 500 );
277
278 std::shared_ptr<VIA_SIZE_MENU> viaSizeMenu = std::make_shared<VIA_SIZE_MENU>();
279 viaSizeMenu->SetTool( this );
280 m_menu->RegisterSubMenu( viaSizeMenu );
281 ctxMenu.AddMenu( viaSizeMenu.get(), viaToolActive, 500 );
282
283 ctxMenu.AddSeparator( 500 );
284
285 // Type-specific sub-menus will be added for us by other tools
286 // For example, zone fill/unfill is provided by the PCB control tool
287
288 // Finally, add the standard zoom/grid items
289 getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( *m_menu.get() );
290
291 return true;
292}
293
294
296{
297 // Init variables used by every drawing tool
298 m_view = getView();
300 m_board = getModel<BOARD>();
301 m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
302
303 // Re-initialize session attributes
305
306 if( aReason == RESET_REASON::SHUTDOWN )
307 return;
308
311 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
312 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
313
322
324}
325
326
328{
329 return m_mode;
330}
331
332
334{
335 if( m_frame )
336 {
338 bool constrained;
339
341 constrained = mgr.GetAppSettings<PCBNEW_SETTINGS>()->m_Use45DegreeLimit;
342 else
343 constrained = mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>()->m_Use45Limit;
344
345 m_frame->DisplayConstraintsMsg( constrained ? _( "Constrain to H, V, 45" ) : wxString( "" ) );
346 }
347}
348
349
351{
353 return 0;
354
355 if( m_inDrawingTool )
356 return 0;
357
359
360 BOARD_ITEM* parent = m_frame->GetModel();
361 PCB_SHAPE* line = new PCB_SHAPE( parent );
362 BOARD_COMMIT commit( m_frame );
363 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
364 std::optional<VECTOR2D> startingPoint;
365 std::stack<PCB_SHAPE*> committedLines;
366
367 line->SetShape( SHAPE_T::SEGMENT );
368 line->SetFlags( IS_NEW );
369
370 if( aEvent.HasPosition() )
371 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
372
373 m_frame->PushTool( aEvent );
374 Activate();
375
376 while( drawShape( aEvent, &line, startingPoint, &committedLines ) )
377 {
378 if( line )
379 {
380 commit.Add( line );
381 commit.Push( _( "Draw Line" ) );
382 startingPoint = VECTOR2D( line->GetEnd() );
383 committedLines.push( line );
384 }
385 else
386 {
387 startingPoint = std::nullopt;
388 }
389
390 line = new PCB_SHAPE( parent );
391 line->SetShape( SHAPE_T::SEGMENT );
392 line->SetFlags( IS_NEW );
393 }
394
395 return 0;
396}
397
398
400{
402 return 0;
403
404 if( m_inDrawingTool )
405 return 0;
406
408
409 bool isTextBox = aEvent.IsAction( &PCB_ACTIONS::drawTextBox );
410 PCB_SHAPE* rect = nullptr;
411 BOARD_COMMIT commit( m_frame );
412 BOARD_ITEM* parent = m_frame->GetModel();
413 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::RECTANGLE );
414 std::optional<VECTOR2D> startingPoint;
415
416 rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
417 rect->SetShape( SHAPE_T::RECTANGLE );
418 rect->SetFilled( false );
419 rect->SetFlags( IS_NEW );
420
421 if( aEvent.HasPosition() )
422 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
423
424 m_frame->PushTool( aEvent );
425 Activate();
426
427 while( drawShape( aEvent, &rect, startingPoint, nullptr ) )
428 {
429 if( rect )
430 {
431 bool cancelled = false;
432
433 if( PCB_TEXTBOX* textbox = dynamic_cast<PCB_TEXTBOX*>( rect ) )
434 cancelled = m_frame->ShowTextBoxPropertiesDialog( textbox ) != wxID_OK;
435
436 if( cancelled )
437 {
438 delete rect;
439 rect = nullptr;
440 }
441 else
442 {
443 rect->Normalize();
444 commit.Add( rect );
445 commit.Push( isTextBox ? _( "Draw Text Box" ) : _( "Draw Rectangle" ) );
446
448 }
449 }
450
451 rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
452 rect->SetShape( SHAPE_T::RECTANGLE );
453 rect->SetFilled( false );
454 rect->SetFlags( IS_NEW );
455 startingPoint = std::nullopt;
456 }
457
458 return 0;
459}
460
461
463{
465 return 0;
466
467 if( m_inDrawingTool )
468 return 0;
469
471
472 BOARD_ITEM* parent = m_frame->GetModel();
473 PCB_SHAPE* circle = new PCB_SHAPE( parent );
474 BOARD_COMMIT commit( m_frame );
475 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::CIRCLE );
476 std::optional<VECTOR2D> startingPoint;
477
478 circle->SetShape( SHAPE_T::CIRCLE );
479 circle->SetFilled( false );
480 circle->SetFlags( IS_NEW );
481
482 if( aEvent.HasPosition() )
483 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
484
485 m_frame->PushTool( aEvent );
486 Activate();
487
488 while( drawShape( aEvent, &circle, startingPoint, nullptr ) )
489 {
490 if( circle )
491 {
492 commit.Add( circle );
493 commit.Push( _( "Draw Circle" ) );
494
496 }
497
498 circle = new PCB_SHAPE( parent );
499 circle->SetShape( SHAPE_T::CIRCLE );
500 circle->SetFilled( false );
501 circle->SetFlags( IS_NEW );
502
503 startingPoint = std::nullopt;
504 }
505
506 return 0;
507}
508
509
511{
513 return 0;
514
515 if( m_inDrawingTool )
516 return 0;
517
519
520 BOARD_ITEM* parent = m_frame->GetModel();
521 PCB_SHAPE* arc = new PCB_SHAPE( parent );
522 BOARD_COMMIT commit( m_frame );
523 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
524 std::optional<VECTOR2D> startingPoint;
525
526 arc->SetShape( SHAPE_T::ARC );
527 arc->SetFlags( IS_NEW );
528
529 m_frame->PushTool( aEvent );
530 Activate();
531
532 if( aEvent.HasPosition() )
533 startingPoint = aEvent.Position();
534
535 while( drawArc( aEvent, &arc, startingPoint ) )
536 {
537 if( arc )
538 {
539 commit.Add( arc );
540 commit.Push( _( "Draw Arc" ) );
541
543 }
544
545 arc = new PCB_SHAPE( parent );
546 arc->SetShape( SHAPE_T::ARC );
547 arc->SetFlags( IS_NEW );
548
549 startingPoint = std::nullopt;
550 }
551
552 return 0;
553}
554
555
557{
559 return 0;
560
561 if( m_inDrawingTool )
562 return 0;
563
565
566 BOARD_COMMIT commit( m_frame );
567 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::BEZIER );
568 OPT_VECTOR2I startingPoint, startingC1;
569
570 m_frame->PushTool( aEvent );
571 Activate();
572
573 if( aEvent.HasPosition() )
574 startingPoint = aEvent.Position();
575
577 while( result != DRAW_ONE_RESULT::CANCELLED )
578 {
579 std::unique_ptr<PCB_SHAPE> bezier =
580 drawOneBezier( aEvent, startingPoint, startingC1, result );
581
582 // Anyting other than accepted means no chaining
583 startingPoint = std::nullopt;
584 startingC1 = std::nullopt;
585
586 // If a bezier was created, add it and go again
587 if( bezier )
588 {
589 PCB_SHAPE& bezierRef = *bezier;
590 commit.Add( bezier.release() );
591 commit.Push( _( "Draw Bezier" ) );
592
593 // Don't chain if reset (or accepted and reset)
594 if( result == DRAW_ONE_RESULT::ACCEPTED )
595 {
596 startingPoint = bezierRef.GetEnd();
597
598 // If the last bezier has a zero C2 control arm, allow the user to
599 // define a new C1 control arm for the next one.
600 if( bezierRef.GetEnd() != bezierRef.GetBezierC2() )
601 {
602 // Mirror the control point across the end point to get
603 // a tangent control point
604 startingC1 =
605 bezierRef.GetEnd() - ( bezierRef.GetBezierC2() - bezierRef.GetEnd() );
606 }
607 }
608 }
609 }
610
611 return 0;
612}
613
614
616{
617 if( m_inDrawingTool )
618 return 0;
619
621
623 bool immediateMode = image != nullptr;
625 bool ignorePrimePosition = false;
626 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
627
630 BOARD_COMMIT commit( m_frame );
631
633
634 // Add all the drawable symbols to preview
635 if( image )
636 {
637 image->SetPosition( cursorPos );
639 m_view->AddToPreview( image, false ); // Add, but not give ownership
640 }
641
642 m_frame->PushTool( aEvent );
643
644 auto setCursor =
645 [&]()
646 {
647 if( image )
648 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
649 else
650 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
651 };
652
653 auto cleanup =
654 [&] ()
655 {
659 delete image;
660 image = nullptr;
661 };
662
663 Activate();
664
665 // Must be done after Activate() so that it gets set into the correct context
666 getViewControls()->ShowCursor( true );
667
668 // Set initial cursor
669 setCursor();
670
671 // Prime the pump
672 if( image )
673 {
675 }
676 else if( aEvent.HasPosition() )
677 {
678 m_toolMgr->PrimeTool( aEvent.Position() );
679 }
680 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
681 {
682 m_toolMgr->PrimeTool( { 0, 0 } );
683 ignorePrimePosition = true;
684 }
685
686 // Main loop: keep receiving events
687 while( TOOL_EVENT* evt = Wait() )
688 {
689 setCursor();
690
691 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
692 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
693 cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
694 { m_frame->GetActiveLayer() },
697 m_controls->ForceCursorPosition( true, cursorPos );
698
699 if( evt->IsCancelInteractive() || ( image && evt->IsAction( &ACTIONS::undo ) ) )
700 {
701 if( image )
702 {
703 cleanup();
704 }
705 else
706 {
707 m_frame->PopTool( aEvent );
708 break;
709 }
710
711 if( immediateMode )
712 {
713 m_frame->PopTool( aEvent );
714 break;
715 }
716 }
717 else if( evt->IsActivate() )
718 {
719 if( image && evt->IsMoveTool() )
720 {
721 // We're already moving our own item; ignore the move tool
722 evt->SetPassEvent( false );
723 continue;
724 }
725
726 if( image )
727 {
728 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel image creation." ) );
729 evt->SetPassEvent( false );
730 continue;
731 }
732
733 if( evt->IsMoveTool() )
734 {
735 // Leave ourselves on the stack so we come back after the move
736 break;
737 }
738 else
739 {
740 m_frame->PopTool( aEvent );
741 break;
742 }
743 }
744 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
745 {
746 if( !image )
747 {
749
750 wxFileDialog dlg( m_frame, _( "Choose Image" ), wxEmptyString, wxEmptyString,
751 _( "Image Files" ) + wxS( " " ) + wxImage::GetImageExtWildcard(),
752 wxFD_OPEN );
753
754 if( dlg.ShowModal() != wxID_OK )
755 continue;
756
757 // If we started with a hotkey which has a position then warp back to that.
758 // Otherwise update to the current mouse position pinned inside the autoscroll
759 // boundaries.
760 if( evt->IsPrime() && !ignorePrimePosition )
761 {
762 cursorPos = grid.Align( evt->Position() );
763 getViewControls()->WarpMouseCursor( cursorPos, true );
764 }
765 else
766 {
768 cursorPos = getViewControls()->GetMousePosition();
769 }
770
771 cursorPos = getViewControls()->GetMousePosition( true );
772
773 wxString fullFilename = dlg.GetPath();
774
775 if( wxFileExists( fullFilename ) )
776 image = new PCB_REFERENCE_IMAGE( m_frame->GetModel(), cursorPos );
777
778 if( !image || !image->GetReferenceImage().ReadImageFile( fullFilename ) )
779 {
780 wxMessageBox( wxString::Format(_( "Could not load image from '%s'." ), fullFilename ) );
781 delete image;
782 image = nullptr;
783 continue;
784 }
785
786 image->SetFlags( IS_NEW | IS_MOVING );
787 image->SetLayer( m_frame->GetActiveLayer() );
788
790 m_view->AddToPreview( image, false ); // Add, but not give ownership
791 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
792 selectionTool->AddItemToSel( image, false );
793
794 getViewControls()->SetCursorPosition( cursorPos, false );
795 setCursor();
796 m_view->ShowPreview( true );
797 }
798 else
799 {
800 commit.Add( image );
801 commit.Push( _( "Place Image" ) );
802
804
805 image = nullptr;
807
809
810 if( immediateMode )
811 {
812 m_frame->PopTool( aEvent );
813 break;
814 }
815 }
816 }
817 else if( evt->IsClick( BUT_RIGHT ) )
818 {
819 // Warp after context menu only if dragging...
820 if( !image )
822
823 m_menu->ShowContextMenu( selectionTool->GetSelection() );
824 }
825 else if( image && ( evt->IsAction( &ACTIONS::refreshPreview )
826 || evt->IsMotion() ) )
827 {
828 image->SetPosition( cursorPos );
830 m_view->AddToPreview( image, false ); // Add, but not give ownership
831 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
832 }
833 else if( image && evt->IsAction( &ACTIONS::doDelete ) )
834 {
835 cleanup();
836 }
837 else if( image && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
838 || evt->IsAction( &ACTIONS::redo ) ) )
839 {
840 wxBell();
841 }
842 else
843 {
844 evt->SetPassEvent();
845 }
846
847 // Enable autopanning and cursor capture only when there is an image to be placed
848 getViewControls()->SetAutoPan( image != nullptr );
849 getViewControls()->CaptureCursor( image != nullptr );
850 }
851
852 getViewControls()->SetAutoPan( false );
853 getViewControls()->CaptureCursor( false );
854 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
855
856 return 0;
857}
858
859
861{
863 return 0;
864
865 if( m_inDrawingTool )
866 return 0;
867
869
870 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
871 PCB_TEXT* text = nullptr;
872 bool ignorePrimePosition = false;
874 BOARD_COMMIT commit( m_frame );
875 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
877
878 auto setCursor =
879 [&]()
880 {
881 if( text )
882 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
883 else
884 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
885 };
886
887 auto cleanup =
888 [&]()
889 {
892 m_controls->ShowCursor( true );
893 m_controls->SetAutoPan( false );
894 m_controls->CaptureCursor( false );
895 delete text;
896 text = nullptr;
897 };
898
900
901 m_frame->PushTool( aEvent );
902
903 Activate();
904 // Must be done after Activate() so that it gets set into the correct context
905 m_controls->ShowCursor( true );
907 // do not capture or auto-pan until we start placing some text
908 // Set initial cursor
909 setCursor();
910
911 if( aEvent.HasPosition() )
912 {
913 m_toolMgr->PrimeTool( aEvent.Position() );
914 }
915 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
916 {
917 m_toolMgr->PrimeTool( { 0, 0 } );
918 ignorePrimePosition = true;
919 }
920
921 // Main loop: keep receiving events
922 while( TOOL_EVENT* evt = Wait() )
923 {
924 setCursor();
925
926 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
927 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
928 VECTOR2I cursorPos =
930 { m_frame->GetActiveLayer() }, GRID_TEXT ),
932 m_controls->ForceCursorPosition( true, cursorPos );
933
934 if( evt->IsCancelInteractive() || ( text && evt->IsAction( &ACTIONS::undo ) ) )
935 {
936 if( text )
937 {
938 cleanup();
939 }
940 else
941 {
942 m_frame->PopTool( aEvent );
943 break;
944 }
945 }
946 else if( evt->IsActivate() )
947 {
948 if( text )
949 cleanup();
950
951 if( evt->IsMoveTool() )
952 {
953 // leave ourselves on the stack so we come back after the move
954 break;
955 }
956 else
957 {
958 m_frame->PopTool( aEvent );
959 break;
960 }
961 }
962 else if( evt->IsClick( BUT_RIGHT ) )
963 {
964 if( !text )
966
967 m_menu->ShowContextMenu( selection() );
968 }
969 else if( evt->IsClick( BUT_LEFT ) )
970 {
971 bool placing = text != nullptr;
972
973 if( !text )
974 {
976
978
980 TEXT_ATTRIBUTES textAttrs;
981
982 textAttrs.m_Size = bds.GetTextSize( layer );
983 textAttrs.m_StrokeWidth = bds.GetTextThickness( layer );
984 InferBold( &textAttrs );
985 textAttrs.m_Italic = bds.GetTextItalic( layer );
986 textAttrs.m_KeepUpright = bds.GetTextUpright( layer );
987 textAttrs.m_Mirrored = IsBackLayer( layer );
988 textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
990
992 text = new PCB_TEXT( static_cast<FOOTPRINT*>( m_frame->GetModel() ) );
993 else
994 text = new PCB_TEXT( m_frame->GetModel() );
995
996 text->SetLayer( layer );
997 text->SetAttributes( textAttrs );
998 text->SetTextPos( cursorPos );
999
1000 DIALOG_TEXT_PROPERTIES textDialog( m_frame, text );
1001 bool cancelled;
1002
1003 RunMainStack( [&]()
1004 {
1005 // QuasiModal required for Scintilla auto-complete
1006 cancelled = !textDialog.ShowQuasiModal();
1007 } );
1008
1009 if( cancelled || NoPrintableChars( text->GetText() ) )
1010 {
1011 delete text;
1012 text = nullptr;
1013 }
1014 else if( text->GetTextPos() != cursorPos )
1015 {
1016 // If the user modified the location then go ahead and place it there.
1017 // Otherwise we'll drag.
1018 placing = true;
1019 }
1020
1021 if( text )
1022 {
1023 if( !m_view->IsLayerVisible( text->GetLayer() ) )
1024 {
1025 m_frame->GetAppearancePanel()->SetLayerVisible( text->GetLayer(), true );
1027 }
1028
1030 m_view->Update( &selection() );
1031
1032 // update the cursor so it looks correct before another event
1033 setCursor();
1034 }
1035 }
1036
1037 if( placing )
1038 {
1039 text->ClearFlags();
1041
1042 commit.Add( text );
1043 commit.Push( _( "Draw Text" ) );
1044
1046
1047 text = nullptr;
1048 }
1049
1051
1052 // If we started with a hotkey which has a position then warp back to that.
1053 // Otherwise update to the current mouse position pinned inside the autoscroll
1054 // boundaries.
1055 if( evt->IsPrime() && !ignorePrimePosition )
1056 {
1057 cursorPos = evt->Position();
1058 m_controls->WarpMouseCursor( cursorPos, true );
1059 }
1060 else
1061 {
1063 cursorPos = m_controls->GetMousePosition();
1064 }
1065
1067
1068 m_controls->ShowCursor( true );
1069 m_controls->CaptureCursor( text != nullptr );
1070 m_controls->SetAutoPan( text != nullptr );
1071 }
1072 else if( text && ( evt->IsMotion()
1073 || evt->IsAction( &PCB_ACTIONS::refreshPreview ) ) )
1074 {
1075 text->SetPosition( cursorPos );
1076 selection().SetReferencePoint( cursorPos );
1077 m_view->Update( &selection() );
1078 }
1079 else if( text && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
1080 || evt->IsAction( &ACTIONS::redo ) ) )
1081 {
1082 wxBell();
1083 }
1084 else if( text && evt->IsAction( &PCB_ACTIONS::properties ) )
1085 {
1086 frame()->OnEditItemRequest( text );
1087 m_view->Update( &selection() );
1088 frame()->SetMsgPanel( text );
1089 }
1090 else
1091 {
1092 evt->SetPassEvent();
1093 }
1094 }
1095
1096 m_controls->SetAutoPan( false );
1097 m_controls->CaptureCursor( false );
1099 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1100
1101 if( selection().Empty() )
1103
1104 return 0;
1105}
1106
1107
1109{
1110 if( m_inDrawingTool )
1111 return 0;
1112
1114
1115 PCB_TABLE* table = nullptr;
1117 BOARD_COMMIT commit( m_frame );
1119
1120 // We might be running as the same shape in another co-routine. Make sure that one
1121 // gets whacked.
1123
1124 auto setCursor =
1125 [&]()
1126 {
1127 if( table )
1128 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1129 else
1130 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1131 };
1132
1133 auto cleanup =
1134 [&] ()
1135 {
1138 m_controls->ShowCursor( true );
1139 m_controls->SetAutoPan( false );
1140 m_controls->CaptureCursor( false );
1141 delete table;
1142 table = nullptr;
1143 };
1144
1146
1147 m_frame->PushTool( aEvent );
1148
1149 Activate();
1150 // Must be done after Activate() so that it gets set into the correct context
1151 getViewControls()->ShowCursor( true );
1153 // Set initial cursor
1154 setCursor();
1155
1156 if( aEvent.HasPosition() )
1157 m_toolMgr->PrimeTool( aEvent.Position() );
1158
1159 // Main loop: keep receiving events
1160 while( TOOL_EVENT* evt = Wait() )
1161 {
1162 setCursor();
1163 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1164 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1165 VECTOR2I cursorPos =
1167 { m_frame->GetActiveLayer() }, GRID_TEXT ),
1169 m_controls->ForceCursorPosition( true, cursorPos );
1170
1171 if( evt->IsCancelInteractive() || ( table && evt->IsAction( &ACTIONS::undo ) ) )
1172 {
1173 if( table )
1174 {
1175 cleanup();
1176 }
1177 else
1178 {
1179 m_frame->PopTool( aEvent );
1180 break;
1181 }
1182 }
1183 else if( evt->IsActivate() )
1184 {
1185 if( table )
1186 cleanup();
1187
1188 if( evt->IsMoveTool() )
1189 {
1190 // leave ourselves on the stack so we come back after the move
1191 break;
1192 }
1193 else
1194 {
1195 m_frame->PopTool( aEvent );
1196 break;
1197 }
1198 }
1199 else if( evt->IsClick( BUT_RIGHT ) )
1200 {
1201 // Warp after context menu only if dragging...
1202 if( !table )
1204
1205 m_menu->ShowContextMenu( selection() );
1206 }
1207 else if( evt->IsClick( BUT_LEFT ) )
1208 {
1209 if( !table )
1210 {
1212
1214
1215 table = new PCB_TABLE( m_frame->GetModel(), bds.GetLineThickness( layer ) );
1216 table->SetFlags( IS_NEW );
1217 table->SetLayer( layer );
1218 table->SetColCount( 1 );
1219 table->AddCell( new PCB_TABLECELL( table ) );
1220
1221 table->SetLayer( layer );
1222 table->SetPosition( cursorPos );
1223
1224 if( !m_view->IsLayerVisible( layer ) )
1225 {
1226 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1228 }
1229
1231 m_view->Update( &selection() );
1232
1233 // update the cursor so it looks correct before another event
1234 setCursor();
1235 }
1236 else
1237 {
1239
1240 table->Normalize();
1241
1242 DIALOG_TABLE_PROPERTIES dlg( m_frame, table );
1243
1244 // QuasiModal required for Scintilla auto-complete
1245 if( dlg.ShowQuasiModal() == wxID_OK )
1246 {
1247 commit.Add( table, m_frame->GetScreen() );
1248 commit.Push( _( "Draw Table" ) );
1249
1252 }
1253 else
1254 {
1255 delete table;
1256 }
1257
1258 table = nullptr;
1259 }
1260 }
1261 else if( table && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
1262 {
1263 VECTOR2I fontSize = bds.GetTextSize( table->GetLayer() );
1264 VECTOR2I gridSize = grid.GetGridSize( grid.GetItemGrid( table ) );
1265 VECTOR2I origin( table->GetPosition() );
1266 VECTOR2I requestedSize( cursorPos - origin );
1267
1268 int colCount = std::max( 1, requestedSize.x / ( fontSize.x * 15 ) );
1269 int rowCount = std::max( 1, requestedSize.y / ( fontSize.y * 3 ) );
1270
1271 VECTOR2I cellSize( std::max( fontSize.x * 5, requestedSize.x / colCount ),
1272 std::max( fontSize.y * 3, requestedSize.y / rowCount ) );
1273
1274 cellSize.x = KiROUND( (double) cellSize.x / gridSize.x ) * gridSize.x;
1275 cellSize.y = KiROUND( (double) cellSize.y / gridSize.y ) * gridSize.y;
1276
1277 table->ClearCells();
1278 table->SetColCount( colCount );
1279
1280 for( int col = 0; col < colCount; ++col )
1281 table->SetColWidth( col, cellSize.x );
1282
1283 for( int row = 0; row < rowCount; ++row )
1284 {
1285 table->SetRowHeight( row, cellSize.y );
1286
1287 for( int col = 0; col < colCount; ++col )
1288 {
1289 PCB_TABLECELL* cell = new PCB_TABLECELL( table );
1290 cell->SetPosition( origin + VECTOR2I( col * cellSize.x, row * cellSize.y ) );
1291 cell->SetEnd( cell->GetPosition() + cellSize );
1292 table->AddCell( cell );
1293 }
1294 }
1295
1296 selection().SetReferencePoint( cursorPos );
1297 m_view->Update( &selection() );
1298 m_frame->SetMsgPanel( table );
1299 }
1300 else if( table && evt->IsAction( &PCB_ACTIONS::properties ) )
1301 {
1302 frame()->OnEditItemRequest( table );
1303 m_view->Update( &selection() );
1304 frame()->SetMsgPanel( table );
1305 }
1306 else if( table && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
1307 || evt->IsAction( &ACTIONS::redo ) ) )
1308 {
1309 wxBell();
1310 }
1311 else
1312 {
1313 evt->SetPassEvent();
1314 }
1315
1316 // Enable autopanning and cursor capture only when there is a shape being drawn
1317 getViewControls()->SetAutoPan( table != nullptr );
1318 getViewControls()->CaptureCursor( table != nullptr );
1319 }
1320
1321 getViewControls()->SetAutoPan( false );
1322 getViewControls()->CaptureCursor( false );
1323 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1324 return 0;
1325}
1326
1327
1329{
1330 const VECTOR2I lineVector{ aDim->GetEnd() - aDim->GetStart() };
1331
1332 aDim->SetEnd( aDim->GetStart() + GetVectorSnapped45( lineVector ) );
1333 aDim->Update();
1334}
1335
1336
1338{
1340 return 0;
1341
1342 if( m_inDrawingTool )
1343 return 0;
1344
1346
1347 enum DIMENSION_STEPS
1348 {
1349 SET_ORIGIN = 0,
1350 SET_END,
1351 SET_HEIGHT,
1352 FINISHED
1353 };
1354
1355 TOOL_EVENT originalEvent = aEvent;
1356 PCB_DIMENSION_BASE* dimension = nullptr;
1357 BOARD_COMMIT commit( m_frame );
1360 PCB_SELECTION preview; // A VIEW_GROUP that serves as a preview for the new item(s)
1361 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
1362 int step = SET_ORIGIN;
1364
1365 m_view->Add( &preview );
1366
1367 auto cleanup =
1368 [&]()
1369 {
1370 m_controls->SetAutoPan( false );
1371 m_controls->CaptureCursor( false );
1373
1374 preview.Clear();
1375 m_view->Update( &preview );
1376
1377 delete dimension;
1378 dimension = nullptr;
1379 step = SET_ORIGIN;
1380 };
1381
1382 auto setCursor =
1383 [&]()
1384 {
1385 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
1386 };
1387
1389
1390 m_frame->PushTool( aEvent );
1391
1392 Activate();
1393 // Must be done after Activate() so that it gets set into the correct context
1394 m_controls->ShowCursor( true );
1396 // Set initial cursor
1397 setCursor();
1398
1400
1401 if( aEvent.HasPosition() )
1402 m_toolMgr->PrimeTool( aEvent.Position() );
1403
1404 // Main loop: keep receiving events
1405 while( TOOL_EVENT* evt = Wait() )
1406 {
1407 if( step > SET_ORIGIN )
1408 frame()->SetMsgPanel( dimension );
1409
1410 setCursor();
1411
1412 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1413 bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
1414 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1415
1416 if( step == SET_HEIGHT && t != PCB_DIM_ORTHOGONAL_T )
1417 {
1418 if( dimension->GetStart().x != dimension->GetEnd().x
1419 && dimension->GetStart().y != dimension->GetEnd().y )
1420 {
1421 // Not cardinal. Grid snapping doesn't make sense for height.
1422 grid.SetUseGrid( false );
1423 }
1424 }
1425
1426 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
1427 cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, nullptr, GRID_GRAPHICS ),
1429
1430 m_controls->ForceCursorPosition( true, cursorPos );
1431
1432 if( evt->IsCancelInteractive() || ( dimension && evt->IsAction( &ACTIONS::undo ) ) )
1433 {
1434 m_controls->SetAutoPan( false );
1435
1436 if( step != SET_ORIGIN ) // start from the beginning
1437 {
1438 cleanup();
1439 }
1440 else
1441 {
1442 m_frame->PopTool( aEvent );
1443 break;
1444 }
1445 }
1446 else if( evt->IsActivate() )
1447 {
1448 if( step != SET_ORIGIN )
1449 cleanup();
1450
1451 if( evt->IsPointEditor() )
1452 {
1453 // don't exit (the point editor runs in the background)
1454 }
1455 else if( evt->IsMoveTool() )
1456 {
1457 // leave ourselves on the stack so we come back after the move
1458 break;
1459 }
1460 else
1461 {
1462 m_frame->PopTool( aEvent );
1463 break;
1464 }
1465 }
1466 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
1467 {
1469 dimension->SetLineThickness( m_stroke.GetWidth() );
1470 m_view->Update( &preview );
1471 frame()->SetMsgPanel( dimension );
1472 }
1473 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
1474 {
1475 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
1476 {
1478 dimension->SetLineThickness( m_stroke.GetWidth() );
1479 m_view->Update( &preview );
1480 frame()->SetMsgPanel( dimension );
1481 }
1482 }
1483 else if( evt->IsClick( BUT_RIGHT ) )
1484 {
1485 if( !dimension )
1487
1488 m_menu->ShowContextMenu( selection() );
1489 }
1490 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1491 {
1492 switch( step )
1493 {
1494 case SET_ORIGIN:
1495 {
1497
1499
1500 // Init the new item attributes
1501 auto setMeasurementAttributes =
1502 [&]( PCB_DIMENSION_BASE* aDim )
1503 {
1504 aDim->SetUnitsMode( boardSettings.m_DimensionUnitsMode );
1505 aDim->SetUnitsFormat( boardSettings.m_DimensionUnitsFormat );
1506 aDim->SetPrecision( boardSettings.m_DimensionPrecision );
1507 aDim->SetSuppressZeroes( boardSettings.m_DimensionSuppressZeroes );
1508 aDim->SetTextPositionMode( boardSettings.m_DimensionTextPosition );
1509 aDim->SetKeepTextAligned( boardSettings.m_DimensionKeepTextAligned );
1510 };
1511
1512 if( originalEvent.IsAction( &PCB_ACTIONS::drawAlignedDimension ) )
1513 {
1514 dimension = new PCB_DIM_ALIGNED( m_frame->GetModel() );
1515 setMeasurementAttributes( dimension );
1516 }
1517 else if( originalEvent.IsAction( &PCB_ACTIONS::drawOrthogonalDimension ) )
1518 {
1519 dimension = new PCB_DIM_ORTHOGONAL( m_frame->GetModel() );
1520 setMeasurementAttributes( dimension );
1521 }
1522 else if( originalEvent.IsAction( &PCB_ACTIONS::drawCenterDimension ) )
1523 {
1524 dimension = new PCB_DIM_CENTER( m_frame->GetModel() );
1525 }
1526 else if( originalEvent.IsAction( &PCB_ACTIONS::drawRadialDimension ) )
1527 {
1528 dimension = new PCB_DIM_RADIAL( m_frame->GetModel() );
1529 setMeasurementAttributes( dimension );
1530 }
1531 else if( originalEvent.IsAction( &PCB_ACTIONS::drawLeader ) )
1532 {
1533 dimension = new PCB_DIM_LEADER( m_frame->GetModel() );
1534 dimension->SetTextPos( cursorPos );
1535 }
1536 else
1537 {
1538 wxFAIL_MSG( wxT( "Unhandled action in DRAWING_TOOL::DrawDimension" ) );
1539 }
1540
1541 t = dimension->Type();
1542
1543 dimension->SetLayer( layer );
1544 dimension->SetMirrored( IsBackLayer( layer ) );
1545 dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
1546 dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
1547 dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
1548 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1549 dimension->SetArrowLength( boardSettings.m_DimensionArrowLength );
1550 dimension->SetExtensionOffset( boardSettings.m_DimensionExtensionOffset );
1551 dimension->SetStart( cursorPos );
1552 dimension->SetEnd( cursorPos );
1553 dimension->Update();
1554
1555 if( !m_view->IsLayerVisible( layer ) )
1556 {
1557 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1559 }
1560
1561 preview.Add( dimension );
1562 frame()->SetMsgPanel( dimension );
1563
1564 m_controls->SetAutoPan( true );
1565 m_controls->CaptureCursor( true );
1566 break;
1567 }
1568
1569 case SET_END:
1570 // Dimensions that have origin and end in the same spot are not valid
1571 if( dimension->GetStart() == dimension->GetEnd() )
1572 {
1573 --step;
1574 break;
1575 }
1576
1577 if( t == PCB_DIM_CENTER_T || t == PCB_DIM_RADIAL_T || t == PCB_DIM_LEADER_T )
1578 {
1579 // No separate height step
1580 ++step;
1582 }
1583 else
1584 {
1585 break;
1586 }
1587
1588 case SET_HEIGHT:
1589 assert( dimension->GetStart() != dimension->GetEnd() );
1590 assert( dimension->GetLineThickness() > 0 );
1591
1592 preview.Remove( dimension );
1593
1594 commit.Add( dimension );
1595 commit.Push( _( "Draw Dimension" ) );
1596
1597 // Run the edit immediately to set the leader text
1598 if( t == PCB_DIM_LEADER_T )
1599 frame()->OnEditItemRequest( dimension );
1600
1602
1603 break;
1604 }
1605
1606 if( ++step >= FINISHED )
1607 {
1608 dimension = nullptr;
1609 step = SET_ORIGIN;
1610 m_controls->SetAutoPan( false );
1611 m_controls->CaptureCursor( false );
1612 }
1613 else if( evt->IsDblClick( BUT_LEFT ) )
1614 {
1616 }
1617 }
1618 else if( evt->IsMotion() )
1619 {
1620 switch( step )
1621 {
1622 case SET_END:
1623 dimension->SetEnd( cursorPos );
1624
1625 if( is45Limited || t == PCB_DIM_CENTER_T )
1626 constrainDimension( dimension );
1627
1628 if( t == PCB_DIM_ORTHOGONAL_T )
1629 {
1630 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1631
1632 BOX2I bounds( dimension->GetStart(),
1633 dimension->GetEnd() - dimension->GetStart() );
1634
1635 // Create a nice preview by measuring the longer dimension
1636 bool vert = bounds.GetWidth() < bounds.GetHeight();
1637
1638 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1640 }
1641 else if( t == PCB_DIM_RADIAL_T )
1642 {
1643 PCB_DIM_RADIAL* radialDim = static_cast<PCB_DIM_RADIAL*>( dimension );
1644 VECTOR2I textOffset( radialDim->GetArrowLength() * 10, 0 );
1645
1646 if( radialDim->GetEnd().x < radialDim->GetStart().x )
1647 textOffset = -textOffset;
1648
1649 radialDim->SetTextPos( radialDim->GetKnee() + textOffset );
1650 }
1651 else if( t == PCB_DIM_LEADER_T )
1652 {
1653 VECTOR2I textOffset( dimension->GetArrowLength() * 10, 0 );
1654
1655 if( dimension->GetEnd().x < dimension->GetStart().x )
1656 textOffset = -textOffset;
1657
1658 dimension->SetTextPos( dimension->GetEnd() + textOffset );
1659 }
1660
1661 dimension->Update();
1662 break;
1663
1664 case SET_HEIGHT:
1665 if( t == PCB_DIM_ALIGNED_T )
1666 {
1667 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dimension );
1668
1669 // Calculating the direction of travel perpendicular to the selected axis
1670 double angle = aligned->GetAngle() + ( M_PI / 2 );
1671
1672 VECTOR2I delta( (VECTOR2I) cursorPos - dimension->GetEnd() );
1673 double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
1674 aligned->SetHeight( height );
1675 aligned->Update();
1676 }
1677 else if( t == PCB_DIM_ORTHOGONAL_T )
1678 {
1679 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1680
1681 BOX2I bbox( dimension->GetStart(),
1682 dimension->GetEnd() - dimension->GetStart() );
1683 VECTOR2I direction( cursorPos - bbox.Centre() );
1684 bool vert;
1685
1686 // Only change the orientation when we move outside the bbox
1687 if( !bbox.Contains( cursorPos ) )
1688 {
1689 // If the dimension is horizontal or vertical, set correct orientation
1690 // otherwise, test if we're left/right of the bounding box or above/below it
1691 if( bbox.GetWidth() == 0 )
1692 vert = true;
1693 else if( bbox.GetHeight() == 0 )
1694 vert = false;
1695 else if( cursorPos.x > bbox.GetLeft() && cursorPos.x < bbox.GetRight() )
1696 vert = false;
1697 else if( cursorPos.y > bbox.GetTop() && cursorPos.y < bbox.GetBottom() )
1698 vert = true;
1699 else
1700 vert = std::abs( direction.y ) < std::abs( direction.x );
1701
1702 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1704 }
1705 else
1706 {
1707 vert = ortho->GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1708 }
1709
1710 VECTOR2I heightVector( cursorPos - dimension->GetStart() );
1711 ortho->SetHeight( vert ? heightVector.x : heightVector.y );
1712 ortho->Update();
1713 }
1714
1715 break;
1716 }
1717
1718 // Show a preview of the item
1719 m_view->Update( &preview );
1720 }
1721 else if( dimension && evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1722 {
1724
1725 if( !m_view->IsLayerVisible( layer ) )
1726 {
1727 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1729 }
1730
1731 dimension->SetLayer( layer );
1732 dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
1733 dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
1734 dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
1735 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1736 dimension->Update();
1737
1738 m_view->Update( &preview );
1739 frame()->SetMsgPanel( dimension );
1740 }
1741 else if( dimension && evt->IsAction( &PCB_ACTIONS::properties ) )
1742 {
1743 if( step == SET_END || step == SET_HEIGHT )
1744 {
1745 frame()->OnEditItemRequest( dimension );
1746 dimension->Update();
1747 frame()->SetMsgPanel( dimension );
1748 break;
1749 }
1750 else
1751 {
1752 wxBell();
1753 }
1754 }
1755 else if( dimension && evt->IsAction( &PCB_ACTIONS::changeDimensionArrows ) )
1756 {
1757 switch( dimension->Type() )
1758 {
1759 case PCB_DIM_ALIGNED_T:
1761 case PCB_DIM_RADIAL_T:
1762 if( dimension->GetArrowDirection() == DIM_ARROW_DIRECTION::INWARD )
1763 dimension->SetArrowDirection( DIM_ARROW_DIRECTION::OUTWARD );
1764 else
1765 dimension->SetArrowDirection( DIM_ARROW_DIRECTION::INWARD );
1766 break;
1767 default:
1768 // Other dimension types don't have arrows that can swap
1769 wxBell();
1770 }
1771
1772 m_view->Update( &preview );
1773 }
1774 else if( dimension && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
1775 || evt->IsAction( &ACTIONS::redo ) ) )
1776 {
1777 wxBell();
1778 }
1779 else
1780 {
1781 evt->SetPassEvent();
1782 }
1783 }
1784
1785 if( step != SET_ORIGIN )
1786 delete dimension;
1787
1788 m_controls->SetAutoPan( false );
1790 m_controls->CaptureCursor( false );
1791 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1792
1793 m_view->Remove( &preview );
1794
1795 if( selection().Empty() )
1797
1798 return 0;
1799}
1800
1801
1803{
1804 if( !m_frame->GetModel() )
1805 return 0;
1806
1807 if( m_inDrawingTool )
1808 return 0;
1809
1811
1813 int dlgResult = dlg.ShowModal();
1814
1815 std::list<std::unique_ptr<EDA_ITEM>>& list = dlg.GetImportedItems();
1816
1817 if( dlgResult != wxID_OK )
1818 return 0;
1819
1820 // Ensure the list is not empty:
1821 if( list.empty() )
1822 {
1823 wxMessageBox( _( "No graphic items found in file.") );
1824 return 0;
1825 }
1826
1828
1829 std::vector<BOARD_ITEM*> newItems; // all new items, including group
1830 std::vector<BOARD_ITEM*> selectedItems; // the group, or newItems if no group
1831 PCB_SELECTION preview;
1832 BOARD_COMMIT commit( m_frame );
1833 PCB_GROUP* group = nullptr;
1834 PICKED_ITEMS_LIST groupUndoList;
1835 PCB_LAYER_ID layer = F_Cu;
1836
1837 if( dlg.ShouldGroupItems() )
1838 {
1839 group = new PCB_GROUP( m_frame->GetModel() );
1840
1841 newItems.push_back( group );
1842 selectedItems.push_back( group );
1843 preview.Add( group );
1844 }
1845
1846 if( dlg.ShouldFixDiscontinuities() )
1847 {
1848 std::vector<PCB_SHAPE*> shapeList;
1849 std::vector<std::unique_ptr<PCB_SHAPE>> newShapes;
1850
1851 for( const std::unique_ptr<EDA_ITEM>& ptr : list )
1852 {
1853 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( ptr.get() ) )
1854 shapeList.push_back( shape );
1855 }
1856
1857 ConnectBoardShapes( shapeList, newShapes, dlg.GetTolerance() );
1858
1859 for( std::unique_ptr<PCB_SHAPE>& ptr : newShapes )
1860 {
1861 ptr->SetParent( m_frame->GetBoard() );
1862 list.push_back( std::move( ptr ) );
1863 }
1864 }
1865
1866 for( std::unique_ptr<EDA_ITEM>& ptr : list )
1867 {
1868 EDA_ITEM* eda_item = ptr.release();
1869
1870 if( eda_item->IsBOARD_ITEM() )
1871 {
1872 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
1873
1874 newItems.push_back( item );
1875
1876 if( group )
1877 {
1878 group->AddItem( item );
1879 groupUndoList.PushItem( ITEM_PICKER( nullptr, item, UNDO_REDO::REGROUP ) );
1880 }
1881 else
1882 {
1883 selectedItems.push_back( item );
1884 }
1885
1886 layer = item->GetLayer();
1887 }
1888
1889 preview.Add( eda_item );
1890 }
1891
1892 // Clear the current selection then select the drawings so that edit tools work on them
1894
1895 EDA_ITEMS selItems( selectedItems.begin(), selectedItems.end() );
1897
1898 if( !dlg.IsPlacementInteractive() )
1899 {
1900 for( BOARD_ITEM* item : newItems )
1901 commit.Add( item );
1902
1903 if( groupUndoList.GetCount() > 0 )
1904 commit.Stage( groupUndoList );
1905
1906 commit.Push( _( "Import Graphics" ) );
1907
1908 return 0;
1909 }
1910
1911 // Turn shapes on if they are off, so that the created object will be visible after completion
1913
1914 if( !m_view->IsLayerVisible( layer ) )
1915 {
1916 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1918 }
1919
1920 m_view->Add( &preview );
1921
1922 m_frame->PushTool( aEvent );
1923
1924 auto setCursor =
1925 [&]()
1926 {
1927 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1928 };
1929
1930 Activate();
1931 // Must be done after Activate() so that it gets set into the correct context
1932 m_controls->ShowCursor( true );
1934 // Set initial cursor
1935 setCursor();
1936
1937 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
1939
1940 // Now move the new items to the current cursor position:
1941 VECTOR2I cursorPos = m_controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
1942 VECTOR2I delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
1943
1944 for( BOARD_ITEM* item : selectedItems )
1945 item->Move( delta );
1946
1947 m_view->Update( &preview );
1948
1949 // Main loop: keep receiving events
1950 while( TOOL_EVENT* evt = Wait() )
1951 {
1952 setCursor();
1953
1954 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1955 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1956 cursorPos = GetClampedCoords(
1957 grid.BestSnapAnchor( m_controls->GetMousePosition(), { layer }, GRID_GRAPHICS ),
1959 m_controls->ForceCursorPosition( true, cursorPos );
1960
1961 if( evt->IsCancelInteractive() || evt->IsActivate() )
1962 {
1964
1965 if( group )
1966 {
1967 preview.Remove( group );
1968 group->RemoveAll();
1969 }
1970
1971 for( BOARD_ITEM* item : newItems )
1972 delete item;
1973
1974 break;
1975 }
1976 else if( evt->IsMotion() )
1977 {
1978 delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
1979
1980 for( BOARD_ITEM* item : selectedItems )
1981 item->Move( delta );
1982
1983 m_view->Update( &preview );
1984 }
1985 else if( evt->IsClick( BUT_RIGHT ) )
1986 {
1987 m_menu->ShowContextMenu( selection() );
1988 }
1989 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1990 {
1991 // Place the imported drawings
1992 for( BOARD_ITEM* item : newItems )
1993 commit.Add( item );
1994
1995 if( groupUndoList.GetCount() > 0 )
1996 commit.Stage( groupUndoList );
1997
1998 commit.Push( _( "Import Graphics" ) );
1999
2000 break; // This is a one-shot command, not a tool
2001 }
2002 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2003 {
2004 wxBell();
2005 }
2006 else
2007 {
2008 evt->SetPassEvent();
2009 }
2010 }
2011
2012 preview.Clear();
2013 m_view->Remove( &preview );
2014
2015 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2017
2018 m_frame->PopTool( aEvent );
2019
2020 return 0;
2021}
2022
2023
2025{
2026 // Make sense only in FP editor
2027 if( !m_isFootprintEditor )
2028 return 0;
2029
2030 if( !m_frame->GetModel() )
2031 return 0;
2032
2033 if( m_inDrawingTool )
2034 return 0;
2035
2037
2038 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
2040
2042
2043 m_frame->PushTool( aEvent );
2044
2045 auto setCursor =
2046 [&]()
2047 {
2048 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
2049 };
2050
2051 Activate();
2052 // Must be done after Activate() so that it gets set into the correct context
2053 m_controls->ShowCursor( true );
2054 m_controls->SetAutoPan( true );
2055 m_controls->CaptureCursor( false );
2057 // Set initial cursor
2058 setCursor();
2059
2060 while( TOOL_EVENT* evt = Wait() )
2061 {
2062 setCursor();
2063
2064 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2065 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2066 VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(),
2068 m_controls->ForceCursorPosition( true, cursorPos );
2069
2070 if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
2071 {
2073 BOARD_COMMIT commit( m_frame );
2074 commit.Modify( footprint );
2075
2076 // set the new relative internal local coordinates of footprint items
2077 VECTOR2I moveVector = footprint->GetPosition() - cursorPos;
2078 footprint->MoveAnchorPosition( moveVector );
2079
2080 commit.Push( _( "Move Footprint Anchor" ) );
2081
2082 // Usually, we do not need to change twice the anchor position,
2083 // so deselect the active tool
2084 m_frame->PopTool( aEvent );
2085 break;
2086 }
2087 else if( evt->IsClick( BUT_RIGHT ) )
2088 {
2089 m_menu->ShowContextMenu( selection() );
2090 }
2091 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2092 {
2093 m_frame->PopTool( aEvent );
2094 break;
2095 }
2096 else
2097 {
2098 evt->SetPassEvent();
2099 }
2100 }
2101
2102 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2104
2105 return 0;
2106}
2107
2108
2110{
2111#define TOGGLE( a ) a = !a
2112
2114
2115 if( frame()->IsType( FRAME_PCB_EDITOR ) )
2117 else
2119
2121
2122 return 0;
2123
2124#undef TOGGLE
2125}
2126
2127
2132 PCB_SHAPE* aGraphic )
2133{
2134 if( !aMgr.IsReset() )
2135 {
2136 aGraphic->SetStart( aMgr.GetOrigin() );
2137 aGraphic->SetEnd( aMgr.GetEnd() );
2138 }
2139}
2140
2141
2142bool DRAWING_TOOL::drawShape( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
2143 std::optional<VECTOR2D> aStartingPoint,
2144 std::stack<PCB_SHAPE*>* aCommittedGraphics )
2145{
2146 SHAPE_T shape = ( *aGraphic )->GetShape();
2147
2148 // Only three shapes are currently supported
2149 wxASSERT( shape == SHAPE_T::SEGMENT || shape == SHAPE_T::CIRCLE || shape == SHAPE_T::RECTANGLE );
2150
2152 EDA_UNITS userUnits = m_frame->GetUserUnits();
2154 PCB_SHAPE*& graphic = *aGraphic;
2155
2156 if( m_layer != m_frame->GetActiveLayer() )
2157 {
2160 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2161 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2162
2171 }
2172
2173 // Turn shapes on if they are off, so that the created object will be visible after completion
2175
2176 // geometric construction manager
2178
2179 // drawing assistant overlay
2180 // TODO: workaround because EDA_SHAPE_TYPE_T is not visible from commons.
2181 KIGFX::PREVIEW::GEOM_SHAPE geomShape( static_cast<KIGFX::PREVIEW::GEOM_SHAPE>( shape ) );
2182 KIGFX::PREVIEW::TWO_POINT_ASSISTANT twoPointAsst( twoPointMgr, pcbIUScale, userUnits, geomShape );
2183
2184 // Add a VIEW_GROUP that serves as a preview for the new item
2185 m_preview.Clear();
2186 m_view->Add( &m_preview );
2187 m_view->Add( &twoPointAsst );
2188
2189 bool started = false;
2190 bool cancelled = false;
2191 bool isLocalOriginSet = ( m_frame->GetScreen()->m_LocalOrigin != VECTOR2D( 0, 0 ) );
2192 VECTOR2I cursorPos = m_controls->GetMousePosition();
2193
2194 auto setCursor =
2195 [&]()
2196 {
2197 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2198 };
2199
2200 auto cleanup =
2201 [&]()
2202 {
2203 m_preview.Clear();
2204 m_view->Update( &m_preview );
2205 delete graphic;
2206 graphic = nullptr;
2207
2208 if( !isLocalOriginSet )
2210 };
2211
2212 m_controls->ShowCursor( true );
2214 // Set initial cursor
2215 setCursor();
2216
2218
2219 if( aStartingPoint )
2220 m_toolMgr->PrimeTool( *aStartingPoint );
2221
2222 // Main loop: keep receiving events
2223 while( TOOL_EVENT* evt = Wait() )
2224 {
2225 setCursor();
2226
2227 if( started )
2228 m_frame->SetMsgPanel( graphic );
2229
2230 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2231 bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
2232
2233 // Rectangular shapes never get 45-degree snapping
2234 if( shape == SHAPE_T::RECTANGLE )
2235 is45Limited = false;
2236
2237 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2238 cursorPos = GetClampedCoords(
2239 grid.BestSnapAnchor( m_controls->GetMousePosition(), { m_layer }, GRID_GRAPHICS ),
2241 m_controls->ForceCursorPosition( true, cursorPos );
2242
2243 if( evt->IsCancelInteractive() || ( started && evt->IsAction( &ACTIONS::undo ) ) )
2244 {
2245 cleanup();
2246
2247 if( !started )
2248 {
2249 // We've handled the cancel event. Don't cancel other tools
2250 evt->SetPassEvent( false );
2251 m_frame->PopTool( aTool );
2252 cancelled = true;
2253 }
2254
2255 break;
2256 }
2257 else if( evt->IsActivate() )
2258 {
2259 if( evt->IsPointEditor() )
2260 {
2261 // don't exit (the point editor runs in the background)
2262 }
2263 else if( evt->IsMoveTool() )
2264 {
2265 cleanup();
2266 // leave ourselves on the stack so we come back after the move
2267 cancelled = true;
2268 break;
2269 }
2270 else
2271 {
2272 cleanup();
2273 m_frame->PopTool( aTool );
2274 cancelled = true;
2275 break;
2276 }
2277 }
2278 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2279 {
2280 if( m_layer != m_frame->GetActiveLayer() )
2281 {
2284 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2285 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2286
2295 }
2296
2297 if( graphic )
2298 {
2299 if( !m_view->IsLayerVisible( m_layer ) )
2300 {
2303 }
2304
2305 graphic->SetLayer( m_layer );
2306 graphic->SetStroke( m_stroke );
2307
2308 if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
2309 pcb_textbox->SetAttributes( m_textAttrs );
2310
2311 m_view->Update( &m_preview );
2312 frame()->SetMsgPanel( graphic );
2313 }
2314 else
2315 {
2316 evt->SetPassEvent();
2317 }
2318 }
2319 else if( evt->IsClick( BUT_RIGHT ) )
2320 {
2321 if( !graphic )
2323
2324 m_menu->ShowContextMenu( selection() );
2325 }
2326 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
2327 {
2328 if( !graphic )
2329 break;
2330
2331 if( !started )
2332 {
2334
2335 if( aStartingPoint )
2336 {
2337 cursorPos = *aStartingPoint;
2338 aStartingPoint = std::nullopt;
2339 }
2340
2341 // Init the new item attributes
2342 if( graphic ) // always true, but Coverity can't seem to figure that out
2343 {
2344 graphic->SetShape( static_cast<SHAPE_T>( shape ) );
2345 graphic->SetFilled( false );
2346 graphic->SetStroke( m_stroke );
2347 graphic->SetLayer( m_layer );
2348 }
2349
2350 if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
2351 pcb_textbox->SetAttributes( m_textAttrs );
2352
2353 grid.SetSkipPoint( cursorPos );
2354
2355 twoPointMgr.SetOrigin( cursorPos );
2356 twoPointMgr.SetEnd( cursorPos );
2357
2358 if( !isLocalOriginSet )
2359 m_frame->GetScreen()->m_LocalOrigin = cursorPos;
2360
2361 m_preview.Add( graphic );
2362 frame()->SetMsgPanel( graphic );
2363 m_controls->SetAutoPan( true );
2364 m_controls->CaptureCursor( true );
2365
2366 if( !m_view->IsLayerVisible( m_layer ) )
2367 {
2370 }
2371
2372 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
2373
2374 started = true;
2375 }
2376 else
2377 {
2378 PCB_SHAPE* snapItem = dynamic_cast<PCB_SHAPE*>( grid.GetSnapped() );
2379
2380 if( shape == SHAPE_T::SEGMENT && snapItem && graphic->GetLength() > 0 )
2381 {
2382 // User has clicked on the end of an existing segment, closing a path
2383 BOARD_COMMIT commit( m_frame );
2384
2385 commit.Add( graphic );
2386 commit.Push( _( "Draw Line" ) );
2388
2389 graphic = nullptr;
2390 }
2391 else if( twoPointMgr.IsEmpty() || evt->IsDblClick( BUT_LEFT ) )
2392 {
2393 // User has clicked twice in the same spot, meaning we're finished
2394 delete graphic;
2395 graphic = nullptr;
2396 }
2397
2398 m_preview.Clear();
2399 twoPointMgr.Reset();
2400 break;
2401 }
2402
2403 twoPointMgr.SetEnd( GetClampedCoords( cursorPos ) );
2404 }
2405 else if( evt->IsMotion() )
2406 {
2407 VECTOR2I clampedCursorPos = cursorPos;
2408
2409 if( shape == SHAPE_T::CIRCLE || shape == SHAPE_T::ARC )
2410 clampedCursorPos = getClampedRadiusEnd( twoPointMgr.GetOrigin(), cursorPos );
2411 else
2412 clampedCursorPos = getClampedDifferenceEnd( twoPointMgr.GetOrigin(), cursorPos );
2413
2414 // 45 degree lines
2415 if( started && is45Limited )
2416 {
2417 const VECTOR2I lineVector( clampedCursorPos - VECTOR2I( twoPointMgr.GetOrigin() ) );
2418
2419 // get a restricted 45/H/V line from the last fixed point to the cursor
2420 VECTOR2I newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECTANGLE ) );
2421 m_controls->ForceCursorPosition( true, VECTOR2I( twoPointMgr.GetEnd() ) );
2422 twoPointMgr.SetEnd( twoPointMgr.GetOrigin() + newEnd );
2423 twoPointMgr.SetAngleSnap( true );
2424 }
2425 else
2426 {
2427 twoPointMgr.SetEnd( clampedCursorPos );
2428 twoPointMgr.SetAngleSnap( false );
2429 }
2430
2431 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
2432 m_view->Update( &m_preview );
2433 m_view->Update( &twoPointAsst );
2434 }
2435 else if( started && ( evt->IsAction( &PCB_ACTIONS::doDelete )
2436 || evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) ) )
2437 {
2438 if( aCommittedGraphics && !aCommittedGraphics->empty() )
2439 {
2440 twoPointMgr.SetOrigin( aCommittedGraphics->top()->GetStart() );
2441 twoPointMgr.SetEnd( aCommittedGraphics->top()->GetEnd() );
2442 aCommittedGraphics->pop();
2443
2444 getViewControls()->WarpMouseCursor( twoPointMgr.GetEnd(), true );
2445
2447 {
2450 delete undo;
2451 }
2452
2453 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
2454 m_view->Update( &m_preview );
2455 m_view->Update( &twoPointAsst );
2456 }
2457 else
2458 {
2459 cleanup();
2460 break;
2461 }
2462 }
2463 else if( graphic && evt->IsAction( &PCB_ACTIONS::incWidth ) )
2464 {
2466 graphic->SetStroke( m_stroke );
2467 m_view->Update( &m_preview );
2468 frame()->SetMsgPanel( graphic );
2469 }
2470 else if( graphic && evt->IsAction( &PCB_ACTIONS::decWidth ) )
2471 {
2472 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
2473 {
2475 graphic->SetStroke( m_stroke );
2476 m_view->Update( &m_preview );
2477 frame()->SetMsgPanel( graphic );
2478 }
2479 }
2480 else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
2481 {
2482 frame()->OnEditItemRequest( graphic );
2483 m_view->Update( &m_preview );
2484 frame()->SetMsgPanel( graphic );
2485 }
2486 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
2487 || evt->IsAction( &ACTIONS::redo ) ) )
2488 {
2489 wxBell();
2490 }
2491 else if( evt->IsAction( &ACTIONS::resetLocalCoords ) )
2492 {
2493 isLocalOriginSet = true;
2494 evt->SetPassEvent();
2495 }
2496 else if( evt->IsAction( &ACTIONS::updateUnits ) )
2497 {
2498 if( frame()->GetUserUnits() != userUnits )
2499 {
2500 userUnits = frame()->GetUserUnits();
2501 twoPointAsst.SetUnits( userUnits );
2502 m_view->Update( &twoPointAsst );
2503 }
2504 evt->SetPassEvent();
2505 }
2506 else
2507 {
2508 evt->SetPassEvent();
2509 }
2510 }
2511
2512 if( !isLocalOriginSet ) // reset the relative coordinate if it was not set before
2514
2515 m_view->Remove( &twoPointAsst );
2516 m_view->Remove( &m_preview );
2517
2518 if( selection().Empty() )
2520
2521 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2522 m_controls->SetAutoPan( false );
2523 m_controls->CaptureCursor( false );
2525
2526 return !cancelled;
2527}
2528
2529
2534 PCB_SHAPE& aArc )
2535{
2536 VECTOR2I vec = aMgr.GetOrigin();
2537
2538 aArc.SetCenter( vec );
2539
2540 if( aMgr.GetSubtended() < ANGLE_0 )
2541 {
2542 vec = aMgr.GetStartRadiusEnd();
2543 aArc.SetStart( vec );
2544 vec = aMgr.GetEndRadiusEnd();
2545 aArc.SetEnd( vec );
2546 }
2547 else
2548 {
2549 vec = aMgr.GetEndRadiusEnd();
2550 aArc.SetStart( vec );
2551 vec = aMgr.GetStartRadiusEnd();
2552 aArc.SetEnd( vec );
2553 }
2554}
2555
2556
2557bool DRAWING_TOOL::drawArc( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
2558 std::optional<VECTOR2D> aStartingPoint )
2559{
2560 wxCHECK( aGraphic, false );
2561
2562 PCB_SHAPE*& graphic = *aGraphic;
2563
2564 wxCHECK( graphic, false );
2565
2566 if( m_layer != m_frame->GetActiveLayer() )
2567 {
2570 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2571 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2572 }
2573
2574 // Turn shapes on if they are off, so that the created object will be visible after completion
2576
2577 // Arc geometric construction manager
2579
2580 // Arc drawing assistant overlay
2582
2583 // Add a VIEW_GROUP that serves as a preview for the new item
2584 PCB_SELECTION preview;
2585 m_view->Add( &preview );
2586 m_view->Add( &arcAsst );
2588
2589 auto setCursor =
2590 [&]()
2591 {
2592 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2593 };
2594
2595 auto cleanup =
2596 [&] ()
2597 {
2598 preview.Clear();
2599 delete *aGraphic;
2600 *aGraphic = nullptr;
2601 };
2602
2603 m_controls->ShowCursor( true );
2605 // Set initial cursor
2606 setCursor();
2607
2608 bool started = false;
2609 bool cancelled = false;
2610
2612
2613 if( aStartingPoint )
2614 m_toolMgr->PrimeTool( *aStartingPoint );
2615
2616 // Main loop: keep receiving events
2617 while( TOOL_EVENT* evt = Wait() )
2618 {
2619 if( started )
2620 m_frame->SetMsgPanel( graphic );
2621
2622 setCursor();
2623
2624 graphic->SetLayer( m_layer );
2625
2626 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2627 bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
2628 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2629 VECTOR2I cursorPos = GetClampedCoords(
2630 grid.BestSnapAnchor( m_controls->GetMousePosition(), graphic, GRID_GRAPHICS ),
2632 m_controls->ForceCursorPosition( true, cursorPos );
2633
2634 if( evt->IsCancelInteractive() || ( started && evt->IsAction( &ACTIONS::undo ) ) )
2635 {
2636 cleanup();
2637
2638 if( !started )
2639 {
2640 // We've handled the cancel event. Don't cancel other tools
2641 evt->SetPassEvent( false );
2642 m_frame->PopTool( aTool );
2643 cancelled = true;
2644 }
2645
2646 break;
2647 }
2648 else if( evt->IsActivate() )
2649 {
2650 if( evt->IsPointEditor() )
2651 {
2652 // don't exit (the point editor runs in the background)
2653 }
2654 else if( evt->IsMoveTool() )
2655 {
2656 cleanup();
2657 // leave ourselves on the stack so we come back after the move
2658 cancelled = true;
2659 break;
2660 }
2661 else
2662 {
2663 cleanup();
2664 m_frame->PopTool( aTool );
2665 cancelled = true;
2666 break;
2667 }
2668 }
2669 else if( evt->IsClick( BUT_LEFT ) )
2670 {
2671 if( !started )
2672 {
2674
2675 m_controls->SetAutoPan( true );
2676 m_controls->CaptureCursor( true );
2677
2678 // Init the new item attributes
2679 // (non-geometric, those are handled by the manager)
2680 graphic->SetShape( SHAPE_T::ARC );
2681 graphic->SetStroke( m_stroke );
2682
2683 if( !m_view->IsLayerVisible( m_layer ) )
2684 {
2687 }
2688
2689 preview.Add( graphic );
2690 frame()->SetMsgPanel( graphic );
2691 started = true;
2692 }
2693
2694 arcManager.AddPoint( cursorPos, true );
2695 }
2696 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
2697 {
2698 arcManager.RemoveLastPoint();
2699 }
2700 else if( evt->IsMotion() )
2701 {
2702 // set angle snap
2703 arcManager.SetAngleSnap( is45Limited );
2704
2705 // update, but don't step the manager state
2706 arcManager.AddPoint( cursorPos, false );
2707 }
2708 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2709 {
2710 if( m_layer != m_frame->GetActiveLayer() )
2711 {
2714 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2715 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2716 }
2717
2718 if( graphic )
2719 {
2720 if( !m_view->IsLayerVisible( m_layer ) )
2721 {
2724 }
2725
2726 graphic->SetLayer( m_layer );
2727 graphic->SetStroke( m_stroke );
2728 m_view->Update( &preview );
2729 frame()->SetMsgPanel( graphic );
2730 }
2731 else
2732 {
2733 evt->SetPassEvent();
2734 }
2735 }
2736 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
2737 {
2739 {
2740 graphic->SetArcAngleAndEnd( ANGLE_90 );
2741 frame()->OnEditItemRequest( graphic );
2742 m_view->Update( &preview );
2743 frame()->SetMsgPanel( graphic );
2744 break;
2745 }
2746 // Don't show the edit panel if we can't represent the arc with it
2747 else if( ( arcManager.GetStep() == KIGFX::PREVIEW::ARC_GEOM_MANAGER::SET_ANGLE )
2748 && ( arcManager.GetStartRadiusEnd() != arcManager.GetEndRadiusEnd() ) )
2749 {
2750 frame()->OnEditItemRequest( graphic );
2751 m_view->Update( &preview );
2752 frame()->SetMsgPanel( graphic );
2753 break;
2754 }
2755 else
2756 {
2757 evt->SetPassEvent();
2758 }
2759 }
2760 else if( evt->IsClick( BUT_RIGHT ) )
2761 {
2762 if( !graphic )
2764
2765 m_menu->ShowContextMenu( selection() );
2766 }
2767 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
2768 {
2770
2771 if( graphic )
2772 {
2773 graphic->SetStroke( m_stroke );
2774 m_view->Update( &preview );
2775 frame()->SetMsgPanel( graphic );
2776 }
2777 }
2778 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
2779 {
2780 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
2781 {
2783
2784 if( graphic )
2785 {
2786 graphic->SetStroke( m_stroke );
2787 m_view->Update( &preview );
2788 frame()->SetMsgPanel( graphic );
2789 }
2790 }
2791 }
2792 else if( evt->IsAction( &PCB_ACTIONS::arcPosture ) )
2793 {
2794 arcManager.ToggleClockwise();
2795 }
2796 else if( evt->IsAction( &ACTIONS::updateUnits ) )
2797 {
2798 arcAsst.SetUnits( frame()->GetUserUnits() );
2799 m_view->Update( &arcAsst );
2800 evt->SetPassEvent();
2801 }
2802 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
2803 || evt->IsAction( &ACTIONS::redo ) ) )
2804 {
2805 wxBell();
2806 }
2807 else
2808 {
2809 evt->SetPassEvent();
2810 }
2811
2812 if( arcManager.IsComplete() )
2813 {
2814 break;
2815 }
2816 else if( arcManager.HasGeometryChanged() )
2817 {
2818 updateArcFromConstructionMgr( arcManager, *graphic );
2819 m_view->Update( &preview );
2820 m_view->Update( &arcAsst );
2821
2822 if( started )
2823 frame()->SetMsgPanel( graphic );
2824 else
2825 frame()->SetMsgPanel( board() );
2826 }
2827 }
2828
2829 preview.Remove( graphic );
2830 m_view->Remove( &arcAsst );
2831 m_view->Remove( &preview );
2832
2833 if( selection().Empty() )
2835
2836 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2837 m_controls->SetAutoPan( false );
2838 m_controls->CaptureCursor( false );
2840
2841 return !cancelled;
2842}
2843
2844
2849 PCB_SHAPE& aBezier )
2850{
2851 VECTOR2I vec = aMgr.GetStart();
2852
2853 aBezier.SetStart( vec );
2854 aBezier.SetBezierC1( aMgr.GetControlC1() );
2855 aBezier.SetEnd( aMgr.GetEnd() );
2856 aBezier.SetBezierC2( aMgr.GetControlC2() );
2857
2858 // Need this for the length preview to work
2860}
2861
2862
2863std::unique_ptr<PCB_SHAPE> DRAWING_TOOL::drawOneBezier( const TOOL_EVENT& aTool,
2864 const OPT_VECTOR2I& aStartingPoint,
2865 const OPT_VECTOR2I& aStartingControl1Point,
2866 DRAW_ONE_RESULT& aResult )
2867{
2868 std::unique_ptr<PCB_SHAPE> bezier = std::make_unique<PCB_SHAPE>( m_frame->GetModel() );
2869 bezier->SetShape( SHAPE_T::BEZIER );
2870 bezier->SetFlags( IS_NEW );
2871
2872 if( m_layer != m_frame->GetActiveLayer() )
2873 {
2876 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2877 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2878 }
2879
2880 // Turn shapes on if they are off, so that the created object will be visible after completion
2882
2883 // Arc geometric construction manager
2885
2886 // Arc drawing assistant overlay
2887 KIGFX::PREVIEW::BEZIER_ASSISTANT bezierAsst( bezierManager, pcbIUScale,
2888 m_frame->GetUserUnits() );
2889
2890 // Add a VIEW_GROUP that serves as a preview for the new item
2891 PCB_SELECTION preview;
2892 m_view->Add( &preview );
2893 m_view->Add( &bezierAsst );
2895
2896 const auto setCursor = [&]()
2897 {
2898 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2899 };
2900
2901 const auto resetProgress = [&]()
2902 {
2903 preview.Clear();
2904 bezier.reset();
2905 };
2906
2907 m_controls->ShowCursor( true );
2909 // Set initial cursor
2910 setCursor();
2911
2912 const auto started = [&]()
2913 {
2915 };
2916
2917 aResult = DRAW_ONE_RESULT::ACCEPTED;
2918 bool priming = false;
2919
2921
2922 // Load in one or two points if they were passed in
2923 if( aStartingPoint )
2924 {
2925 priming = true;
2926
2927 if( aStartingControl1Point )
2928 {
2929 bezierManager.AddPoint( *aStartingPoint, true );
2930 bezierManager.AddPoint( *aStartingControl1Point, true );
2931 m_toolMgr->PrimeTool( *aStartingControl1Point );
2932 }
2933 else
2934 {
2935 bezierManager.AddPoint( *aStartingPoint, true );
2936 m_toolMgr->PrimeTool( *aStartingPoint );
2937 }
2938 }
2939
2940 // Main loop: keep receiving events
2941 while( TOOL_EVENT* evt = Wait() )
2942 {
2943 if( started() )
2944 m_frame->SetMsgPanel( bezier.get() );
2945
2946 setCursor();
2947
2948 // Init the new item attributes
2949 // (non-geometric, those are handled by the manager)
2950 bezier->SetShape( SHAPE_T::BEZIER );
2951 bezier->SetStroke( m_stroke );
2952 bezier->SetLayer( m_layer );
2953
2954 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2955 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2956 VECTOR2I cursorPos = GetClampedCoords(
2957 grid.BestSnapAnchor( m_controls->GetMousePosition(), bezier.get(), GRID_GRAPHICS ),
2959 m_controls->ForceCursorPosition( true, cursorPos );
2960
2961 if( evt->IsCancelInteractive() || ( started() && evt->IsAction( &ACTIONS::undo ) ) )
2962 {
2963 resetProgress();
2964
2965 if( !started() )
2966 {
2967 // We've handled the cancel event. Don't cancel other tools
2968 evt->SetPassEvent( false );
2969 m_frame->PopTool( aTool );
2971 }
2972 else
2973 {
2974 // We're not cancelling, but we're also not returning a finished bezier
2975 // So we'll be called again.
2976 aResult = DRAW_ONE_RESULT::RESET;
2977 }
2978
2979 break;
2980 }
2981 else if( evt->IsActivate() )
2982 {
2983 if( evt->IsPointEditor() )
2984 {
2985 // don't exit (the point editor runs in the background)
2986 }
2987 else if( evt->IsMoveTool() )
2988 {
2989 resetProgress();
2990 // leave ourselves on the stack so we come back after the move
2992 break;
2993 }
2994 else
2995 {
2996 resetProgress();
2997 m_frame->PopTool( aTool );
2999 break;
3000 }
3001 }
3002 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
3003 {
3004 if( !started() )
3005 {
3007
3008 m_controls->SetAutoPan( true );
3009 m_controls->CaptureCursor( true );
3010
3011 if( !m_view->IsLayerVisible( m_layer ) )
3012 {
3015 }
3016
3017 frame()->SetMsgPanel( bezier.get() );
3018 }
3019
3020 if( !priming )
3021 bezierManager.AddPoint( cursorPos, true );
3022 else
3023 priming = false;
3024
3025 const bool doubleClick = evt->IsDblClick( BUT_LEFT );
3026
3027 if( doubleClick )
3028 {
3029 // Use the current point for all remaining points
3030 while( bezierManager.GetStep() < KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END )
3031 {
3032 bezierManager.AddPoint( cursorPos, true );
3033 }
3034 }
3035
3037 {
3038 preview.Add( bezier.get() );
3039 }
3040
3041 // Return to the caller for a reset
3042 if( doubleClick )
3043 {
3044 // Don't chain to this one
3046 break;
3047 }
3048 }
3049 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
3050 {
3051 bezierManager.RemoveLastPoint();
3052
3054 {
3055 preview.Remove( bezier.get() );
3056 }
3057 }
3058 else if( evt->IsMotion() )
3059 {
3060 // set angle snap
3061 // bezierManager.SetAngleSnap( Is45Limited() );
3062
3063 // update, but don't step the manager state
3064 bezierManager.AddPoint( cursorPos, false );
3065 }
3066 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3067 {
3068 if( m_layer != m_frame->GetActiveLayer() )
3069 {
3072 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
3073 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
3074 }
3075
3076 if( bezier )
3077 {
3078 if( !m_view->IsLayerVisible( m_layer ) )
3079 {
3082 }
3083
3084 bezier->SetLayer( m_layer );
3085 bezier->SetStroke( m_stroke );
3086 m_view->Update( &preview );
3087 frame()->SetMsgPanel( bezier.get() );
3088 }
3089 else
3090 {
3091 evt->SetPassEvent();
3092 }
3093 }
3094 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
3095 {
3096 // Don't show the edit panel if we can't represent the arc with it
3097 if( ( bezierManager.GetStep() >= KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END ) )
3098 {
3099 frame()->OnEditItemRequest( bezier.get() );
3100 m_view->Update( &preview );
3101 frame()->SetMsgPanel( bezier.get() );
3102 break;
3103 }
3104 else
3105 {
3106 evt->SetPassEvent();
3107 }
3108 }
3109 else if( evt->IsClick( BUT_RIGHT ) )
3110 {
3111 if( !bezier )
3113
3114 m_menu->ShowContextMenu( selection() );
3115 }
3116 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
3117 {
3119
3120 if( bezier )
3121 {
3122 bezier->SetStroke( m_stroke );
3123 m_view->Update( &preview );
3124 frame()->SetMsgPanel( bezier.get() );
3125 }
3126 }
3127 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
3128 {
3129 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
3130 {
3132
3133 if( bezier )
3134 {
3135 bezier->SetStroke( m_stroke );
3136 m_view->Update( &preview );
3137 frame()->SetMsgPanel( bezier.get() );
3138 }
3139 }
3140 }
3141 else if( evt->IsAction( &ACTIONS::updateUnits ) )
3142 {
3143 bezierAsst.SetUnits( frame()->GetUserUnits() );
3144 m_view->Update( &bezierAsst );
3145 evt->SetPassEvent();
3146 }
3147 else if( started()
3149 || evt->IsAction( &ACTIONS::redo ) ) )
3150 {
3151 wxBell();
3152 }
3153 else
3154 {
3155 evt->SetPassEvent();
3156 }
3157
3158 if( bezierManager.IsComplete() )
3159 {
3160 break;
3161 }
3162 else if( bezierManager.HasGeometryChanged() )
3163 {
3164 updateBezierFromConstructionMgr( bezierManager, *bezier );
3165 m_view->Update( &preview );
3166 m_view->Update( &bezierAsst );
3167
3168 // Once we are receiving end points, we can show the bezier in the preview
3170 frame()->SetMsgPanel( bezier.get() );
3171 else
3172 frame()->SetMsgPanel( board() );
3173 }
3174 }
3175
3176 preview.Remove( bezier.get() );
3177 m_view->Remove( &bezierAsst );
3178 m_view->Remove( &preview );
3179
3180 if( selection().Empty() )
3182
3183 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3184 m_controls->SetAutoPan( false );
3185 m_controls->CaptureCursor( false );
3187
3188 return bezier;
3189};
3190
3192{
3193 bool clearSelection = false;
3194 *aZone = nullptr;
3195
3196 // not an action that needs a source zone
3197 if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON )
3198 return true;
3199
3201 const PCB_SELECTION& selection = selTool->GetSelection();
3202
3203 if( selection.Empty() )
3204 {
3205 clearSelection = true;
3207 }
3208
3209 // we want a single zone
3210 if( selection.Size() == 1 && selection[0]->Type() == PCB_ZONE_T )
3211 *aZone = static_cast<ZONE*>( selection[0] );
3212
3213 // expected a zone, but didn't get one
3214 if( !*aZone )
3215 {
3216 if( clearSelection )
3218
3219 return false;
3220 }
3221
3222 return true;
3223}
3224
3226{
3228 return 0;
3229
3230 ZONE_MODE zoneMode = aEvent.Parameter<ZONE_MODE>();
3231 MODE drawMode = MODE::ZONE;
3232
3233 if( aEvent.IsAction( &PCB_ACTIONS::drawRuleArea ) )
3234 drawMode = MODE::KEEPOUT;
3235
3236 if( aEvent.IsAction( &PCB_ACTIONS::drawPolygon ) )
3237 drawMode = MODE::GRAPHIC_POLYGON;
3238
3239 SCOPED_DRAW_MODE scopedDrawMode( m_mode, drawMode );
3240
3241 // get a source zone, if we need one. We need it for:
3242 // ZONE_MODE::CUTOUT (adding a hole to the source zone)
3243 // ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
3244 ZONE* sourceZone = nullptr;
3245
3246 if( !getSourceZoneForAction( zoneMode, &sourceZone ) )
3247 return 0;
3248
3249 // Turn zones on if they are off, so that the created object will be visible after completion
3251
3253
3254 params.m_keepout = drawMode == MODE::KEEPOUT;
3255 params.m_mode = zoneMode;
3256 params.m_sourceZone = sourceZone;
3257 params.m_layer = m_frame->GetActiveLayer();
3258
3259 if( zoneMode == ZONE_MODE::SIMILAR && !sourceZone->IsOnLayer( params.m_layer ) )
3260 params.m_layer = sourceZone->GetFirstLayer();
3261
3262 ZONE_CREATE_HELPER zoneTool( *this, params );
3263 // the geometry manager which handles the zone geometry, and hands the calculated points
3264 // over to the zone creator tool
3265 POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
3266 bool started = false;
3268
3269 m_frame->PushTool( aEvent );
3270
3271 auto setCursor =
3272 [&]()
3273 {
3274 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
3275 };
3276
3277 auto cleanup =
3278 [&] ()
3279 {
3280 polyGeomMgr.Reset();
3281 started = false;
3282 grid.ClearSkipPoint();
3283 m_controls->SetAutoPan( false );
3284 m_controls->CaptureCursor( false );
3285 };
3286
3287 Activate();
3288 // Must be done after Activate() so that it gets set into the correct context
3289 m_controls->ShowCursor( true );
3291 // Set initial cursor
3292 setCursor();
3293
3294 if( aEvent.HasPosition() )
3295 m_toolMgr->PrimeTool( aEvent.Position() );
3296
3297 // Main loop: keep receiving events
3298 while( TOOL_EVENT* evt = Wait() )
3299 {
3300 setCursor();
3301
3302 LSET layers( { m_frame->GetActiveLayer() } );
3303 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
3304 bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
3305 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
3306
3307 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
3308 cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, layers, GRID_GRAPHICS ),
3310
3311 m_controls->ForceCursorPosition( true, cursorPos );
3312
3315
3316 if( evt->IsCancelInteractive() )
3317 {
3318 if( started )
3319 {
3320 cleanup();
3321 }
3322 else
3323 {
3324 m_frame->PopTool( aEvent );
3325
3326 // We've handled the cancel event. Don't cancel other tools
3327 evt->SetPassEvent( false );
3328 break;
3329 }
3330 }
3331 else if( evt->IsActivate() )
3332 {
3333 if( started )
3334 cleanup();
3335
3336 if( evt->IsPointEditor() )
3337 {
3338 // don't exit (the point editor runs in the background)
3339 }
3340 else if( evt->IsMoveTool() )
3341 {
3342 // leave ourselves on the stack so we come back after the move
3343 break;
3344 }
3345 else
3346 {
3347 m_frame->PopTool( aEvent );
3348 break;
3349 }
3350 }
3351 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3352 {
3353 if( zoneMode != ZONE_MODE::SIMILAR )
3354 params.m_layer = frame()->GetActiveLayer();
3355
3356 if( !m_view->IsLayerVisible( params.m_layer ) )
3357 {
3360 }
3361 }
3362 else if( evt->IsClick( BUT_RIGHT ) )
3363 {
3364 if( !started )
3366
3367 m_menu->ShowContextMenu( selection() );
3368 }
3369 // events that lock in nodes
3370 else if( evt->IsClick( BUT_LEFT )
3371 || evt->IsDblClick( BUT_LEFT )
3372 || evt->IsAction( &PCB_ACTIONS::closeOutline ) )
3373 {
3374 // Check if it is double click / closing line (so we have to finish the zone)
3375 const bool endPolygon = evt->IsDblClick( BUT_LEFT )
3376 || evt->IsAction( &PCB_ACTIONS::closeOutline )
3377 || polyGeomMgr.NewPointClosesOutline( cursorPos );
3378
3379 if( endPolygon )
3380 {
3381 polyGeomMgr.SetFinished();
3382 polyGeomMgr.Reset();
3383
3384 started = false;
3385 m_controls->SetAutoPan( false );
3386 m_controls->CaptureCursor( false );
3387 }
3388 // adding a corner
3389 else if( polyGeomMgr.AddPoint( cursorPos ) )
3390 {
3391 if( !started )
3392 {
3393 started = true;
3394
3395 m_controls->SetAutoPan( true );
3396 m_controls->CaptureCursor( true );
3397
3398 if( !m_view->IsLayerVisible( params.m_layer ) )
3399 {
3402 }
3403 }
3404 }
3405 }
3406 else if( started && ( evt->IsAction( &PCB_ACTIONS::deleteLastPoint )
3407 || evt->IsAction( &ACTIONS::doDelete )
3408 || evt->IsAction( &ACTIONS::undo ) ) )
3409 {
3410 if( std::optional<VECTOR2I> last = polyGeomMgr.DeleteLastCorner() )
3411 {
3412 cursorPos = last.value();
3413 getViewControls()->WarpMouseCursor( cursorPos, true );
3414 m_controls->ForceCursorPosition( true, cursorPos );
3415 polyGeomMgr.SetCursorPosition( cursorPos );
3416 }
3417 else
3418 {
3419 cleanup();
3420 }
3421 }
3422 else if( started && ( evt->IsMotion()
3423 || evt->IsDrag( BUT_LEFT ) ) )
3424 {
3425 polyGeomMgr.SetCursorPosition( cursorPos );
3426 }
3427 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
3428 || evt->IsAction( &ACTIONS::redo ) ) )
3429 {
3430 wxBell();
3431 }
3432 else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
3433 {
3434 frame()->OnEditItemRequest( zoneTool.GetZone() );
3435 zoneTool.OnGeometryChange( polyGeomMgr );
3436 frame()->SetMsgPanel( zoneTool.GetZone() );
3437 }
3438 /*else if( evt->IsAction( &ACTIONS::updateUnits ) )
3439 {
3440 // If we ever have an assistant here that reports dimensions, we'll want to
3441 // update its units here....
3442 // zoneAsst.SetUnits( frame()->GetUserUnits() );
3443 // m_view->Update( &zoneAsst );
3444 evt->SetPassEvent();
3445 }*/
3446 else
3447 {
3448 evt->SetPassEvent();
3449 }
3450
3451 } // end while
3452
3453 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3455 controls()->SetAutoPan( false );
3456 m_controls->CaptureCursor( false );
3457 return 0;
3458}
3459
3460
3462{
3464 return 0;
3465
3466 struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
3467 {
3468 PCB_BASE_EDIT_FRAME* m_frame;
3469 PCB_GRID_HELPER m_gridHelper;
3470 std::shared_ptr<DRC_ENGINE> m_drcEngine;
3471 int m_drcEpsilon;
3472 int m_worstClearance;
3473 bool m_allowDRCViolations;
3474
3475 VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) :
3476 m_frame( aFrame ),
3477 m_gridHelper( aFrame->GetToolManager(), aFrame->GetMagneticItemsSettings() ),
3478 m_drcEngine( aFrame->GetBoard()->GetDesignSettings().m_DRCEngine ),
3479 m_drcEpsilon( aFrame->GetBoard()->GetDesignSettings().GetDRCEpsilon() ),
3480 m_worstClearance( 0 )
3481 {
3482 ROUTER_TOOL* router = m_frame->GetToolManager()->GetTool<ROUTER_TOOL>();
3483
3484 if( router )
3485 m_allowDRCViolations = router->Router()->Settings().AllowDRCViolations();
3486
3487 try
3488 {
3489 if( aFrame )
3490 m_drcEngine->InitEngine( aFrame->GetDesignRulesPath() );
3491
3492 DRC_CONSTRAINT constraint;
3493
3494 if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, constraint ) )
3495 m_worstClearance = constraint.GetValue().Min();
3496
3497 if( m_drcEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, constraint ) )
3498 m_worstClearance = std::max( m_worstClearance, constraint.GetValue().Min() );
3499
3500 for( FOOTPRINT* footprint : aFrame->GetBoard()->Footprints() )
3501 {
3502 for( PAD* pad : footprint->Pads() )
3503 {
3504 std::optional<int> padOverride = pad->GetClearanceOverrides( nullptr );
3505
3506 if( padOverride.has_value() )
3507 m_worstClearance = std::max( m_worstClearance, padOverride.value() );
3508 }
3509 }
3510 }
3511 catch( PARSE_ERROR& )
3512 {
3513 }
3514 }
3515
3516 virtual ~VIA_PLACER()
3517 {
3518 }
3519
3520 PCB_TRACK* findTrack( PCB_VIA* aVia )
3521 {
3522 const LSET lset = aVia->GetLayerSet();
3523 VECTOR2I position = aVia->GetPosition();
3524 BOX2I bbox = aVia->GetBoundingBox();
3525
3526 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
3527 KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
3528 std::vector<PCB_TRACK*> possible_tracks;
3529
3530 wxCHECK( view, nullptr );
3531
3532 view->Query( bbox, items );
3533
3534 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
3535 {
3536 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
3537
3538 if( !( item->GetLayerSet() & lset ).any() )
3539 continue;
3540
3541 if( item->Type() == PCB_TRACE_T )
3542 {
3543 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
3544
3545 if( TestSegmentHit( position, track->GetStart(), track->GetEnd(),
3546 ( track->GetWidth()
3547 + aVia->GetWidth( track->GetLayer() ) ) / 2 ) )
3548 {
3549 possible_tracks.push_back( track );
3550 }
3551 }
3552 else if( item->Type() == PCB_ARC_T )
3553 {
3554 PCB_ARC* arc = static_cast<PCB_ARC*>( item );
3555
3556 if( arc->HitTest( position, aVia->GetWidth( arc->GetLayer() ) / 2 ) )
3557 possible_tracks.push_back( arc );
3558 }
3559 }
3560
3561 PCB_TRACK* return_track = nullptr;
3562 int min_d = std::numeric_limits<int>::max();
3563
3564 for( PCB_TRACK* track : possible_tracks )
3565 {
3566 SEG test( track->GetStart(), track->GetEnd() );
3567 int dist = ( test.NearestPoint( position ) - position ).EuclideanNorm();
3568
3569 if( dist < min_d )
3570 {
3571 min_d = dist;
3572 return_track = track;
3573 }
3574 }
3575
3576 return return_track;
3577 }
3578
3579 bool hasDRCViolation( PCB_VIA* aVia, BOARD_ITEM* aOther )
3580 {
3581 DRC_CONSTRAINT constraint;
3582 int clearance;
3583 BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aOther );
3584 ZONE* zone = dynamic_cast<ZONE*>( aOther );
3585
3586 if( zone && zone->GetIsRuleArea() )
3587 {
3588 if( zone->GetDoNotAllowVias() )
3589 {
3590 bool hit = false;
3591
3593 [&]( PCB_LAYER_ID aLayer )
3594 {
3595 if( hit )
3596 return;
3597
3598 if( zone->Outline()->Collide( aVia->GetPosition(),
3599 aVia->GetWidth( aLayer ) / 2 ) )
3600 {
3601 hit = true;
3602 }
3603 } );
3604
3605 return hit;
3606 }
3607
3608 return false;
3609 }
3610
3611 if( connectedItem )
3612 {
3613 int connectedItemNet = connectedItem->GetNetCode();
3614
3615 if( connectedItemNet == 0 || connectedItemNet == aVia->GetNetCode() )
3616 return false;
3617 }
3618
3619 for( PCB_LAYER_ID layer : aOther->GetLayerSet().Seq() )
3620 {
3621 // Reference images are "on" a copper layer but are not actually part of it
3622 if( !IsCopperLayer( layer ) || aOther->Type() == PCB_REFERENCE_IMAGE_T )
3623 continue;
3624
3625 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aVia, aOther, layer );
3626 clearance = constraint.GetValue().Min();
3627
3628 if( clearance >= 0 )
3629 {
3630 std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( layer );
3631 std::shared_ptr<SHAPE> otherShape = aOther->GetEffectiveShape( layer );
3632
3633 if( viaShape->Collide( otherShape.get(), clearance - m_drcEpsilon ) )
3634 return true;
3635 }
3636 }
3637
3638 if( aOther->HasHole() )
3639 {
3640 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aVia, aOther,
3642 clearance = constraint.GetValue().Min();
3643
3644 if( clearance >= 0 )
3645 {
3646 std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( UNDEFINED_LAYER );
3647
3648 if( viaShape->Collide( aOther->GetEffectiveHoleShape().get(),
3649 clearance - m_drcEpsilon ) )
3650 {
3651 return true;
3652 }
3653 }
3654 }
3655
3656 return false;
3657 }
3658
3659 bool checkDRCViolation( PCB_VIA* aVia )
3660 {
3661 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
3662 std::set<BOARD_ITEM*> checkedItems;
3663 BOX2I bbox = aVia->GetBoundingBox();
3664
3665 bbox.Inflate( m_worstClearance );
3666 m_frame->GetCanvas()->GetView()->Query( bbox, items );
3667
3668 for( std::pair<KIGFX::VIEW_ITEM*, int> it : items )
3669 {
3670 if( !it.first->IsBOARD_ITEM() )
3671 continue;
3672
3673 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
3674
3675 if( item->Type() == PCB_ZONE_T && !static_cast<ZONE*>( item )->GetIsRuleArea() )
3676 {
3677 continue; // stitching vias bind to zones, so ignore them
3678 }
3679 else if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_GROUP_T )
3680 {
3681 continue; // check against children, but not against footprint itself
3682 }
3683 else if( ( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
3684 && !static_cast<PCB_TEXT*>( item )->IsVisible() )
3685 {
3686 continue; // ignore hidden items
3687 }
3688 else if( checkedItems.count( item ) )
3689 {
3690 continue; // already checked
3691 }
3692
3693 if( hasDRCViolation( aVia, item ) )
3694 return true;
3695
3696 checkedItems.insert( item );
3697 }
3698
3699 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( DISALLOW_CONSTRAINT, aVia, nullptr,
3701
3702 if( constraint.m_DisallowFlags && constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
3703 return true;
3704
3705 return false;
3706 }
3707
3708 PAD* findPad( PCB_VIA* aVia )
3709 {
3710 const VECTOR2I position = aVia->GetPosition();
3711 const LSET lset = aVia->GetLayerSet();
3712
3713 for( FOOTPRINT* fp : m_board->Footprints() )
3714 {
3715 for( PAD* pad : fp->Pads() )
3716 {
3717 if( pad->HitTest( position ) && ( pad->GetLayerSet() & lset ).any() )
3718 {
3719 return pad;
3720 }
3721 }
3722 }
3723
3724 return nullptr;
3725 }
3726
3727 PCB_SHAPE* findGraphic( const PCB_VIA* aVia ) const
3728 {
3729 const LSET lset = aVia->GetLayerSet() & LSET::AllCuMask();
3730 VECTOR2I position = aVia->GetPosition();
3731 BOX2I bbox = aVia->GetBoundingBox();
3732
3733 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
3734 KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
3735 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
3736 std::vector<PCB_SHAPE*> possible_shapes;
3737
3738 view->Query( bbox, items );
3739
3740 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
3741 {
3742 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
3743
3744 if( !( item->GetLayerSet() & lset ).any() )
3745 continue;
3746
3747 if( item->Type() == PCB_SHAPE_T )
3748 {
3749 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
3750
3751 if( shape->HitTest( position, aVia->GetWidth( activeLayer ) / 2 ) )
3752 possible_shapes.push_back( shape );
3753 }
3754 }
3755
3756 PCB_SHAPE* return_shape = nullptr;
3757 int min_d = std::numeric_limits<int>::max();
3758
3759 for( PCB_SHAPE* shape : possible_shapes )
3760 {
3761 int dist = ( shape->GetPosition() - position ).EuclideanNorm();
3762
3763 if( dist < min_d )
3764 {
3765 min_d = dist;
3766 return_shape = shape;
3767 }
3768 }
3769
3770 return return_shape;
3771 }
3772
3773 std::optional<int> selectPossibleNetsByPopupMenu( std::set<int>& aNetcodeList )
3774 {
3775 ACTION_MENU menu( true );
3776 const NETINFO_LIST& netInfo = m_board->GetNetInfo();
3777 std::map<int, int> menuIDNetCodeMap;
3778 int menuID = 1;
3779
3780 for( int netcode : aNetcodeList )
3781 {
3782 wxString menuText;
3783 if( menuID < 10 )
3784 {
3785#ifdef __WXMAC__
3786 menuText = wxString::Format( "%s\t",
3787 netInfo.GetNetItem( netcode )->GetNetname() );
3788#else
3789 menuText = wxString::Format( "&%d %s\t",
3790 menuID,
3791 netInfo.GetNetItem( netcode )->GetNetname() );
3792#endif
3793 }
3794 else
3795 {
3796 menuText = netInfo.GetNetItem( netcode )->GetNetname();
3797 }
3798
3799 menu.Add( menuText, menuID, BITMAPS::INVALID_BITMAP );
3800 menuIDNetCodeMap[ menuID ] = netcode;
3801 menuID++;
3802 }
3803
3804 menu.SetTitle( _( "Select Net:" ) );
3805 menu.DisplayTitle( true );
3806
3807 DRAWING_TOOL* drawingTool = m_frame->GetToolManager()->GetTool<DRAWING_TOOL>();
3808 drawingTool->SetContextMenu( &menu, CMENU_NOW );
3809
3810 int selectedNetCode = -1;
3811 bool cancelled = false;
3812
3813 while( TOOL_EVENT* evt = drawingTool->Wait() )
3814 {
3815 if( evt->Action() == TA_CHOICE_MENU_UPDATE )
3816 {
3817 evt->SetPassEvent();
3818 }
3819 else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
3820 {
3821 std::optional<int> id = evt->GetCommandId();
3822
3823 // User has selected an item, so this one will be returned
3824 if( id && ( *id > 0 ) && ( *id < menuID ) )
3825 {
3826 selectedNetCode = menuIDNetCodeMap.at( *id );
3827 }
3828 // User has cancelled the menu (either by <esc> or clicking out of it),
3829 else
3830 {
3831 cancelled = true;
3832 }
3833 }
3834 else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
3835 {
3836 break;
3837 }
3838 }
3839
3840 if( cancelled )
3841 return std::optional<int>();
3842 else
3843 return selectedNetCode;
3844 }
3845
3846 std::optional<int> findStitchedZoneNet( PCB_VIA* aVia )
3847 {
3848 const VECTOR2I position = aVia->GetPosition();
3849 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
3850 std::set<int> netcodeList;
3851
3852 // See if there are any connections available on a high-contrast layer
3853 if( opts.m_ContrastModeDisplay == HIGH_CONTRAST_MODE::DIMMED
3854 || opts.m_ContrastModeDisplay == HIGH_CONTRAST_MODE::HIDDEN )
3855 {
3856 if( aVia->GetLayerSet().test( m_frame->GetActiveLayer() ) )
3857 {
3858 for( ZONE* z : m_board->Zones() )
3859 {
3860 if( z->IsOnLayer( m_frame->GetActiveLayer() ) )
3861 {
3862 if( z->HitTestFilledArea( m_frame->GetActiveLayer(), position ) )
3863 netcodeList.insert( z->GetNetCode() );
3864 }
3865 }
3866 }
3867 }
3868
3869 // If there's only one, return it.
3870 if( netcodeList.size() == 1 )
3871 return *netcodeList.begin();
3872
3873 // See if there are any connections available on a visible layer
3874 LSET lset = LSET( m_board->GetVisibleLayers() & aVia->GetLayerSet() );
3875
3876 for( ZONE* z : m_board->Zones() )
3877 {
3878 for( PCB_LAYER_ID layer : lset.Seq() )
3879 {
3880 if( z->IsOnLayer( layer ) )
3881 {
3882 if( z->HitTestFilledArea( layer, position ) )
3883 netcodeList.insert( z->GetNetCode() );
3884 }
3885 }
3886 }
3887
3888 // If there's only one, return it.
3889 if( netcodeList.size() == 1 )
3890 return *netcodeList.begin();
3891
3892 if( netcodeList.size() > 1 )
3893 {
3894 // The net assignment is ambiguous. Let the user decide.
3895 return selectPossibleNetsByPopupMenu( netcodeList );
3896 }
3897 else
3898 {
3900 }
3901 }
3902
3903 void SnapItem( BOARD_ITEM *aItem ) override
3904 {
3905 m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
3906
3907 MAGNETIC_SETTINGS* settings = m_frame->GetMagneticItemsSettings();
3908 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
3909 VECTOR2I position = via->GetPosition();
3910
3911 if( settings->tracks != MAGNETIC_OPTIONS::NO_EFFECT && m_gridHelper.GetSnap() )
3912 {
3913 if( PCB_TRACK* track = findTrack( via ) )
3914 {
3915 SEG trackSeg( track->GetStart(), track->GetEnd() );
3916 VECTOR2I snap = m_gridHelper.AlignToSegment( position, trackSeg );
3917
3918 aItem->SetPosition( snap );
3919 return;
3920 }
3921 }
3922
3923 if( settings->pads != MAGNETIC_OPTIONS::NO_EFFECT && m_gridHelper.GetSnap() )
3924 {
3925 if( PAD* pad = findPad( via ) )
3926 {
3927 aItem->SetPosition( pad->GetPosition() );
3928 return;
3929 }
3930 }
3931
3932 if( settings->graphics && m_gridHelper.GetSnap() )
3933 {
3934 if( PCB_SHAPE* shape = findGraphic( via ) )
3935 {
3936 if( shape->IsFilled() )
3937 {
3938 aItem->SetPosition( shape->GetPosition() );
3939 }
3940 else
3941 {
3942 switch( shape->GetShape() )
3943 {
3944 case SHAPE_T::SEGMENT:
3945 {
3946 SEG seg( shape->GetStart(), shape->GetEnd() );
3947 VECTOR2I snap = m_gridHelper.AlignToSegment( position, seg );
3948 aItem->SetPosition( snap );
3949 break;
3950 }
3951
3952 case SHAPE_T::ARC:
3953 {
3954 if( ( shape->GetEnd() - position ).SquaredEuclideanNorm() <
3955 ( shape->GetStart() - position ).SquaredEuclideanNorm() )
3956 {
3957 aItem->SetPosition( shape->GetEnd() );
3958 }
3959 else
3960 {
3961 aItem->SetPosition( shape->GetStart() );
3962 }
3963
3964 break;
3965 }
3966
3967 case SHAPE_T::POLY:
3968 {
3969 if( !shape->IsPolyShapeValid() )
3970 {
3971 aItem->SetPosition( shape->GetPosition() );
3972 break;
3973 }
3974
3975 const SHAPE_POLY_SET& polySet = shape->GetPolyShape();
3976 std::optional<SEG> nearestSeg;
3977 int minDist = std::numeric_limits<int>::max();
3978
3979 for( int ii = 0; ii < polySet.OutlineCount(); ++ii )
3980 {
3981 const SHAPE_LINE_CHAIN& poly = polySet.Outline( ii );
3982
3983 for( int jj = 0; jj < poly.SegmentCount(); ++jj )
3984 {
3985 const SEG& seg = poly.GetSegment( jj );
3986 int dist = seg.Distance( position );
3987
3988 if( dist < minDist )
3989 {
3990 minDist = dist;
3991 nearestSeg = seg;
3992 }
3993 }
3994 }
3995
3996 if( nearestSeg )
3997 {
3998 VECTOR2I snap = m_gridHelper.AlignToSegment( position, *nearestSeg );
3999 aItem->SetPosition( snap );
4000 }
4001
4002 break;
4003 }
4004
4005 default:
4006 aItem->SetPosition( shape->GetPosition() );
4007 }
4008
4009 }
4010 }
4011
4012 }
4013 }
4014
4015 bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
4016 {
4017 WX_INFOBAR* infobar = m_frame->GetInfoBar();
4018 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
4019 VECTOR2I viaPos = via->GetPosition();
4020 PCB_TRACK* track = findTrack( via );
4021 PAD* pad = findPad( via );
4022
4023 if( track )
4024 {
4025 via->SetNetCode( track->GetNetCode() );
4026 via->SetIsFree( false );
4027 }
4028 else if( pad )
4029 {
4030 via->SetNetCode( pad->GetNetCode() );
4031 via->SetIsFree( false );
4032 }
4033 else
4034 {
4035 std::optional<int> netcode = findStitchedZoneNet( via );
4036
4037 if( !netcode.has_value() ) // user cancelled net disambiguation menu
4038 return false;
4039
4040 via->SetNetCode( netcode.value() );
4041 via->SetIsFree( via->GetNetCode() > 0 );
4042 }
4043
4044 if( checkDRCViolation( via ) )
4045 {
4046 m_frame->ShowInfoBarError( _( "Via location violates DRC." ), true,
4048
4049 if( !m_allowDRCViolations )
4050 return false;
4051 }
4052 else
4053 {
4055 infobar->Dismiss();
4056 }
4057
4058 aCommit.Add( via );
4059
4060 // If the user explicitly disables snap (using shift), then don't break the tracks.
4061 // This will prevent PNS from being able to connect the via and track but
4062 // it is explicitly requested by the user
4063 if( track && m_gridHelper.GetSnap() )
4064 {
4065 VECTOR2I trackStart = track->GetStart();
4066 VECTOR2I trackEnd = track->GetEnd();
4067 SEG trackSeg( trackStart, trackEnd );
4068
4069 if( viaPos == trackStart || viaPos == trackEnd )
4070 return true;
4071
4072 if( !trackSeg.Contains( viaPos ) )
4073 return true;
4074
4075 aCommit.Modify( track );
4076 track->SetStart( trackStart );
4077 track->SetEnd( viaPos );
4078
4079 PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
4080 const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
4081
4082 newTrack->SetStart( viaPos );
4083 newTrack->SetEnd( trackEnd );
4084 aCommit.Add( newTrack );
4085 }
4086
4087 return true;
4088 }
4089
4090 std::unique_ptr<BOARD_ITEM> CreateItem() override
4091 {
4092 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
4093 PCB_VIA* via = new PCB_VIA( m_board );
4094
4095 via->SetNetCode( 0 );
4096 via->SetViaType( bds.m_CurrentViaType );
4097
4098 if( via->GetViaType() == VIATYPE::THROUGH )
4099 {
4100 via->SetLayerPair( B_Cu, F_Cu );
4101 }
4102 else
4103 {
4104 PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
4105 PCB_LAYER_ID last_layer;
4106
4107 // prepare switch to new active layer:
4108 if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
4109 last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
4110 else
4111 last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
4112
4113 via->SetLayerPair( first_layer, last_layer );
4114 }
4115
4116 if( via->GetViaType() == VIATYPE::MICROVIA )
4117 {
4118 via->SetWidth( PADSTACK::ALL_LAYERS,
4119 via->GetEffectiveNetClass()->GetuViaDiameter() );
4120 via->SetDrill( via->GetEffectiveNetClass()->GetuViaDrill() );
4121 }
4122 else
4123 {
4124 via->SetWidth( PADSTACK::ALL_LAYERS, bds.GetCurrentViaSize() );
4125 via->SetDrill( bds.GetCurrentViaDrill() );
4126 }
4127
4128 return std::unique_ptr<BOARD_ITEM>( via );
4129 }
4130 };
4131
4132 VIA_PLACER placer( frame() );
4133
4134 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
4135
4136 doInteractiveItemPlacement( aEvent, &placer, _( "Place via" ), IPO_REPEAT | IPO_SINGLE_CLICK );
4137
4138 return 0;
4139}
4140
4141
4142const unsigned int DRAWING_TOOL::WIDTH_STEP = pcbIUScale.mmToIU( 0.1 );
4143
4144
4146{
4147 // clang-format off
4172
4174
4178 // clang-format on
4179}
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
static TOOL_ACTION cancelInteractive
Definition: actions.h:65
static TOOL_ACTION updateUnits
Definition: actions.h:196
static TOOL_ACTION undo
Definition: actions.h:68
static TOOL_ACTION activatePointEditor
Definition: actions.h:221
static TOOL_ACTION doDelete
Definition: actions.h:78
static TOOL_ACTION cursorClick
Definition: actions.h:169
static TOOL_ACTION redo
Definition: actions.h:69
static TOOL_ACTION refreshPreview
Definition: actions.h:149
static TOOL_ACTION resetLocalCoords
Definition: actions.h:199
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:49
TOOL_MANAGER * getToolManager() const
void DisplayTitle(bool aDisplay=true)
Decide whether a title for a pop up menu should be displayed.
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:92
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
Definition: action_menu.cpp:78
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
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=wxEmptyString, int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen=nullptr) override
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
Container for design settings for a BOARD object.
DIM_PRECISION m_DimensionPrecision
Number of digits after the decimal.
void UseCustomTrackViaSize(bool aEnabled)
Enables/disables custom track/via size settings.
VIATYPE m_CurrentViaType
(VIA_BLIND_BURIED, VIA_THROUGH, VIA_MICROVIA)
DIM_UNITS_FORMAT m_DimensionUnitsFormat
bool GetTextUpright(PCB_LAYER_ID aLayer) const
int GetTextThickness(PCB_LAYER_ID aLayer) const
Return the default text thickness from the layer class for the given layer.
bool GetTextItalic(PCB_LAYER_ID aLayer) const
std::shared_ptr< DRC_ENGINE > m_DRCEngine
void SetViaSizeIndex(unsigned aIndex)
Set the current via size list index to aIndex.
unsigned GetViaSizeIndex() const
VECTOR2I GetTextSize(PCB_LAYER_ID aLayer) const
Return the default text size from the layer class for the given layer.
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
DIM_TEXT_POSITION m_DimensionTextPosition
DIM_UNITS_MODE m_DimensionUnitsMode
std::vector< VIA_DIMENSION > m_ViasDimensionsList
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:237
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:288
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:278
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:257
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:288
virtual bool HasHole() const
Definition: board_item.h:155
const FOOTPRINTS & Footprints() const
Definition: board.h:331
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:895
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:558
constexpr size_type GetWidth() const
Definition: box2.h:214
constexpr Vec Centre() const
Definition: box2.h:97
constexpr size_type GetHeight() const
Definition: box2.h:215
constexpr coord_type GetLeft() const
Definition: box2.h:228
constexpr bool Contains(const Vec &aPoint) const
Definition: box2.h:168
constexpr coord_type GetRight() const
Definition: box2.h:217
constexpr coord_type GetTop() const
Definition: box2.h:229
constexpr coord_type GetBottom() const
Definition: box2.h:222
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition: commit.h:80
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
void AddCheckItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a checked menu entry to run a TOOL_ACTION on selected items.
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
std::list< std::unique_ptr< EDA_ITEM > > & GetImportedItems()
int ShowQuasiModal()
int ShowModal() override
Implementing DIALOG_TRACK_VIA_SIZE_BASE.
Tool responsible for drawing graphical elements like lines, arcs, circles, etc.
Definition: drawing_tool.h:55
TEXT_ATTRIBUTES m_textAttrs
Definition: drawing_tool.h:402
MODE GetDrawingMode() const
Return the current drawing mode of the DRAWING_TOOL or MODE::NONE if not currently in any drawing mod...
PCB_SELECTION m_preview
Definition: drawing_tool.h:404
PCB_LAYER_ID m_layer
Definition: drawing_tool.h:400
int DrawTable(const TOOL_EVENT &aEvent)
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:394
KIGFX::VIEW * m_view
Definition: drawing_tool.h:393
STROKE_PARAMS m_stroke
Definition: drawing_tool.h:401
int DrawZone(const TOOL_EVENT &aEvent)
Start interactively drawing a zone.
int DrawBezier(const TOOL_EVENT &aEvent)
Start interactively drawing a bezier curve.
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:341
int ToggleHV45Mode(const TOOL_EVENT &toolEvent)
Toggle the horizontal/vertical/45-degree constraint for drawing tools.
int PlaceCharacteristics(const TOOL_EVENT &aEvent)
bool drawShape(const TOOL_EVENT &aTool, PCB_SHAPE **aGraphic, std::optional< VECTOR2D > aStartingPoint, std::stack< PCB_SHAPE * > *aCommittedGraphics)
Start drawing a selected shape (i.e.
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:371
bool m_inDrawingTool
Definition: drawing_tool.h:398
static const unsigned int WIDTH_STEP
Definition: drawing_tool.h:408
static const unsigned int COORDS_PADDING
Definition: drawing_tool.h:409
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 DrawCircle(const TOOL_EVENT &aEvent)
Start interactively drawing a circle.
int PlaceTuningPattern(const TOOL_EVENT &aEvent)
BOARD * m_board
Definition: drawing_tool.h:395
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:396
void UpdateStatusBar() const
int PlaceReferenceImage(const TOOL_EVENT &aEvent)
Display a dialog that allows one to select a reference image and then decide where to place the image...
bool getSourceZoneForAction(ZONE_MODE aMode, ZONE **aZone)
Draw a polygon, that is added as a zone or a keepout area.
std::unique_ptr< PCB_SHAPE > drawOneBezier(const TOOL_EVENT &aTool, const OPT_VECTOR2I &aStartingPoint, const OPT_VECTOR2I &aStartingControl1Point, DRAW_ONE_RESULT &aResult)
Draw a bezier curve.
int m_DisallowFlags
Definition: drc_rule.h:191
SEVERITY GetSeverity() const
Definition: drc_rule.h:173
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:152
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...
virtual PICKED_ITEMS_LIST * PopCommandFromUndoList()
Return the last command to undo and remove it from list, nothing is deleted.
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
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
virtual void SetPosition(const VECTOR2I &aPos)
Definition: eda_item.h:244
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:127
const KIID m_Uuid
Definition: eda_item.h:489
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:213
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:212
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:778
void SetFilled(bool aFlag)
Definition: eda_shape.h:108
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:725
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:174
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:141
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:131
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:178
void SetBezierC1(const VECTOR2I &aPt)
Definition: eda_shape.h:209
double GetLength() const
Definition: eda_shape.cpp:374
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:924
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition: eda_text.cpp:506
virtual bool IsVisible() const
Definition: eda_text.h:174
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:551
void SetMirrored(bool isMirrored)
Definition: eda_text.cpp:384
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:283
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition: eda_text.cpp:299
void MoveAnchorPosition(const VECTOR2I &aMoveVector)
Move the reference point of the footprint.
Definition: footprint.cpp:2413
VECTOR2I GetPosition() const override
Definition: footprint.h:224
bool GetSnap() const
Definition: grid_helper.h:112
void SetSnap(bool aSnap)
Definition: grid_helper.h:111
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:69
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.
Represents an assistant draw when interactively drawing a bezier on a canvas.
void SetUnits(EDA_UNITS aUnits)
Manage the construction of a bezier through a series of steps.
VECTOR2I GetControlC2() const
Get the coordinates of the arc end point.
VECTOR2I GetStart() const
< Get the center point of the arc (valid when state > SET_ORIGIN)
@ SET_END
Waiting to lock in the end point.
@ SET_START
Waiting to lock in the start point.
BEZIER_STEPS GetStep() const
Get the current step the manager is on (useful when drawing something depends on the current state)
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
bool IsBOARD_ITEM() const
Definition: view_item.h:100
void ShowPreview(bool aShow=true)
Definition: view.cpp:1727
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:299
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:334
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:412
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:1669
void ClearPreview()
Definition: view.cpp:1691
void RecacheAllItems()
Rebuild GAL display lists.
Definition: view.cpp:1435
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition: view.h:72
void AddToPreview(VIEW_ITEM *aItem, bool aTakeOwnership=true)
Definition: view.cpp:1713
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:419
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:217
Definition: kiid.h:49
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:36
static LSET AllLayersMask()
Definition: lset.cpp:701
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:676
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:410
T Min() const
Definition: minoptmax.h:33
const wxString & GetNetname() const
Definition: netinfo.h:114
Container for NETINFO_ITEM elements, which are the nets.
Definition: netinfo.h:346
static const int ORPHANED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:385
NETINFO_ITEM * GetNetItem(int aNetCode) const
void ForEachUniqueLayer(const std::function< void(PCB_LAYER_ID)> &aMethod) const
Runs the given callable for each active unique copper layer in this padstack, meaning F_Cu for MODE::...
Definition: padstack.cpp:870
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:144
Definition: pad.h:54
static TOOL_ACTION deleteLastPoint
Definition: pcb_actions.h:231
static TOOL_ACTION toggleHV45Mode
Definition: pcb_actions.h:524
static TOOL_ACTION drawRuleArea
Definition: pcb_actions.h:223
static TOOL_ACTION changeDimensionArrows
Switch between dimension arrow directions.
Definition: pcb_actions.h:244
static TOOL_ACTION drawBezier
Definition: pcb_actions.h:206
static TOOL_ACTION placeText
Definition: pcb_actions.h:208
static TOOL_ACTION drawOrthogonalDimension
Definition: pcb_actions.h:219
static TOOL_ACTION drawRectangle
Definition: pcb_actions.h:203
static TOOL_ACTION setAnchor
Definition: pcb_actions.h:230
static TOOL_ACTION placeReferenceImage
Definition: pcb_actions.h:207
static TOOL_ACTION drawCircle
Definition: pcb_actions.h:204
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition: pcb_actions.h:65
static TOOL_ACTION tuneDiffPair
Definition: pcb_actions.h:260
static TOOL_ACTION trackViaSizeChanged
Definition: pcb_actions.h:402
static TOOL_ACTION layerChanged
Definition: pcb_actions.h:392
static TOOL_ACTION drawTable
Definition: pcb_actions.h:210
static TOOL_ACTION drawTextBox
Definition: pcb_actions.h:209
static TOOL_ACTION drawZoneCutout
Definition: pcb_actions.h:224
static TOOL_ACTION drawPolygon
Definition: pcb_actions.h:202
static TOOL_ACTION drawRadialDimension
Definition: pcb_actions.h:218
static TOOL_ACTION tuneSingleTrack
Definition: pcb_actions.h:259
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:180
static TOOL_ACTION drawLeader
Definition: pcb_actions.h:220
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:68
static TOOL_ACTION placeCharacteristics
Definition: pcb_actions.h:226
static TOOL_ACTION tuneSkew
Definition: pcb_actions.h:261
static TOOL_ACTION incWidth
Increase width of currently drawn line.
Definition: pcb_actions.h:235
static TOOL_ACTION clearHighlight
Definition: pcb_actions.h:557
static TOOL_ACTION spacingDecrease
Definition: pcb_actions.h:212
static TOOL_ACTION placeImportedGraphics
Definition: pcb_actions.h:229
static TOOL_ACTION drawVia
Definition: pcb_actions.h:222
static TOOL_ACTION drawArc
Definition: pcb_actions.h:205
static TOOL_ACTION drawSimilarZone
Definition: pcb_actions.h:225
static TOOL_ACTION decWidth
Decrease width of currently drawn line.
Definition: pcb_actions.h:238
static TOOL_ACTION drawCenterDimension
Definition: pcb_actions.h:217
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: pcb_actions.h:71
static TOOL_ACTION arcPosture
Switch posture when drawing arc.
Definition: pcb_actions.h:241
static TOOL_ACTION closeOutline
Definition: pcb_actions.h:232
static TOOL_ACTION amplIncrease
Definition: pcb_actions.h:213
static TOOL_ACTION amplDecrease
Definition: pcb_actions.h:214
static TOOL_ACTION lengthTunerSettings
Definition: pcb_actions.h:215
static TOOL_ACTION spacingIncrease
Definition: pcb_actions.h:211
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: pcb_actions.h:76
static TOOL_ACTION drawLine
Definition: pcb_actions.h:201
static TOOL_ACTION placeStackup
Definition: pcb_actions.h:227
static TOOL_ACTION drawAlignedDimension
Definition: pcb_actions.h:216
static TOOL_ACTION drawZone
Definition: pcb_actions.h:221
virtual bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition: pcb_track.cpp:1696
Common, abstract interface for edit frames.
wxString GetDesignRulesPath()
Return the absolute path to the design rules file for the currently-loaded board.
void ClearListAndDeleteItems(PICKED_ITEMS_LIST *aList)
Definition: undo_redo.cpp:647
int ShowTextBoxPropertiesDialog(PCB_TEXTBOX *aTextBox)
APPEARANCE_CONTROLS * GetAppearancePanel()
void PutDataInPreviousState(PICKED_ITEMS_LIST *aList)
Used in undo or redo command.
Definition: undo_redo.cpp:258
void SetObjectVisible(GAL_LAYER_ID aLayer, bool aVisible=true)
const PCB_DISPLAY_OPTIONS & GetDisplayOptions() const
Display options control the way tracks, vias, outlines and other things are shown (for instance solid...
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
Abstract dimension API.
void Update()
Update the dimension's cached text and geometry.
int GetLineThickness() const
void SetExtensionOffset(int aOffset)
void SetLineThickness(int aWidth)
void SetArrowLength(int aLength)
virtual const VECTOR2I & GetStart() const
The dimension's origin is the first feature point for the dimension.
DIM_ARROW_DIRECTION GetArrowDirection() const
virtual void SetEnd(const VECTOR2I &aPoint)
virtual void SetStart(const VECTOR2I &aPoint)
void SetArrowDirection(const DIM_ARROW_DIRECTION &aDirection)
int GetArrowLength() const
virtual const VECTOR2I & GetEnd() const