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