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