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