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