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 The 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>
35#include <dialogs/dialog_text_properties.h>
46#include <router/router_tool.h>
47#include <status_popup.h>
48#include <tool/tool_manager.h>
49#include <tools/pcb_actions.h>
51#include <kiplatform/ui.h>
53#include <pcb_barcode.h>
57#include <view/view.h>
59#include <widgets/wx_infobar.h>
61#include <wx/filedlg.h>
62#include <wx/msgdlg.h>
63
64#include <bitmaps.h>
65#include <board.h>
66#include <board_commit.h>
68#include <drc/drc_engine.h>
69#include <drc/drc_rule.h>
70#include <confirm.h>
71#include <footprint.h>
72#include <macros.h>
73#include <gal/painter.h>
74#include <pad.h>
75#include <pcb_edit_frame.h>
76#include <pcb_group.h>
77#include <pcb_point.h>
78#include <pcb_reference_image.h>
79#include <pcb_text.h>
80#include <pcb_textbox.h>
81#include <pcb_table.h>
82#include <pcb_tablecell.h>
83#include <pcb_track.h>
84#include <pcb_dimension.h>
85#include <pcbnew_id.h>
86#include <scoped_set_reset.h>
87#include <string_utils.h>
88#include <zone.h>
89#include <fix_board_shape.h>
90#include <view/view_controls.h>
91
92const unsigned int DRAWING_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
93
95
96
98{
99public:
101 ACTION_MENU( true )
102 {
104 SetTitle( _( "Select Via Size" ) );
105 }
106
107protected:
108 ACTION_MENU* create() const override
109 {
110 return new VIA_SIZE_MENU();
111 }
112
113 void update() override
114 {
117 bool useIndex = !bds.m_UseConnectedTrackWidth && !bds.UseCustomTrackViaSize();
118 wxString msg;
119
120 Clear();
121
122 Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
123 _( "Specify custom track and via sizes" ), wxITEM_CHECK );
125
126 AppendSeparator();
127
128 for( int i = 1; i < (int) bds.m_ViasDimensionsList.size(); i++ )
129 {
131
132 if( via.m_Drill > 0 )
133 {
134 msg.Printf( _("Via %s, hole %s" ),
135 frame->MessageTextFromValue( via.m_Diameter ),
136 frame->MessageTextFromValue( via.m_Drill ) );
137 }
138 else
139 {
140 msg.Printf( _( "Via %s" ),
141 frame->MessageTextFromValue( via.m_Diameter ) );
142 }
143
144 int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
145 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
146 Check( menuIdx, useIndex && bds.GetViaSizeIndex() == i );
147 }
148 }
149
150 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
151 {
154 int id = aEvent.GetId();
155
156 // On Windows, this handler can be called with an event ID not existing in any
157 // menuitem, so only set flags when we have an ID match.
158
160 {
161 DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds );
162
163 if( sizeDlg.ShowModal() == wxID_OK )
164 {
165 bds.UseCustomTrackViaSize( true );
166 bds.m_UseConnectedTrackWidth = false;
167 }
168 }
170 {
171 bds.UseCustomTrackViaSize( false );
172 bds.m_UseConnectedTrackWidth = false;
174 }
175
177 }
178};
179
180
182 PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ),
183 m_view( nullptr ),
184 m_controls( nullptr ),
185 m_board( nullptr ),
186 m_frame( nullptr ),
187 m_mode( MODE::NONE ),
188 m_inDrawingTool( false ),
191 m_pickerItem( nullptr ),
192 m_tuningPattern( nullptr )
193{
194}
195
196
200
201
203{
204 auto haveHighlight =
205 [this]( const SELECTION& sel )
206 {
207 KIGFX::RENDER_SETTINGS* cfg = m_toolMgr->GetView()->GetPainter()->GetSettings();
208
209 return !cfg->GetHighlightNetCodes().empty();
210 };
211
212 auto activeToolFunctor =
213 [this]( const SELECTION& aSel )
214 {
215 return m_mode != MODE::NONE;
216 };
217
218 // some interactive drawing tools can undo the last point
219 auto canUndoPoint =
220 [this]( const SELECTION& aSel )
221 {
222 return ( m_mode == MODE::ARC
223 || m_mode == MODE::ZONE
226 || m_mode == MODE::BEZIER
227 || m_mode == MODE::LINE );
228 };
229
230 // functor for tools that can automatically close the outline
231 auto canCloseOutline =
232 [this]( const SELECTION& aSel )
233 {
234 return ( m_mode == MODE::ZONE
237 };
238
239 auto arcToolActive =
240 [this]( const SELECTION& aSel )
241 {
242 return m_mode == MODE::ARC;
243 };
244
245 auto viaToolActive =
246 [this]( const SELECTION& aSel )
247 {
248 return m_mode == MODE::VIA;
249 };
250
251 auto tuningToolActive =
252 [this]( const SELECTION& aSel )
253 {
254 return m_mode == MODE::TUNING;
255 };
256
257 auto dimensionToolActive =
258 [this]( const SELECTION& aSel )
259 {
260 return m_mode == MODE::DIMENSION;
261 };
262
263 CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
264
265 // cancel current tool goes in main context menu at the top if present
266 ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
267 ctxMenu.AddSeparator( 1 );
268
269 ctxMenu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 2 );
270 ctxMenu.AddSeparator( haveHighlight, 2 );
271
272 // tool-specific actions
273 ctxMenu.AddItem( PCB_ACTIONS::closeOutline, canCloseOutline, 200 );
274 ctxMenu.AddItem( PCB_ACTIONS::deleteLastPoint, canUndoPoint, 200 );
275 ctxMenu.AddItem( PCB_ACTIONS::arcPosture, arcToolActive, 200 );
276 ctxMenu.AddItem( PCB_ACTIONS::spacingIncrease, tuningToolActive, 200 );
277 ctxMenu.AddItem( PCB_ACTIONS::spacingDecrease, tuningToolActive, 200 );
278 ctxMenu.AddItem( PCB_ACTIONS::amplIncrease, tuningToolActive, 200 );
279 ctxMenu.AddItem( PCB_ACTIONS::amplDecrease, tuningToolActive, 200 );
280 ctxMenu.AddItem( PCB_ACTIONS::lengthTunerSettings, tuningToolActive, 200 );
281 ctxMenu.AddItem( PCB_ACTIONS::changeDimensionArrows, dimensionToolActive, 200 );
282
283 ctxMenu.AddSeparator( 500 );
284
285 std::shared_ptr<VIA_SIZE_MENU> viaSizeMenu = std::make_shared<VIA_SIZE_MENU>();
286 viaSizeMenu->SetTool( this );
287 m_menu->RegisterSubMenu( viaSizeMenu );
288 ctxMenu.AddMenu( viaSizeMenu.get(), viaToolActive, 500 );
289
290 ctxMenu.AddSeparator( 500 );
291
292 // Type-specific sub-menus will be added for us by other tools
293 // For example, zone fill/unfill is provided by the PCB control tool
294
295 // Finally, add the standard zoom/grid items
296 getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( *m_menu.get() );
297
298 return true;
299}
300
301
303{
304 // Init variables used by every drawing tool
305 m_view = getView();
309
310 // Re-initialize session attributes
311 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
312
313 if( aReason == RESET_REASON::SHUTDOWN )
314 return;
315
316 m_layer = m_frame->GetActiveLayer();
317 m_stroke.SetWidth( bds.GetLineThickness( m_layer ) );
318 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
319 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
320
321 m_textAttrs.m_Size = bds.GetTextSize( m_layer );
322 m_textAttrs.m_StrokeWidth = bds.GetTextThickness( m_layer );
324 m_textAttrs.m_Italic = bds.GetTextItalic( m_layer );
325 m_textAttrs.m_KeepUpright = bds.GetTextUpright( m_layer );
326 m_textAttrs.m_Mirrored = m_board->IsBackLayer( m_layer );
329
331}
332
333
338
339
341{
342 if( m_frame )
343 {
344 switch( GetAngleSnapMode() )
345 {
347 m_frame->DisplayConstraintsMsg( _( "Constrain to H, V, 45" ) );
348 break;
350 m_frame->DisplayConstraintsMsg( _( "Constrain to H, V" ) );
351 break;
352 default:
353 m_frame->DisplayConstraintsMsg( wxString( "" ) );
354 break;
355 }
356 }
357}
358
359
361{
362 if( m_isFootprintEditor && !m_frame->GetModel() )
363 return 0;
364
365 if( m_inDrawingTool )
366 return 0;
367
369
370 BOARD_ITEM* parent = m_frame->GetModel();
371 PCB_SHAPE* line = new PCB_SHAPE( parent );
372 BOARD_COMMIT commit( m_frame );
373 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
374 std::optional<VECTOR2D> startingPoint;
375 std::stack<PCB_SHAPE*> committedLines;
376
377 line->SetShape( SHAPE_T::SEGMENT );
378 line->SetFlags( IS_NEW );
379
380 if( aEvent.HasPosition() )
381 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
382
383 m_frame->PushTool( aEvent );
384 Activate();
385
386 while( drawShape( aEvent, &line, startingPoint, &committedLines ) )
387 {
388 if( line )
389 {
390 commit.Add( line );
391 commit.Push( _( "Draw Line" ) );
392 startingPoint = VECTOR2D( line->GetEnd() );
393 committedLines.push( line );
394 }
395 else
396 {
397 startingPoint = std::nullopt;
398 }
399
400 line = new PCB_SHAPE( parent );
401 line->SetShape( SHAPE_T::SEGMENT );
402 line->SetFlags( IS_NEW );
403 }
404
405 return 0;
406}
407
408
410{
411 if( m_isFootprintEditor && !m_frame->GetModel() )
412 return 0;
413
414 if( m_inDrawingTool )
415 return 0;
416
418
419 bool isTextBox = aEvent.IsAction( &PCB_ACTIONS::drawTextBox );
420 PCB_SHAPE* rect = nullptr;
421 BOARD_COMMIT commit( m_frame );
422 BOARD_ITEM* parent = m_frame->GetModel();
423 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::RECTANGLE );
424 std::optional<VECTOR2D> startingPoint;
425
426 rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
428 rect->SetFilled( false );
429 rect->SetFlags( IS_NEW );
430
431 if( aEvent.HasPosition() )
432 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
433
434 m_frame->PushTool( aEvent );
435 Activate();
436
437 while( drawShape( aEvent, &rect, startingPoint, nullptr ) )
438 {
439 if( rect )
440 {
441 bool cancelled = false;
442
443 if( PCB_TEXTBOX* textbox = dynamic_cast<PCB_TEXTBOX*>( rect ) )
444 cancelled = m_frame->ShowTextBoxPropertiesDialog( textbox ) != wxID_OK;
445
446 if( cancelled )
447 {
448 delete rect;
449 rect = nullptr;
450 }
451 else
452 {
453 rect->Normalize();
454 commit.Add( rect );
455 commit.Push( isTextBox ? _( "Draw Text Box" ) : _( "Draw Rectangle" ) );
456
457 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, rect );
458 }
459 }
460
461 rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
463 rect->SetFilled( false );
464 rect->SetFlags( IS_NEW );
465 startingPoint = std::nullopt;
466 }
467
468 return 0;
469}
470
471
473{
474 return runSimpleShapeDraw( aEvent, SHAPE_T::CIRCLE, MODE::CIRCLE, _( "Draw Circle" ),
475 [this]( const TOOL_EVENT& e, PCB_SHAPE** s, std::optional<VECTOR2D> sp )
476 {
477 return drawShape( e, s, sp, nullptr );
478 } );
479}
480
481
483{
484 return runSimpleShapeDraw( aEvent, SHAPE_T::ELLIPSE, MODE::ELLIPSE, _( "Draw Ellipse" ),
485 [this]( const TOOL_EVENT& e, PCB_SHAPE** s, std::optional<VECTOR2D> sp )
486 {
487 return drawShape( e, s, sp, nullptr );
488 } );
489}
490
492{
493 return runSimpleShapeDraw( aEvent, SHAPE_T::ELLIPSE_ARC, MODE::ELLIPSE_ARC, _( "Draw Elliptical Arc" ),
494 [this]( const TOOL_EVENT& e, PCB_SHAPE** s, std::optional<VECTOR2D> sp )
495 {
496 return drawShape( e, s, sp, nullptr );
497 } );
498}
499
500
502{
503 if( m_isFootprintEditor && !m_frame->GetModel() )
504 return 0;
505
506 if( m_inDrawingTool )
507 return 0;
508
510
511 BOARD_ITEM* parent = m_frame->GetModel();
512 PCB_SHAPE* arc = new PCB_SHAPE( parent );
513 BOARD_COMMIT commit( m_frame );
514 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
515 std::optional<VECTOR2D> startingPoint;
516
517 arc->SetShape( SHAPE_T::ARC );
518 arc->SetFlags( IS_NEW );
519
520 m_frame->PushTool( aEvent );
521 Activate();
522
523 if( aEvent.HasPosition() )
524 startingPoint = aEvent.Position();
525
526 while( drawArc( aEvent, &arc, startingPoint ) )
527 {
528 if( arc )
529 {
530 commit.Add( arc );
531 commit.Push( _( "Draw Arc" ) );
532
533 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, arc );
534 }
535
536 arc = new PCB_SHAPE( parent );
537 arc->SetShape( SHAPE_T::ARC );
538 arc->SetFlags( IS_NEW );
539
540 startingPoint = std::nullopt;
541 }
542
543 return 0;
544}
545
546
548{
549 if( m_isFootprintEditor && !m_frame->GetModel() )
550 return 0;
551
552 if( m_inDrawingTool )
553 return 0;
554
556
557 BOARD_COMMIT commit( m_frame );
558 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::BEZIER );
559 OPT_VECTOR2I startingPoint, startingC1;
560
561 m_frame->PushTool( aEvent );
562 Activate();
563
564 if( aEvent.HasPosition() )
565 startingPoint = aEvent.Position();
566
569 {
570 std::unique_ptr<PCB_SHAPE> bezier =
571 drawOneBezier( aEvent, startingPoint, startingC1, result );
572
573 // Anyting other than accepted means no chaining
574 startingPoint = std::nullopt;
575 startingC1 = std::nullopt;
576
577 // If a bezier was created, add it and go again
578 if( bezier )
579 {
580 PCB_SHAPE& bezierRef = *bezier;
581 commit.Add( bezier.release() );
582 commit.Push( _( "Draw Bezier" ) );
583
584 // Don't chain if reset (or accepted and reset)
586 {
587 startingPoint = bezierRef.GetEnd();
588
589 // If the last bezier has a zero C2 control arm, allow the user to define a new C1
590 // control arm for the next one.
591 if( bezierRef.GetEnd() != bezierRef.GetBezierC2() )
592 {
593 // Mirror the control point across the end point to get a tangent control point
594 startingC1 = bezierRef.GetEnd() - ( bezierRef.GetBezierC2() - bezierRef.GetEnd() );
595 }
596 }
597 }
598 }
599
600 return 0;
601}
602
603
605{
606 if( m_inDrawingTool )
607 return 0;
608
610
612 bool immediateMode = image != nullptr;
613 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
614 bool ignorePrimePosition = false;
615 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
616
618 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
619 BOARD_COMMIT commit( m_frame );
620 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::IMAGE );
621
623
624 // Add all the drawable symbols to preview
625 if( image )
626 {
627 image->SetPosition( cursorPos );
628 m_view->ClearPreview();
629 m_view->AddToPreview( image, false ); // Add, but not give ownership
630 }
631
632 m_frame->PushTool( aEvent );
633
634 auto setCursor =
635 [&]()
636 {
637 if( image )
638 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
639 else
640 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
641 };
642
643 auto cleanup =
644 [&] ()
645 {
647 m_view->ClearPreview();
648 m_view->RecacheAllItems();
649 delete image;
650 image = nullptr;
651 };
652
653 Activate();
654
655 // Must be done after Activate() so that it gets set into the correct context
656 getViewControls()->ShowCursor( true );
657
658 // Set initial cursor
659 setCursor();
660
661 // Prime the pump
662 if( image )
663 {
664 m_toolMgr->PostAction( ACTIONS::refreshPreview );
665 }
666 else if( aEvent.HasPosition() )
667 {
668 m_toolMgr->PrimeTool( aEvent.Position() );
669 }
670 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
671 {
672 m_toolMgr->PrimeTool( { 0, 0 } );
673 ignorePrimePosition = true;
674 }
675
676 // Main loop: keep receiving events
677 while( TOOL_EVENT* evt = Wait() )
678 {
679 setCursor();
680
681 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
682 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
683 cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
684 { m_frame->GetActiveLayer() }, GRID_GRAPHICS ),
686 m_controls->ForceCursorPosition( true, cursorPos );
687
688 if( evt->IsCancelInteractive() || ( image && evt->IsAction( &ACTIONS::undo ) ) )
689 {
690 if( image )
691 {
692 cleanup();
693 }
694 else
695 {
696 m_frame->PopTool( aEvent );
697 break;
698 }
699
700 if( immediateMode )
701 {
702 m_frame->PopTool( aEvent );
703 break;
704 }
705 }
706 else if( evt->IsActivate() )
707 {
708 if( image && evt->IsMoveTool() )
709 {
710 // We're already moving our own item; ignore the move tool
711 evt->SetPassEvent( false );
712 continue;
713 }
714
715 if( image )
716 {
717 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel image creation." ) );
718 evt->SetPassEvent( false );
719 continue;
720 }
721
722 if( evt->IsMoveTool() )
723 {
724 // Leave ourselves on the stack so we come back after the move
725 break;
726 }
727 else
728 {
729 m_frame->PopTool( aEvent );
730 break;
731 }
732 }
733 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
734 {
735 if( !image )
736 {
738
739 wxFileDialog dlg( m_frame, _( "Choose Image" ), wxEmptyString, wxEmptyString,
740 FILEEXT::ImageFileWildcard(), wxFD_OPEN );
741
743
744 bool cancelled = false;
745
747 [&]()
748 {
749 cancelled = dlg.ShowModal() != wxID_OK;
750 } );
751
752 if( cancelled )
753 continue;
754
755 // If we started with a hotkey which has a position then warp back to that.
756 // Otherwise update to the current mouse position pinned inside the autoscroll
757 // boundaries.
758 if( evt->IsPrime() && !ignorePrimePosition )
759 {
760 cursorPos = grid.Align( evt->Position() );
761 getViewControls()->WarpMouseCursor( cursorPos, true );
762 }
763 else
764 {
766 cursorPos = getViewControls()->GetMousePosition();
767 }
768
769 cursorPos = getViewControls()->GetMousePosition( true );
770
771 wxString fullFilename = dlg.GetPath();
772
773 if( wxFileExists( fullFilename ) )
774 image = new PCB_REFERENCE_IMAGE( m_frame->GetModel(), cursorPos );
775
776 if( !image || !image->GetReferenceImage().ReadImageFile( fullFilename ) )
777 {
778 wxMessageBox( wxString::Format(_( "Could not load image from '%s'." ), fullFilename ) );
779 delete image;
780 image = nullptr;
781 continue;
782 }
783
784 image->SetFlags( IS_NEW | IS_MOVING );
785 image->SetLayer( m_frame->GetActiveLayer() );
786
787 m_view->ClearPreview();
788 m_view->AddToPreview( image, false ); // Add, but not give ownership
789 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
790 selectionTool->AddItemToSel( image, false );
791
792 getViewControls()->SetCursorPosition( cursorPos, false );
793 setCursor();
794 m_view->ShowPreview( true );
795 }
796 else
797 {
798 commit.Add( image );
799 commit.Push( _( "Place Image" ) );
800
802
803 image = nullptr;
805
806 m_view->ClearPreview();
807
808 if( immediateMode )
809 {
810 m_frame->PopTool( aEvent );
811 break;
812 }
813 }
814 }
815 else if( evt->IsClick( BUT_RIGHT ) )
816 {
817 // Warp after context menu only if dragging...
818 if( !image )
819 m_toolMgr->VetoContextMenuMouseWarp();
820
821 m_menu->ShowContextMenu( selectionTool->GetSelection() );
822 }
823 else if( image && ( evt->IsAction( &ACTIONS::refreshPreview )
824 || evt->IsMotion() ) )
825 {
826 image->SetPosition( cursorPos );
827 m_view->ClearPreview();
828 m_view->AddToPreview( image, false ); // Add, but not give ownership
829 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
830 }
831 else if( image && evt->IsAction( &ACTIONS::doDelete ) )
832 {
833 cleanup();
834 }
835 else if( image && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
836 || evt->IsAction( &ACTIONS::redo ) ) )
837 {
838 wxBell();
839 }
840 else
841 {
842 evt->SetPassEvent();
843 }
844
845 // Enable autopanning and cursor capture only when there is an image to be placed
846 getViewControls()->SetAutoPan( image != nullptr );
847 getViewControls()->CaptureCursor( image != nullptr );
848 }
849
850 getViewControls()->SetAutoPan( false );
851 getViewControls()->CaptureCursor( false );
852 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
853
854 return 0;
855}
856
858{
860 m_drawingTool( aDrawingTool ),
861 m_frame( aFrame ),
862 m_gridHelper( aDrawingTool.GetManager(), aFrame.GetMagneticItemsSettings() )
863 {
864 }
865
866 std::unique_ptr<BOARD_ITEM> CreateItem() override
867 {
868 std::unique_ptr<PCB_POINT> new_point = std::make_unique<PCB_POINT>( m_frame.GetModel() );
869
870 PCB_LAYER_ID layer = m_frame.GetActiveLayer();
871 new_point->SetLayer( layer );
872
873 return new_point;
874 }
875
876 void SnapItem( BOARD_ITEM* aItem ) override
877 {
878 m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
879 m_gridHelper.SetUseGrid( !( m_modifiers & MD_CTRL ) );
880
881 KIGFX::VIEW_CONTROLS& viewControls = *m_drawingTool.GetManager()->GetViewControls();
882 const VECTOR2I position = viewControls.GetMousePosition();
883
884 VECTOR2I cursorPos = m_gridHelper.BestSnapAnchor( position, aItem->GetLayerSet() );
885 viewControls.ForceCursorPosition( true, cursorPos );
886 aItem->SetPosition( cursorPos );
887 }
888
892};
893
894
896{
897 if( m_isFootprintEditor && !m_frame->GetModel() )
898 return 0;
899
900 if( m_inDrawingTool )
901 return 0;
902
904
905 POINT_PLACER placer( *this, *frame() );
906 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::MD_POINT );
907
908 doInteractiveItemPlacement( aEvent, &placer, _( "Place point" ), IPO_REPEAT | IPO_SINGLE_CLICK );
909
910 return 0;
911}
912
913
915{
916 if( m_isFootprintEditor && !m_frame->GetModel() )
917 return 0;
918
919 if( m_inDrawingTool )
920 return 0;
921
923
924 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
925 PCB_TEXT* text = nullptr;
926 bool ignorePrimePosition = false;
927 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
928 BOARD_COMMIT commit( m_frame );
929 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
930 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
931
932 auto setCursor =
933 [&]()
934 {
935 if( text )
936 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
937 else
938 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
939 };
940
941 auto cleanup =
942 [&]()
943 {
945 m_controls->ForceCursorPosition( false );
946 m_controls->ShowCursor( true );
947 m_controls->SetAutoPan( false );
948 m_controls->CaptureCursor( false );
949 delete text;
950 text = nullptr;
951 };
952
954
955 m_frame->PushTool( aEvent );
956
957 Activate();
958 // Must be done after Activate() so that it gets set into the correct context
959 m_controls->ShowCursor( true );
960 m_controls->ForceCursorPosition( false );
961 // do not capture or auto-pan until we start placing some text
962 // Set initial cursor
963 setCursor();
964
965 if( aEvent.HasPosition() )
966 {
967 m_toolMgr->PrimeTool( aEvent.Position() );
968 }
969 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
970 {
971 m_toolMgr->PrimeTool( { 0, 0 } );
972 ignorePrimePosition = true;
973 }
974
975 // Main loop: keep receiving events
976 while( TOOL_EVENT* evt = Wait() )
977 {
978 setCursor();
979
980 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
981 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
982 VECTOR2I cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
983 { m_frame->GetActiveLayer() }, GRID_TEXT ),
985 m_controls->ForceCursorPosition( true, cursorPos );
986
987 if( evt->IsDrag() )
988 {
989 continue;
990 }
991 else if( evt->IsCancelInteractive() || ( text && evt->IsAction( &ACTIONS::undo ) ) )
992 {
993 if( text )
994 {
995 cleanup();
996 }
997 else
998 {
999 m_frame->PopTool( aEvent );
1000 break;
1001 }
1002 }
1003 else if( evt->IsActivate() )
1004 {
1005 if( text )
1006 cleanup();
1007
1008 if( evt->IsMoveTool() )
1009 {
1010 // leave ourselves on the stack so we come back after the move
1011 break;
1012 }
1013 else
1014 {
1015 m_frame->PopTool( aEvent );
1016 break;
1017 }
1018 }
1019 else if( evt->IsClick( BUT_RIGHT ) )
1020 {
1021 if( !text )
1022 m_toolMgr->VetoContextMenuMouseWarp();
1023
1024 m_menu->ShowContextMenu( selection() );
1025 }
1026 else if( evt->IsClick( BUT_LEFT ) )
1027 {
1028 bool placing = text != nullptr;
1029
1030 if( !text )
1031 {
1032 m_toolMgr->RunAction( ACTIONS::selectionClear );
1033
1034 m_controls->ForceCursorPosition( true, m_controls->GetCursorPosition() );
1035
1036 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1037 TEXT_ATTRIBUTES textAttrs;
1038
1039 textAttrs.m_Size = bds.GetTextSize( layer );
1040 textAttrs.m_StrokeWidth = bds.GetTextThickness( layer );
1041 InferBold( &textAttrs );
1042 textAttrs.m_Italic = bds.GetTextItalic( layer );
1043 textAttrs.m_KeepUpright = bds.GetTextUpright( layer );
1044 textAttrs.m_Mirrored = m_board->IsBackLayer( layer );
1045 textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
1046 textAttrs.m_Valign = GR_TEXT_V_ALIGN_BOTTOM;
1047
1049 text = new PCB_TEXT( static_cast<FOOTPRINT*>( m_frame->GetModel() ) );
1050 else
1051 text = new PCB_TEXT( m_frame->GetModel() );
1052
1053 text->SetLayer( layer );
1054 text->SetAttributes( textAttrs );
1055 text->SetTextPos( cursorPos );
1056 text->SetFlags( IS_NEW ); // Prevent double undo commits
1057
1058 DIALOG_TEXT_PROPERTIES textDialog( m_frame, text );
1059 bool cancelled;
1060
1062 [&]()
1063 {
1064 // QuasiModal required for Scintilla auto-complete
1065 cancelled = textDialog.ShowQuasiModal() != wxID_OK;
1066 } );
1067
1068 if( cancelled || NoPrintableChars( text->GetText() ) )
1069 {
1070 delete text;
1071 text = nullptr;
1072 }
1073 else if( text->GetTextPos() != cursorPos )
1074 {
1075 // If the user modified the location then go ahead and place it there.
1076 // Otherwise we'll drag.
1077 placing = true;
1078 }
1079
1080 if( text )
1081 {
1082 if( !m_view->IsLayerVisible( text->GetLayer() ) )
1083 {
1084 m_frame->GetAppearancePanel()->SetLayerVisible( text->GetLayer(), true );
1085 m_frame->GetCanvas()->Refresh();
1086 }
1087
1088 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, text );
1089 m_view->Update( &selection() );
1090
1091 // update the cursor so it looks correct before another event
1092 setCursor();
1093 }
1094 }
1095
1096 if( placing )
1097 {
1098 text->ClearFlags();
1099 m_toolMgr->RunAction( ACTIONS::selectionClear );
1100
1101 commit.Add( text );
1102 commit.Push( _( "Draw Text" ) );
1103
1104 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, text );
1105
1106 text = nullptr;
1107 }
1108
1109 m_controls->ForceCursorPosition( false );
1110
1111 // If we started with a hotkey which has a position then warp back to that.
1112 // Otherwise update to the current mouse position pinned inside the autoscroll
1113 // boundaries.
1114 if( evt->IsPrime() && !ignorePrimePosition )
1115 {
1116 cursorPos = evt->Position();
1117 m_controls->WarpMouseCursor( cursorPos, true );
1118 }
1119 else
1120 {
1121 m_controls->PinCursorInsideNonAutoscrollArea( true );
1122 cursorPos = m_controls->GetMousePosition();
1123 }
1124
1126
1127 m_controls->ShowCursor( true );
1128 m_controls->CaptureCursor( text != nullptr );
1129 m_controls->SetAutoPan( text != nullptr );
1130 }
1131 else if( text && ( evt->IsMotion() || evt->IsAction( &PCB_ACTIONS::refreshPreview ) ) )
1132 {
1133 text->SetPosition( cursorPos );
1134 selection().SetReferencePoint( cursorPos );
1135 m_view->Update( &selection() );
1136 }
1137 else if( text
1139 || evt->IsAction( &ACTIONS::redo ) ) )
1140 {
1141 wxBell();
1142 }
1143 else if( text && evt->IsAction( &PCB_ACTIONS::properties ) )
1144 {
1145 frame()->OnEditItemRequest( text );
1146 m_view->Update( &selection() );
1147 frame()->SetMsgPanel( text );
1148 }
1149 else
1150 {
1151 evt->SetPassEvent();
1152 }
1153 }
1154
1155 m_controls->SetAutoPan( false );
1156 m_controls->CaptureCursor( false );
1157 m_controls->ForceCursorPosition( false );
1158 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1159
1160 if( selection().Empty() )
1161 m_frame->SetMsgPanel( board() );
1162
1163 return 0;
1164}
1165
1166
1168{
1169 if( m_inDrawingTool )
1170 return 0;
1171
1173
1174 PCB_TABLE* table = nullptr;
1175 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
1176 BOARD_COMMIT commit( m_frame );
1177 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TABLE );
1178 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
1179
1180 // We might be running as the same shape in another co-routine. Make sure that one
1181 // gets whacked.
1182 m_toolMgr->DeactivateTool();
1183
1184 auto setCursor =
1185 [&]()
1186 {
1187 if( table )
1188 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1189 else
1190 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1191 };
1192
1193 auto cleanup =
1194 [&] ()
1195 {
1196 m_toolMgr->RunAction( ACTIONS::selectionClear );
1197 m_controls->ForceCursorPosition( false );
1198 m_controls->ShowCursor( true );
1199 m_controls->SetAutoPan( false );
1200 m_controls->CaptureCursor( false );
1201 delete table;
1202 table = nullptr;
1203 };
1204
1205 m_toolMgr->RunAction( ACTIONS::selectionClear );
1206
1207 m_frame->PushTool( aEvent );
1208
1209 Activate();
1210 // Must be done after Activate() so that it gets set into the correct context
1211 getViewControls()->ShowCursor( true );
1212 m_controls->ForceCursorPosition( false );
1213 // Set initial cursor
1214 setCursor();
1215
1216 if( aEvent.HasPosition() )
1217 m_toolMgr->PrimeTool( aEvent.Position() );
1218
1219 // Main loop: keep receiving events
1220 while( TOOL_EVENT* evt = Wait() )
1221 {
1222 setCursor();
1223 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1224 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1225 VECTOR2I cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
1226 { m_frame->GetActiveLayer() }, GRID_TEXT ),
1228 m_controls->ForceCursorPosition( true, cursorPos );
1229
1230 if( evt->IsDrag() )
1231 {
1232 continue;
1233 }
1234 else if( evt->IsCancelInteractive() || ( table && evt->IsAction( &ACTIONS::undo ) ) )
1235 {
1236 if( table )
1237 {
1238 cleanup();
1239 }
1240 else
1241 {
1242 m_frame->PopTool( aEvent );
1243 break;
1244 }
1245 }
1246 else if( evt->IsActivate() )
1247 {
1248 if( table )
1249 cleanup();
1250
1251 if( evt->IsMoveTool() )
1252 {
1253 // leave ourselves on the stack so we come back after the move
1254 break;
1255 }
1256 else
1257 {
1258 m_frame->PopTool( aEvent );
1259 break;
1260 }
1261 }
1262 else if( evt->IsClick( BUT_RIGHT ) )
1263 {
1264 // Warp after context menu only if dragging...
1265 if( !table )
1266 m_toolMgr->VetoContextMenuMouseWarp();
1267
1268 m_menu->ShowContextMenu( selection() );
1269 }
1270 else if( evt->IsClick( BUT_LEFT ) )
1271 {
1272 if( !table )
1273 {
1274 m_toolMgr->RunAction( ACTIONS::selectionClear );
1275
1276 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1277
1278 table = new PCB_TABLE( m_frame->GetModel(), bds.GetLineThickness( layer ) );
1279 table->SetFlags( IS_NEW );
1280 table->SetLayer( layer );
1281 table->SetColCount( 1 );
1282 table->AddCell( new PCB_TABLECELL( table ) );
1283
1284 table->SetLayer( layer );
1285 table->SetPosition( cursorPos );
1286
1287 if( !m_view->IsLayerVisible( layer ) )
1288 {
1289 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1290 m_frame->GetCanvas()->Refresh();
1291 }
1292
1294 m_view->Update( &selection() );
1295
1296 // update the cursor so it looks correct before another event
1297 setCursor();
1298 }
1299 else
1300 {
1301 m_toolMgr->RunAction( ACTIONS::selectionClear );
1302
1303 table->Normalize();
1304
1306 bool cancelled;
1307
1309 [&]()
1310 {
1311 // QuasiModal required for Scintilla auto-complete
1312 cancelled = dlg.ShowQuasiModal() != wxID_OK;
1313 } );
1314
1315 if( cancelled )
1316 {
1317 delete table;
1318 }
1319 else
1320 {
1321 commit.Add( table, m_frame->GetScreen() );
1322 commit.Push( _( "Draw Table" ) );
1323
1326 }
1327
1328 table = nullptr;
1329 }
1330 }
1331 else if( table && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
1332 {
1333 VECTOR2I fontSize = bds.GetTextSize( table->GetLayer() );
1334 VECTOR2I gridSize = grid.GetGridSize( grid.GetItemGrid( table ) );
1335 VECTOR2I origin( table->GetPosition() );
1336 VECTOR2I requestedSize( cursorPos - origin );
1337
1338 int colCount = std::max( 1, requestedSize.x / ( fontSize.x * 15 ) );
1339 int rowCount = std::max( 1, requestedSize.y / ( fontSize.y * 3 ) );
1340
1341 VECTOR2I cellSize( std::max( fontSize.x * 5, requestedSize.x / colCount ),
1342 std::max( fontSize.y * 3, requestedSize.y / rowCount ) );
1343
1344 cellSize.x = KiROUND( (double) cellSize.x / gridSize.x ) * gridSize.x;
1345 cellSize.y = KiROUND( (double) cellSize.y / gridSize.y ) * gridSize.y;
1346
1347 table->ClearCells();
1348 table->SetColCount( colCount );
1349
1350 for( int col = 0; col < colCount; ++col )
1351 table->SetColWidth( col, cellSize.x );
1352
1353 for( int row = 0; row < rowCount; ++row )
1354 {
1355 table->SetRowHeight( row, cellSize.y );
1356
1357 for( int col = 0; col < colCount; ++col )
1358 {
1359 PCB_TABLECELL* cell = new PCB_TABLECELL( table );
1360 cell->SetPosition( origin + VECTOR2I( col * cellSize.x, row * cellSize.y ) );
1361 cell->SetEnd( cell->GetPosition() + cellSize );
1362 table->AddCell( cell );
1363 }
1364 }
1365
1366 selection().SetReferencePoint( cursorPos );
1367 m_view->Update( &selection() );
1368 m_frame->SetMsgPanel( table );
1369 }
1370 else if( table && evt->IsAction( &PCB_ACTIONS::properties ) )
1371 {
1372 frame()->OnEditItemRequest( table );
1373 m_view->Update( &selection() );
1374 frame()->SetMsgPanel( table );
1375 }
1376 else if( table && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
1377 || evt->IsAction( &ACTIONS::redo ) ) )
1378 {
1379 wxBell();
1380 }
1381 else
1382 {
1383 evt->SetPassEvent();
1384 }
1385
1386 // Enable autopanning and cursor capture only when there is a shape being drawn
1387 getViewControls()->SetAutoPan( table != nullptr );
1388 getViewControls()->CaptureCursor( table != nullptr );
1389 }
1390
1391 getViewControls()->SetAutoPan( false );
1392 getViewControls()->CaptureCursor( false );
1393 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1394 return 0;
1395}
1396
1397
1399{
1400 const VECTOR2I lineVector{ aDim->GetEnd() - aDim->GetStart() };
1401
1402 aDim->SetEnd( aDim->GetStart() + GetVectorSnapped45( lineVector ) );
1403 aDim->Update();
1404}
1405
1407{
1408 if( m_inDrawingTool )
1409 return 0;
1410
1412
1413 PCB_BARCODE* barcode = nullptr;
1414 BOARD_COMMIT commit( m_frame );
1415 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::BARCODE );
1416 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
1417 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
1418
1419 auto setCursor =
1420 [&]()
1421 {
1422 if( barcode )
1423 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1424 else
1425 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1426 };
1427
1428 auto cleanup =
1429 [&]()
1430 {
1431 m_toolMgr->RunAction( ACTIONS::selectionClear );
1432 m_controls->ForceCursorPosition( false );
1433 m_controls->ShowCursor( true );
1434 m_controls->SetAutoPan( false );
1435 m_controls->CaptureCursor( false );
1436 delete barcode;
1437 barcode = nullptr;
1438 };
1439
1440 m_toolMgr->RunAction( ACTIONS::selectionClear );
1441
1442 m_frame->PushTool( aEvent );
1443
1444 Activate();
1445 // Must be done after Activate() so that it gets set into the correct context
1446 getViewControls()->ShowCursor( true );
1447 m_controls->ForceCursorPosition( false );
1448 setCursor();
1449
1450 if( aEvent.HasPosition() )
1451 m_toolMgr->PrimeTool( aEvent.Position() );
1452
1453 while( TOOL_EVENT* evt = Wait() )
1454 {
1455 setCursor();
1456
1457 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1458 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1459 VECTOR2I cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
1460 { m_frame->GetActiveLayer() }, GRID_TEXT ),
1462 m_controls->ForceCursorPosition( true, cursorPos );
1463
1464 if( evt->IsDrag() )
1465 {
1466 continue;
1467 }
1468 else if( evt->IsCancelInteractive() || ( barcode && evt->IsAction( &ACTIONS::undo ) ) )
1469 {
1470 if( barcode )
1471 {
1472 cleanup();
1473 }
1474 else
1475 {
1476 m_frame->PopTool( aEvent );
1477 break;
1478 }
1479 }
1480 else if( evt->IsActivate() )
1481 {
1482 if( barcode )
1483 cleanup();
1484
1485 if( evt->IsMoveTool() )
1486 {
1487 // leave ourselves on the stack so we come back after the move
1488 break;
1489 }
1490 else
1491 {
1492 m_frame->PopTool( aEvent );
1493 break;
1494 }
1495 }
1496 else if( evt->IsClick( BUT_RIGHT ) )
1497 {
1498 if( !barcode )
1499 m_toolMgr->VetoContextMenuMouseWarp();
1500
1501 m_menu->ShowContextMenu( selection() );
1502 }
1503 else if( evt->IsClick( BUT_LEFT ) )
1504 {
1505 m_toolMgr->RunAction( ACTIONS::selectionClear );
1506
1507 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1508
1509 barcode = new PCB_BARCODE( m_frame->GetModel() );
1510 barcode->SetFlags( IS_NEW );
1511 barcode->SetLayer( layer );
1512 barcode->SetPosition( cursorPos );
1513 barcode->SetTextSize( bds.GetTextSize( layer ).y );
1514
1515 DIALOG_BARCODE_PROPERTIES dlg( m_frame, barcode );
1516 bool cancelled;
1517
1519 [&]()
1520 {
1521 cancelled = dlg.ShowModal() != wxID_OK;
1522 } );
1523
1524 if( cancelled )
1525 {
1526 delete barcode;
1527 }
1528 else
1529 {
1530 if( !m_view->IsLayerVisible( layer ) )
1531 {
1532 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1533 m_frame->GetCanvas()->Refresh();
1534 }
1535
1536 commit.Add( barcode );
1537 commit.Push( _( "Draw Barcode" ) );
1538
1539 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, barcode );
1540 m_view->Update( &selection() );
1541 }
1542
1543 barcode = nullptr;
1544 }
1545 else
1546 {
1547 evt->SetPassEvent();
1548 }
1549
1550 getViewControls()->SetAutoPan( false );
1551 getViewControls()->CaptureCursor( false );
1552 }
1553
1554 getViewControls()->SetAutoPan( false );
1555 getViewControls()->CaptureCursor( false );
1556 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1557 return 0;
1558}
1559
1560
1562{
1563 if( m_isFootprintEditor && !m_frame->GetModel() )
1564 return 0;
1565
1566 if( m_inDrawingTool )
1567 return 0;
1568
1570
1571 enum DIMENSION_STEPS
1572 {
1573 SET_ORIGIN = 0,
1574 SET_END,
1575 SET_HEIGHT,
1576 FINISHED
1577 };
1578
1579 TOOL_EVENT originalEvent = aEvent;
1580 PCB_DIMENSION_BASE* dimension = nullptr;
1581 BOARD_COMMIT commit( m_frame );
1582 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
1583 BOARD_DESIGN_SETTINGS& boardSettings = m_board->GetDesignSettings();
1584 PCB_SELECTION preview; // A VIEW_GROUP that serves as a preview for the new item(s)
1585 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
1586 int step = SET_ORIGIN;
1588
1589 m_view->Add( &preview );
1590
1591 auto cleanup =
1592 [&]()
1593 {
1594 m_controls->SetAutoPan( false );
1595 m_controls->CaptureCursor( false );
1596 m_controls->ForceCursorPosition( false );
1597
1598 preview.Clear();
1599 m_view->Update( &preview );
1600
1601 delete dimension;
1602 dimension = nullptr;
1603 step = SET_ORIGIN;
1604 };
1605
1606 auto setCursor =
1607 [&]()
1608 {
1609 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
1610 };
1611
1612 m_toolMgr->RunAction( ACTIONS::selectionClear );
1613
1614 m_frame->PushTool( aEvent );
1615
1616 Activate();
1617 // Must be done after Activate() so that it gets set into the correct context
1618 m_controls->ShowCursor( true );
1619 m_controls->ForceCursorPosition( false );
1620 // Set initial cursor
1621 setCursor();
1622
1623 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1624
1625 if( aEvent.HasPosition() )
1626 m_toolMgr->PrimeTool( aEvent.Position() );
1627
1628 // Main loop: keep receiving events
1629 while( TOOL_EVENT* evt = Wait() )
1630 {
1631 if( step > SET_ORIGIN )
1632 frame()->SetMsgPanel( dimension );
1633
1634 setCursor();
1635
1636 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1637 auto angleSnap = GetAngleSnapMode();
1638 if( evt->Modifier( MD_CTRL ) )
1639 angleSnap = LEADER_MODE::DIRECT;
1640 bool constrained = angleSnap != LEADER_MODE::DIRECT;
1641 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1642
1643 if( step == SET_HEIGHT && t != PCB_DIM_ORTHOGONAL_T )
1644 {
1645 if( dimension->GetStart().x != dimension->GetEnd().x
1646 && dimension->GetStart().y != dimension->GetEnd().y )
1647 {
1648 // Not cardinal. Grid snapping doesn't make sense for height.
1649 grid.SetUseGrid( false );
1650 }
1651 }
1652
1653 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
1654 cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, nullptr, GRID_GRAPHICS ),
1656
1657 m_controls->ForceCursorPosition( true, cursorPos );
1658
1659 if( evt->IsCancelInteractive() || ( dimension && evt->IsAction( &ACTIONS::undo ) ) )
1660 {
1661 m_controls->SetAutoPan( false );
1662
1663 if( step != SET_ORIGIN ) // start from the beginning
1664 {
1665 cleanup();
1666 }
1667 else
1668 {
1669 m_frame->PopTool( aEvent );
1670 break;
1671 }
1672 }
1673 else if( evt->IsActivate() )
1674 {
1675 if( step != SET_ORIGIN )
1676 cleanup();
1677
1678 if( evt->IsPointEditor() )
1679 {
1680 // don't exit (the point editor runs in the background)
1681 }
1682 else if( evt->IsMoveTool() )
1683 {
1684 // leave ourselves on the stack so we come back after the move
1685 break;
1686 }
1687 else
1688 {
1689 m_frame->PopTool( aEvent );
1690 break;
1691 }
1692 }
1693 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
1694 {
1695 m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
1696 dimension->SetLineThickness( m_stroke.GetWidth() );
1697 m_view->Update( &preview );
1698 frame()->SetMsgPanel( dimension );
1699 }
1700 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
1701 {
1702 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
1703 {
1704 m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
1705 dimension->SetLineThickness( m_stroke.GetWidth() );
1706 m_view->Update( &preview );
1707 frame()->SetMsgPanel( dimension );
1708 }
1709 }
1710 else if( evt->IsClick( BUT_RIGHT ) )
1711 {
1712 if( !dimension )
1713 m_toolMgr->VetoContextMenuMouseWarp();
1714
1715 m_menu->ShowContextMenu( selection() );
1716 }
1717 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1718 {
1719 switch( step )
1720 {
1721 case SET_ORIGIN:
1722 {
1723 m_toolMgr->RunAction( ACTIONS::selectionClear );
1724
1725 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1726
1727 // Init the new item attributes
1728 auto setMeasurementAttributes =
1729 [&]( PCB_DIMENSION_BASE* aDim )
1730 {
1731 aDim->SetUnitsMode( boardSettings.m_DimensionUnitsMode );
1732 aDim->SetUnitsFormat( boardSettings.m_DimensionUnitsFormat );
1733 aDim->SetPrecision( boardSettings.m_DimensionPrecision );
1734 aDim->SetSuppressZeroes( boardSettings.m_DimensionSuppressZeroes );
1735 aDim->SetTextPositionMode( boardSettings.m_DimensionTextPosition );
1736 aDim->SetKeepTextAligned( boardSettings.m_DimensionKeepTextAligned );
1737 };
1738
1739 if( originalEvent.IsAction( &PCB_ACTIONS::drawAlignedDimension ) )
1740 {
1741 dimension = new PCB_DIM_ALIGNED( m_frame->GetModel() );
1742 setMeasurementAttributes( dimension );
1743 }
1744 else if( originalEvent.IsAction( &PCB_ACTIONS::drawOrthogonalDimension ) )
1745 {
1746 dimension = new PCB_DIM_ORTHOGONAL( m_frame->GetModel() );
1747 setMeasurementAttributes( dimension );
1748 }
1749 else if( originalEvent.IsAction( &PCB_ACTIONS::drawCenterDimension ) )
1750 {
1751 dimension = new PCB_DIM_CENTER( m_frame->GetModel() );
1752 }
1753 else if( originalEvent.IsAction( &PCB_ACTIONS::drawRadialDimension ) )
1754 {
1755 dimension = new PCB_DIM_RADIAL( m_frame->GetModel() );
1756 setMeasurementAttributes( dimension );
1757 }
1758 else if( originalEvent.IsAction( &PCB_ACTIONS::drawLeader ) )
1759 {
1760 dimension = new PCB_DIM_LEADER( m_frame->GetModel() );
1761 dimension->SetTextPos( cursorPos );
1762 }
1763 else
1764 {
1765 wxFAIL_MSG( wxT( "Unhandled action in DRAWING_TOOL::DrawDimension" ) );
1766 }
1767
1768 t = dimension->Type();
1769
1770 dimension->SetLayer( layer );
1771 dimension->SetMirrored( m_board->IsBackLayer( layer ) );
1772 dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
1773 dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
1774 dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
1775 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1776 dimension->SetArrowLength( boardSettings.m_DimensionArrowLength );
1777 dimension->SetExtensionOffset( boardSettings.m_DimensionExtensionOffset );
1778 dimension->SetStart( cursorPos );
1779 dimension->SetEnd( cursorPos );
1780 dimension->Update();
1781
1782 if( !m_view->IsLayerVisible( layer ) )
1783 {
1784 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1785 m_frame->GetCanvas()->Refresh();
1786 }
1787
1788 preview.Add( dimension );
1789 frame()->SetMsgPanel( dimension );
1790
1791 m_controls->SetAutoPan( true );
1792 m_controls->CaptureCursor( true );
1793 break;
1794 }
1795
1796 case SET_END:
1797 // Dimensions that have origin and end in the same spot are not valid
1798 if( dimension->GetStart() == dimension->GetEnd() )
1799 {
1800 --step;
1801 break;
1802 }
1803
1804 if( t != PCB_DIM_CENTER_T && t != PCB_DIM_RADIAL_T && t != PCB_DIM_LEADER_T )
1805 {
1806 break;
1807 }
1808
1809 ++step;
1811 case SET_HEIGHT:
1812 assert( dimension->GetStart() != dimension->GetEnd() );
1813 assert( dimension->GetLineThickness() > 0 );
1814
1815 preview.Remove( dimension );
1816
1817 commit.Add( dimension );
1818 commit.Push( _( "Draw Dimension" ) );
1819
1820 // Run the edit immediately to set the leader text
1821 if( t == PCB_DIM_LEADER_T )
1822 frame()->OnEditItemRequest( dimension );
1823
1824 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, dimension );
1825
1826 break;
1827 }
1828
1829 if( ++step >= FINISHED )
1830 {
1831 dimension = nullptr;
1832 step = SET_ORIGIN;
1833 m_controls->SetAutoPan( false );
1834 m_controls->CaptureCursor( false );
1835 }
1836 else if( evt->IsDblClick( BUT_LEFT ) )
1837 {
1838 m_toolMgr->PostAction( PCB_ACTIONS::cursorClick );
1839 }
1840 }
1841 else if( evt->IsMotion() )
1842 {
1843 switch( step )
1844 {
1845 case SET_END:
1846 dimension->SetEnd( cursorPos );
1847
1848 if( constrained || t == PCB_DIM_CENTER_T )
1849 constrainDimension( dimension );
1850
1851 if( t == PCB_DIM_ORTHOGONAL_T )
1852 {
1853 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1854
1855 BOX2I bounds( dimension->GetStart(),
1856 dimension->GetEnd() - dimension->GetStart() );
1857
1858 // Create a nice preview by measuring the longer dimension
1859 bool vert = bounds.GetWidth() < bounds.GetHeight();
1860
1861 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1863 }
1864 else if( t == PCB_DIM_RADIAL_T )
1865 {
1866 PCB_DIM_RADIAL* radialDim = static_cast<PCB_DIM_RADIAL*>( dimension );
1867 VECTOR2I textOffset( radialDim->GetArrowLength() * 10, 0 );
1868
1869 if( radialDim->GetEnd().x < radialDim->GetStart().x )
1870 textOffset = -textOffset;
1871
1872 radialDim->SetTextPos( radialDim->GetKnee() + textOffset );
1873 }
1874 else if( t == PCB_DIM_LEADER_T )
1875 {
1876 VECTOR2I textOffset( dimension->GetArrowLength() * 10, 0 );
1877
1878 if( dimension->GetEnd().x < dimension->GetStart().x )
1879 textOffset = -textOffset;
1880
1881 dimension->SetTextPos( dimension->GetEnd() + textOffset );
1882 }
1883
1884 dimension->Update();
1885 break;
1886
1887 case SET_HEIGHT:
1888 if( t == PCB_DIM_ALIGNED_T )
1889 {
1890 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dimension );
1891
1892 // Calculating the direction of travel perpendicular to the selected axis
1893 double angle = aligned->GetAngle() + ( M_PI / 2 );
1894
1895 VECTOR2I delta( (VECTOR2I) cursorPos - dimension->GetEnd() );
1896 double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
1897 aligned->SetHeight( height );
1898 aligned->Update();
1899 }
1900 else if( t == PCB_DIM_ORTHOGONAL_T )
1901 {
1902 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1903
1904 BOX2I bbox( dimension->GetStart(),
1905 dimension->GetEnd() - dimension->GetStart() );
1906 VECTOR2I direction( cursorPos - bbox.Centre() );
1907 bool vert;
1908
1909 // Only change the orientation when we move outside the bbox
1910 if( !bbox.Contains( cursorPos ) )
1911 {
1912 // If the dimension is horizontal or vertical, set correct orientation
1913 // otherwise, test if we're left/right of the bounding box or above/below it
1914 if( bbox.GetWidth() == 0 )
1915 vert = true;
1916 else if( bbox.GetHeight() == 0 )
1917 vert = false;
1918 else if( cursorPos.x > bbox.GetLeft() && cursorPos.x < bbox.GetRight() )
1919 vert = false;
1920 else if( cursorPos.y > bbox.GetTop() && cursorPos.y < bbox.GetBottom() )
1921 vert = true;
1922 else
1923 vert = std::abs( direction.y ) < std::abs( direction.x );
1924
1925 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1927 }
1928 else
1929 {
1930 vert = ortho->GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1931 }
1932
1933 VECTOR2I heightVector( cursorPos - dimension->GetStart() );
1934 ortho->SetHeight( vert ? heightVector.x : heightVector.y );
1935 ortho->Update();
1936 }
1937
1938 break;
1939 }
1940
1941 // Show a preview of the item
1942 m_view->Update( &preview );
1943 }
1944 else if( dimension && evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1945 {
1946 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1947
1948 if( !m_view->IsLayerVisible( layer ) )
1949 {
1950 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1951 m_frame->GetCanvas()->Refresh();
1952 }
1953
1954 dimension->SetLayer( layer );
1955 dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
1956 dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
1957 dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
1958 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1959 dimension->Update();
1960
1961 m_view->Update( &preview );
1962 frame()->SetMsgPanel( dimension );
1963 }
1964 else if( dimension && evt->IsAction( &PCB_ACTIONS::properties ) )
1965 {
1966 if( step == SET_END || step == SET_HEIGHT )
1967 {
1968 frame()->OnEditItemRequest( dimension );
1969 dimension->Update();
1970 frame()->SetMsgPanel( dimension );
1971 break;
1972 }
1973 else
1974 {
1975 wxBell();
1976 }
1977 }
1978 else if( dimension && evt->IsAction( &PCB_ACTIONS::changeDimensionArrows ) )
1979 {
1980 switch( dimension->Type() )
1981 {
1982 case PCB_DIM_ALIGNED_T:
1984 case PCB_DIM_RADIAL_T:
1987 else
1989 break;
1990 default:
1991 // Other dimension types don't have arrows that can swap
1992 wxBell();
1993 }
1994
1995 m_view->Update( &preview );
1996 }
1997 else if( dimension && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
1998 || evt->IsAction( &ACTIONS::redo ) ) )
1999 {
2000 wxBell();
2001 }
2002 else
2003 {
2004 evt->SetPassEvent();
2005 }
2006 }
2007
2008 if( step != SET_ORIGIN )
2009 delete dimension;
2010
2011 m_controls->SetAutoPan( false );
2012 m_controls->ForceCursorPosition( false );
2013 m_controls->CaptureCursor( false );
2014 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2015
2016 m_view->Remove( &preview );
2017
2018 if( selection().Empty() )
2019 m_frame->SetMsgPanel( board() );
2020
2021 return 0;
2022}
2023
2024
2026{
2027 if( !m_frame->GetModel() )
2028 return 0;
2029
2030 if( m_inDrawingTool )
2031 return 0;
2032
2034
2036
2037 // Set filename on drag-and-drop
2038 if( aEvent.HasParameter() )
2039 dlg.SetFilenameOverride( *aEvent.Parameter<wxString*>() );
2040
2041 int dlgResult = dlg.ShowModal();
2042
2043 std::list<std::unique_ptr<EDA_ITEM>>& list = dlg.GetImportedItems();
2044
2045 if( dlgResult != wxID_OK )
2046 return 0;
2047
2048 // Ensure the list is not empty:
2049 if( list.empty() )
2050 {
2051 wxMessageBox( _( "No graphic items found in file.") );
2052 return 0;
2053 }
2054
2056
2057 std::vector<BOARD_ITEM*> newItems; // all new items, including group
2058 std::vector<BOARD_ITEM*> selectedItems; // the group, or newItems if no group
2059 PCB_SELECTION preview;
2060 BOARD_COMMIT commit( m_frame );
2061 PCB_GROUP* group = nullptr;
2062 PCB_LAYER_ID layer = F_Cu;
2063
2064 if( dlg.ShouldGroupItems() )
2065 {
2066 size_t boardItemCount = std::count_if( list.begin(), list.end(),
2067 []( const std::unique_ptr<EDA_ITEM>& ptr )
2068 {
2069 return ptr->IsBOARD_ITEM();
2070 } );
2071
2072 if( boardItemCount >= 2 )
2073 {
2074 group = new PCB_GROUP( m_frame->GetModel() );
2075
2076 newItems.push_back( group );
2077 selectedItems.push_back( group );
2078 preview.Add( group );
2079 }
2080 }
2081
2082 if( dlg.ShouldFixDiscontinuities() )
2083 {
2084 std::vector<PCB_SHAPE*> shapeList;
2085
2086 for( const std::unique_ptr<EDA_ITEM>& ptr : list )
2087 {
2088 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( ptr.get() ) )
2089 shapeList.push_back( shape );
2090 }
2091
2092 ConnectBoardShapes( shapeList, dlg.GetTolerance() );
2093 }
2094
2095 for( std::unique_ptr<EDA_ITEM>& ptr : list )
2096 {
2097 EDA_ITEM* eda_item = ptr.release();
2098
2099 if( eda_item->IsBOARD_ITEM() )
2100 {
2101 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
2102
2103 newItems.push_back( item );
2104
2105 if( group )
2106 group->AddItem( item );
2107 else
2108 selectedItems.push_back( item );
2109
2110 layer = item->GetLayer();
2111 }
2112
2113 preview.Add( eda_item );
2114 }
2115
2116 // Clear the current selection then select the drawings so that edit tools work on them
2117 m_toolMgr->RunAction( ACTIONS::selectionClear );
2118
2119 EDA_ITEMS selItems( selectedItems.begin(), selectedItems.end() );
2120 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &selItems );
2121
2122 if( !dlg.IsPlacementInteractive() )
2123 {
2124 for( BOARD_ITEM* item : newItems )
2125 commit.Add( item );
2126
2127 commit.Push( _( "Import Graphics" ) );
2128
2129 return 0;
2130 }
2131
2132 // Turn shapes on if they are off, so that the created object will be visible after completion
2133 m_frame->SetObjectVisible( LAYER_FILLED_SHAPES );
2134
2135 if( !m_view->IsLayerVisible( layer ) )
2136 {
2137 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
2138 m_frame->GetCanvas()->Refresh();
2139 }
2140
2141 m_view->Add( &preview );
2142
2143 m_frame->PushTool( aEvent );
2144
2145 auto setCursor =
2146 [&]()
2147 {
2148 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
2149 };
2150
2151 Activate();
2152 // Must be done after Activate() so that it gets set into the correct context
2153 m_controls->ShowCursor( true );
2154 m_controls->ForceCursorPosition( false );
2155 // Set initial cursor
2156 setCursor();
2157
2158 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
2159 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
2160
2161 // Now move the new items to the current cursor position:
2162 VECTOR2I cursorPos = m_controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
2163 VECTOR2I delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
2164
2165 for( BOARD_ITEM* item : selectedItems )
2166 item->Move( delta );
2167
2168 m_view->Update( &preview );
2169
2170 // Main loop: keep receiving events
2171 while( TOOL_EVENT* evt = Wait() )
2172 {
2173 setCursor();
2174
2175 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2176 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2177 cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(), { layer }, GRID_GRAPHICS ),
2179 m_controls->ForceCursorPosition( true, cursorPos );
2180
2181 if( evt->IsCancelInteractive() || evt->IsActivate() )
2182 {
2183 m_toolMgr->RunAction( ACTIONS::selectionClear );
2184
2185 if( group )
2186 preview.Remove( group );
2187
2188 for( BOARD_ITEM* item : newItems )
2189 delete item;
2190
2191 break;
2192 }
2193 else if( evt->IsMotion() )
2194 {
2195 delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
2196
2197 for( BOARD_ITEM* item : selectedItems )
2198 item->Move( delta );
2199
2200 m_view->Update( &preview );
2201 }
2202 else if( evt->IsClick( BUT_RIGHT ) )
2203 {
2204 m_menu->ShowContextMenu( selection() );
2205 }
2206 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
2207 {
2208 // Place the imported drawings
2209 for( BOARD_ITEM* item : newItems )
2210 commit.Add( item );
2211
2212 commit.Push( _( "Import Graphics" ) );
2213
2214 break; // This is a one-shot command, not a tool
2215 }
2216 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2217 {
2218 wxBell();
2219 }
2220 else
2221 {
2222 evt->SetPassEvent();
2223 }
2224 }
2225
2226 preview.Clear();
2227 m_view->Remove( &preview );
2228
2229 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2230 m_controls->ForceCursorPosition( false );
2231
2232 m_frame->PopTool( aEvent );
2233
2234 return 0;
2235}
2236
2237
2239{
2240 // Make sense only in FP editor
2241 if( !m_isFootprintEditor )
2242 return 0;
2243
2244 if( !m_frame->GetModel() )
2245 return 0;
2246
2247 if( m_inDrawingTool )
2248 return 0;
2249
2251
2252 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
2253 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
2254
2255 m_toolMgr->RunAction( ACTIONS::selectionClear );
2256
2257 m_frame->PushTool( aEvent );
2258
2259 auto setCursor =
2260 [&]()
2261 {
2262 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
2263 };
2264
2265 Activate();
2266 // Must be done after Activate() so that it gets set into the correct context
2267 m_controls->ShowCursor( true );
2268 m_controls->SetAutoPan( true );
2269 m_controls->CaptureCursor( false );
2270 m_controls->ForceCursorPosition( false );
2271 // Set initial cursor
2272 setCursor();
2273
2274 while( TOOL_EVENT* evt = Wait() )
2275 {
2276 setCursor();
2277
2278 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2279 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2280 VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), LSET::AllLayersMask() );
2281 m_controls->ForceCursorPosition( true, cursorPos );
2282
2283 if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
2284 {
2285 FOOTPRINT* footprint = (FOOTPRINT*) m_frame->GetModel();
2286 BOARD_COMMIT commit( m_frame );
2287 commit.Modify( footprint );
2288
2289 // set the new relative internal local coordinates of footprint items
2290 VECTOR2I moveVector = footprint->GetPosition() - cursorPos;
2291 footprint->MoveAnchorPosition( moveVector );
2292
2293 commit.Push( _( "Move Footprint Anchor" ) );
2294
2295 // Usually, we do not need to change twice the anchor position,
2296 // so deselect the active tool
2297 m_frame->PopTool( aEvent );
2298 break;
2299 }
2300 else if( evt->IsClick( BUT_RIGHT ) )
2301 {
2302 m_menu->ShowContextMenu( selection() );
2303 }
2304 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2305 {
2306 m_frame->PopTool( aEvent );
2307 break;
2308 }
2309 else
2310 {
2311 evt->SetPassEvent();
2312 }
2313 }
2314
2315 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2316 m_controls->ForceCursorPosition( false );
2317
2318 return 0;
2319}
2320
2321
2322static VECTOR2I evalEllipsePoint( const PCB_SHAPE* aGraphic, const VECTOR2I& aCursorPos )
2323{
2324 const VECTOR2I center = aGraphic->GetEllipseCenter();
2325 const double a = std::max( 1, aGraphic->GetEllipseMajorRadius() );
2326 const double b = std::max( 1, aGraphic->GetEllipseMinorRadius() );
2327 const EDA_ANGLE rot = aGraphic->GetEllipseRotation();
2328 const double cosRot = rot.Cos();
2329 const double sinRot = rot.Sin();
2330
2331 const double dx = aCursorPos.x - center.x;
2332 const double dy = aCursorPos.y - center.y;
2333 const double lx = dx * cosRot + dy * sinRot;
2334 const double ly = -dx * sinRot + dy * cosRot;
2335
2336 const EDA_ANGLE t( std::atan2( ly / b, lx / a ), RADIANS_T );
2337 const double px = a * t.Cos();
2338 const double py = b * t.Sin();
2339
2340 return center + VECTOR2I( KiROUND( px * cosRot - py * sinRot ), KiROUND( px * sinRot + py * cosRot ) );
2341}
2342
2343
2345{
2346 if( aMgr.IsReset() )
2347 return;
2348
2349 if( aGraphic->GetShape() == SHAPE_T::ELLIPSE || aGraphic->GetShape() == SHAPE_T::ELLIPSE_ARC )
2350 {
2351 const VECTOR2I origin = aMgr.GetOrigin();
2352 const VECTOR2I end = aMgr.GetEnd();
2353 const VECTOR2I center = ( origin + end ) / 2;
2354 const int halfW = std::abs( end.x - origin.x ) / 2;
2355 const int halfH = std::abs( end.y - origin.y ) / 2;
2356
2357 int majorRadius;
2358 int minorRadius;
2359 EDA_ANGLE rotation;
2360
2361 if( halfW >= halfH )
2362 {
2363 majorRadius = std::max( halfW, 1 );
2364 minorRadius = std::max( halfH, 1 );
2365 rotation = ANGLE_0;
2366 }
2367 else
2368 {
2369 majorRadius = std::max( halfH, 1 );
2370 minorRadius = std::max( halfW, 1 );
2371 rotation = ANGLE_90;
2372 }
2373
2374 aGraphic->SetStart( origin );
2375 aGraphic->SetEllipseCenter( center );
2376 aGraphic->SetEllipseMajorRadius( majorRadius );
2377 aGraphic->SetEllipseMinorRadius( minorRadius );
2378 aGraphic->SetEllipseRotation( rotation );
2379
2380 if( aGraphic->GetShape() == SHAPE_T::ELLIPSE_ARC )
2381 {
2382 aGraphic->SetEllipseStartAngle( ANGLE_0 );
2383 aGraphic->SetEllipseEndAngle( ANGLE_360 );
2384 }
2385 }
2386 else
2387 {
2388 aGraphic->SetStart( aMgr.GetOrigin() );
2389 aGraphic->SetEnd( aMgr.GetEnd() );
2390 }
2391}
2392
2393
2394bool DRAWING_TOOL::drawShape( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
2395 std::optional<VECTOR2D> aStartingPoint,
2396 std::stack<PCB_SHAPE*>* aCommittedGraphics )
2397{
2398 SHAPE_T shape = ( *aGraphic )->GetShape();
2399
2400 wxASSERT( shape == SHAPE_T::SEGMENT || shape == SHAPE_T::CIRCLE || shape == SHAPE_T::RECTANGLE
2401 || shape == SHAPE_T::ELLIPSE || shape == SHAPE_T::ELLIPSE_ARC );
2402
2403 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
2404 EDA_UNITS userUnits = m_frame->GetUserUnits();
2405 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
2406 PCB_SHAPE*& graphic = *aGraphic;
2407
2408 if( m_layer != m_frame->GetActiveLayer() )
2409 {
2410 m_layer = m_frame->GetActiveLayer();
2411 m_stroke.SetWidth( bds.GetLineThickness( m_layer ) );
2412 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2413 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2414
2415 m_textAttrs.m_Size = bds.GetTextSize( m_layer );
2416 m_textAttrs.m_StrokeWidth = bds.GetTextThickness( m_layer );
2418 m_textAttrs.m_Italic = bds.GetTextItalic( m_layer );
2419 m_textAttrs.m_KeepUpright = bds.GetTextUpright( m_layer );
2420 m_textAttrs.m_Mirrored = m_board->IsBackLayer( m_layer );
2423 }
2424
2425 // Turn shapes on if they are off, so that the created object will be visible after completion
2426 m_frame->SetObjectVisible( LAYER_FILLED_SHAPES );
2427
2428 // geometric construction manager
2430
2431 // drawing assistant overlay
2432 // TODO: workaround because EDA_SHAPE_TYPE_T is not visible from commons.
2433 KIGFX::PREVIEW::GEOM_SHAPE geomShape = ( shape == SHAPE_T::ELLIPSE || shape == SHAPE_T::ELLIPSE_ARC )
2435 : static_cast<KIGFX::PREVIEW::GEOM_SHAPE>( shape );
2436 KIGFX::PREVIEW::TWO_POINT_ASSISTANT twoPointAsst( twoPointMgr, pcbIUScale, userUnits, geomShape );
2437
2438 // Add a VIEW_GROUP that serves as a preview for the new item
2439 m_preview.Clear();
2440 m_view->Add( &m_preview );
2441 m_view->Add( &twoPointAsst );
2442
2443 bool started = false;
2444 bool cancelled = false;
2445 bool multiPhase = false;
2446 PCB_SHAPE* marker = nullptr;
2447 bool isLocalOriginSet = ( m_frame->GetScreen()->m_LocalOrigin != VECTOR2D( 0, 0 ) );
2448 VECTOR2I cursorPos = m_controls->GetMousePosition();
2449
2450 auto setCursor =
2451 [&]()
2452 {
2453 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2454 };
2455
2456 auto cleanup =
2457 [&]()
2458 {
2459 m_preview.Clear();
2460 m_view->Update( &m_preview );
2461 delete graphic;
2462 graphic = nullptr;
2463
2464 delete marker;
2465 marker = nullptr;
2466
2467 if( !isLocalOriginSet )
2468 m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
2469 };
2470
2471 m_controls->ShowCursor( true );
2472 m_controls->ForceCursorPosition( false );
2473 // Set initial cursor
2474 setCursor();
2475
2476 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2477
2478 if( aStartingPoint )
2479 m_toolMgr->PrimeTool( *aStartingPoint );
2480
2481 // Main loop: keep receiving events
2482 while( TOOL_EVENT* evt = Wait() )
2483 {
2484 setCursor();
2485
2486 if( started )
2487 m_frame->SetMsgPanel( graphic );
2488
2489 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2490 auto angleSnap = GetAngleSnapMode();
2491
2492 // Drawing rectangles and circles ignore the snap behavior by default, but constrains
2493 // when the modifier key is pressed
2494 if( shape == SHAPE_T::RECTANGLE || shape == SHAPE_T::CIRCLE || shape == SHAPE_T::ELLIPSE
2495 || shape == SHAPE_T::ELLIPSE_ARC )
2496 {
2497 if( evt->Modifier( MD_CTRL ) )
2498 angleSnap = LEADER_MODE::DEG45;
2499 else
2500 angleSnap = LEADER_MODE::DIRECT;
2501 }
2502 else {
2503 // All other drawing uses the snap mode, except that is disabled with the modifier key
2504 if( evt->Modifier( MD_CTRL ) )
2505 angleSnap = LEADER_MODE::DIRECT;
2506 }
2507
2508 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2509 cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(), { m_layer }, GRID_GRAPHICS ),
2511 m_controls->ForceCursorPosition( true, cursorPos );
2512
2513 if( evt->IsCancelInteractive() || ( started && evt->IsAction( &ACTIONS::undo ) ) )
2514 {
2515 cleanup();
2516
2517 if( !started )
2518 {
2519 // We've handled the cancel event. Don't cancel other tools
2520 evt->SetPassEvent( false );
2521 m_frame->PopTool( aTool );
2522 cancelled = true;
2523 }
2524
2525 break;
2526 }
2527 else if( evt->IsActivate() )
2528 {
2529 if( evt->IsPointEditor() )
2530 {
2531 // don't exit (the point editor runs in the background)
2532 }
2533 else if( evt->IsMoveTool() )
2534 {
2535 cleanup();
2536 // leave ourselves on the stack so we come back after the move
2537 cancelled = true;
2538 break;
2539 }
2540 else
2541 {
2542 cleanup();
2543 m_frame->PopTool( aTool );
2544 cancelled = true;
2545 break;
2546 }
2547 }
2548 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2549 {
2550 if( m_layer != m_frame->GetActiveLayer() )
2551 {
2552 m_layer = m_frame->GetActiveLayer();
2553 m_stroke.SetWidth( bds.GetLineThickness( m_layer ) );
2554 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2555 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2556
2557 m_textAttrs.m_Size = bds.GetTextSize( m_layer );
2558 m_textAttrs.m_StrokeWidth = bds.GetTextThickness( m_layer );
2560 m_textAttrs.m_Italic = bds.GetTextItalic( m_layer );
2561 m_textAttrs.m_KeepUpright = bds.GetTextUpright( m_layer );
2562 m_textAttrs.m_Mirrored = m_board->IsBackLayer( m_layer );
2565 }
2566
2567 if( graphic )
2568 {
2569 if( !m_view->IsLayerVisible( m_layer ) )
2570 {
2571 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
2572 m_frame->GetCanvas()->Refresh();
2573 }
2574
2575 graphic->SetLayer( m_layer );
2576 graphic->SetStroke( m_stroke );
2577
2578 if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
2579 pcb_textbox->SetAttributes( m_textAttrs );
2580
2581 m_view->Update( &m_preview );
2582 frame()->SetMsgPanel( graphic );
2583 }
2584 else
2585 {
2586 evt->SetPassEvent();
2587 }
2588 }
2589 else if( evt->IsClick( BUT_RIGHT ) )
2590 {
2591 if( !graphic )
2592 m_toolMgr->VetoContextMenuMouseWarp();
2593
2594 m_menu->ShowContextMenu( selection() );
2595 }
2596 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
2597 {
2598 if( !graphic )
2599 break;
2600
2601 if( !started )
2602 {
2603 m_toolMgr->RunAction( ACTIONS::selectionClear );
2604
2605 if( aStartingPoint )
2606 {
2607 cursorPos = *aStartingPoint;
2608 aStartingPoint = std::nullopt;
2609 }
2610
2611 // Init the new item attributes
2612 if( graphic ) // always true, but Coverity can't seem to figure that out
2613 {
2614 graphic->SetShape( static_cast<SHAPE_T>( shape ) );
2615 graphic->SetFilled( false );
2616 graphic->SetStroke( m_stroke );
2617 graphic->SetLayer( m_layer );
2618 }
2619
2620 if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
2621 pcb_textbox->SetAttributes( m_textAttrs );
2622
2623 grid.SetSkipPoint( cursorPos );
2624
2625 twoPointMgr.SetOrigin( cursorPos );
2626 twoPointMgr.SetEnd( cursorPos );
2627
2628 if( !isLocalOriginSet )
2629 m_frame->GetScreen()->m_LocalOrigin = cursorPos;
2630
2631 m_preview.Add( graphic );
2632 frame()->SetMsgPanel( graphic );
2633 m_controls->SetAutoPan( true );
2634 m_controls->CaptureCursor( true );
2635
2636 if( !m_view->IsLayerVisible( m_layer ) )
2637 {
2638 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
2639 m_frame->GetCanvas()->Refresh();
2640 }
2641
2642 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
2643
2644 if( shape == SHAPE_T::ELLIPSE_ARC )
2645 graphic->SetEditState( 1 );
2646
2647 started = true;
2648 }
2649 else if( multiPhase )
2650 {
2651 if( !graphic->ContinueEdit( cursorPos ) )
2652 {
2653 // Multi-phase shape is complete
2654 if( marker )
2655 {
2656 m_preview.Remove( marker );
2657 delete marker;
2658 marker = nullptr;
2659 }
2660
2661 graphic->EndEdit();
2662 graphic->ClearEditFlags();
2663 graphic->SetFlags( IS_NEW );
2664 m_preview.Clear();
2665 break;
2666 }
2667 }
2668 else
2669 {
2670 // Check if the shape needs more clicks
2671 if( graphic->ContinueEdit( cursorPos ) )
2672 {
2673 multiPhase = true;
2674 m_view->Remove( &twoPointAsst );
2675 m_view->Update( &m_preview );
2676 }
2677 else
2678 {
2679 PCB_SHAPE* snapItem = dynamic_cast<PCB_SHAPE*>( grid.GetSnapped() );
2680
2681 if( shape == SHAPE_T::SEGMENT && snapItem && graphic->GetLength() > 0 )
2682 {
2683 // User has clicked on the end of an existing segment, closing a path
2684 BOARD_COMMIT commit( m_frame );
2685
2686 commit.Add( graphic );
2687 commit.Push( _( "Draw Line" ) );
2688 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, graphic );
2689
2690 graphic = nullptr;
2691 }
2692 else if( twoPointMgr.IsEmpty() || evt->IsDblClick( BUT_LEFT ) )
2693 {
2694 // User has clicked twice in the same spot, meaning we're finished
2695 delete graphic;
2696 graphic = nullptr;
2697 }
2698
2699 m_preview.Clear();
2700 twoPointMgr.Reset();
2701 break;
2702 }
2703 }
2704
2705 twoPointMgr.SetEnd( GetClampedCoords( cursorPos ) );
2706 }
2707 else if( evt->IsMotion() )
2708 {
2709 if( multiPhase )
2710 {
2711 graphic->CalcEdit( GetClampedCoords( cursorPos ) );
2712
2713 if( shape == SHAPE_T::ELLIPSE_ARC )
2714 {
2715 VECTOR2I markerPos = evalEllipsePoint( graphic, cursorPos );
2716
2717 if( !marker )
2718 {
2719 marker = new PCB_SHAPE( static_cast<BOARD_ITEM*>( m_frame->GetModel() ) );
2720 marker->SetShape( SHAPE_T::CIRCLE );
2721 marker->SetFilled( true );
2722 marker->SetLayer( m_layer );
2723 marker->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
2724 m_preview.Add( marker );
2725 }
2726
2727 int radius = KiROUND( m_view->ToWorld( 4 ) );
2728 marker->SetStart( markerPos );
2729 marker->SetEnd( markerPos + VECTOR2I( radius, 0 ) );
2730 }
2731
2732 m_view->Update( &m_preview );
2733 frame()->SetMsgPanel( graphic );
2734 }
2735 else
2736 {
2737 VECTOR2I clampedCursorPos = cursorPos;
2738
2739 if( shape == SHAPE_T::CIRCLE || shape == SHAPE_T::ARC )
2740 clampedCursorPos = getClampedRadiusEnd( twoPointMgr.GetOrigin(), cursorPos );
2741 else
2742 clampedCursorPos = getClampedDifferenceEnd( twoPointMgr.GetOrigin(), cursorPos );
2743
2744 // constrained lines
2745 if( started && angleSnap != LEADER_MODE::DIRECT )
2746 {
2747 const VECTOR2I lineVector( clampedCursorPos - VECTOR2I( twoPointMgr.GetOrigin() ) );
2748
2749 VECTOR2I newEnd;
2750 if( angleSnap == LEADER_MODE::DEG90 )
2751 newEnd = GetVectorSnapped90( lineVector );
2752 else
2753 newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECTANGLE ) );
2754
2755 m_controls->ForceCursorPosition( true, VECTOR2I( twoPointMgr.GetEnd() ) );
2756 twoPointMgr.SetEnd( twoPointMgr.GetOrigin() + newEnd );
2757 twoPointMgr.SetAngleSnap( angleSnap );
2758 }
2759 else
2760 {
2761 twoPointMgr.SetEnd( clampedCursorPos );
2762 twoPointMgr.SetAngleSnap( LEADER_MODE::DIRECT );
2763 }
2764
2765 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
2766 m_view->Update( &m_preview );
2767 m_view->Update( &twoPointAsst );
2768 }
2769 }
2770 else if( started && ( evt->IsAction( &PCB_ACTIONS::doDelete )
2771 || evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) ) )
2772 {
2773 if( aCommittedGraphics && !aCommittedGraphics->empty() )
2774 {
2775 twoPointMgr.SetOrigin( aCommittedGraphics->top()->GetStart() );
2776 twoPointMgr.SetEnd( aCommittedGraphics->top()->GetEnd() );
2777 aCommittedGraphics->pop();
2778
2779 getViewControls()->WarpMouseCursor( twoPointMgr.GetEnd(), true );
2780
2781 if( PICKED_ITEMS_LIST* undo = m_frame->PopCommandFromUndoList() )
2782 {
2783 m_frame->PutDataInPreviousState( undo );
2784 m_frame->ClearListAndDeleteItems( undo );
2785 delete undo;
2786 }
2787
2788 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
2789 m_view->Update( &m_preview );
2790 m_view->Update( &twoPointAsst );
2791 }
2792 else
2793 {
2794 cleanup();
2795 break;
2796 }
2797 }
2798 else if( graphic && evt->IsAction( &PCB_ACTIONS::incWidth ) )
2799 {
2800 m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
2801 graphic->SetStroke( m_stroke );
2802 m_view->Update( &m_preview );
2803 frame()->SetMsgPanel( graphic );
2804 }
2805 else if( graphic && evt->IsAction( &PCB_ACTIONS::decWidth ) )
2806 {
2807 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
2808 {
2809 m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
2810 graphic->SetStroke( m_stroke );
2811 m_view->Update( &m_preview );
2812 frame()->SetMsgPanel( graphic );
2813 }
2814 }
2815 else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
2816 {
2817 frame()->OnEditItemRequest( graphic );
2818 m_view->Update( &m_preview );
2819 frame()->SetMsgPanel( graphic );
2820 }
2821 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
2822 || evt->IsAction( &ACTIONS::redo ) ) )
2823 {
2824 wxBell();
2825 }
2826 else if( evt->IsAction( &ACTIONS::resetLocalCoords ) )
2827 {
2828 isLocalOriginSet = true;
2829 evt->SetPassEvent();
2830 }
2831 else if( evt->IsAction( &ACTIONS::updateUnits ) )
2832 {
2833 if( frame()->GetUserUnits() != userUnits )
2834 {
2835 userUnits = frame()->GetUserUnits();
2836 twoPointAsst.SetUnits( userUnits );
2837 m_view->Update( &twoPointAsst );
2838 }
2839 evt->SetPassEvent();
2840 }
2841 else
2842 {
2843 evt->SetPassEvent();
2844 }
2845 }
2846
2847 if( !isLocalOriginSet ) // reset the relative coordinate if it was not set before
2848 m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
2849
2850 if( !multiPhase )
2851 m_view->Remove( &twoPointAsst );
2852
2853 m_view->Remove( &m_preview );
2854
2855 if( selection().Empty() )
2856 m_frame->SetMsgPanel( board() );
2857
2858 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2859 m_controls->SetAutoPan( false );
2860 m_controls->CaptureCursor( false );
2861 m_controls->ForceCursorPosition( false );
2862
2863 return !cancelled;
2864}
2865
2866
2871 PCB_SHAPE& aArc )
2872{
2873 VECTOR2I vec = aMgr.GetOrigin();
2874
2875 aArc.SetCenter( vec );
2876
2877 if( aMgr.GetSubtended() < ANGLE_0 )
2878 {
2879 vec = aMgr.GetStartRadiusEnd();
2880 aArc.SetStart( vec );
2881 vec = aMgr.GetEndRadiusEnd();
2882 aArc.SetEnd( vec );
2883 }
2884 else
2885 {
2886 vec = aMgr.GetEndRadiusEnd();
2887 aArc.SetStart( vec );
2888 vec = aMgr.GetStartRadiusEnd();
2889 aArc.SetEnd( vec );
2890 }
2891}
2892
2893
2894bool DRAWING_TOOL::drawArc( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
2895 std::optional<VECTOR2D> aStartingPoint )
2896{
2897 wxCHECK( aGraphic, false );
2898
2899 PCB_SHAPE*& graphic = *aGraphic;
2900
2901 wxCHECK( graphic, false );
2902
2903 if( m_layer != m_frame->GetActiveLayer() )
2904 {
2905 m_layer = m_frame->GetActiveLayer();
2906 m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
2907 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2908 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2909 }
2910
2911 // Arc geometric construction manager
2913
2914 // Arc drawing assistant overlay
2915 KIGFX::PREVIEW::ARC_ASSISTANT arcAsst( arcManager, pcbIUScale, m_frame->GetUserUnits() );
2916
2917 // Add a VIEW_GROUP that serves as a preview for the new item
2918 PCB_SELECTION preview;
2919 m_view->Add( &preview );
2920 m_view->Add( &arcAsst );
2921 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
2922
2923 auto setCursor =
2924 [&]()
2925 {
2926 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2927 };
2928
2929 auto cleanup =
2930 [&] ()
2931 {
2932 preview.Clear();
2933 delete *aGraphic;
2934 *aGraphic = nullptr;
2935 };
2936
2937 m_controls->ShowCursor( true );
2938 m_controls->ForceCursorPosition( false );
2939 // Set initial cursor
2940 setCursor();
2941
2942 bool started = false;
2943 bool cancelled = false;
2944
2945 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2946
2947 if( aStartingPoint )
2948 m_toolMgr->PrimeTool( *aStartingPoint );
2949
2950 // Main loop: keep receiving events
2951 while( TOOL_EVENT* evt = Wait() )
2952 {
2953 if( started )
2954 m_frame->SetMsgPanel( graphic );
2955
2956 setCursor();
2957
2958 graphic->SetLayer( m_layer );
2959
2960 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2961 LEADER_MODE angleSnap = GetAngleSnapMode();
2962
2963 if( evt->Modifier( MD_CTRL ) )
2964 angleSnap = LEADER_MODE::DIRECT;
2965
2966 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2967 VECTOR2I cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(), graphic,
2968 GRID_GRAPHICS ),
2970 m_controls->ForceCursorPosition( true, cursorPos );
2971
2972 if( evt->IsCancelInteractive() || ( started && evt->IsAction( &ACTIONS::undo ) ) )
2973 {
2974 cleanup();
2975
2976 if( !started )
2977 {
2978 // We've handled the cancel event. Don't cancel other tools
2979 evt->SetPassEvent( false );
2980 m_frame->PopTool( aTool );
2981 cancelled = true;
2982 }
2983
2984 break;
2985 }
2986 else if( evt->IsActivate() )
2987 {
2988 if( evt->IsPointEditor() )
2989 {
2990 // don't exit (the point editor runs in the background)
2991 }
2992 else if( evt->IsMoveTool() )
2993 {
2994 cleanup();
2995 // leave ourselves on the stack so we come back after the move
2996 cancelled = true;
2997 break;
2998 }
2999 else
3000 {
3001 cleanup();
3002 m_frame->PopTool( aTool );
3003 cancelled = true;
3004 break;
3005 }
3006 }
3007 else if( evt->IsClick( BUT_LEFT ) )
3008 {
3009 if( !started )
3010 {
3011 m_toolMgr->RunAction( ACTIONS::selectionClear );
3012
3013 m_controls->SetAutoPan( true );
3014 m_controls->CaptureCursor( true );
3015
3016 // Init the new item attributes
3017 // (non-geometric, those are handled by the manager)
3018 graphic->SetShape( SHAPE_T::ARC );
3019 graphic->SetStroke( m_stroke );
3020
3021 if( !m_view->IsLayerVisible( m_layer ) )
3022 {
3023 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3024 m_frame->GetCanvas()->Refresh();
3025 }
3026
3027 preview.Add( graphic );
3028 frame()->SetMsgPanel( graphic );
3029 started = true;
3030 }
3031
3032 arcManager.AddPoint( cursorPos, true );
3033 }
3034 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
3035 {
3036 arcManager.RemoveLastPoint();
3037 }
3038 else if( evt->IsMotion() )
3039 {
3040 // set angle snap
3041 arcManager.SetAngleSnap( angleSnap != LEADER_MODE::DIRECT );
3042
3043 // update, but don't step the manager state
3044 arcManager.AddPoint( cursorPos, false );
3045 }
3046 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3047 {
3048 if( m_layer != m_frame->GetActiveLayer() )
3049 {
3050 m_layer = m_frame->GetActiveLayer();
3051 m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
3052 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
3053 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
3054 }
3055
3056 if( graphic )
3057 {
3058 if( !m_view->IsLayerVisible( m_layer ) )
3059 {
3060 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3061 m_frame->GetCanvas()->Refresh();
3062 }
3063
3064 graphic->SetLayer( m_layer );
3065 graphic->SetStroke( m_stroke );
3066 m_view->Update( &preview );
3067 frame()->SetMsgPanel( graphic );
3068 }
3069 else
3070 {
3071 evt->SetPassEvent();
3072 }
3073 }
3074 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
3075 {
3077 {
3078 graphic->SetArcAngleAndEnd( ANGLE_90 );
3079 frame()->OnEditItemRequest( graphic );
3080 m_view->Update( &preview );
3081 frame()->SetMsgPanel( graphic );
3082 break;
3083 }
3084 // Don't show the edit panel if we can't represent the arc with it
3085 else if( ( arcManager.GetStep() == KIGFX::PREVIEW::ARC_GEOM_MANAGER::SET_ANGLE )
3086 && ( arcManager.GetStartRadiusEnd() != arcManager.GetEndRadiusEnd() ) )
3087 {
3088 frame()->OnEditItemRequest( graphic );
3089 m_view->Update( &preview );
3090 frame()->SetMsgPanel( graphic );
3091 break;
3092 }
3093 else
3094 {
3095 evt->SetPassEvent();
3096 }
3097 }
3098 else if( evt->IsClick( BUT_RIGHT ) )
3099 {
3100 if( !graphic )
3101 m_toolMgr->VetoContextMenuMouseWarp();
3102
3103 m_menu->ShowContextMenu( selection() );
3104 }
3105 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
3106 {
3107 m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
3108
3109 if( graphic )
3110 {
3111 graphic->SetStroke( m_stroke );
3112 m_view->Update( &preview );
3113 frame()->SetMsgPanel( graphic );
3114 }
3115 }
3116 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
3117 {
3118 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
3119 {
3120 m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
3121
3122 if( graphic )
3123 {
3124 graphic->SetStroke( m_stroke );
3125 m_view->Update( &preview );
3126 frame()->SetMsgPanel( graphic );
3127 }
3128 }
3129 }
3130 else if( evt->IsAction( &PCB_ACTIONS::arcPosture ) )
3131 {
3132 arcManager.ToggleClockwise();
3133 }
3134 else if( evt->IsAction( &ACTIONS::updateUnits ) )
3135 {
3136 arcAsst.SetUnits( frame()->GetUserUnits() );
3137 m_view->Update( &arcAsst );
3138 evt->SetPassEvent();
3139 }
3140 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
3141 || evt->IsAction( &ACTIONS::redo ) ) )
3142 {
3143 wxBell();
3144 }
3145 else
3146 {
3147 evt->SetPassEvent();
3148 }
3149
3150 if( arcManager.IsComplete() )
3151 {
3152 break;
3153 }
3154 else if( arcManager.HasGeometryChanged() )
3155 {
3156 updateArcFromConstructionMgr( arcManager, *graphic );
3157 m_view->Update( &preview );
3158 m_view->Update( &arcAsst );
3159
3160 if( started )
3161 frame()->SetMsgPanel( graphic );
3162 else
3163 frame()->SetMsgPanel( board() );
3164 }
3165 }
3166
3167 preview.Remove( graphic );
3168 m_view->Remove( &arcAsst );
3169 m_view->Remove( &preview );
3170
3171 if( selection().Empty() )
3172 m_frame->SetMsgPanel( board() );
3173
3174 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3175 m_controls->SetAutoPan( false );
3176 m_controls->CaptureCursor( false );
3177 m_controls->ForceCursorPosition( false );
3178
3179 return !cancelled;
3180}
3181
3182
3183std::unique_ptr<PCB_SHAPE> DRAWING_TOOL::drawOneBezier( const TOOL_EVENT& aTool,
3184 const OPT_VECTOR2I& aStartingPoint,
3185 const OPT_VECTOR2I& aStartingControl1Point,
3186 DRAW_ONE_RESULT& aResult )
3187{
3188 int maxError = board()->GetDesignSettings().m_MaxError;
3189
3190 std::unique_ptr<PCB_SHAPE> bezier = std::make_unique<PCB_SHAPE>( m_frame->GetModel() );
3191 bezier->SetShape( SHAPE_T::BEZIER );
3192 bezier->SetFlags( IS_NEW );
3193
3194 if( m_layer != m_frame->GetActiveLayer() )
3195 {
3196 m_layer = m_frame->GetActiveLayer();
3197 m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
3198 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
3199 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
3200 }
3201
3202 // Arc geometric construction manager
3204
3205 // Arc drawing assistant overlay
3206 KIGFX::PREVIEW::BEZIER_ASSISTANT bezierAsst( bezierManager, pcbIUScale, m_frame->GetUserUnits() );
3207
3208 // Add a VIEW_GROUP that serves as a preview for the new item
3209 PCB_SELECTION preview;
3210 m_view->Add( &preview );
3211 m_view->Add( &bezierAsst );
3212 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
3213
3214 const auto setCursor =
3215 [&]()
3216 {
3217 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
3218 };
3219
3220 const auto resetProgress =
3221 [&]()
3222 {
3223 preview.Clear();
3224 bezier.reset();
3225 };
3226
3227 m_controls->ShowCursor( true );
3228 m_controls->ForceCursorPosition( false );
3229 // Set initial cursor
3230 setCursor();
3231
3232 const auto started =
3233 [&]()
3234 {
3236 };
3237
3238 aResult = DRAW_ONE_RESULT::ACCEPTED;
3239 bool priming = false;
3240
3241 m_toolMgr->PostAction( ACTIONS::refreshPreview );
3242
3243 // Load in one or two points if they were passed in
3244 if( aStartingPoint )
3245 {
3246 priming = true;
3247
3248 if( aStartingControl1Point )
3249 {
3250 bezierManager.AddPoint( *aStartingPoint, true );
3251 bezierManager.AddPoint( *aStartingControl1Point, true );
3252 m_toolMgr->PrimeTool( *aStartingControl1Point );
3253 }
3254 else
3255 {
3256 bezierManager.AddPoint( *aStartingPoint, true );
3257 m_toolMgr->PrimeTool( *aStartingPoint );
3258 }
3259 }
3260
3261 // Main loop: keep receiving events
3262 while( TOOL_EVENT* evt = Wait() )
3263 {
3264 if( started() )
3265 m_frame->SetMsgPanel( bezier.get() );
3266
3267 setCursor();
3268
3269 // Init the new item attributes
3270 // (non-geometric, those are handled by the manager)
3271 bezier->SetShape( SHAPE_T::BEZIER );
3272 bezier->SetStroke( m_stroke );
3273 bezier->SetLayer( m_layer );
3274
3275 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
3276 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
3277 VECTOR2I cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(), bezier.get(),
3278 GRID_GRAPHICS ),
3280 m_controls->ForceCursorPosition( true, cursorPos );
3281
3282 if( evt->IsCancelInteractive() || ( started() && evt->IsAction( &ACTIONS::undo ) ) )
3283 {
3284 resetProgress();
3285
3286 if( !started() )
3287 {
3288 // We've handled the cancel event. Don't cancel other tools
3289 evt->SetPassEvent( false );
3290 m_frame->PopTool( aTool );
3292 }
3293 else
3294 {
3295 // We're not cancelling, but we're also not returning a finished bezier
3296 // So we'll be called again.
3297 aResult = DRAW_ONE_RESULT::RESET;
3298 }
3299
3300 break;
3301 }
3302 else if( evt->IsActivate() )
3303 {
3304 if( evt->IsPointEditor() )
3305 {
3306 // don't exit (the point editor runs in the background)
3307 }
3308 else if( evt->IsMoveTool() )
3309 {
3310 resetProgress();
3311 // leave ourselves on the stack so we come back after the move
3313 break;
3314 }
3315 else
3316 {
3317 resetProgress();
3318 m_frame->PopTool( aTool );
3320 break;
3321 }
3322 }
3323 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
3324 {
3325 if( !started() )
3326 {
3327 m_toolMgr->RunAction( ACTIONS::selectionClear );
3328
3329 m_controls->SetAutoPan( true );
3330 m_controls->CaptureCursor( true );
3331
3332 if( !m_view->IsLayerVisible( m_layer ) )
3333 {
3334 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3335 m_frame->GetCanvas()->Refresh();
3336 }
3337
3338 frame()->SetMsgPanel( bezier.get() );
3339 }
3340
3341 if( !priming )
3342 bezierManager.AddPoint( cursorPos, true );
3343 else
3344 priming = false;
3345
3346 const bool doubleClick = evt->IsDblClick( BUT_LEFT );
3347
3348 if( doubleClick )
3349 {
3350 // Use the current point for all remaining points
3351 while( bezierManager.GetStep() < KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END )
3352 bezierManager.AddPoint( cursorPos, true );
3353 }
3354
3356 preview.Add( bezier.get() );
3357
3358 // Return to the caller for a reset
3359 if( doubleClick )
3360 {
3361 // Don't chain to this one
3363 break;
3364 }
3365 }
3366 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
3367 {
3368 bezierManager.RemoveLastPoint();
3369
3371 preview.Remove( bezier.get() );
3372 }
3373 else if( evt->IsMotion() )
3374 {
3375 // set angle snap
3376 // bezierManager.SetAngleSnap( Is45Limited() );
3377
3378 // update, but don't step the manager state
3379 bezierManager.AddPoint( cursorPos, false );
3380 }
3381 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3382 {
3383 if( m_layer != m_frame->GetActiveLayer() )
3384 {
3385 m_layer = m_frame->GetActiveLayer();
3386 m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
3387 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
3388 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
3389 }
3390
3391 if( !m_view->IsLayerVisible( m_layer ) )
3392 {
3393 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3394 m_frame->GetCanvas()->Refresh();
3395 }
3396
3397 bezier->SetLayer( m_layer );
3398 bezier->SetStroke( m_stroke );
3399 m_view->Update( &preview );
3400 frame()->SetMsgPanel( bezier.get() );
3401 }
3402 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
3403 {
3404 // Don't show the edit panel if we can't represent the arc with it
3405 if( ( bezierManager.GetStep() >= KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END ) )
3406 {
3407 frame()->OnEditItemRequest( bezier.get() );
3408 m_view->Update( &preview );
3409 frame()->SetMsgPanel( bezier.get() );
3410 break;
3411 }
3412 else
3413 {
3414 evt->SetPassEvent();
3415 }
3416 }
3417 else if( evt->IsClick( BUT_RIGHT ) )
3418 {
3419 m_menu->ShowContextMenu( selection() );
3420 }
3421 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
3422 {
3423 m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
3424
3425 bezier->SetStroke( m_stroke );
3426 m_view->Update( &preview );
3427 frame()->SetMsgPanel( bezier.get() );
3428 }
3429 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
3430 {
3431 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
3432 {
3433 m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
3434
3435 bezier->SetStroke( m_stroke );
3436 m_view->Update( &preview );
3437 frame()->SetMsgPanel( bezier.get() );
3438 }
3439 }
3440 else if( evt->IsAction( &ACTIONS::updateUnits ) )
3441 {
3442 bezierAsst.SetUnits( frame()->GetUserUnits() );
3443 m_view->Update( &bezierAsst );
3444 evt->SetPassEvent();
3445 }
3446 else if( started() && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
3447 || evt->IsAction( &ACTIONS::redo ) ) )
3448 {
3449 wxBell();
3450 }
3451 else
3452 {
3453 evt->SetPassEvent();
3454 }
3455
3456 if( bezierManager.IsComplete() )
3457 {
3458 break;
3459 }
3460 else if( bezierManager.HasGeometryChanged() )
3461 {
3462 bezier->SetStart( bezierManager.GetStart() );
3463 bezier->SetBezierC1( bezierManager.GetControlC1() );
3464 bezier->SetEnd( bezierManager.GetEnd() );
3465 bezier->SetBezierC2( bezierManager.GetControlC2() );
3466 bezier->RebuildBezierToSegmentsPointsList( maxError );
3467
3468 m_view->Update( &preview );
3469 m_view->Update( &bezierAsst );
3470
3471 // Once we are receiving end points, we can show the bezier in the preview
3473 frame()->SetMsgPanel( bezier.get() );
3474 else
3475 frame()->SetMsgPanel( board() );
3476 }
3477 }
3478
3479 preview.Remove( bezier.get() );
3480 m_view->Remove( &bezierAsst );
3481 m_view->Remove( &preview );
3482
3483 if( selection().Empty() )
3484 m_frame->SetMsgPanel( board() );
3485
3486 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3487 m_controls->SetAutoPan( false );
3488 m_controls->CaptureCursor( false );
3489 m_controls->ForceCursorPosition( false );
3490
3491 return bezier;
3492};
3493
3494
3496{
3497 bool clearSelection = false;
3498 *aZone = nullptr;
3499
3500 // not an action that needs a source zone
3501 if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON )
3502 return true;
3503
3504 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3505 const PCB_SELECTION& selection = selTool->GetSelection();
3506
3507 if( selection.Empty() )
3508 {
3509 clearSelection = true;
3510 m_toolMgr->RunAction( ACTIONS::selectionCursor );
3511 }
3512
3513 // we want a single zone
3514 if( selection.Size() == 1 && selection[0]->Type() == PCB_ZONE_T )
3515 *aZone = static_cast<ZONE*>( selection[0] );
3516
3517 // expected a zone, but didn't get one
3518 if( !*aZone )
3519 {
3520 if( clearSelection )
3521 m_toolMgr->RunAction( ACTIONS::selectionClear );
3522
3523 return false;
3524 }
3525
3526 return true;
3527}
3528
3529
3531{
3532 if( m_isFootprintEditor && !m_frame->GetModel() )
3533 return 0;
3534
3535 if( m_inDrawingTool )
3536 return 0;
3537
3539
3540 ZONE_MODE zoneMode = aEvent.Parameter<ZONE_MODE>();
3541 MODE drawMode = MODE::ZONE;
3542
3543 if( aEvent.IsAction( &PCB_ACTIONS::drawRuleArea ) )
3544 drawMode = MODE::KEEPOUT;
3545
3546 if( aEvent.IsAction( &PCB_ACTIONS::drawPolygon ) )
3547 drawMode = MODE::GRAPHIC_POLYGON;
3548
3549 const bool drawingThieving = aEvent.IsAction( &PCB_ACTIONS::drawCopperThievingZone );
3550
3551 SCOPED_DRAW_MODE scopedDrawMode( m_mode, drawMode );
3552
3553 // get a source zone, if we need one. We need it for:
3554 // ZONE_MODE::CUTOUT (adding a hole to the source zone)
3555 // ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
3556 ZONE* sourceZone = nullptr;
3557
3558 if( !getSourceZoneForAction( zoneMode, &sourceZone ) )
3559 return 0;
3560
3561 // Turn zones on if they are off, so that the created object will be visible after completion
3562 m_frame->SetObjectVisible( LAYER_ZONES );
3563
3565
3566 params.m_keepout = drawMode == MODE::KEEPOUT;
3567 params.m_thieving = drawingThieving;
3568 params.m_mode = zoneMode;
3569 params.m_sourceZone = sourceZone;
3570 params.m_layer = m_frame->GetActiveLayer();
3571
3572 if( zoneMode == ZONE_MODE::SIMILAR && !sourceZone->IsOnLayer( params.m_layer ) )
3573 params.m_layer = sourceZone->GetFirstLayer();
3574
3575 ZONE_CREATE_HELPER zoneTool( *this, params );
3576 // the geometry manager which handles the zone geometry, and hands the calculated points
3577 // over to the zone creator tool
3578 POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
3579 bool started = false;
3580 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
3581
3582 m_frame->PushTool( aEvent );
3583
3584 auto setCursor =
3585 [&]()
3586 {
3587 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
3588 };
3589
3590 auto cleanup =
3591 [&] ()
3592 {
3593 polyGeomMgr.Reset();
3594 started = false;
3595 grid.ClearSkipPoint();
3596 m_controls->SetAutoPan( false );
3597 m_controls->CaptureCursor( false );
3598 };
3599
3600 Activate();
3601 // Must be done after Activate() so that it gets set into the correct context
3602 m_controls->ShowCursor( true );
3603 m_controls->ForceCursorPosition( false );
3604 // Set initial cursor
3605 setCursor();
3606
3607 if( aEvent.HasPosition() )
3608 m_toolMgr->PrimeTool( aEvent.Position() );
3609
3610 // Main loop: keep receiving events
3611 while( TOOL_EVENT* evt = Wait() )
3612 {
3613 setCursor();
3614
3615 LSET layers( { m_frame->GetActiveLayer() } );
3616 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
3617 LEADER_MODE angleSnap = GetAngleSnapMode();
3618
3619 if( evt->Modifier( MD_CTRL ) )
3620 angleSnap = LEADER_MODE::DIRECT;
3621
3622 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
3623
3624 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
3625 cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, layers, GRID_GRAPHICS ), COORDS_PADDING );
3626
3627 m_controls->ForceCursorPosition( true, cursorPos );
3628
3629 polyGeomMgr.SetLeaderMode( angleSnap );
3630
3631 if( evt->IsCancelInteractive() )
3632 {
3633 if( started )
3634 {
3635 cleanup();
3636 }
3637 else
3638 {
3639 m_frame->PopTool( aEvent );
3640
3641 // We've handled the cancel event. Don't cancel other tools
3642 evt->SetPassEvent( false );
3643 break;
3644 }
3645 }
3646 else if( evt->IsActivate() )
3647 {
3648 if( started )
3649 cleanup();
3650
3651 if( evt->IsPointEditor() )
3652 {
3653 // don't exit (the point editor runs in the background)
3654 }
3655 else if( evt->IsMoveTool() )
3656 {
3657 // leave ourselves on the stack so we come back after the move
3658 break;
3659 }
3660 else
3661 {
3662 m_frame->PopTool( aEvent );
3663 break;
3664 }
3665 }
3666 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3667 {
3668 if( zoneMode != ZONE_MODE::SIMILAR )
3669 params.m_layer = frame()->GetActiveLayer();
3670
3671 if( !m_view->IsLayerVisible( params.m_layer ) )
3672 {
3673 m_frame->GetAppearancePanel()->SetLayerVisible( params.m_layer, true );
3674 m_frame->GetCanvas()->Refresh();
3675 }
3676 }
3677 else if( evt->IsClick( BUT_RIGHT ) )
3678 {
3679 if( !started )
3680 m_toolMgr->VetoContextMenuMouseWarp();
3681
3682 m_menu->ShowContextMenu( selection() );
3683 }
3684 // events that lock in nodes
3685 else if( evt->IsClick( BUT_LEFT )
3686 || evt->IsDblClick( BUT_LEFT )
3687 || evt->IsAction( &PCB_ACTIONS::closeOutline ) )
3688 {
3689 // Check if it is double click / closing line (so we have to finish the zone)
3690 const bool endPolygon = evt->IsDblClick( BUT_LEFT )
3691 || evt->IsAction( &PCB_ACTIONS::closeOutline )
3692 || polyGeomMgr.NewPointClosesOutline( cursorPos );
3693
3694 if( endPolygon )
3695 {
3696 polyGeomMgr.SetFinished();
3697 polyGeomMgr.Reset();
3698
3699 cleanup();
3700 m_frame->PopTool( aEvent );
3701 break;
3702 }
3703 // adding a corner
3704 else if( polyGeomMgr.AddPoint( cursorPos ) )
3705 {
3706 if( !started )
3707 {
3708 started = true;
3709
3710 m_controls->SetAutoPan( true );
3711 m_controls->CaptureCursor( true );
3712
3713 if( !m_view->IsLayerVisible( params.m_layer ) )
3714 {
3715 m_frame->GetAppearancePanel()->SetLayerVisible( params.m_layer, true );
3716 m_frame->GetCanvas()->Refresh();
3717 }
3718 }
3719 }
3720 }
3721 else if( started && ( evt->IsAction( &PCB_ACTIONS::deleteLastPoint )
3722 || evt->IsAction( &ACTIONS::doDelete )
3723 || evt->IsAction( &ACTIONS::undo ) ) )
3724 {
3725 if( std::optional<VECTOR2I> last = polyGeomMgr.DeleteLastCorner() )
3726 {
3727 cursorPos = last.value();
3728 getViewControls()->WarpMouseCursor( cursorPos, true );
3729 m_controls->ForceCursorPosition( true, cursorPos );
3730 polyGeomMgr.SetCursorPosition( cursorPos );
3731 }
3732 else
3733 {
3734 cleanup();
3735 }
3736 }
3737 else if( started && ( evt->IsMotion()
3738 || evt->IsDrag( BUT_LEFT ) ) )
3739 {
3740 polyGeomMgr.SetCursorPosition( cursorPos );
3741 }
3742 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
3743 || evt->IsAction( &ACTIONS::redo ) ) )
3744 {
3745 wxBell();
3746 }
3747 else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
3748 {
3749 frame()->OnEditItemRequest( zoneTool.GetZone() );
3750 zoneTool.OnGeometryChange( polyGeomMgr );
3751 frame()->SetMsgPanel( zoneTool.GetZone() );
3752 }
3753 /*else if( evt->IsAction( &ACTIONS::updateUnits ) )
3754 {
3755 // If we ever have an assistant here that reports dimensions, we'll want to
3756 // update its units here....
3757 // zoneAsst.SetUnits( frame()->GetUserUnits() );
3758 // m_view->Update( &zoneAsst );
3759 evt->SetPassEvent();
3760 }*/
3761 else
3762 {
3763 evt->SetPassEvent();
3764 }
3765
3766 } // end while
3767
3768 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3769 m_controls->ForceCursorPosition( false );
3770 controls()->SetAutoPan( false );
3771 m_controls->CaptureCursor( false );
3772 return 0;
3773}
3774
3775
3777{
3779 return 0;
3780
3781 if( m_inDrawingTool )
3782 return 0;
3783
3785
3786 struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
3787 {
3789 PCB_GRID_HELPER m_gridHelper;
3790 std::shared_ptr<DRC_ENGINE> m_drcEngine;
3791 int m_drcEpsilon;
3792 int m_worstClearance;
3793 bool m_allowDRCViolations;
3794
3795 int sub_e( int aClearance )
3796 {
3797 return std::max( 0, aClearance - m_drcEpsilon );
3798 };
3799
3800 VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) :
3801 m_frame( aFrame ),
3802 m_gridHelper( aFrame->GetToolManager(), aFrame->GetMagneticItemsSettings() ),
3803 m_drcEngine( aFrame->GetBoard()->GetDesignSettings().m_DRCEngine ),
3804 m_drcEpsilon( aFrame->GetBoard()->GetDesignSettings().GetDRCEpsilon() ),
3805 m_worstClearance( 0 )
3806 {
3807 ROUTER_TOOL* router = m_frame->GetToolManager()->GetTool<ROUTER_TOOL>();
3808
3809 if( router )
3810 m_allowDRCViolations = router->Router()->Settings().AllowDRCViolations();
3811
3812 try
3813 {
3814 if( aFrame )
3815 m_drcEngine->InitEngine( aFrame->GetDesignRulesPath() );
3816
3817 DRC_CONSTRAINT constraint;
3818
3819 if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, constraint ) )
3820 m_worstClearance = constraint.GetValue().Min();
3821
3822 if( m_drcEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, constraint ) )
3823 m_worstClearance = std::max( m_worstClearance, constraint.GetValue().Min() );
3824
3825 for( FOOTPRINT* footprint : aFrame->GetBoard()->Footprints() )
3826 {
3827 for( PAD* pad : footprint->Pads() )
3828 {
3829 std::optional<int> padOverride = pad->GetClearanceOverrides( nullptr );
3830
3831 if( padOverride.has_value() )
3832 m_worstClearance = std::max( m_worstClearance, padOverride.value() );
3833 }
3834 }
3835 }
3836 catch( PARSE_ERROR& )
3837 {
3838 }
3839 }
3840
3841 virtual ~VIA_PLACER()
3842 {
3843 }
3844
3849 static BOX2I getEffectiveBoundingBox( const PCB_VIA& aVia, const VECTOR2I& aPosition )
3850 {
3851 BOX2I bbox = aVia.GetBoundingBox();
3852 bbox.Move( aPosition - aVia.GetPosition() );
3853 return bbox;
3854 }
3855
3856 PCB_TRACK* findTrack( const PCB_VIA* aVia, const VECTOR2I& aPosition ) const
3857 {
3858 const LSET lset = aVia->GetLayerSet();
3859 const BOX2I bbox = getEffectiveBoundingBox( *aVia, aPosition );
3860
3861 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
3862 KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
3863 std::vector<PCB_TRACK*> possible_tracks;
3864
3865 wxCHECK( view, nullptr );
3866
3867 view->Query( bbox, items );
3868
3869 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
3870 {
3871 if( !it.first->IsBOARD_ITEM() )
3872 continue;
3873
3874 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
3875
3876 if( !( item->GetLayerSet() & lset ).any() )
3877 continue;
3878
3879 if( item->Type() == PCB_TRACE_T )
3880 {
3881 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
3882
3883 if( TestSegmentHit( aPosition, track->GetStart(), track->GetEnd(),
3884 ( track->GetWidth() + aVia->GetWidth( track->GetLayer() ) ) / 2 ) )
3885 {
3886 possible_tracks.push_back( track );
3887 }
3888 }
3889 else if( item->Type() == PCB_ARC_T )
3890 {
3891 PCB_ARC* arc = static_cast<PCB_ARC*>( item );
3892
3893 if( arc->HitTest( aPosition, aVia->GetWidth( arc->GetLayer() ) / 2 ) )
3894 possible_tracks.push_back( arc );
3895 }
3896 }
3897
3898 PCB_TRACK* return_track = nullptr;
3899 int min_d = std::numeric_limits<int>::max();
3900
3901 for( PCB_TRACK* track : possible_tracks )
3902 {
3903 SEG test( track->GetStart(), track->GetEnd() );
3904 int dist = ( test.NearestPoint( aPosition ) - aPosition ).EuclideanNorm();
3905
3906 if( dist < min_d )
3907 {
3908 min_d = dist;
3909 return_track = track;
3910 }
3911 }
3912
3913 return return_track;
3914 }
3915
3916 bool hasDRCViolation( PCB_VIA* aVia, BOARD_ITEM* aOther )
3917 {
3918 DRC_CONSTRAINT constraint;
3919 int clearance;
3920 BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aOther );
3921 ZONE* zone = dynamic_cast<ZONE*>( aOther );
3922
3923 if( zone && zone->GetIsRuleArea() )
3924 {
3925 if( zone->GetDoNotAllowVias() )
3926 {
3927 bool hit = false;
3928
3930 [&]( PCB_LAYER_ID aLayer )
3931 {
3932 if( hit )
3933 return;
3934
3935 if( zone->Outline()->Collide( aVia->GetPosition(), aVia->GetWidth( aLayer ) / 2 ) )
3936 hit = true;
3937 } );
3938
3939 return hit;
3940 }
3941
3942 return false;
3943 }
3944
3945 if( connectedItem )
3946 {
3947 int connectedItemNet = connectedItem->GetNetCode();
3948
3949 if( connectedItemNet == 0 || connectedItemNet == aVia->GetNetCode() )
3950 return false;
3951 }
3952
3953 for( PCB_LAYER_ID layer : aOther->GetLayerSet() )
3954 {
3955 // Reference images are "on" a copper layer but are not actually part of it
3956 if( !IsCopperLayer( layer ) || aOther->Type() == PCB_REFERENCE_IMAGE_T )
3957 continue;
3958
3959 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aVia, aOther, layer );
3960 clearance = constraint.GetValue().Min();
3961
3962 if( clearance >= 0 )
3963 {
3964 std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( layer );
3965 std::shared_ptr<SHAPE> otherShape = aOther->GetEffectiveShape( layer );
3966
3967 if( viaShape->Collide( otherShape.get(), sub_e( clearance ) ) )
3968 return true;
3969 }
3970 }
3971
3972 if( aOther->HasHole() )
3973 {
3974 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aVia, aOther, UNDEFINED_LAYER );
3975 clearance = constraint.GetValue().Min();
3976
3977 if( clearance >= 0 )
3978 {
3979 std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( UNDEFINED_LAYER );
3980
3981 if( viaShape->Collide( aOther->GetEffectiveHoleShape().get(), sub_e( clearance ) ) )
3982 return true;
3983 }
3984 }
3985
3986 return false;
3987 }
3988
3989 bool checkDRCViolation( PCB_VIA* aVia )
3990 {
3991 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
3992 std::set<BOARD_ITEM*> checkedItems;
3993 BOX2I bbox = aVia->GetBoundingBox();
3994
3995 bbox.Inflate( m_worstClearance );
3996 m_frame->GetCanvas()->GetView()->Query( bbox, items );
3997
3998 for( std::pair<KIGFX::VIEW_ITEM*, int> it : items )
3999 {
4000 if( !it.first->IsBOARD_ITEM() )
4001 continue;
4002
4003 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
4004
4005 if( item->Type() == PCB_ZONE_T && !static_cast<ZONE*>( item )->GetIsRuleArea() )
4006 {
4007 continue; // stitching vias bind to zones, so ignore them
4008 }
4009 else if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_GROUP_T )
4010 {
4011 continue; // check against children, but not against footprint itself
4012 }
4013 else if( ( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
4014 && !static_cast<PCB_TEXT*>( item )->IsVisible() )
4015 {
4016 continue; // ignore hidden items
4017 }
4018 else if( checkedItems.count( item ) )
4019 {
4020 continue; // already checked
4021 }
4022
4023 if( hasDRCViolation( aVia, item ) )
4024 return true;
4025
4026 checkedItems.insert( item );
4027 }
4028
4029 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( DISALLOW_CONSTRAINT, aVia, nullptr,
4031
4032 if( constraint.m_DisallowFlags && constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
4033 return true;
4034
4035 return false;
4036 }
4037
4038 PAD* findPad( const PCB_VIA* aVia, const VECTOR2I& aPosition ) const
4039 {
4040 const LSET lset = aVia->GetLayerSet();
4041 const BOX2I bbox = getEffectiveBoundingBox( *aVia, aPosition );
4042
4043 const KIGFX::PCB_VIEW& view = *m_frame->GetCanvas()->GetView();
4044 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
4045
4046 view.Query( bbox, items );
4047
4048 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
4049 {
4050 if( !it.first->IsBOARD_ITEM() )
4051 continue;
4052
4053 BOARD_ITEM& item = static_cast<BOARD_ITEM&>( *it.first );
4054
4055 if( item.Type() == PCB_PAD_T && ( item.GetLayerSet() & lset ).any() )
4056 {
4057 PAD& pad = static_cast<PAD&>( item );
4058
4059 if( pad.HitTest( aPosition ) )
4060 return &pad;
4061 }
4062 }
4063
4064 return nullptr;
4065 }
4066
4067 PCB_SHAPE* findGraphic( const PCB_VIA* aVia, const VECTOR2I& aPosition ) const
4068 {
4069 const LSET lset = aVia->GetLayerSet() & LSET::AllCuMask();
4070 BOX2I bbox = getEffectiveBoundingBox( *aVia, aPosition );
4071
4072 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
4073 KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
4074 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
4075 std::vector<PCB_SHAPE*> possible_shapes;
4076
4077 view->Query( bbox, items );
4078
4079 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
4080 {
4081 if( !it.first->IsBOARD_ITEM() )
4082 continue;
4083
4084 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
4085
4086 if( !( item->GetLayerSet() & lset ).any() )
4087 continue;
4088
4089 if( item->Type() == PCB_SHAPE_T )
4090 {
4091 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
4092
4093 if( shape->HitTest( aPosition, aVia->GetWidth( activeLayer ) / 2 ) )
4094 possible_shapes.push_back( shape );
4095 }
4096 }
4097
4098 PCB_SHAPE* return_shape = nullptr;
4099 int min_d = std::numeric_limits<int>::max();
4100
4101 for( PCB_SHAPE* shape : possible_shapes )
4102 {
4103 int dist = ( shape->GetPosition() - aPosition ).EuclideanNorm();
4104
4105 if( dist < min_d )
4106 {
4107 min_d = dist;
4108 return_shape = shape;
4109 }
4110 }
4111
4112 return return_shape;
4113 }
4114
4115 std::optional<int> selectPossibleNetsByPopupMenu( std::set<int>& aNetcodeList )
4116 {
4117 ACTION_MENU menu( true );
4118 const NETINFO_LIST& netInfo = m_board->GetNetInfo();
4119 std::map<int, int> menuIDNetCodeMap;
4120 int menuID = 1;
4121
4122 for( int netcode : aNetcodeList )
4123 {
4124 wxString menuText;
4125 if( menuID < 10 )
4126 {
4127#ifdef __WXMAC__
4128 menuText = wxString::Format( "%s\t",
4129 netInfo.GetNetItem( netcode )->GetNetname() );
4130#else
4131 menuText = wxString::Format( "&%d %s\t",
4132 menuID,
4133 netInfo.GetNetItem( netcode )->GetNetname() );
4134#endif
4135 }
4136 else
4137 {
4138 menuText = netInfo.GetNetItem( netcode )->GetNetname();
4139 }
4140
4141 menu.Add( menuText, menuID, BITMAPS::INVALID_BITMAP );
4142 menuIDNetCodeMap[ menuID ] = netcode;
4143 menuID++;
4144 }
4145
4146 menu.SetTitle( _( "Select Net:" ) );
4147 menu.DisplayTitle( true );
4148
4149 DRAWING_TOOL* drawingTool = m_frame->GetToolManager()->GetTool<DRAWING_TOOL>();
4150 drawingTool->SetContextMenu( &menu, CMENU_NOW );
4151
4152 int selectedNetCode = -1;
4153 bool cancelled = false;
4154
4155 while( TOOL_EVENT* evt = drawingTool->Wait() )
4156 {
4157 if( evt->Action() == TA_CHOICE_MENU_UPDATE )
4158 {
4159 evt->SetPassEvent();
4160 }
4161 else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
4162 {
4163 std::optional<int> id = evt->GetCommandId();
4164
4165 // User has selected an item, so this one will be returned
4166 if( id && ( *id > 0 ) && ( *id < menuID ) )
4167 {
4168 selectedNetCode = menuIDNetCodeMap.at( *id );
4169 }
4170 // User has cancelled the menu (either by <esc> or clicking out of it),
4171 else
4172 {
4173 cancelled = true;
4174 }
4175 }
4176 else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
4177 {
4178 break;
4179 }
4180 }
4181
4182 if( cancelled )
4183 return std::optional<int>();
4184 else
4185 return selectedNetCode;
4186 }
4187
4188 std::optional<int> findStitchedZoneNet( PCB_VIA* aVia )
4189 {
4190 const VECTOR2I position = aVia->GetPosition();
4191 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
4192 std::set<int> netcodeList;
4193
4194 // See if there are any connections available on a high-contrast layer
4197 {
4198 if( aVia->GetLayerSet().test( m_frame->GetActiveLayer() ) )
4199 {
4200 for( ZONE* z : m_board->Zones() )
4201 {
4202 if( z->IsOnLayer( m_frame->GetActiveLayer() ) )
4203 {
4204 if( z->HitTestFilledArea( m_frame->GetActiveLayer(), position ) )
4205 netcodeList.insert( z->GetNetCode() );
4206 }
4207 }
4208 }
4209 }
4210
4211 // If there's only one, return it.
4212 if( netcodeList.size() == 1 )
4213 return *netcodeList.begin();
4214
4215 // See if there are any connections available on a visible layer
4216 LSET lset = LSET( m_board->GetVisibleLayers() & aVia->GetLayerSet() );
4217
4218 for( ZONE* z : m_board->Zones() )
4219 {
4220 if( z->GetIsRuleArea() )
4221 continue; // ignore rule areas
4222
4223 for( PCB_LAYER_ID layer : lset )
4224 {
4225 if( z->IsOnLayer( layer ) )
4226 {
4227 if( z->HitTestFilledArea( layer, position ) )
4228 netcodeList.insert( z->GetNetCode() );
4229 }
4230 }
4231 }
4232
4233 // If there's only one, return it.
4234 if( netcodeList.size() == 1 )
4235 return *netcodeList.begin();
4236
4237 if( netcodeList.size() > 1 )
4238 {
4239 // The net assignment is ambiguous. Let the user decide.
4240 return selectPossibleNetsByPopupMenu( netcodeList );
4241 }
4242 else
4243 {
4245 }
4246 }
4247
4248 void SnapItem( BOARD_ITEM *aItem ) override
4249 {
4250 m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
4251
4252 MAGNETIC_SETTINGS* settings = m_frame->GetMagneticItemsSettings();
4253 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
4254
4255 // When snapping, use the mouse position, not the item position, which may be
4256 // grid-snapped, so that we can get the cursor within snap-range of snap points.
4257 // If we don't get a snap, the via will be left as it is (i.e. maybe grid-snapped).
4258 KIGFX::VIEW_CONTROLS& viewControls = *m_frame->GetCanvas()->GetViewControls();
4259 const VECTOR2I position = viewControls.GetMousePosition();
4260
4261 if( settings->tracks != MAGNETIC_OPTIONS::NO_EFFECT && m_gridHelper.GetSnap() )
4262 {
4263 if( PCB_TRACK* track = findTrack( via, position ) )
4264 {
4265 SEG trackSeg( track->GetStart(), track->GetEnd() );
4266 VECTOR2I snap = m_gridHelper.AlignToSegment( position, trackSeg );
4267
4268 aItem->SetPosition( snap );
4269 return;
4270 }
4271 }
4272
4273 if( settings->pads != MAGNETIC_OPTIONS::NO_EFFECT && m_gridHelper.GetSnap() )
4274 {
4275 if( PAD* pad = findPad( via, position ) )
4276 {
4277 aItem->SetPosition( pad->GetPosition() );
4278 return;
4279 }
4280 }
4281
4282 if( settings->graphics && m_gridHelper.GetSnap() )
4283 {
4284 if( PCB_SHAPE* shape = findGraphic( via, position ) )
4285 {
4286 if( shape->IsAnyFill() )
4287 {
4288 // Is this shape something to be replaced by the via, or something to be
4289 // stitched by multiple vias? Use an area-based test to make a guess.
4290 SHAPE_POLY_SET poly;
4291 shape->TransformShapeToPolygon( poly, shape->GetLayer(), 0, ARC_LOW_DEF, ERROR_INSIDE );
4292 double shapeArea = poly.Area();
4293
4294 int R = via->GetWidth( shape->GetLayer() ) / 2;
4295 double viaArea = M_PI * R * R;
4296
4297 if( viaArea * 4 > shapeArea )
4298 aItem->SetPosition( shape->GetPosition() );
4299 }
4300 else
4301 {
4302 switch( shape->GetShape() )
4303 {
4304 case SHAPE_T::SEGMENT:
4305 {
4306 SEG seg( shape->GetStart(), shape->GetEnd() );
4307 VECTOR2I snap = m_gridHelper.AlignToSegment( position, seg );
4308 aItem->SetPosition( snap );
4309 break;
4310 }
4311
4312 case SHAPE_T::ARC:
4313 {
4314 if( ( shape->GetEnd() - position ).SquaredEuclideanNorm() <
4315 ( shape->GetStart() - position ).SquaredEuclideanNorm() )
4316 {
4317 aItem->SetPosition( shape->GetEnd() );
4318 }
4319 else
4320 {
4321 aItem->SetPosition( shape->GetStart() );
4322 }
4323
4324 break;
4325 }
4326
4327 case SHAPE_T::POLY:
4328 {
4329 if( !shape->IsPolyShapeValid() )
4330 {
4331 aItem->SetPosition( shape->GetPosition() );
4332 break;
4333 }
4334
4335 const SHAPE_POLY_SET& polySet = shape->GetPolyShape();
4336 std::optional<SEG> nearestSeg;
4337 int minDist = std::numeric_limits<int>::max();
4338
4339 for( int ii = 0; ii < polySet.OutlineCount(); ++ii )
4340 {
4341 const SHAPE_LINE_CHAIN& poly = polySet.Outline( ii );
4342
4343 for( int jj = 0; jj < poly.SegmentCount(); ++jj )
4344 {
4345 const SEG& seg = poly.GetSegment( jj );
4346 int dist = seg.Distance( position );
4347
4348 if( dist < minDist )
4349 {
4350 minDist = dist;
4351 nearestSeg = seg;
4352 }
4353 }
4354 }
4355
4356 if( nearestSeg )
4357 {
4358 VECTOR2I snap = m_gridHelper.AlignToSegment( position, *nearestSeg );
4359 aItem->SetPosition( snap );
4360 }
4361
4362 break;
4363 }
4364
4365 default:
4366 aItem->SetPosition( shape->GetPosition() );
4367 }
4368
4369 }
4370 }
4371
4372 }
4373 }
4374
4375 bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
4376 {
4377 WX_INFOBAR* infobar = m_frame->GetInfoBar();
4378 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
4379 VECTOR2I viaPos = via->GetPosition();
4380 PCB_TRACK* track = findTrack( via, via->GetPosition() );
4381 PAD* pad = findPad( via, via->GetPosition() );
4382 PCB_SHAPE* shape = findGraphic( via, via->GetPosition() );
4383
4384 if( track )
4385 {
4386 via->SetNetCode( track->GetNetCode() );
4387 via->SetIsFree( false );
4388 }
4389 else if( pad )
4390 {
4391 via->SetNetCode( pad->GetNetCode() );
4392 via->SetIsFree( false );
4393 }
4394 else if( shape && shape->GetNetCode() > 0 )
4395 {
4396 via->SetNetCode( shape->GetNetCode() );
4397 via->SetIsFree( false );
4398 }
4399 else
4400 {
4401 std::optional<int> netcode = findStitchedZoneNet( via );
4402
4403 if( !netcode.has_value() ) // user cancelled net disambiguation menu
4404 return false;
4405
4406 via->SetNetCode( netcode.value() );
4407 via->SetIsFree( via->GetNetCode() > 0 );
4408 }
4409
4410 if( checkDRCViolation( via ) )
4411 {
4412 m_frame->ShowInfoBarError( _( "Via location violates DRC." ), true,
4413 WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION );
4414
4415 if( !m_allowDRCViolations )
4416 return false;
4417 }
4418 else
4419 {
4420 if( infobar->GetMessageType() == WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION )
4421 infobar->Dismiss();
4422 }
4423
4424 aCommit.Add( via );
4425
4426 // If the user explicitly disables snap (using shift), then don't break the tracks.
4427 // This will prevent PNS from being able to connect the via and track but
4428 // it is explicitly requested by the user
4429 if( track && m_gridHelper.GetSnap() )
4430 {
4431 VECTOR2I trackStart = track->GetStart();
4432 VECTOR2I trackEnd = track->GetEnd();
4433 SEG trackSeg( trackStart, trackEnd );
4434
4435 if( viaPos == trackStart || viaPos == trackEnd )
4436 return true;
4437
4438 if( !trackSeg.Contains( viaPos ) )
4439 return true;
4440
4441 aCommit.Modify( track );
4442 track->SetStart( trackStart );
4443 track->SetEnd( viaPos );
4444
4445 PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
4446 newTrack->ResetUuidDirect();
4447
4448 newTrack->SetStart( viaPos );
4449 newTrack->SetEnd( trackEnd );
4450 aCommit.Add( newTrack );
4451 }
4452
4453 return true;
4454 }
4455
4456 std::unique_ptr<BOARD_ITEM> CreateItem() override
4457 {
4458 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
4459 PCB_VIA* via = new PCB_VIA( m_board );
4460
4461 via->SetNetCode( 0 );
4462 via->SetViaType( bds.m_CurrentViaType );
4463
4464 if( via->GetViaType() == VIATYPE::THROUGH )
4465 {
4466 via->SetLayerPair( B_Cu, F_Cu );
4467 }
4468 else
4469 {
4470 PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
4471 PCB_LAYER_ID last_layer;
4472
4473 // prepare switch to new active layer:
4474 if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
4475 last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
4476 else
4477 last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
4478
4479 via->SetLayerPair( first_layer, last_layer );
4480 }
4481
4482 if( via->GetViaType() == VIATYPE::MICROVIA )
4483 {
4484 via->SetWidth( PADSTACK::ALL_LAYERS,
4485 via->GetEffectiveNetClass()->GetuViaDiameter() );
4486 via->SetDrill( via->GetEffectiveNetClass()->GetuViaDrill() );
4487 }
4488 else
4489 {
4490 via->SetWidth( PADSTACK::ALL_LAYERS, bds.GetCurrentViaSize() );
4491 via->SetDrill( bds.GetCurrentViaDrill() );
4492 }
4493
4494 return std::unique_ptr<BOARD_ITEM>( via );
4495 }
4496 };
4497
4498 VIA_PLACER placer( frame() );
4499
4500 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
4501
4502 doInteractiveItemPlacement( aEvent, &placer, _( "Place via" ), IPO_REPEAT | IPO_SINGLE_CLICK );
4503
4504 return 0;
4505}
4506
4507
4509 const TOOL_EVENT& aEvent, SHAPE_T aShapeType, MODE aMode, const wxString& aCommitLabel,
4510 std::function<bool( const TOOL_EVENT&, PCB_SHAPE**, std::optional<VECTOR2D> )> aDrawer )
4511{
4512 if( m_isFootprintEditor && !m_frame->GetModel() )
4513 return 0;
4514
4515 if( m_inDrawingTool )
4516 return 0;
4517
4519
4520 BOARD_ITEM* parent = m_frame->GetModel();
4521 BOARD_COMMIT commit( m_frame );
4522 SCOPED_DRAW_MODE scopedDrawMode( m_mode, aMode );
4523 std::optional<VECTOR2D> startingPoint;
4524
4525 auto makeShape = [&]()
4526 {
4527 PCB_SHAPE* s = new PCB_SHAPE( parent );
4528 s->SetShape( aShapeType );
4529 s->SetFilled( false );
4530 s->SetFlags( IS_NEW );
4531 return s;
4532 };
4533
4534 PCB_SHAPE* shape = makeShape();
4535
4536 if( aEvent.HasPosition() )
4537 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
4538
4539 m_frame->PushTool( aEvent );
4540 Activate();
4541
4542 while( aDrawer( aEvent, &shape, startingPoint ) )
4543 {
4544 if( shape )
4545 {
4546 commit.Add( shape );
4547 commit.Push( aCommitLabel );
4548 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, shape );
4549 }
4550
4551 shape = makeShape();
4552 startingPoint = std::nullopt;
4553 }
4554
4555 return 0;
4556}
4557
4558
4559const unsigned int DRAWING_TOOL::WIDTH_STEP = pcbIUScale.mmToIU( 0.1 );
4560
4561
4563{
4564 // clang-format off
4593
4597 // clang-format on
4598}
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
constexpr int ARC_LOW_DEF
Definition base_units.h:140
@ width_track_via
@ INVALID_BITMAP
@ HIDDEN
Inactive layers are hidden.
@ DIMMED
Inactive layers are dimmed (old high-contrast mode)
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
static TOOL_ACTION cancelInteractive
Definition actions.h:72
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition actions.h:227
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition actions.h:217
static TOOL_ACTION updateUnits
Definition actions.h:207
static TOOL_ACTION undo
Definition actions.h:75
static TOOL_ACTION activatePointEditor
Definition actions.h:271
static TOOL_ACTION doDelete
Definition actions.h:85
static TOOL_ACTION cursorClick
Definition actions.h:180
static TOOL_ACTION redo
Definition actions.h:76
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:224
static TOOL_ACTION refreshPreview
Definition actions.h:159
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:232
static TOOL_ACTION resetLocalCoords
Definition actions.h:210
Define the structure of a menu based on ACTIONs.
Definition action_menu.h:47
ACTION_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool=nullptr)
Default constructor.
TOOL_MANAGER * getToolManager() const
Return an instance of TOOL_MANAGER class.
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.
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
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
void SetViaSizeIndex(int aIndex)
Set the current via size list index to aIndex.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
int GetDRCEpsilon() const
Return an epsilon which accounts for rounding errors, etc.
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
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:84
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:268
void ResetUuidDirect()
Definition board_item.h:245
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:316
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.
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:288
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
virtual bool HasHole() const
Definition board_item.h:180
const FOOTPRINTS & Footprints() const
Definition board.h:364
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1101
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 void Move(const Vec &aMoveVector)
Move the rectangle by the aMoveVector.
Definition box2.h:138
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
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:402
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:106
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:78
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
DIALOG_BARCODE_PROPERTIES, derived from DIALOG_BARCODE_PROPERTIES_BASE, created by wxFormBuilder.
std::list< std::unique_ptr< EDA_ITEM > > & GetImportedItems()
void SetFilenameOverride(const wxString &aFilenameOverride)
Set the filename override to be applied in TransferDataToWindow.
int ShowModal() override
Implementing DIALOG_TRACK_VIA_SIZE_BASE.
Tool responsible for drawing graphical elements like lines, arcs, circles, etc.
TEXT_ATTRIBUTES m_textAttrs
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
int DrawEllipseArc(const TOOL_EVENT &aEvent)
PCB_LAYER_ID m_layer
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 PlacePoint(const TOOL_EVENT &aEvent)
Place a reference 0D point.
int DrawVia(const TOOL_EVENT &aEvent)
KIGFX::VIEW_CONTROLS * m_controls
friend class ZONE_CREATE_HELPER
KIGFX::VIEW * m_view
STROKE_PARAMS m_stroke
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.
BOARD_CONNECTED_ITEM * m_pickerItem
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.
VECTOR2I getClampedRadiusEnd(const VECTOR2I &aOrigin, const VECTOR2I &aEnd)
Clamps the end vector to respect numeric limits of radius representation.
static const unsigned int WIDTH_STEP
int runSimpleShapeDraw(const TOOL_EVENT &aEvent, SHAPE_T aShapeType, MODE aMode, const wxString &aCommitLabel, std::function< bool(const TOOL_EVENT &, PCB_SHAPE **, std::optional< VECTOR2D >)> aDrawer)
static const unsigned int COORDS_PADDING
bool drawArc(const TOOL_EVENT &aTool, PCB_SHAPE **aGraphic, std::optional< VECTOR2D > aStartingPoint)
Start drawing an arc.
int DrawBarcode(const TOOL_EVENT &aEvent)
Starts interactively drawing a barcode.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
int DrawEllipse(const TOOL_EVENT &aEvent)
int DrawCircle(const TOOL_EVENT &aEvent)
Start interactively drawing a circle.
int PlaceTuningPattern(const TOOL_EVENT &aEvent)
BOARD * m_board
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
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...
PCB_TUNING_PATTERN * m_tuningPattern
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:245
SEVERITY GetSeverity() const
Definition drc_rule.h:221
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:200
double Sin() const
Definition eda_angle.h:178
double Cos() const
Definition eda_angle.h:197
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:100
virtual VECTOR2I GetPosition() const
Definition eda_item.h:279
virtual void ClearEditFlags()
Definition eda_item.h:163
virtual void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:280
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:149
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
void SetEllipseRotation(const EDA_ANGLE &aA)
Definition eda_shape.h:308
int GetEllipseMinorRadius() const
Definition eda_shape.h:306
const VECTOR2I & GetBezierC2() const
Definition eda_shape.h:279
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:288
void SetCenter(const VECTOR2I &aCenter)
void SetEllipseCenter(const VECTOR2I &aPt)
Definition eda_shape.h:281
int GetEllipseMajorRadius() const
Definition eda_shape.h:297
void SetEllipseStartAngle(const EDA_ANGLE &aA)
Definition eda_shape.h:318
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:315
SHAPE_T GetShape() const
Definition eda_shape.h:189
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:156
void SetEllipseEndAngle(const EDA_ANGLE &aA)
Definition eda_shape.h:327
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:236
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:198
void SetShape(SHAPE_T aShape)
Definition eda_shape.h:188
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:240
double GetLength() const
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
void SetEllipseMajorRadius(int aR)
Definition eda_shape.h:290
void SetEllipseMinorRadius(int aR)
Definition eda_shape.h:299
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:532
virtual bool IsVisible() const
Definition eda_text.h:212
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:576
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:392
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:306
bool GetSnap() const
void SetSnap(bool aSnap)
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
Represents an assistant draw when interactively drawing an arc on a canvas.
void SetUnits(EDA_UNITS aUnits)
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.
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
virtual void WarpMouseCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
If enabled (.
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:102
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition view.h:71
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
static const LSET & AllLayersMask()
Definition lset.cpp:641
T Min() const
Definition minoptmax.h:33
const wxString & GetNetname() const
Definition netinfo.h:103
Container for NETINFO_ITEM elements, which are the nets.
Definition netinfo.h:224
static const int ORPHANED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition netinfo.h:263
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::...
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:55
static TOOL_ACTION deleteLastPoint
static TOOL_ACTION drawRuleArea
static TOOL_ACTION changeDimensionArrows
Switch between dimension arrow directions.
static TOOL_ACTION drawBezier
static TOOL_ACTION placeText
static TOOL_ACTION drawOrthogonalDimension
static TOOL_ACTION drawRectangle
static TOOL_ACTION setAnchor
static TOOL_ACTION placeReferenceImage
static TOOL_ACTION drawCircle
static TOOL_ACTION tuneDiffPair
static TOOL_ACTION trackViaSizeChanged
static TOOL_ACTION layerChanged
static TOOL_ACTION drawEllipseArc
static TOOL_ACTION drawTable
static TOOL_ACTION drawTextBox
static TOOL_ACTION drawZoneCutout
static TOOL_ACTION drawPolygon
static TOOL_ACTION drawRadialDimension
static TOOL_ACTION tuneSingleTrack
static TOOL_ACTION properties
Activation of the edit tool.
static TOOL_ACTION drawLeader
static TOOL_ACTION drawCopperThievingZone
static TOOL_ACTION tuneSkew
static TOOL_ACTION incWidth
Increase width of currently drawn line.
static TOOL_ACTION drawEllipse
static TOOL_ACTION clearHighlight
static TOOL_ACTION spacingDecrease
static TOOL_ACTION placeImportedGraphics
static TOOL_ACTION drawVia
static TOOL_ACTION drawArc
static TOOL_ACTION drawSimilarZone
static TOOL_ACTION decWidth
Decrease width of currently drawn line.
static TOOL_ACTION drawCenterDimension
static TOOL_ACTION arcPosture
Switch posture when drawing arc.
static TOOL_ACTION ddImportGraphics
static TOOL_ACTION placeBarcode
static TOOL_ACTION placePoint
static TOOL_ACTION closeOutline
static TOOL_ACTION amplIncrease
static TOOL_ACTION amplDecrease
static TOOL_ACTION lengthTunerSettings
static TOOL_ACTION spacingIncrease
static TOOL_ACTION drawLine
static TOOL_ACTION drawAlignedDimension
static TOOL_ACTION drawZone
virtual bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
void SetTextSize(int aTextSize)
Change the height of the human-readable text displayed below the barcode.
void SetPosition(const VECTOR2I &aPos) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the drawing layer for the barcode and its text.
Common, abstract interface for edit frames.
wxString GetDesignRulesPath()
Return the absolute path to the design rules file for the currently-loaded board.
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
BOARD * GetBoard() const
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
For better understanding of the points that make a dimension:
double GetAngle() const
Return the angle of the crossbar.
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
Mark the center of a circle or arc with a cross shape.
A leader is a dimension-like object pointing to a specific point.
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
A radial dimension indicates either the radius or diameter of an arc or circle.
VECTOR2I GetKnee() const
HIGH_CONTRAST_MODE m_ContrastModeDisplay
How inactive layers are displayed.
The main frame for Pcbnew.
VECTOR2I AlignToSegment(const VECTOR2I &aPoint, const SEG &aSeg)
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:53
Object to handle a bitmap image that can be inserted in a PCB.
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
EDA_ITEM * GetTopLeftItem(bool aFootprintsOnly=false) const override
void EndEdit(bool aClosed=true)
Definition pcb_shape.h:86
void CalcEdit(const VECTOR2I &aPosition)
Definition pcb_shape.h:85
void SetEditState(int aState)
Definition pcb_shape.h:87
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition pcb_shape.h:129
void SetPosition(const VECTOR2I &aPos) override
Definition pcb_shape.h:78
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
bool ContinueEdit(const VECTOR2I &aPosition)
Definition pcb_shape.h:84
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition pcb_shape.h:98
void Normalize() override
Perform any normalization required after a user rotate and/or flip.
VECTOR2I GetPosition() const override
Definition pcb_shape.h:79
T * frame() const
KIGFX::PCB_VIEW * view() const
LEADER_MODE GetAngleSnapMode() const
Get the current angle snapping mode.
KIGFX::VIEW_CONTROLS * controls() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
BOARD * board() const
@ IPO_SINGLE_CLICK
Create an item immediately on placement starting, otherwise show the pencil cursor until the item is ...
@ IPO_REPEAT
Allow repeat placement of the item.
void doInteractiveItemPlacement(const TOOL_EVENT &aTool, INTERACTIVE_PLACER_BASE *aPlacer, const wxString &aCommitMessage, int aOptions=IPO_ROTATE|IPO_FLIP|IPO_REPEAT)
Helper function for performing a common interactive idiom: wait for a left click, place an item there...
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:93
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:96
virtual EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition pcb_track.cpp:73
const VECTOR2I & GetStart() const
Definition pcb_track.h:97
const VECTOR2I & GetEnd() const
Definition pcb_track.h:94
virtual int GetWidth() const
Definition pcb_track.h:91
VECTOR2I GetPosition() const override
Definition pcb_track.h:557
std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
const PADSTACK & Padstack() const
Definition pcb_track.h:406
int GetWidth() const override
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:541
A holder to handle information on schematic or board items.
ROUTING_SETTINGS & Settings()
Definition pns_router.h:214
ROUTER * Router() const
Class that handles the drawing of a polygon, including management of last corner deletion and drawing...
bool AddPoint(const VECTOR2I &aPt)
Lock in a polygon point.
void SetCursorPosition(const VECTOR2I &aPos)
Set the current cursor position.
bool NewPointClosesOutline(const VECTOR2I &aPt) const
std::optional< VECTOR2I > DeleteLastCorner()
Remove the last-added point from the polygon.
void SetFinished()
Mark the polygon finished and update the client.
void SetLeaderMode(LEADER_MODE aMode)
Set the leader mode to use when calculating the leader/returner lines.
void Reset()
Clear the manager state and start again.
RAII class that sets an value at construction and resets it to the original value at destruction.
Definition seg.h:42
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:702
bool Contains(const SEG &aSeg) const
Definition seg.h:324
int AddItemToSel(const TOOL_EVENT &aEvent)
virtual void Add(EDA_ITEM *aItem)
Definition selection.cpp:42
virtual void Remove(EDA_ITEM *aItem)
Definition selection.cpp:60
virtual void Clear() override
Remove all the stored items from the group.
Definition selection.h:98
void SetReferencePoint(const VECTOR2I &aP)
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
virtual const SEG GetSegment(int aIndex) const override
int SegmentCount() const
Return the number of segments in this line chain.
Represent a set of closed polygons.
double Area()
Return the area of this poly set.
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int OutlineCount() const
Return the number of outlines in the set.
Simple container to manage line stroke parameters.
GR_TEXT_H_ALIGN_T m_Halign
GR_TEXT_V_ALIGN_T m_Valign
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:186
T * getModel() const
Return the model object if it matches the requested type.
Definition tool_base.h:198
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:44
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:38
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:78
@ SHUTDOWN
Tool is being shut down.
Definition tool_base.h:84
Generic, UI-independent tool event.
Definition tool_event.h:171
bool HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition tool_event.h:260
bool DisableGridSnapping() const
Definition tool_event.h:371
bool HasParameter() const
Definition tool_event.h:464
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:293
bool IsReactivate() const
Control whether the tool is first being pushed to the stack or being reactivated after a pause.
Definition tool_event.h:273
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:473
void SetContextMenu(ACTION_MENU *aMenu, CONTEXT_MENU_TRIGGER aTrigger=CMENU_BUTTON)
Assign a context menu and tells when it should be activated.
void RunMainStack(std::function< void()> aFunc)
Call a function using the main stack.
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
std::unique_ptr< TOOL_MENU > m_menu
The functions below are not yet implemented - their interface may change.
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
TOOLS_HOLDER * GetToolHolder() const
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
void update() override
Update menu state stub.
OPT_TOOL_EVENT eventHandler(const wxMenuEvent &aEvent) override
Event handler stub.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
A modified version of the wxInfoBar class that allows us to:
Definition wx_infobar.h:77
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
MESSAGE_TYPE GetMessageType() const
Definition wx_infobar.h:93
void OnGeometryChange(const POLYGON_GEOM_MANAGER &aMgr) override
Called when the polygon is complete.
static bool IsZoneFillAction(const TOOL_EVENT *aEvent)
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:802
bool GetDoNotAllowVias() const
Definition zone.h:813
SHAPE_POLY_SET * Outline()
Definition zone.h:422
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition zone.cpp:734
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:558
This file is part of the common library.
@ MEASURE
Definition cursors.h:68
@ MOVING
Definition cursors.h:48
@ ARROW
Definition cursors.h:46
@ BULLSEYE
Definition cursors.h:58
@ PENCIL
Definition cursors.h:52
static void updateArcFromConstructionMgr(const KIGFX::PREVIEW::ARC_GEOM_MANAGER &aMgr, PCB_SHAPE &aArc)
Update an arc PCB_SHAPE from the current state of an Arc Geometry Manager.
static void updateSegmentFromGeometryMgr(const KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER &aMgr, PCB_SHAPE *aGraphic)
static VECTOR2I evalEllipsePoint(const PCB_SHAPE *aGraphic, const VECTOR2I &aCursorPos)
@ DISALLOW_CONSTRAINT
Definition drc_rule.h:75
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:55
@ HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:57
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ RADIANS_T
Definition eda_angle.h:32
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
#define IS_NEW
New item, just created.
#define IS_MOVING
Item being moved.
SHAPE_T
Definition eda_shape.h:48
@ ELLIPSE
Definition eda_shape.h:56
@ SEGMENT
Definition eda_shape.h:50
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:51
@ ELLIPSE_ARC
Definition eda_shape.h:57
EDA_UNITS
Definition eda_units.h:48
void ConnectBoardShapes(std::vector< PCB_SHAPE * > &aShapeList, int aChainingEpsilon)
Connects shapes to each other, making continious contours (adjacent shapes will have a common vertex)...
a few functions useful in geometry calculations.
VECTOR2< T > GetVectorSnapped45(const VECTOR2< T > &aVec, bool only45=false)
Snap a vector onto the nearest 0, 45 or 90 degree line.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamps a vector to values that can be negated, respecting numeric limits of coordinates data type wit...
LEADER_MODE
The kind of the leader line.
@ DEG45
45 Degree only
@ DIRECT
Unconstrained point-to-point.
@ DEG90
90 Degree only
VECTOR2< T > GetVectorSnapped90(const VECTOR2< T > &aVec)
Snap a vector onto the nearest horizontal or vertical line.
void InferBold(TEXT_ATTRIBUTES *aAttrs)
Definition gr_text.h:82
@ GRID_TEXT
Definition grid_helper.h:51
@ GRID_GRAPHICS
Definition grid_helper.h:52
static wxString ImageFileWildcard()
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:679
@ LAYER_FILLED_SHAPES
Copper graphic shape opacity/visibility (color ignored).
Definition layer_ids.h:313
@ LAYER_ZONES
Control for copper zone opacity/visibility (color ignored).
Definition layer_ids.h:295
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ B_Cu
Definition layer_ids.h:65
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:83
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:435
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
ZONE_MODE
Definition pcb_actions.h:35
@ SIMILAR
Add a new zone with the same settings as an existing one.
Definition pcb_actions.h:38
@ GRAPHIC_POLYGON
Definition pcb_actions.h:39
@ ADD
Add a new zone/keepout with fresh settings.
Definition pcb_actions.h:36
BARCODE class definition.
Class to handle a set of BOARD_ITEMs.
SCOPED_SET_RESET< DRAWING_TOOL::MODE > SCOPED_DRAW_MODE
@ ID_POPUP_PCB_SELECT_CUSTOM_WIDTH
Definition pcbnew_id.h:21
@ ID_POPUP_PCB_SELECT_VIASIZE1
Definition pcbnew_id.h:40
@ ID_POPUP_PCB_SELECT_VIASIZE16
Definition pcbnew_id.h:55
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
Class that computes missing connections on a PCB.
@ RPT_SEVERITY_IGNORE
std::vector< EDA_ITEM * > EDA_ITEMS
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:39
bool NoPrintableChars(const wxString &aString)
Return true if the string is empty or contains only whitespace.
LINE_STYLE
Dashed line types.
MAGNETIC_OPTIONS tracks
MAGNETIC_OPTIONS pads
A filename or source description, a problem input line, a line number, a byte offset,...
std::unique_ptr< BOARD_ITEM > CreateItem() override
DRAWING_TOOL & m_drawingTool
PCB_BASE_EDIT_FRAME & m_frame
PCB_GRID_HELPER m_gridHelper
void SnapItem(BOARD_ITEM *aItem) override
POINT_PLACER(DRAWING_TOOL &aDrawingTool, PCB_BASE_EDIT_FRAME &aFrame)
Container to handle a stock of specific vias each with unique diameter and drill sizes in the BOARD c...
Parameters used to fully describe a zone creation process.
bool m_thieving
Layer to begin drawing.
ZONE_MODE m_mode
Zone settings source (for similar and cutout zones)
bool m_keepout
< Should create a keepout zone?
ZONE * m_sourceZone
Zone leader mode.
PCB_LAYER_ID m_layer
The zone mode to operate in.
std::vector< std::vector< std::string > > table
VECTOR2I center
int radius
VECTOR2I end
int clearance
wxString result
Test unit parsing edge cases and error handling.
int delta
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_TOP
#define M_PI
@ TA_CHOICE_MENU_CHOICE
Context menu choice.
Definition tool_event.h:98
@ TA_CHOICE_MENU_UPDATE
Context menu update.
Definition tool_event.h:94
@ TA_CHOICE_MENU_CLOSED
Context menu is closed, no matter whether anything has been chosen or not.
Definition tool_event.h:101
@ CMENU_NOW
Right now (after TOOL_INTERACTIVE::SetContextMenu).
Definition tool_event.h:156
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition tool_event.h:641
@ MD_CTRL
Definition tool_event.h:144
@ MD_SHIFT
Definition tool_event.h:143
@ BUT_LEFT
Definition tool_event.h:132
@ BUT_RIGHT
Definition tool_event.h:133
bool TestSegmentHit(const VECTOR2I &aRefPoint, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aDist)
Test if aRefPoint is with aDistance on the line defined by aStart and aEnd.
Definition trigo.cpp:175
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:75
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:85
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:103
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:100
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:101
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:108
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:105
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:89
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:86
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:87
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:83
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:99
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:84
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:95
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:97
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:93
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:102
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686
Definition of file extensions used in Kicad.