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