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