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