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