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