You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4186 lines
139 KiB

9 years ago
9 years ago
7 years ago
7 years ago
9 years ago
9 years ago
3 years ago
3 years ago
  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 <maciej.suminski@cern.ch>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include "drawing_tool.h"
  26. #include "geometry/shape_rect.h"
  27. #include "dialog_table_properties.h"
  28. #include <pgm_base.h>
  29. #include <settings/settings_manager.h>
  30. #include <pcbnew_settings.h>
  31. #include <footprint_editor_settings.h>
  32. #include <dialogs/dialog_text_properties.h>
  33. #include <dialogs/dialog_track_via_size.h>
  34. #include <gal/graphics_abstraction_layer.h>
  35. #include <geometry/geometry_utils.h>
  36. #include <geometry/shape_segment.h>
  37. #include <import_gfx/dialog_import_graphics.h>
  38. #include <preview_items/arc_assistant.h>
  39. #include <preview_items/bezier_assistant.h>
  40. #include <preview_items/two_point_assistant.h>
  41. #include <preview_items/two_point_geom_manager.h>
  42. #include <ratsnest/ratsnest_data.h>
  43. #include <router/router_tool.h>
  44. #include <status_popup.h>
  45. #include <tool/tool_manager.h>
  46. #include <tools/pcb_actions.h>
  47. #include <tools/pcb_grid_helper.h>
  48. #include <tools/pcb_selection_tool.h>
  49. #include <tools/tool_event_utils.h>
  50. #include <tools/zone_create_helper.h>
  51. #include <tools/zone_filler_tool.h>
  52. #include <view/view.h>
  53. #include <widgets/appearance_controls.h>
  54. #include <widgets/wx_infobar.h>
  55. #include <wx/filedlg.h>
  56. #include <wx/msgdlg.h>
  57. #include <bitmaps.h>
  58. #include <board.h>
  59. #include <board_commit.h>
  60. #include <board_design_settings.h>
  61. #include <confirm.h>
  62. #include <footprint.h>
  63. #include <macros.h>
  64. #include <gal/painter.h>
  65. #include <pcb_edit_frame.h>
  66. #include <pcb_group.h>
  67. #include <pcb_reference_image.h>
  68. #include <pcb_text.h>
  69. #include <pcb_textbox.h>
  70. #include <pcb_table.h>
  71. #include <pcb_tablecell.h>
  72. #include <pcb_dimension.h>
  73. #include <pcbnew_id.h>
  74. #include <scoped_set_reset.h>
  75. #include <string_utils.h>
  76. #include <zone.h>
  77. #include <fix_board_shape.h>
  78. #include <view/view_controls.h>
  79. const unsigned int DRAWING_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
  80. using SCOPED_DRAW_MODE = SCOPED_SET_RESET<DRAWING_TOOL::MODE>;
  81. class VIA_SIZE_MENU : public ACTION_MENU
  82. {
  83. public:
  84. VIA_SIZE_MENU() :
  85. ACTION_MENU( true )
  86. {
  87. SetIcon( BITMAPS::width_track_via );
  88. SetTitle( _( "Select Via Size" ) );
  89. }
  90. protected:
  91. ACTION_MENU* create() const override
  92. {
  93. return new VIA_SIZE_MENU();
  94. }
  95. void update() override
  96. {
  97. PCB_EDIT_FRAME* frame = (PCB_EDIT_FRAME*) getToolManager()->GetToolHolder();
  98. BOARD_DESIGN_SETTINGS& bds = frame->GetBoard()->GetDesignSettings();
  99. bool useIndex = !bds.m_UseConnectedTrackWidth
  100. && !bds.UseCustomTrackViaSize();
  101. wxString msg;
  102. Clear();
  103. Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
  104. _( "Specify custom track and via sizes" ), wxITEM_CHECK );
  105. Check( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, bds.UseCustomTrackViaSize() );
  106. AppendSeparator();
  107. for( unsigned i = 1; i < bds.m_ViasDimensionsList.size(); i++ )
  108. {
  109. VIA_DIMENSION via = bds.m_ViasDimensionsList[i];
  110. if( via.m_Drill > 0 )
  111. {
  112. msg.Printf( _("Via %s, hole %s" ),
  113. frame->MessageTextFromValue( via.m_Diameter ),
  114. frame->MessageTextFromValue( via.m_Drill ) );
  115. }
  116. else
  117. {
  118. msg.Printf( _( "Via %s" ),
  119. frame->MessageTextFromValue( via.m_Diameter ) );
  120. }
  121. int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
  122. Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
  123. Check( menuIdx, useIndex && bds.GetViaSizeIndex() == i );
  124. }
  125. }
  126. OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
  127. {
  128. PCB_EDIT_FRAME* frame = (PCB_EDIT_FRAME*) getToolManager()->GetToolHolder();
  129. BOARD_DESIGN_SETTINGS& bds = frame->GetBoard()->GetDesignSettings();
  130. int id = aEvent.GetId();
  131. // On Windows, this handler can be called with an event ID not existing in any
  132. // menuitem, so only set flags when we have an ID match.
  133. if( id == ID_POPUP_PCB_SELECT_CUSTOM_WIDTH )
  134. {
  135. DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds );
  136. if( sizeDlg.ShowModal() == wxID_OK )
  137. {
  138. bds.UseCustomTrackViaSize( true );
  139. bds.m_UseConnectedTrackWidth = false;
  140. }
  141. }
  142. else if( id >= ID_POPUP_PCB_SELECT_VIASIZE1 && id <= ID_POPUP_PCB_SELECT_VIASIZE16 )
  143. {
  144. bds.UseCustomTrackViaSize( false );
  145. bds.m_UseConnectedTrackWidth = false;
  146. bds.SetViaSizeIndex( id - ID_POPUP_PCB_SELECT_VIASIZE1 );
  147. }
  148. return OPT_TOOL_EVENT( PCB_ACTIONS::trackViaSizeChanged.MakeEvent() );
  149. }
  150. };
  151. DRAWING_TOOL::DRAWING_TOOL() :
  152. PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ),
  153. m_view( nullptr ),
  154. m_controls( nullptr ),
  155. m_board( nullptr ),
  156. m_frame( nullptr ),
  157. m_mode( MODE::NONE ),
  158. m_inDrawingTool( false ),
  159. m_layer( UNDEFINED_LAYER ),
  160. m_stroke( 1, LINE_STYLE::DEFAULT, COLOR4D::UNSPECIFIED ),
  161. m_pickerItem( nullptr ),
  162. m_tuningPattern( nullptr )
  163. {
  164. }
  165. DRAWING_TOOL::~DRAWING_TOOL()
  166. {
  167. }
  168. bool DRAWING_TOOL::Init()
  169. {
  170. auto haveHighlight =
  171. [&]( const SELECTION& sel )
  172. {
  173. KIGFX::RENDER_SETTINGS* cfg = m_toolMgr->GetView()->GetPainter()->GetSettings();
  174. return !cfg->GetHighlightNetCodes().empty();
  175. };
  176. auto activeToolFunctor =
  177. [this]( const SELECTION& aSel )
  178. {
  179. return m_mode != MODE::NONE;
  180. };
  181. // some interactive drawing tools can undo the last point
  182. auto canUndoPoint =
  183. [this]( const SELECTION& aSel )
  184. {
  185. return ( m_mode == MODE::ARC
  186. || m_mode == MODE::ZONE
  187. || m_mode == MODE::KEEPOUT
  188. || m_mode == MODE::GRAPHIC_POLYGON
  189. || m_mode == MODE::BEZIER
  190. || m_mode == MODE::LINE );
  191. };
  192. // functor for tools that can automatically close the outline
  193. auto canCloseOutline =
  194. [this]( const SELECTION& aSel )
  195. {
  196. return ( m_mode == MODE::ZONE
  197. || m_mode == MODE::KEEPOUT
  198. || m_mode == MODE::GRAPHIC_POLYGON );
  199. };
  200. auto arcToolActive =
  201. [this]( const SELECTION& aSel )
  202. {
  203. return m_mode == MODE::ARC;
  204. };
  205. auto viaToolActive =
  206. [this]( const SELECTION& aSel )
  207. {
  208. return m_mode == MODE::VIA;
  209. };
  210. auto tuningToolActive =
  211. [this]( const SELECTION& aSel )
  212. {
  213. return m_mode == MODE::TUNING;
  214. };
  215. auto dimensionToolActive =
  216. [this]( const SELECTION& aSel )
  217. {
  218. return m_mode == MODE::DIMENSION;
  219. };
  220. CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
  221. // cancel current tool goes in main context menu at the top if present
  222. ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
  223. ctxMenu.AddSeparator( 1 );
  224. ctxMenu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 2 );
  225. ctxMenu.AddSeparator( haveHighlight, 2 );
  226. // tool-specific actions
  227. ctxMenu.AddItem( PCB_ACTIONS::closeOutline, canCloseOutline, 200 );
  228. ctxMenu.AddItem( PCB_ACTIONS::deleteLastPoint, canUndoPoint, 200 );
  229. ctxMenu.AddItem( PCB_ACTIONS::arcPosture, arcToolActive, 200 );
  230. ctxMenu.AddItem( PCB_ACTIONS::spacingIncrease, tuningToolActive, 200 );
  231. ctxMenu.AddItem( PCB_ACTIONS::spacingDecrease, tuningToolActive, 200 );
  232. ctxMenu.AddItem( PCB_ACTIONS::amplIncrease, tuningToolActive, 200 );
  233. ctxMenu.AddItem( PCB_ACTIONS::amplDecrease, tuningToolActive, 200 );
  234. ctxMenu.AddItem( PCB_ACTIONS::lengthTunerSettings, tuningToolActive, 200 );
  235. ctxMenu.AddItem( PCB_ACTIONS::changeDimensionArrows, dimensionToolActive, 200 );
  236. ctxMenu.AddCheckItem( PCB_ACTIONS::toggleHV45Mode, !tuningToolActive, 250 );
  237. ctxMenu.AddSeparator( 500 );
  238. std::shared_ptr<VIA_SIZE_MENU> viaSizeMenu = std::make_shared<VIA_SIZE_MENU>();
  239. viaSizeMenu->SetTool( this );
  240. m_menu->RegisterSubMenu( viaSizeMenu );
  241. ctxMenu.AddMenu( viaSizeMenu.get(), viaToolActive, 500 );
  242. ctxMenu.AddSeparator( 500 );
  243. // Type-specific sub-menus will be added for us by other tools
  244. // For example, zone fill/unfill is provided by the PCB control tool
  245. // Finally, add the standard zoom/grid items
  246. getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( *m_menu.get() );
  247. return true;
  248. }
  249. void DRAWING_TOOL::Reset( RESET_REASON aReason )
  250. {
  251. // Init variables used by every drawing tool
  252. m_view = getView();
  253. m_controls = getViewControls();
  254. m_board = getModel<BOARD>();
  255. m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
  256. // Re-initialize session attributes
  257. const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
  258. if( aReason == RESET_REASON::SHUTDOWN )
  259. return;
  260. m_layer = m_frame->GetActiveLayer();
  261. m_stroke.SetWidth( bds.GetLineThickness( m_layer ) );
  262. m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
  263. m_stroke.SetColor( COLOR4D::UNSPECIFIED );
  264. m_textAttrs.m_Size = bds.GetTextSize( m_layer );
  265. m_textAttrs.m_StrokeWidth = bds.GetTextThickness( m_layer );
  266. InferBold( &m_textAttrs );
  267. m_textAttrs.m_Italic = bds.GetTextItalic( m_layer );
  268. m_textAttrs.m_KeepUpright = bds.GetTextUpright( m_layer );
  269. m_textAttrs.m_Mirrored = IsBackLayer( m_layer );
  270. m_textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
  271. m_textAttrs.m_Valign = GR_TEXT_V_ALIGN_TOP;
  272. UpdateStatusBar();
  273. }
  274. DRAWING_TOOL::MODE DRAWING_TOOL::GetDrawingMode() const
  275. {
  276. return m_mode;
  277. }
  278. void DRAWING_TOOL::UpdateStatusBar() const
  279. {
  280. if( m_frame )
  281. {
  282. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  283. bool constrained;
  284. if( m_frame->IsType( FRAME_PCB_EDITOR ) )
  285. constrained = mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_Use45DegreeLimit;
  286. else
  287. constrained = mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_Use45Limit;
  288. m_frame->DisplayConstraintsMsg( constrained ? _( "Constrain to H, V, 45" ) : wxString( "" ) );
  289. }
  290. }
  291. int DRAWING_TOOL::DrawLine( const TOOL_EVENT& aEvent )
  292. {
  293. if( m_isFootprintEditor && !m_frame->GetModel() )
  294. return 0;
  295. if( m_inDrawingTool )
  296. return 0;
  297. REENTRANCY_GUARD guard( &m_inDrawingTool );
  298. BOARD_ITEM* parent = m_frame->GetModel();
  299. PCB_SHAPE* line = new PCB_SHAPE( parent );
  300. BOARD_COMMIT commit( m_frame );
  301. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
  302. std::optional<VECTOR2D> startingPoint;
  303. std::stack<PCB_SHAPE*> committedLines;
  304. line->SetShape( SHAPE_T::SEGMENT );
  305. line->SetFlags( IS_NEW );
  306. if( aEvent.HasPosition() )
  307. startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
  308. m_frame->PushTool( aEvent );
  309. Activate();
  310. while( drawShape( aEvent, &line, startingPoint, &committedLines ) )
  311. {
  312. if( line )
  313. {
  314. commit.Add( line );
  315. commit.Push( _( "Draw Line" ) );
  316. startingPoint = VECTOR2D( line->GetEnd() );
  317. committedLines.push( line );
  318. }
  319. else
  320. {
  321. startingPoint = std::nullopt;
  322. }
  323. line = new PCB_SHAPE( parent );
  324. line->SetShape( SHAPE_T::SEGMENT );
  325. line->SetFlags( IS_NEW );
  326. }
  327. return 0;
  328. }
  329. int DRAWING_TOOL::DrawRectangle( const TOOL_EVENT& aEvent )
  330. {
  331. if( m_isFootprintEditor && !m_frame->GetModel() )
  332. return 0;
  333. if( m_inDrawingTool )
  334. return 0;
  335. REENTRANCY_GUARD guard( &m_inDrawingTool );
  336. bool isTextBox = aEvent.IsAction( &PCB_ACTIONS::drawTextBox );
  337. PCB_SHAPE* rect = nullptr;
  338. BOARD_COMMIT commit( m_frame );
  339. BOARD_ITEM* parent = m_frame->GetModel();
  340. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::RECTANGLE );
  341. std::optional<VECTOR2D> startingPoint;
  342. rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
  343. rect->SetShape( SHAPE_T::RECTANGLE );
  344. rect->SetFilled( false );
  345. rect->SetFlags( IS_NEW );
  346. if( aEvent.HasPosition() )
  347. startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
  348. m_frame->PushTool( aEvent );
  349. Activate();
  350. while( drawShape( aEvent, &rect, startingPoint, nullptr ) )
  351. {
  352. if( rect )
  353. {
  354. bool cancelled = false;
  355. if( PCB_TEXTBOX* textbox = dynamic_cast<PCB_TEXTBOX*>( rect ) )
  356. cancelled = m_frame->ShowTextBoxPropertiesDialog( textbox ) != wxID_OK;
  357. if( cancelled )
  358. {
  359. delete rect;
  360. rect = nullptr;
  361. }
  362. else
  363. {
  364. rect->Normalize();
  365. commit.Add( rect );
  366. commit.Push( isTextBox ? _( "Draw Text Box" ) : _( "Draw Rectangle" ) );
  367. m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, rect );
  368. }
  369. }
  370. rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
  371. rect->SetShape( SHAPE_T::RECTANGLE );
  372. rect->SetFilled( false );
  373. rect->SetFlags( IS_NEW );
  374. startingPoint = std::nullopt;
  375. }
  376. return 0;
  377. }
  378. int DRAWING_TOOL::DrawCircle( const TOOL_EVENT& aEvent )
  379. {
  380. if( m_isFootprintEditor && !m_frame->GetModel() )
  381. return 0;
  382. if( m_inDrawingTool )
  383. return 0;
  384. REENTRANCY_GUARD guard( &m_inDrawingTool );
  385. BOARD_ITEM* parent = m_frame->GetModel();
  386. PCB_SHAPE* circle = new PCB_SHAPE( parent );
  387. BOARD_COMMIT commit( m_frame );
  388. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::CIRCLE );
  389. std::optional<VECTOR2D> startingPoint;
  390. circle->SetShape( SHAPE_T::CIRCLE );
  391. circle->SetFilled( false );
  392. circle->SetFlags( IS_NEW );
  393. if( aEvent.HasPosition() )
  394. startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
  395. m_frame->PushTool( aEvent );
  396. Activate();
  397. while( drawShape( aEvent, &circle, startingPoint, nullptr ) )
  398. {
  399. if( circle )
  400. {
  401. commit.Add( circle );
  402. commit.Push( _( "Draw Circle" ) );
  403. m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, circle );
  404. }
  405. circle = new PCB_SHAPE( parent );
  406. circle->SetShape( SHAPE_T::CIRCLE );
  407. circle->SetFilled( false );
  408. circle->SetFlags( IS_NEW );
  409. startingPoint = std::nullopt;
  410. }
  411. return 0;
  412. }
  413. int DRAWING_TOOL::DrawArc( const TOOL_EVENT& aEvent )
  414. {
  415. if( m_isFootprintEditor && !m_frame->GetModel() )
  416. return 0;
  417. if( m_inDrawingTool )
  418. return 0;
  419. REENTRANCY_GUARD guard( &m_inDrawingTool );
  420. BOARD_ITEM* parent = m_frame->GetModel();
  421. PCB_SHAPE* arc = new PCB_SHAPE( parent );
  422. BOARD_COMMIT commit( m_frame );
  423. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
  424. std::optional<VECTOR2D> startingPoint;
  425. arc->SetShape( SHAPE_T::ARC );
  426. arc->SetFlags( IS_NEW );
  427. m_frame->PushTool( aEvent );
  428. Activate();
  429. if( aEvent.HasPosition() )
  430. startingPoint = aEvent.Position();
  431. while( drawArc( aEvent, &arc, startingPoint ) )
  432. {
  433. if( arc )
  434. {
  435. commit.Add( arc );
  436. commit.Push( _( "Draw Arc" ) );
  437. m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, arc );
  438. }
  439. arc = new PCB_SHAPE( parent );
  440. arc->SetShape( SHAPE_T::ARC );
  441. arc->SetFlags( IS_NEW );
  442. startingPoint = std::nullopt;
  443. }
  444. return 0;
  445. }
  446. int DRAWING_TOOL::DrawBezier( const TOOL_EVENT& aEvent )
  447. {
  448. if( m_isFootprintEditor && !m_frame->GetModel() )
  449. return 0;
  450. if( m_inDrawingTool )
  451. return 0;
  452. REENTRANCY_GUARD guard( &m_inDrawingTool );
  453. BOARD_COMMIT commit( m_frame );
  454. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::BEZIER );
  455. OPT_VECTOR2I startingPoint, startingC1;
  456. m_frame->PushTool( aEvent );
  457. Activate();
  458. if( aEvent.HasPosition() )
  459. startingPoint = aEvent.Position();
  460. DRAW_ONE_RESULT result = DRAW_ONE_RESULT::ACCEPTED;
  461. while( result != DRAW_ONE_RESULT::CANCELLED )
  462. {
  463. std::unique_ptr<PCB_SHAPE> bezier =
  464. drawOneBezier( aEvent, startingPoint, startingC1, result );
  465. // Anyting other than accepted means no chaining
  466. startingPoint = std::nullopt;
  467. startingC1 = std::nullopt;
  468. // If a bezier was created, add it and go again
  469. if( bezier )
  470. {
  471. PCB_SHAPE& bezierRef = *bezier;
  472. commit.Add( bezier.release() );
  473. commit.Push( _( "Draw Bezier" ) );
  474. // Don't chain if reset (or accepted and reset)
  475. if( result == DRAW_ONE_RESULT::ACCEPTED )
  476. {
  477. startingPoint = bezierRef.GetEnd();
  478. // If the last bezier has a zero C2 control arm, allow the user to
  479. // define a new C1 control arm for the next one.
  480. if( bezierRef.GetEnd() != bezierRef.GetBezierC2() )
  481. {
  482. // Mirror the control point across the end point to get
  483. // a tangent control point
  484. startingC1 =
  485. bezierRef.GetEnd() - ( bezierRef.GetBezierC2() - bezierRef.GetEnd() );
  486. }
  487. }
  488. }
  489. }
  490. return 0;
  491. }
  492. int DRAWING_TOOL::PlaceReferenceImage( const TOOL_EVENT& aEvent )
  493. {
  494. if( m_inDrawingTool )
  495. return 0;
  496. REENTRANCY_GUARD guard( &m_inDrawingTool );
  497. PCB_REFERENCE_IMAGE* image = aEvent.Parameter<PCB_REFERENCE_IMAGE*>();
  498. bool immediateMode = image != nullptr;
  499. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  500. bool ignorePrimePosition = false;
  501. COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
  502. VECTOR2I cursorPos = getViewControls()->GetCursorPosition();
  503. PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  504. BOARD_COMMIT commit( m_frame );
  505. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  506. // Add all the drawable symbols to preview
  507. if( image )
  508. {
  509. image->SetPosition( cursorPos );
  510. m_view->ClearPreview();
  511. m_view->AddToPreview( image, false ); // Add, but not give ownership
  512. }
  513. m_frame->PushTool( aEvent );
  514. auto setCursor =
  515. [&]()
  516. {
  517. if( image )
  518. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
  519. else
  520. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  521. };
  522. auto cleanup =
  523. [&] ()
  524. {
  525. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  526. m_view->ClearPreview();
  527. m_view->RecacheAllItems();
  528. delete image;
  529. image = nullptr;
  530. };
  531. Activate();
  532. // Must be done after Activate() so that it gets set into the correct context
  533. getViewControls()->ShowCursor( true );
  534. // Set initial cursor
  535. setCursor();
  536. // Prime the pump
  537. if( image )
  538. {
  539. m_toolMgr->PostAction( ACTIONS::refreshPreview );
  540. }
  541. else if( aEvent.HasPosition() )
  542. {
  543. m_toolMgr->PrimeTool( aEvent.Position() );
  544. }
  545. else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
  546. {
  547. m_toolMgr->PrimeTool( { 0, 0 } );
  548. ignorePrimePosition = true;
  549. }
  550. // Main loop: keep receiving events
  551. while( TOOL_EVENT* evt = Wait() )
  552. {
  553. setCursor();
  554. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  555. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  556. cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
  557. { m_frame->GetActiveLayer() },
  558. GRID_GRAPHICS ),
  559. COORDS_PADDING );
  560. m_controls->ForceCursorPosition( true, cursorPos );
  561. if( evt->IsCancelInteractive() || ( image && evt->IsAction( &ACTIONS::undo ) ) )
  562. {
  563. if( image )
  564. {
  565. cleanup();
  566. }
  567. else
  568. {
  569. m_frame->PopTool( aEvent );
  570. break;
  571. }
  572. if( immediateMode )
  573. {
  574. m_frame->PopTool( aEvent );
  575. break;
  576. }
  577. }
  578. else if( evt->IsActivate() )
  579. {
  580. if( image && evt->IsMoveTool() )
  581. {
  582. // We're already moving our own item; ignore the move tool
  583. evt->SetPassEvent( false );
  584. continue;
  585. }
  586. if( image )
  587. {
  588. m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel image creation." ) );
  589. evt->SetPassEvent( false );
  590. continue;
  591. }
  592. if( evt->IsMoveTool() )
  593. {
  594. // Leave ourselves on the stack so we come back after the move
  595. break;
  596. }
  597. else
  598. {
  599. m_frame->PopTool( aEvent );
  600. break;
  601. }
  602. }
  603. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  604. {
  605. if( !image )
  606. {
  607. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  608. wxFileDialog dlg( m_frame, _( "Choose Image" ), wxEmptyString, wxEmptyString,
  609. _( "Image Files" ) + wxS( " " ) + wxImage::GetImageExtWildcard(),
  610. wxFD_OPEN );
  611. if( dlg.ShowModal() != wxID_OK )
  612. continue;
  613. // If we started with a hotkey which has a position then warp back to that.
  614. // Otherwise update to the current mouse position pinned inside the autoscroll
  615. // boundaries.
  616. if( evt->IsPrime() && !ignorePrimePosition )
  617. {
  618. cursorPos = grid.Align( evt->Position() );
  619. getViewControls()->WarpMouseCursor( cursorPos, true );
  620. }
  621. else
  622. {
  623. getViewControls()->PinCursorInsideNonAutoscrollArea( true );
  624. cursorPos = getViewControls()->GetMousePosition();
  625. }
  626. cursorPos = getViewControls()->GetMousePosition( true );
  627. wxString fullFilename = dlg.GetPath();
  628. if( wxFileExists( fullFilename ) )
  629. image = new PCB_REFERENCE_IMAGE( m_frame->GetModel(), cursorPos );
  630. if( !image || !image->GetReferenceImage().ReadImageFile( fullFilename ) )
  631. {
  632. wxMessageBox( wxString::Format(_( "Could not load image from '%s'." ), fullFilename ) );
  633. delete image;
  634. image = nullptr;
  635. continue;
  636. }
  637. image->SetFlags( IS_NEW | IS_MOVING );
  638. image->SetLayer( m_frame->GetActiveLayer() );
  639. m_view->ClearPreview();
  640. m_view->AddToPreview( image, false ); // Add, but not give ownership
  641. m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
  642. selectionTool->AddItemToSel( image, false );
  643. getViewControls()->SetCursorPosition( cursorPos, false );
  644. setCursor();
  645. m_view->ShowPreview( true );
  646. }
  647. else
  648. {
  649. commit.Add( image );
  650. commit.Push( _( "Place Image" ) );
  651. m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, image );
  652. image = nullptr;
  653. m_toolMgr->PostAction( ACTIONS::activatePointEditor );
  654. m_view->ClearPreview();
  655. if( immediateMode )
  656. {
  657. m_frame->PopTool( aEvent );
  658. break;
  659. }
  660. }
  661. }
  662. else if( evt->IsClick( BUT_RIGHT ) )
  663. {
  664. // Warp after context menu only if dragging...
  665. if( !image )
  666. m_toolMgr->VetoContextMenuMouseWarp();
  667. m_menu->ShowContextMenu( selectionTool->GetSelection() );
  668. }
  669. else if( image && ( evt->IsAction( &ACTIONS::refreshPreview )
  670. || evt->IsMotion() ) )
  671. {
  672. image->SetPosition( cursorPos );
  673. m_view->ClearPreview();
  674. m_view->AddToPreview( image, false ); // Add, but not give ownership
  675. m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
  676. }
  677. else if( image && evt->IsAction( &ACTIONS::doDelete ) )
  678. {
  679. cleanup();
  680. }
  681. else if( image && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
  682. || evt->IsAction( &ACTIONS::redo ) ) )
  683. {
  684. wxBell();
  685. }
  686. else
  687. {
  688. evt->SetPassEvent();
  689. }
  690. // Enable autopanning and cursor capture only when there is an image to be placed
  691. getViewControls()->SetAutoPan( image != nullptr );
  692. getViewControls()->CaptureCursor( image != nullptr );
  693. }
  694. getViewControls()->SetAutoPan( false );
  695. getViewControls()->CaptureCursor( false );
  696. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  697. return 0;
  698. }
  699. int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
  700. {
  701. if( m_isFootprintEditor && !m_frame->GetModel() )
  702. return 0;
  703. if( m_inDrawingTool )
  704. return 0;
  705. REENTRANCY_GUARD guard( &m_inDrawingTool );
  706. COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
  707. PCB_TEXT* text = nullptr;
  708. bool ignorePrimePosition = false;
  709. const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
  710. BOARD_COMMIT commit( m_frame );
  711. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
  712. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  713. auto setCursor =
  714. [&]()
  715. {
  716. if( text )
  717. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
  718. else
  719. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
  720. };
  721. auto cleanup =
  722. [&]()
  723. {
  724. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  725. m_controls->ForceCursorPosition( false );
  726. m_controls->ShowCursor( true );
  727. m_controls->SetAutoPan( false );
  728. m_controls->CaptureCursor( false );
  729. delete text;
  730. text = nullptr;
  731. };
  732. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  733. m_frame->PushTool( aEvent );
  734. Activate();
  735. // Must be done after Activate() so that it gets set into the correct context
  736. m_controls->ShowCursor( true );
  737. m_controls->ForceCursorPosition( false );
  738. // do not capture or auto-pan until we start placing some text
  739. // Set initial cursor
  740. setCursor();
  741. if( aEvent.HasPosition() )
  742. {
  743. m_toolMgr->PrimeTool( aEvent.Position() );
  744. }
  745. else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
  746. {
  747. m_toolMgr->PrimeTool( { 0, 0 } );
  748. ignorePrimePosition = true;
  749. }
  750. // Main loop: keep receiving events
  751. while( TOOL_EVENT* evt = Wait() )
  752. {
  753. setCursor();
  754. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  755. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  756. VECTOR2I cursorPos =
  757. GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
  758. { m_frame->GetActiveLayer() }, GRID_TEXT ),
  759. COORDS_PADDING );
  760. m_controls->ForceCursorPosition( true, cursorPos );
  761. if( evt->IsDrag() )
  762. {
  763. continue;
  764. }
  765. else if( evt->IsCancelInteractive() || ( text && evt->IsAction( &ACTIONS::undo ) ) )
  766. {
  767. if( text )
  768. {
  769. cleanup();
  770. }
  771. else
  772. {
  773. m_frame->PopTool( aEvent );
  774. break;
  775. }
  776. }
  777. else if( evt->IsActivate() )
  778. {
  779. if( text )
  780. cleanup();
  781. if( evt->IsMoveTool() )
  782. {
  783. // leave ourselves on the stack so we come back after the move
  784. break;
  785. }
  786. else
  787. {
  788. m_frame->PopTool( aEvent );
  789. break;
  790. }
  791. }
  792. else if( evt->IsClick( BUT_RIGHT ) )
  793. {
  794. if( !text )
  795. m_toolMgr->VetoContextMenuMouseWarp();
  796. m_menu->ShowContextMenu( selection() );
  797. }
  798. else if( evt->IsClick( BUT_LEFT ) )
  799. {
  800. bool placing = text != nullptr;
  801. if( !text )
  802. {
  803. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  804. m_controls->ForceCursorPosition( true, m_controls->GetCursorPosition() );
  805. PCB_LAYER_ID layer = m_frame->GetActiveLayer();
  806. TEXT_ATTRIBUTES textAttrs;
  807. textAttrs.m_Size = bds.GetTextSize( layer );
  808. textAttrs.m_StrokeWidth = bds.GetTextThickness( layer );
  809. InferBold( &textAttrs );
  810. textAttrs.m_Italic = bds.GetTextItalic( layer );
  811. textAttrs.m_KeepUpright = bds.GetTextUpright( layer );
  812. textAttrs.m_Mirrored = IsBackLayer( layer );
  813. textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
  814. textAttrs.m_Valign = GR_TEXT_V_ALIGN_BOTTOM;
  815. if( m_isFootprintEditor )
  816. text = new PCB_TEXT( static_cast<FOOTPRINT*>( m_frame->GetModel() ) );
  817. else
  818. text = new PCB_TEXT( m_frame->GetModel() );
  819. text->SetLayer( layer );
  820. text->SetAttributes( textAttrs );
  821. text->SetTextPos( cursorPos );
  822. text->SetFlags( IS_NEW ); // Prevent double undo commits
  823. DIALOG_TEXT_PROPERTIES textDialog( m_frame, text );
  824. bool cancelled;
  825. RunMainStack(
  826. [&]()
  827. {
  828. // QuasiModal required for Scintilla auto-complete
  829. cancelled = !textDialog.ShowQuasiModal();
  830. } );
  831. if( cancelled || NoPrintableChars( text->GetText() ) )
  832. {
  833. delete text;
  834. text = nullptr;
  835. }
  836. else if( text->GetTextPos() != cursorPos )
  837. {
  838. // If the user modified the location then go ahead and place it there.
  839. // Otherwise we'll drag.
  840. placing = true;
  841. }
  842. if( text )
  843. {
  844. if( !m_view->IsLayerVisible( text->GetLayer() ) )
  845. {
  846. m_frame->GetAppearancePanel()->SetLayerVisible( text->GetLayer(), true );
  847. m_frame->GetCanvas()->Refresh();
  848. }
  849. m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, text );
  850. m_view->Update( &selection() );
  851. // update the cursor so it looks correct before another event
  852. setCursor();
  853. }
  854. }
  855. if( placing )
  856. {
  857. text->ClearFlags();
  858. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  859. commit.Add( text );
  860. commit.Push( _( "Draw Text" ) );
  861. m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, text );
  862. text = nullptr;
  863. }
  864. m_controls->ForceCursorPosition( false );
  865. // If we started with a hotkey which has a position then warp back to that.
  866. // Otherwise update to the current mouse position pinned inside the autoscroll
  867. // boundaries.
  868. if( evt->IsPrime() && !ignorePrimePosition )
  869. {
  870. cursorPos = evt->Position();
  871. m_controls->WarpMouseCursor( cursorPos, true );
  872. }
  873. else
  874. {
  875. m_controls->PinCursorInsideNonAutoscrollArea( true );
  876. cursorPos = m_controls->GetMousePosition();
  877. }
  878. m_toolMgr->PostAction( PCB_ACTIONS::refreshPreview );
  879. m_controls->ShowCursor( true );
  880. m_controls->CaptureCursor( text != nullptr );
  881. m_controls->SetAutoPan( text != nullptr );
  882. }
  883. else if( text && ( evt->IsMotion() || evt->IsAction( &PCB_ACTIONS::refreshPreview ) ) )
  884. {
  885. text->SetPosition( cursorPos );
  886. selection().SetReferencePoint( cursorPos );
  887. m_view->Update( &selection() );
  888. }
  889. else if( text
  890. && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
  891. || evt->IsAction( &ACTIONS::redo ) ) )
  892. {
  893. wxBell();
  894. }
  895. else if( text && evt->IsAction( &PCB_ACTIONS::properties ) )
  896. {
  897. frame()->OnEditItemRequest( text );
  898. m_view->Update( &selection() );
  899. frame()->SetMsgPanel( text );
  900. }
  901. else
  902. {
  903. evt->SetPassEvent();
  904. }
  905. }
  906. m_controls->SetAutoPan( false );
  907. m_controls->CaptureCursor( false );
  908. m_controls->ForceCursorPosition( false );
  909. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  910. if( selection().Empty() )
  911. m_frame->SetMsgPanel( board() );
  912. return 0;
  913. }
  914. int DRAWING_TOOL::DrawTable( const TOOL_EVENT& aEvent )
  915. {
  916. if( m_inDrawingTool )
  917. return 0;
  918. REENTRANCY_GUARD guard( &m_inDrawingTool );
  919. PCB_TABLE* table = nullptr;
  920. const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
  921. BOARD_COMMIT commit( m_frame );
  922. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  923. // We might be running as the same shape in another co-routine. Make sure that one
  924. // gets whacked.
  925. m_toolMgr->DeactivateTool();
  926. auto setCursor =
  927. [&]()
  928. {
  929. if( table )
  930. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
  931. else
  932. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  933. };
  934. auto cleanup =
  935. [&] ()
  936. {
  937. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  938. m_controls->ForceCursorPosition( false );
  939. m_controls->ShowCursor( true );
  940. m_controls->SetAutoPan( false );
  941. m_controls->CaptureCursor( false );
  942. delete table;
  943. table = nullptr;
  944. };
  945. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  946. m_frame->PushTool( aEvent );
  947. Activate();
  948. // Must be done after Activate() so that it gets set into the correct context
  949. getViewControls()->ShowCursor( true );
  950. m_controls->ForceCursorPosition( false );
  951. // Set initial cursor
  952. setCursor();
  953. if( aEvent.HasPosition() )
  954. m_toolMgr->PrimeTool( aEvent.Position() );
  955. // Main loop: keep receiving events
  956. while( TOOL_EVENT* evt = Wait() )
  957. {
  958. setCursor();
  959. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  960. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  961. VECTOR2I cursorPos =
  962. GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
  963. { m_frame->GetActiveLayer() }, GRID_TEXT ),
  964. COORDS_PADDING );
  965. m_controls->ForceCursorPosition( true, cursorPos );
  966. if( evt->IsCancelInteractive() || ( table && evt->IsAction( &ACTIONS::undo ) )
  967. || evt->IsDrag() )
  968. {
  969. if( table )
  970. {
  971. cleanup();
  972. }
  973. else
  974. {
  975. m_frame->PopTool( aEvent );
  976. break;
  977. }
  978. }
  979. else if( evt->IsActivate() )
  980. {
  981. if( table )
  982. cleanup();
  983. if( evt->IsMoveTool() )
  984. {
  985. // leave ourselves on the stack so we come back after the move
  986. break;
  987. }
  988. else
  989. {
  990. m_frame->PopTool( aEvent );
  991. break;
  992. }
  993. }
  994. else if( evt->IsClick( BUT_RIGHT ) )
  995. {
  996. // Warp after context menu only if dragging...
  997. if( !table )
  998. m_toolMgr->VetoContextMenuMouseWarp();
  999. m_menu->ShowContextMenu( selection() );
  1000. }
  1001. else if( evt->IsClick( BUT_LEFT ) )
  1002. {
  1003. if( !table )
  1004. {
  1005. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  1006. PCB_LAYER_ID layer = m_frame->GetActiveLayer();
  1007. table = new PCB_TABLE( m_frame->GetModel(), bds.GetLineThickness( layer ) );
  1008. table->SetFlags( IS_NEW );
  1009. table->SetLayer( layer );
  1010. table->SetColCount( 1 );
  1011. table->AddCell( new PCB_TABLECELL( table ) );
  1012. table->SetLayer( layer );
  1013. table->SetPosition( cursorPos );
  1014. if( !m_view->IsLayerVisible( layer ) )
  1015. {
  1016. m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
  1017. m_frame->GetCanvas()->Refresh();
  1018. }
  1019. m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, table );
  1020. m_view->Update( &selection() );
  1021. // update the cursor so it looks correct before another event
  1022. setCursor();
  1023. }
  1024. else
  1025. {
  1026. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  1027. table->Normalize();
  1028. DIALOG_TABLE_PROPERTIES dlg( m_frame, table );
  1029. // QuasiModal required for Scintilla auto-complete
  1030. if( dlg.ShowQuasiModal() == wxID_OK )
  1031. {
  1032. commit.Add( table, m_frame->GetScreen() );
  1033. commit.Push( _( "Draw Table" ) );
  1034. m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, table );
  1035. m_toolMgr->PostAction( ACTIONS::activatePointEditor );
  1036. }
  1037. else
  1038. {
  1039. delete table;
  1040. }
  1041. table = nullptr;
  1042. }
  1043. }
  1044. else if( table && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
  1045. {
  1046. VECTOR2I fontSize = bds.GetTextSize( table->GetLayer() );
  1047. VECTOR2I gridSize = grid.GetGridSize( grid.GetItemGrid( table ) );
  1048. VECTOR2I origin( table->GetPosition() );
  1049. VECTOR2I requestedSize( cursorPos - origin );
  1050. int colCount = std::max( 1, requestedSize.x / ( fontSize.x * 15 ) );
  1051. int rowCount = std::max( 1, requestedSize.y / ( fontSize.y * 3 ) );
  1052. VECTOR2I cellSize( std::max( fontSize.x * 5, requestedSize.x / colCount ),
  1053. std::max( fontSize.y * 3, requestedSize.y / rowCount ) );
  1054. cellSize.x = KiROUND( (double) cellSize.x / gridSize.x ) * gridSize.x;
  1055. cellSize.y = KiROUND( (double) cellSize.y / gridSize.y ) * gridSize.y;
  1056. table->ClearCells();
  1057. table->SetColCount( colCount );
  1058. for( int col = 0; col < colCount; ++col )
  1059. table->SetColWidth( col, cellSize.x );
  1060. for( int row = 0; row < rowCount; ++row )
  1061. {
  1062. table->SetRowHeight( row, cellSize.y );
  1063. for( int col = 0; col < colCount; ++col )
  1064. {
  1065. PCB_TABLECELL* cell = new PCB_TABLECELL( table );
  1066. cell->SetPosition( origin + VECTOR2I( col * cellSize.x, row * cellSize.y ) );
  1067. cell->SetEnd( cell->GetPosition() + cellSize );
  1068. table->AddCell( cell );
  1069. }
  1070. }
  1071. selection().SetReferencePoint( cursorPos );
  1072. m_view->Update( &selection() );
  1073. m_frame->SetMsgPanel( table );
  1074. }
  1075. else if( table && evt->IsAction( &PCB_ACTIONS::properties ) )
  1076. {
  1077. frame()->OnEditItemRequest( table );
  1078. m_view->Update( &selection() );
  1079. frame()->SetMsgPanel( table );
  1080. }
  1081. else if( table && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
  1082. || evt->IsAction( &ACTIONS::redo ) ) )
  1083. {
  1084. wxBell();
  1085. }
  1086. else
  1087. {
  1088. evt->SetPassEvent();
  1089. }
  1090. // Enable autopanning and cursor capture only when there is a shape being drawn
  1091. getViewControls()->SetAutoPan( table != nullptr );
  1092. getViewControls()->CaptureCursor( table != nullptr );
  1093. }
  1094. getViewControls()->SetAutoPan( false );
  1095. getViewControls()->CaptureCursor( false );
  1096. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  1097. return 0;
  1098. }
  1099. void DRAWING_TOOL::constrainDimension( PCB_DIMENSION_BASE* aDim )
  1100. {
  1101. const VECTOR2I lineVector{ aDim->GetEnd() - aDim->GetStart() };
  1102. aDim->SetEnd( aDim->GetStart() + GetVectorSnapped45( lineVector ) );
  1103. aDim->Update();
  1104. }
  1105. int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
  1106. {
  1107. if( m_isFootprintEditor && !m_frame->GetModel() )
  1108. return 0;
  1109. if( m_inDrawingTool )
  1110. return 0;
  1111. REENTRANCY_GUARD guard( &m_inDrawingTool );
  1112. enum DIMENSION_STEPS
  1113. {
  1114. SET_ORIGIN = 0,
  1115. SET_END,
  1116. SET_HEIGHT,
  1117. FINISHED
  1118. };
  1119. TOOL_EVENT originalEvent = aEvent;
  1120. PCB_DIMENSION_BASE* dimension = nullptr;
  1121. BOARD_COMMIT commit( m_frame );
  1122. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  1123. BOARD_DESIGN_SETTINGS& boardSettings = m_board->GetDesignSettings();
  1124. PCB_SELECTION preview; // A VIEW_GROUP that serves as a preview for the new item(s)
  1125. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
  1126. int step = SET_ORIGIN;
  1127. KICAD_T t = PCB_DIMENSION_T;
  1128. m_view->Add( &preview );
  1129. auto cleanup =
  1130. [&]()
  1131. {
  1132. m_controls->SetAutoPan( false );
  1133. m_controls->CaptureCursor( false );
  1134. m_controls->ForceCursorPosition( false );
  1135. preview.Clear();
  1136. m_view->Update( &preview );
  1137. delete dimension;
  1138. dimension = nullptr;
  1139. step = SET_ORIGIN;
  1140. };
  1141. auto setCursor =
  1142. [&]()
  1143. {
  1144. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
  1145. };
  1146. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  1147. m_frame->PushTool( aEvent );
  1148. Activate();
  1149. // Must be done after Activate() so that it gets set into the correct context
  1150. m_controls->ShowCursor( true );
  1151. m_controls->ForceCursorPosition( false );
  1152. // Set initial cursor
  1153. setCursor();
  1154. m_toolMgr->PostAction( ACTIONS::refreshPreview );
  1155. if( aEvent.HasPosition() )
  1156. m_toolMgr->PrimeTool( aEvent.Position() );
  1157. // Main loop: keep receiving events
  1158. while( TOOL_EVENT* evt = Wait() )
  1159. {
  1160. if( step > SET_ORIGIN )
  1161. frame()->SetMsgPanel( dimension );
  1162. setCursor();
  1163. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  1164. bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
  1165. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  1166. if( step == SET_HEIGHT && t != PCB_DIM_ORTHOGONAL_T )
  1167. {
  1168. if( dimension->GetStart().x != dimension->GetEnd().x
  1169. && dimension->GetStart().y != dimension->GetEnd().y )
  1170. {
  1171. // Not cardinal. Grid snapping doesn't make sense for height.
  1172. grid.SetUseGrid( false );
  1173. }
  1174. }
  1175. VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
  1176. cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, nullptr, GRID_GRAPHICS ),
  1177. COORDS_PADDING );
  1178. m_controls->ForceCursorPosition( true, cursorPos );
  1179. if( evt->IsCancelInteractive() || ( dimension && evt->IsAction( &ACTIONS::undo ) ) )
  1180. {
  1181. m_controls->SetAutoPan( false );
  1182. if( step != SET_ORIGIN ) // start from the beginning
  1183. {
  1184. cleanup();
  1185. }
  1186. else
  1187. {
  1188. m_frame->PopTool( aEvent );
  1189. break;
  1190. }
  1191. }
  1192. else if( evt->IsActivate() )
  1193. {
  1194. if( step != SET_ORIGIN )
  1195. cleanup();
  1196. if( evt->IsPointEditor() )
  1197. {
  1198. // don't exit (the point editor runs in the background)
  1199. }
  1200. else if( evt->IsMoveTool() )
  1201. {
  1202. // leave ourselves on the stack so we come back after the move
  1203. break;
  1204. }
  1205. else
  1206. {
  1207. m_frame->PopTool( aEvent );
  1208. break;
  1209. }
  1210. }
  1211. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
  1212. {
  1213. m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
  1214. dimension->SetLineThickness( m_stroke.GetWidth() );
  1215. m_view->Update( &preview );
  1216. frame()->SetMsgPanel( dimension );
  1217. }
  1218. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
  1219. {
  1220. if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
  1221. {
  1222. m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
  1223. dimension->SetLineThickness( m_stroke.GetWidth() );
  1224. m_view->Update( &preview );
  1225. frame()->SetMsgPanel( dimension );
  1226. }
  1227. }
  1228. else if( evt->IsClick( BUT_RIGHT ) )
  1229. {
  1230. if( !dimension )
  1231. m_toolMgr->VetoContextMenuMouseWarp();
  1232. m_menu->ShowContextMenu( selection() );
  1233. }
  1234. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  1235. {
  1236. switch( step )
  1237. {
  1238. case SET_ORIGIN:
  1239. {
  1240. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  1241. PCB_LAYER_ID layer = m_frame->GetActiveLayer();
  1242. // Init the new item attributes
  1243. auto setMeasurementAttributes =
  1244. [&]( PCB_DIMENSION_BASE* aDim )
  1245. {
  1246. aDim->SetUnitsMode( boardSettings.m_DimensionUnitsMode );
  1247. aDim->SetUnitsFormat( boardSettings.m_DimensionUnitsFormat );
  1248. aDim->SetPrecision( boardSettings.m_DimensionPrecision );
  1249. aDim->SetSuppressZeroes( boardSettings.m_DimensionSuppressZeroes );
  1250. aDim->SetTextPositionMode( boardSettings.m_DimensionTextPosition );
  1251. aDim->SetKeepTextAligned( boardSettings.m_DimensionKeepTextAligned );
  1252. };
  1253. if( originalEvent.IsAction( &PCB_ACTIONS::drawAlignedDimension ) )
  1254. {
  1255. dimension = new PCB_DIM_ALIGNED( m_frame->GetModel() );
  1256. setMeasurementAttributes( dimension );
  1257. }
  1258. else if( originalEvent.IsAction( &PCB_ACTIONS::drawOrthogonalDimension ) )
  1259. {
  1260. dimension = new PCB_DIM_ORTHOGONAL( m_frame->GetModel() );
  1261. setMeasurementAttributes( dimension );
  1262. }
  1263. else if( originalEvent.IsAction( &PCB_ACTIONS::drawCenterDimension ) )
  1264. {
  1265. dimension = new PCB_DIM_CENTER( m_frame->GetModel() );
  1266. }
  1267. else if( originalEvent.IsAction( &PCB_ACTIONS::drawRadialDimension ) )
  1268. {
  1269. dimension = new PCB_DIM_RADIAL( m_frame->GetModel() );
  1270. setMeasurementAttributes( dimension );
  1271. }
  1272. else if( originalEvent.IsAction( &PCB_ACTIONS::drawLeader ) )
  1273. {
  1274. dimension = new PCB_DIM_LEADER( m_frame->GetModel() );
  1275. dimension->SetTextPos( cursorPos );
  1276. }
  1277. else
  1278. {
  1279. wxFAIL_MSG( wxT( "Unhandled action in DRAWING_TOOL::DrawDimension" ) );
  1280. }
  1281. t = dimension->Type();
  1282. dimension->SetLayer( layer );
  1283. dimension->SetMirrored( IsBackLayer( layer ) );
  1284. dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
  1285. dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
  1286. dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
  1287. dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
  1288. dimension->SetArrowLength( boardSettings.m_DimensionArrowLength );
  1289. dimension->SetExtensionOffset( boardSettings.m_DimensionExtensionOffset );
  1290. dimension->SetStart( cursorPos );
  1291. dimension->SetEnd( cursorPos );
  1292. dimension->Update();
  1293. if( !m_view->IsLayerVisible( layer ) )
  1294. {
  1295. m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
  1296. m_frame->GetCanvas()->Refresh();
  1297. }
  1298. preview.Add( dimension );
  1299. frame()->SetMsgPanel( dimension );
  1300. m_controls->SetAutoPan( true );
  1301. m_controls->CaptureCursor( true );
  1302. break;
  1303. }
  1304. case SET_END:
  1305. // Dimensions that have origin and end in the same spot are not valid
  1306. if( dimension->GetStart() == dimension->GetEnd() )
  1307. {
  1308. --step;
  1309. break;
  1310. }
  1311. if( t == PCB_DIM_CENTER_T || t == PCB_DIM_RADIAL_T || t == PCB_DIM_LEADER_T )
  1312. {
  1313. // No separate height step
  1314. ++step;
  1315. KI_FALLTHROUGH;
  1316. }
  1317. else
  1318. {
  1319. break;
  1320. }
  1321. case SET_HEIGHT:
  1322. assert( dimension->GetStart() != dimension->GetEnd() );
  1323. assert( dimension->GetLineThickness() > 0 );
  1324. preview.Remove( dimension );
  1325. commit.Add( dimension );
  1326. commit.Push( _( "Draw Dimension" ) );
  1327. // Run the edit immediately to set the leader text
  1328. if( t == PCB_DIM_LEADER_T )
  1329. frame()->OnEditItemRequest( dimension );
  1330. m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, dimension );
  1331. break;
  1332. }
  1333. if( ++step >= FINISHED )
  1334. {
  1335. dimension = nullptr;
  1336. step = SET_ORIGIN;
  1337. m_controls->SetAutoPan( false );
  1338. m_controls->CaptureCursor( false );
  1339. }
  1340. else if( evt->IsDblClick( BUT_LEFT ) )
  1341. {
  1342. m_toolMgr->PostAction( PCB_ACTIONS::cursorClick );
  1343. }
  1344. }
  1345. else if( evt->IsMotion() )
  1346. {
  1347. switch( step )
  1348. {
  1349. case SET_END:
  1350. dimension->SetEnd( cursorPos );
  1351. if( is45Limited || t == PCB_DIM_CENTER_T )
  1352. constrainDimension( dimension );
  1353. if( t == PCB_DIM_ORTHOGONAL_T )
  1354. {
  1355. PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
  1356. BOX2I bounds( dimension->GetStart(),
  1357. dimension->GetEnd() - dimension->GetStart() );
  1358. // Create a nice preview by measuring the longer dimension
  1359. bool vert = bounds.GetWidth() < bounds.GetHeight();
  1360. ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
  1361. : PCB_DIM_ORTHOGONAL::DIR::HORIZONTAL );
  1362. }
  1363. else if( t == PCB_DIM_RADIAL_T )
  1364. {
  1365. PCB_DIM_RADIAL* radialDim = static_cast<PCB_DIM_RADIAL*>( dimension );
  1366. VECTOR2I textOffset( radialDim->GetArrowLength() * 10, 0 );
  1367. if( radialDim->GetEnd().x < radialDim->GetStart().x )
  1368. textOffset = -textOffset;
  1369. radialDim->SetTextPos( radialDim->GetKnee() + textOffset );
  1370. }
  1371. else if( t == PCB_DIM_LEADER_T )
  1372. {
  1373. VECTOR2I textOffset( dimension->GetArrowLength() * 10, 0 );
  1374. if( dimension->GetEnd().x < dimension->GetStart().x )
  1375. textOffset = -textOffset;
  1376. dimension->SetTextPos( dimension->GetEnd() + textOffset );
  1377. }
  1378. dimension->Update();
  1379. break;
  1380. case SET_HEIGHT:
  1381. if( t == PCB_DIM_ALIGNED_T )
  1382. {
  1383. PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dimension );
  1384. // Calculating the direction of travel perpendicular to the selected axis
  1385. double angle = aligned->GetAngle() + ( M_PI / 2 );
  1386. VECTOR2I delta( (VECTOR2I) cursorPos - dimension->GetEnd() );
  1387. double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
  1388. aligned->SetHeight( height );
  1389. aligned->Update();
  1390. }
  1391. else if( t == PCB_DIM_ORTHOGONAL_T )
  1392. {
  1393. PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
  1394. BOX2I bbox( dimension->GetStart(),
  1395. dimension->GetEnd() - dimension->GetStart() );
  1396. VECTOR2I direction( cursorPos - bbox.Centre() );
  1397. bool vert;
  1398. // Only change the orientation when we move outside the bbox
  1399. if( !bbox.Contains( cursorPos ) )
  1400. {
  1401. // If the dimension is horizontal or vertical, set correct orientation
  1402. // otherwise, test if we're left/right of the bounding box or above/below it
  1403. if( bbox.GetWidth() == 0 )
  1404. vert = true;
  1405. else if( bbox.GetHeight() == 0 )
  1406. vert = false;
  1407. else if( cursorPos.x > bbox.GetLeft() && cursorPos.x < bbox.GetRight() )
  1408. vert = false;
  1409. else if( cursorPos.y > bbox.GetTop() && cursorPos.y < bbox.GetBottom() )
  1410. vert = true;
  1411. else
  1412. vert = std::abs( direction.y ) < std::abs( direction.x );
  1413. ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
  1414. : PCB_DIM_ORTHOGONAL::DIR::HORIZONTAL );
  1415. }
  1416. else
  1417. {
  1418. vert = ortho->GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
  1419. }
  1420. VECTOR2I heightVector( cursorPos - dimension->GetStart() );
  1421. ortho->SetHeight( vert ? heightVector.x : heightVector.y );
  1422. ortho->Update();
  1423. }
  1424. break;
  1425. }
  1426. // Show a preview of the item
  1427. m_view->Update( &preview );
  1428. }
  1429. else if( dimension && evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  1430. {
  1431. PCB_LAYER_ID layer = m_frame->GetActiveLayer();
  1432. if( !m_view->IsLayerVisible( layer ) )
  1433. {
  1434. m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
  1435. m_frame->GetCanvas()->Refresh();
  1436. }
  1437. dimension->SetLayer( layer );
  1438. dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
  1439. dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
  1440. dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
  1441. dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
  1442. dimension->Update();
  1443. m_view->Update( &preview );
  1444. frame()->SetMsgPanel( dimension );
  1445. }
  1446. else if( dimension && evt->IsAction( &PCB_ACTIONS::properties ) )
  1447. {
  1448. if( step == SET_END || step == SET_HEIGHT )
  1449. {
  1450. frame()->OnEditItemRequest( dimension );
  1451. dimension->Update();
  1452. frame()->SetMsgPanel( dimension );
  1453. break;
  1454. }
  1455. else
  1456. {
  1457. wxBell();
  1458. }
  1459. }
  1460. else if( dimension && evt->IsAction( &PCB_ACTIONS::changeDimensionArrows ) )
  1461. {
  1462. switch( dimension->Type() )
  1463. {
  1464. case PCB_DIM_ALIGNED_T:
  1465. case PCB_DIM_ORTHOGONAL_T:
  1466. case PCB_DIM_RADIAL_T:
  1467. if( dimension->GetArrowDirection() == DIM_ARROW_DIRECTION::INWARD )
  1468. dimension->SetArrowDirection( DIM_ARROW_DIRECTION::OUTWARD );
  1469. else
  1470. dimension->SetArrowDirection( DIM_ARROW_DIRECTION::INWARD );
  1471. break;
  1472. default:
  1473. // Other dimension types don't have arrows that can swap
  1474. wxBell();
  1475. }
  1476. m_view->Update( &preview );
  1477. }
  1478. else if( dimension && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
  1479. || evt->IsAction( &ACTIONS::redo ) ) )
  1480. {
  1481. wxBell();
  1482. }
  1483. else
  1484. {
  1485. evt->SetPassEvent();
  1486. }
  1487. }
  1488. if( step != SET_ORIGIN )
  1489. delete dimension;
  1490. m_controls->SetAutoPan( false );
  1491. m_controls->ForceCursorPosition( false );
  1492. m_controls->CaptureCursor( false );
  1493. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  1494. m_view->Remove( &preview );
  1495. if( selection().Empty() )
  1496. m_frame->SetMsgPanel( board() );
  1497. return 0;
  1498. }
  1499. int DRAWING_TOOL::PlaceImportedGraphics( const TOOL_EVENT& aEvent )
  1500. {
  1501. if( !m_frame->GetModel() )
  1502. return 0;
  1503. if( m_inDrawingTool )
  1504. return 0;
  1505. REENTRANCY_GUARD guard( &m_inDrawingTool );
  1506. DIALOG_IMPORT_GRAPHICS dlg( m_frame );
  1507. int dlgResult = dlg.ShowModal();
  1508. std::list<std::unique_ptr<EDA_ITEM>>& list = dlg.GetImportedItems();
  1509. if( dlgResult != wxID_OK )
  1510. return 0;
  1511. // Ensure the list is not empty:
  1512. if( list.empty() )
  1513. {
  1514. wxMessageBox( _( "No graphic items found in file.") );
  1515. return 0;
  1516. }
  1517. m_toolMgr->RunAction( ACTIONS::cancelInteractive );
  1518. std::vector<BOARD_ITEM*> newItems; // all new items, including group
  1519. std::vector<BOARD_ITEM*> selectedItems; // the group, or newItems if no group
  1520. PCB_SELECTION preview;
  1521. BOARD_COMMIT commit( m_frame );
  1522. PCB_GROUP* group = nullptr;
  1523. PICKED_ITEMS_LIST groupUndoList;
  1524. PCB_LAYER_ID layer = F_Cu;
  1525. if( dlg.ShouldGroupItems() )
  1526. {
  1527. group = new PCB_GROUP( m_frame->GetModel() );
  1528. newItems.push_back( group );
  1529. selectedItems.push_back( group );
  1530. preview.Add( group );
  1531. }
  1532. if( dlg.ShouldFixDiscontinuities() )
  1533. {
  1534. std::vector<PCB_SHAPE*> shapeList;
  1535. std::vector<std::unique_ptr<PCB_SHAPE>> newShapes;
  1536. for( const std::unique_ptr<EDA_ITEM>& ptr : list )
  1537. {
  1538. if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( ptr.get() ) )
  1539. shapeList.push_back( shape );
  1540. }
  1541. ConnectBoardShapes( shapeList, newShapes, dlg.GetTolerance() );
  1542. for( std::unique_ptr<PCB_SHAPE>& ptr : newShapes )
  1543. {
  1544. ptr->SetParent( m_frame->GetBoard() );
  1545. list.push_back( std::move( ptr ) );
  1546. }
  1547. }
  1548. for( std::unique_ptr<EDA_ITEM>& ptr : list )
  1549. {
  1550. EDA_ITEM* eda_item = ptr.release();
  1551. if( eda_item->IsBOARD_ITEM() )
  1552. {
  1553. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
  1554. newItems.push_back( item );
  1555. if( group )
  1556. {
  1557. group->AddItem( item );
  1558. groupUndoList.PushItem( ITEM_PICKER( nullptr, item, UNDO_REDO::REGROUP ) );
  1559. }
  1560. else
  1561. {
  1562. selectedItems.push_back( item );
  1563. }
  1564. layer = item->GetLayer();
  1565. }
  1566. preview.Add( eda_item );
  1567. }
  1568. // Clear the current selection then select the drawings so that edit tools work on them
  1569. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  1570. EDA_ITEMS selItems( selectedItems.begin(), selectedItems.end() );
  1571. m_toolMgr->RunAction<EDA_ITEMS*>( PCB_ACTIONS::selectItems, &selItems );
  1572. if( !dlg.IsPlacementInteractive() )
  1573. {
  1574. for( BOARD_ITEM* item : newItems )
  1575. commit.Add( item );
  1576. if( groupUndoList.GetCount() > 0 )
  1577. commit.Stage( groupUndoList );
  1578. commit.Push( _( "Import Graphics" ) );
  1579. return 0;
  1580. }
  1581. // Turn shapes on if they are off, so that the created object will be visible after completion
  1582. m_frame->SetObjectVisible( LAYER_SHAPES );
  1583. if( !m_view->IsLayerVisible( layer ) )
  1584. {
  1585. m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
  1586. m_frame->GetCanvas()->Refresh();
  1587. }
  1588. m_view->Add( &preview );
  1589. m_frame->PushTool( aEvent );
  1590. auto setCursor =
  1591. [&]()
  1592. {
  1593. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
  1594. };
  1595. Activate();
  1596. // Must be done after Activate() so that it gets set into the correct context
  1597. m_controls->ShowCursor( true );
  1598. m_controls->ForceCursorPosition( false );
  1599. // Set initial cursor
  1600. setCursor();
  1601. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
  1602. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  1603. // Now move the new items to the current cursor position:
  1604. VECTOR2I cursorPos = m_controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
  1605. VECTOR2I delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
  1606. for( BOARD_ITEM* item : selectedItems )
  1607. item->Move( delta );
  1608. m_view->Update( &preview );
  1609. // Main loop: keep receiving events
  1610. while( TOOL_EVENT* evt = Wait() )
  1611. {
  1612. setCursor();
  1613. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  1614. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  1615. cursorPos = GetClampedCoords(
  1616. grid.BestSnapAnchor( m_controls->GetMousePosition(), { layer }, GRID_GRAPHICS ),
  1617. COORDS_PADDING );
  1618. m_controls->ForceCursorPosition( true, cursorPos );
  1619. if( evt->IsCancelInteractive() || evt->IsActivate() )
  1620. {
  1621. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  1622. if( group )
  1623. {
  1624. preview.Remove( group );
  1625. group->RemoveAll();
  1626. }
  1627. for( BOARD_ITEM* item : newItems )
  1628. delete item;
  1629. break;
  1630. }
  1631. else if( evt->IsMotion() )
  1632. {
  1633. delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
  1634. for( BOARD_ITEM* item : selectedItems )
  1635. item->Move( delta );
  1636. m_view->Update( &preview );
  1637. }
  1638. else if( evt->IsClick( BUT_RIGHT ) )
  1639. {
  1640. m_menu->ShowContextMenu( selection() );
  1641. }
  1642. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  1643. {
  1644. // Place the imported drawings
  1645. for( BOARD_ITEM* item : newItems )
  1646. commit.Add( item );
  1647. if( groupUndoList.GetCount() > 0 )
  1648. commit.Stage( groupUndoList );
  1649. commit.Push( _( "Import Graphics" ) );
  1650. break; // This is a one-shot command, not a tool
  1651. }
  1652. else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
  1653. {
  1654. wxBell();
  1655. }
  1656. else
  1657. {
  1658. evt->SetPassEvent();
  1659. }
  1660. }
  1661. preview.Clear();
  1662. m_view->Remove( &preview );
  1663. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  1664. m_controls->ForceCursorPosition( false );
  1665. m_frame->PopTool( aEvent );
  1666. return 0;
  1667. }
  1668. int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent )
  1669. {
  1670. // Make sense only in FP editor
  1671. if( !m_isFootprintEditor )
  1672. return 0;
  1673. if( !m_frame->GetModel() )
  1674. return 0;
  1675. if( m_inDrawingTool )
  1676. return 0;
  1677. REENTRANCY_GUARD guard( &m_inDrawingTool );
  1678. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
  1679. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  1680. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  1681. m_frame->PushTool( aEvent );
  1682. auto setCursor =
  1683. [&]()
  1684. {
  1685. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
  1686. };
  1687. Activate();
  1688. // Must be done after Activate() so that it gets set into the correct context
  1689. m_controls->ShowCursor( true );
  1690. m_controls->SetAutoPan( true );
  1691. m_controls->CaptureCursor( false );
  1692. m_controls->ForceCursorPosition( false );
  1693. // Set initial cursor
  1694. setCursor();
  1695. while( TOOL_EVENT* evt = Wait() )
  1696. {
  1697. setCursor();
  1698. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  1699. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  1700. VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(),
  1701. LSET::AllLayersMask() );
  1702. m_controls->ForceCursorPosition( true, cursorPos );
  1703. if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  1704. {
  1705. FOOTPRINT* footprint = (FOOTPRINT*) m_frame->GetModel();
  1706. BOARD_COMMIT commit( m_frame );
  1707. commit.Modify( footprint );
  1708. // set the new relative internal local coordinates of footprint items
  1709. VECTOR2I moveVector = footprint->GetPosition() - cursorPos;
  1710. footprint->MoveAnchorPosition( moveVector );
  1711. commit.Push( _( "Move Footprint Anchor" ) );
  1712. // Usually, we do not need to change twice the anchor position,
  1713. // so deselect the active tool
  1714. m_frame->PopTool( aEvent );
  1715. break;
  1716. }
  1717. else if( evt->IsClick( BUT_RIGHT ) )
  1718. {
  1719. m_menu->ShowContextMenu( selection() );
  1720. }
  1721. else if( evt->IsCancelInteractive() || evt->IsActivate() )
  1722. {
  1723. m_frame->PopTool( aEvent );
  1724. break;
  1725. }
  1726. else
  1727. {
  1728. evt->SetPassEvent();
  1729. }
  1730. }
  1731. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  1732. m_controls->ForceCursorPosition( false );
  1733. return 0;
  1734. }
  1735. int DRAWING_TOOL::ToggleHV45Mode( const TOOL_EVENT& toolEvent )
  1736. {
  1737. #define TOGGLE( a ) a = !a
  1738. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  1739. if( frame()->IsType( FRAME_PCB_EDITOR ) )
  1740. TOGGLE( mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_Use45DegreeLimit );
  1741. else
  1742. TOGGLE( mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_Use45Limit );
  1743. UpdateStatusBar();
  1744. return 0;
  1745. #undef TOGGLE
  1746. }
  1747. /**
  1748. * Update a #PCB_SHAPE from the current state of a #TWO_POINT_GEOMETRY_MANAGER.
  1749. */
  1750. static void updateSegmentFromGeometryMgr( const KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER& aMgr,
  1751. PCB_SHAPE* aGraphic )
  1752. {
  1753. if( !aMgr.IsReset() )
  1754. {
  1755. aGraphic->SetStart( aMgr.GetOrigin() );
  1756. aGraphic->SetEnd( aMgr.GetEnd() );
  1757. }
  1758. }
  1759. bool DRAWING_TOOL::drawShape( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
  1760. std::optional<VECTOR2D> aStartingPoint,
  1761. std::stack<PCB_SHAPE*>* aCommittedGraphics )
  1762. {
  1763. SHAPE_T shape = ( *aGraphic )->GetShape();
  1764. // Only three shapes are currently supported
  1765. wxASSERT( shape == SHAPE_T::SEGMENT || shape == SHAPE_T::CIRCLE || shape == SHAPE_T::RECTANGLE );
  1766. const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
  1767. EDA_UNITS userUnits = m_frame->GetUserUnits();
  1768. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  1769. PCB_SHAPE*& graphic = *aGraphic;
  1770. if( m_layer != m_frame->GetActiveLayer() )
  1771. {
  1772. m_layer = m_frame->GetActiveLayer();
  1773. m_stroke.SetWidth( bds.GetLineThickness( m_layer ) );
  1774. m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
  1775. m_stroke.SetColor( COLOR4D::UNSPECIFIED );
  1776. m_textAttrs.m_Size = bds.GetTextSize( m_layer );
  1777. m_textAttrs.m_StrokeWidth = bds.GetTextThickness( m_layer );
  1778. InferBold( &m_textAttrs );
  1779. m_textAttrs.m_Italic = bds.GetTextItalic( m_layer );
  1780. m_textAttrs.m_KeepUpright = bds.GetTextUpright( m_layer );
  1781. m_textAttrs.m_Mirrored = IsBackLayer( m_layer );
  1782. m_textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
  1783. m_textAttrs.m_Valign = GR_TEXT_V_ALIGN_TOP;
  1784. }
  1785. // Turn shapes on if they are off, so that the created object will be visible after completion
  1786. m_frame->SetObjectVisible( LAYER_SHAPES );
  1787. // geometric construction manager
  1788. KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER twoPointMgr;
  1789. // drawing assistant overlay
  1790. // TODO: workaround because EDA_SHAPE_TYPE_T is not visible from commons.
  1791. KIGFX::PREVIEW::GEOM_SHAPE geomShape( static_cast<KIGFX::PREVIEW::GEOM_SHAPE>( shape ) );
  1792. KIGFX::PREVIEW::TWO_POINT_ASSISTANT twoPointAsst( twoPointMgr, pcbIUScale, userUnits, geomShape );
  1793. // Add a VIEW_GROUP that serves as a preview for the new item
  1794. m_preview.Clear();
  1795. m_view->Add( &m_preview );
  1796. m_view->Add( &twoPointAsst );
  1797. bool started = false;
  1798. bool cancelled = false;
  1799. bool isLocalOriginSet = ( m_frame->GetScreen()->m_LocalOrigin != VECTOR2D( 0, 0 ) );
  1800. VECTOR2I cursorPos = m_controls->GetMousePosition();
  1801. auto setCursor =
  1802. [&]()
  1803. {
  1804. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  1805. };
  1806. auto cleanup =
  1807. [&]()
  1808. {
  1809. m_preview.Clear();
  1810. m_view->Update( &m_preview );
  1811. delete graphic;
  1812. graphic = nullptr;
  1813. if( !isLocalOriginSet )
  1814. m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
  1815. };
  1816. m_controls->ShowCursor( true );
  1817. m_controls->ForceCursorPosition( false );
  1818. // Set initial cursor
  1819. setCursor();
  1820. m_toolMgr->PostAction( ACTIONS::refreshPreview );
  1821. if( aStartingPoint )
  1822. m_toolMgr->PrimeTool( *aStartingPoint );
  1823. // Main loop: keep receiving events
  1824. while( TOOL_EVENT* evt = Wait() )
  1825. {
  1826. setCursor();
  1827. if( started )
  1828. m_frame->SetMsgPanel( graphic );
  1829. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  1830. bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
  1831. // Rectangular shapes never get 45-degree snapping
  1832. if( shape == SHAPE_T::RECTANGLE )
  1833. is45Limited = false;
  1834. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  1835. cursorPos = GetClampedCoords(
  1836. grid.BestSnapAnchor( m_controls->GetMousePosition(), { m_layer }, GRID_GRAPHICS ),
  1837. COORDS_PADDING );
  1838. m_controls->ForceCursorPosition( true, cursorPos );
  1839. if( evt->IsCancelInteractive() || ( started && evt->IsAction( &ACTIONS::undo ) ) )
  1840. {
  1841. cleanup();
  1842. if( !started )
  1843. {
  1844. // We've handled the cancel event. Don't cancel other tools
  1845. evt->SetPassEvent( false );
  1846. m_frame->PopTool( aTool );
  1847. cancelled = true;
  1848. }
  1849. break;
  1850. }
  1851. else if( evt->IsActivate() )
  1852. {
  1853. if( evt->IsPointEditor() )
  1854. {
  1855. // don't exit (the point editor runs in the background)
  1856. }
  1857. else if( evt->IsMoveTool() )
  1858. {
  1859. cleanup();
  1860. // leave ourselves on the stack so we come back after the move
  1861. cancelled = true;
  1862. break;
  1863. }
  1864. else
  1865. {
  1866. cleanup();
  1867. m_frame->PopTool( aTool );
  1868. cancelled = true;
  1869. break;
  1870. }
  1871. }
  1872. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  1873. {
  1874. if( m_layer != m_frame->GetActiveLayer() )
  1875. {
  1876. m_layer = m_frame->GetActiveLayer();
  1877. m_stroke.SetWidth( bds.GetLineThickness( m_layer ) );
  1878. m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
  1879. m_stroke.SetColor( COLOR4D::UNSPECIFIED );
  1880. m_textAttrs.m_Size = bds.GetTextSize( m_layer );
  1881. m_textAttrs.m_StrokeWidth = bds.GetTextThickness( m_layer );
  1882. InferBold( &m_textAttrs );
  1883. m_textAttrs.m_Italic = bds.GetTextItalic( m_layer );
  1884. m_textAttrs.m_KeepUpright = bds.GetTextUpright( m_layer );
  1885. m_textAttrs.m_Mirrored = IsBackLayer( m_layer );
  1886. m_textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
  1887. m_textAttrs.m_Valign = GR_TEXT_V_ALIGN_TOP;
  1888. }
  1889. if( graphic )
  1890. {
  1891. if( !m_view->IsLayerVisible( m_layer ) )
  1892. {
  1893. m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
  1894. m_frame->GetCanvas()->Refresh();
  1895. }
  1896. graphic->SetLayer( m_layer );
  1897. graphic->SetStroke( m_stroke );
  1898. if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
  1899. pcb_textbox->SetAttributes( m_textAttrs );
  1900. m_view->Update( &m_preview );
  1901. frame()->SetMsgPanel( graphic );
  1902. }
  1903. else
  1904. {
  1905. evt->SetPassEvent();
  1906. }
  1907. }
  1908. else if( evt->IsClick( BUT_RIGHT ) )
  1909. {
  1910. if( !graphic )
  1911. m_toolMgr->VetoContextMenuMouseWarp();
  1912. m_menu->ShowContextMenu( selection() );
  1913. }
  1914. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  1915. {
  1916. if( !graphic )
  1917. break;
  1918. if( !started )
  1919. {
  1920. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  1921. if( aStartingPoint )
  1922. {
  1923. cursorPos = *aStartingPoint;
  1924. aStartingPoint = std::nullopt;
  1925. }
  1926. // Init the new item attributes
  1927. if( graphic ) // always true, but Coverity can't seem to figure that out
  1928. {
  1929. graphic->SetShape( static_cast<SHAPE_T>( shape ) );
  1930. graphic->SetFilled( false );
  1931. graphic->SetStroke( m_stroke );
  1932. graphic->SetLayer( m_layer );
  1933. }
  1934. if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
  1935. pcb_textbox->SetAttributes( m_textAttrs );
  1936. grid.SetSkipPoint( cursorPos );
  1937. twoPointMgr.SetOrigin( cursorPos );
  1938. twoPointMgr.SetEnd( cursorPos );
  1939. if( !isLocalOriginSet )
  1940. m_frame->GetScreen()->m_LocalOrigin = cursorPos;
  1941. m_preview.Add( graphic );
  1942. frame()->SetMsgPanel( graphic );
  1943. m_controls->SetAutoPan( true );
  1944. m_controls->CaptureCursor( true );
  1945. if( !m_view->IsLayerVisible( m_layer ) )
  1946. {
  1947. m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
  1948. m_frame->GetCanvas()->Refresh();
  1949. }
  1950. updateSegmentFromGeometryMgr( twoPointMgr, graphic );
  1951. started = true;
  1952. }
  1953. else
  1954. {
  1955. PCB_SHAPE* snapItem = dynamic_cast<PCB_SHAPE*>( grid.GetSnapped() );
  1956. if( shape == SHAPE_T::SEGMENT && snapItem && graphic->GetLength() > 0 )
  1957. {
  1958. // User has clicked on the end of an existing segment, closing a path
  1959. BOARD_COMMIT commit( m_frame );
  1960. commit.Add( graphic );
  1961. commit.Push( _( "Draw Line" ) );
  1962. m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, graphic );
  1963. graphic = nullptr;
  1964. }
  1965. else if( twoPointMgr.IsEmpty() || evt->IsDblClick( BUT_LEFT ) )
  1966. {
  1967. // User has clicked twice in the same spot, meaning we're finished
  1968. delete graphic;
  1969. graphic = nullptr;
  1970. }
  1971. m_preview.Clear();
  1972. twoPointMgr.Reset();
  1973. break;
  1974. }
  1975. twoPointMgr.SetEnd( GetClampedCoords( cursorPos ) );
  1976. }
  1977. else if( evt->IsMotion() )
  1978. {
  1979. VECTOR2I clampedCursorPos = cursorPos;
  1980. if( shape == SHAPE_T::CIRCLE || shape == SHAPE_T::ARC )
  1981. clampedCursorPos = getClampedRadiusEnd( twoPointMgr.GetOrigin(), cursorPos );
  1982. else
  1983. clampedCursorPos = getClampedDifferenceEnd( twoPointMgr.GetOrigin(), cursorPos );
  1984. // 45 degree lines
  1985. if( started && is45Limited )
  1986. {
  1987. const VECTOR2I lineVector( clampedCursorPos - VECTOR2I( twoPointMgr.GetOrigin() ) );
  1988. // get a restricted 45/H/V line from the last fixed point to the cursor
  1989. VECTOR2I newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECTANGLE ) );
  1990. m_controls->ForceCursorPosition( true, VECTOR2I( twoPointMgr.GetEnd() ) );
  1991. twoPointMgr.SetEnd( twoPointMgr.GetOrigin() + newEnd );
  1992. twoPointMgr.SetAngleSnap( true );
  1993. }
  1994. else
  1995. {
  1996. twoPointMgr.SetEnd( clampedCursorPos );
  1997. twoPointMgr.SetAngleSnap( false );
  1998. }
  1999. updateSegmentFromGeometryMgr( twoPointMgr, graphic );
  2000. m_view->Update( &m_preview );
  2001. m_view->Update( &twoPointAsst );
  2002. }
  2003. else if( started && ( evt->IsAction( &PCB_ACTIONS::doDelete )
  2004. || evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) ) )
  2005. {
  2006. if( aCommittedGraphics && !aCommittedGraphics->empty() )
  2007. {
  2008. twoPointMgr.SetOrigin( aCommittedGraphics->top()->GetStart() );
  2009. twoPointMgr.SetEnd( aCommittedGraphics->top()->GetEnd() );
  2010. aCommittedGraphics->pop();
  2011. getViewControls()->WarpMouseCursor( twoPointMgr.GetEnd(), true );
  2012. if( PICKED_ITEMS_LIST* undo = m_frame->PopCommandFromUndoList() )
  2013. {
  2014. m_frame->PutDataInPreviousState( undo );
  2015. m_frame->ClearListAndDeleteItems( undo );
  2016. delete undo;
  2017. }
  2018. updateSegmentFromGeometryMgr( twoPointMgr, graphic );
  2019. m_view->Update( &m_preview );
  2020. m_view->Update( &twoPointAsst );
  2021. }
  2022. else
  2023. {
  2024. cleanup();
  2025. break;
  2026. }
  2027. }
  2028. else if( graphic && evt->IsAction( &PCB_ACTIONS::incWidth ) )
  2029. {
  2030. m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
  2031. graphic->SetStroke( m_stroke );
  2032. m_view->Update( &m_preview );
  2033. frame()->SetMsgPanel( graphic );
  2034. }
  2035. else if( graphic && evt->IsAction( &PCB_ACTIONS::decWidth ) )
  2036. {
  2037. if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
  2038. {
  2039. m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
  2040. graphic->SetStroke( m_stroke );
  2041. m_view->Update( &m_preview );
  2042. frame()->SetMsgPanel( graphic );
  2043. }
  2044. }
  2045. else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
  2046. {
  2047. frame()->OnEditItemRequest( graphic );
  2048. m_view->Update( &m_preview );
  2049. frame()->SetMsgPanel( graphic );
  2050. }
  2051. else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
  2052. || evt->IsAction( &ACTIONS::redo ) ) )
  2053. {
  2054. wxBell();
  2055. }
  2056. else if( evt->IsAction( &ACTIONS::resetLocalCoords ) )
  2057. {
  2058. isLocalOriginSet = true;
  2059. evt->SetPassEvent();
  2060. }
  2061. else if( evt->IsAction( &ACTIONS::updateUnits ) )
  2062. {
  2063. if( frame()->GetUserUnits() != userUnits )
  2064. {
  2065. userUnits = frame()->GetUserUnits();
  2066. twoPointAsst.SetUnits( userUnits );
  2067. m_view->Update( &twoPointAsst );
  2068. }
  2069. evt->SetPassEvent();
  2070. }
  2071. else
  2072. {
  2073. evt->SetPassEvent();
  2074. }
  2075. }
  2076. if( !isLocalOriginSet ) // reset the relative coordinate if it was not set before
  2077. m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
  2078. m_view->Remove( &twoPointAsst );
  2079. m_view->Remove( &m_preview );
  2080. if( selection().Empty() )
  2081. m_frame->SetMsgPanel( board() );
  2082. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  2083. m_controls->SetAutoPan( false );
  2084. m_controls->CaptureCursor( false );
  2085. m_controls->ForceCursorPosition( false );
  2086. return !cancelled;
  2087. }
  2088. /**
  2089. * Update an arc PCB_SHAPE from the current state of an Arc Geometry Manager.
  2090. */
  2091. static void updateArcFromConstructionMgr( const KIGFX::PREVIEW::ARC_GEOM_MANAGER& aMgr,
  2092. PCB_SHAPE& aArc )
  2093. {
  2094. VECTOR2I vec = aMgr.GetOrigin();
  2095. aArc.SetCenter( vec );
  2096. if( aMgr.GetSubtended() < ANGLE_0 )
  2097. {
  2098. vec = aMgr.GetStartRadiusEnd();
  2099. aArc.SetStart( vec );
  2100. vec = aMgr.GetEndRadiusEnd();
  2101. aArc.SetEnd( vec );
  2102. }
  2103. else
  2104. {
  2105. vec = aMgr.GetEndRadiusEnd();
  2106. aArc.SetStart( vec );
  2107. vec = aMgr.GetStartRadiusEnd();
  2108. aArc.SetEnd( vec );
  2109. }
  2110. }
  2111. bool DRAWING_TOOL::drawArc( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
  2112. std::optional<VECTOR2D> aStartingPoint )
  2113. {
  2114. wxCHECK( aGraphic, false );
  2115. PCB_SHAPE*& graphic = *aGraphic;
  2116. wxCHECK( graphic, false );
  2117. if( m_layer != m_frame->GetActiveLayer() )
  2118. {
  2119. m_layer = m_frame->GetActiveLayer();
  2120. m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
  2121. m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
  2122. m_stroke.SetColor( COLOR4D::UNSPECIFIED );
  2123. }
  2124. // Turn shapes on if they are off, so that the created object will be visible after completion
  2125. m_frame->SetObjectVisible( LAYER_SHAPES );
  2126. // Arc geometric construction manager
  2127. KIGFX::PREVIEW::ARC_GEOM_MANAGER arcManager;
  2128. // Arc drawing assistant overlay
  2129. KIGFX::PREVIEW::ARC_ASSISTANT arcAsst( arcManager, pcbIUScale, m_frame->GetUserUnits() );
  2130. // Add a VIEW_GROUP that serves as a preview for the new item
  2131. PCB_SELECTION preview;
  2132. m_view->Add( &preview );
  2133. m_view->Add( &arcAsst );
  2134. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  2135. auto setCursor =
  2136. [&]()
  2137. {
  2138. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  2139. };
  2140. auto cleanup =
  2141. [&] ()
  2142. {
  2143. preview.Clear();
  2144. delete *aGraphic;
  2145. *aGraphic = nullptr;
  2146. };
  2147. m_controls->ShowCursor( true );
  2148. m_controls->ForceCursorPosition( false );
  2149. // Set initial cursor
  2150. setCursor();
  2151. bool started = false;
  2152. bool cancelled = false;
  2153. m_toolMgr->PostAction( ACTIONS::refreshPreview );
  2154. if( aStartingPoint )
  2155. m_toolMgr->PrimeTool( *aStartingPoint );
  2156. // Main loop: keep receiving events
  2157. while( TOOL_EVENT* evt = Wait() )
  2158. {
  2159. if( started )
  2160. m_frame->SetMsgPanel( graphic );
  2161. setCursor();
  2162. graphic->SetLayer( m_layer );
  2163. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  2164. bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
  2165. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  2166. VECTOR2I cursorPos = GetClampedCoords(
  2167. grid.BestSnapAnchor( m_controls->GetMousePosition(), graphic, GRID_GRAPHICS ),
  2168. COORDS_PADDING );
  2169. m_controls->ForceCursorPosition( true, cursorPos );
  2170. if( evt->IsCancelInteractive() || ( started && evt->IsAction( &ACTIONS::undo ) ) )
  2171. {
  2172. cleanup();
  2173. if( !started )
  2174. {
  2175. // We've handled the cancel event. Don't cancel other tools
  2176. evt->SetPassEvent( false );
  2177. m_frame->PopTool( aTool );
  2178. cancelled = true;
  2179. }
  2180. break;
  2181. }
  2182. else if( evt->IsActivate() )
  2183. {
  2184. if( evt->IsPointEditor() )
  2185. {
  2186. // don't exit (the point editor runs in the background)
  2187. }
  2188. else if( evt->IsMoveTool() )
  2189. {
  2190. cleanup();
  2191. // leave ourselves on the stack so we come back after the move
  2192. cancelled = true;
  2193. break;
  2194. }
  2195. else
  2196. {
  2197. cleanup();
  2198. m_frame->PopTool( aTool );
  2199. cancelled = true;
  2200. break;
  2201. }
  2202. }
  2203. else if( evt->IsClick( BUT_LEFT ) )
  2204. {
  2205. if( !started )
  2206. {
  2207. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  2208. m_controls->SetAutoPan( true );
  2209. m_controls->CaptureCursor( true );
  2210. // Init the new item attributes
  2211. // (non-geometric, those are handled by the manager)
  2212. graphic->SetShape( SHAPE_T::ARC );
  2213. graphic->SetStroke( m_stroke );
  2214. if( !m_view->IsLayerVisible( m_layer ) )
  2215. {
  2216. m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
  2217. m_frame->GetCanvas()->Refresh();
  2218. }
  2219. preview.Add( graphic );
  2220. frame()->SetMsgPanel( graphic );
  2221. started = true;
  2222. }
  2223. arcManager.AddPoint( cursorPos, true );
  2224. }
  2225. else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
  2226. {
  2227. arcManager.RemoveLastPoint();
  2228. }
  2229. else if( evt->IsMotion() )
  2230. {
  2231. // set angle snap
  2232. arcManager.SetAngleSnap( is45Limited );
  2233. // update, but don't step the manager state
  2234. arcManager.AddPoint( cursorPos, false );
  2235. }
  2236. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  2237. {
  2238. if( m_layer != m_frame->GetActiveLayer() )
  2239. {
  2240. m_layer = m_frame->GetActiveLayer();
  2241. m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
  2242. m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
  2243. m_stroke.SetColor( COLOR4D::UNSPECIFIED );
  2244. }
  2245. if( graphic )
  2246. {
  2247. if( !m_view->IsLayerVisible( m_layer ) )
  2248. {
  2249. m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
  2250. m_frame->GetCanvas()->Refresh();
  2251. }
  2252. graphic->SetLayer( m_layer );
  2253. graphic->SetStroke( m_stroke );
  2254. m_view->Update( &preview );
  2255. frame()->SetMsgPanel( graphic );
  2256. }
  2257. else
  2258. {
  2259. evt->SetPassEvent();
  2260. }
  2261. }
  2262. else if( evt->IsAction( &PCB_ACTIONS::properties ) )
  2263. {
  2264. if( arcManager.GetStep() == KIGFX::PREVIEW::ARC_GEOM_MANAGER::SET_START )
  2265. {
  2266. graphic->SetArcAngleAndEnd( ANGLE_90 );
  2267. frame()->OnEditItemRequest( graphic );
  2268. m_view->Update( &preview );
  2269. frame()->SetMsgPanel( graphic );
  2270. break;
  2271. }
  2272. // Don't show the edit panel if we can't represent the arc with it
  2273. else if( ( arcManager.GetStep() == KIGFX::PREVIEW::ARC_GEOM_MANAGER::SET_ANGLE )
  2274. && ( arcManager.GetStartRadiusEnd() != arcManager.GetEndRadiusEnd() ) )
  2275. {
  2276. frame()->OnEditItemRequest( graphic );
  2277. m_view->Update( &preview );
  2278. frame()->SetMsgPanel( graphic );
  2279. break;
  2280. }
  2281. else
  2282. {
  2283. evt->SetPassEvent();
  2284. }
  2285. }
  2286. else if( evt->IsClick( BUT_RIGHT ) )
  2287. {
  2288. if( !graphic )
  2289. m_toolMgr->VetoContextMenuMouseWarp();
  2290. m_menu->ShowContextMenu( selection() );
  2291. }
  2292. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
  2293. {
  2294. m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
  2295. if( graphic )
  2296. {
  2297. graphic->SetStroke( m_stroke );
  2298. m_view->Update( &preview );
  2299. frame()->SetMsgPanel( graphic );
  2300. }
  2301. }
  2302. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
  2303. {
  2304. if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
  2305. {
  2306. m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
  2307. if( graphic )
  2308. {
  2309. graphic->SetStroke( m_stroke );
  2310. m_view->Update( &preview );
  2311. frame()->SetMsgPanel( graphic );
  2312. }
  2313. }
  2314. }
  2315. else if( evt->IsAction( &PCB_ACTIONS::arcPosture ) )
  2316. {
  2317. arcManager.ToggleClockwise();
  2318. }
  2319. else if( evt->IsAction( &ACTIONS::updateUnits ) )
  2320. {
  2321. arcAsst.SetUnits( frame()->GetUserUnits() );
  2322. m_view->Update( &arcAsst );
  2323. evt->SetPassEvent();
  2324. }
  2325. else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
  2326. || evt->IsAction( &ACTIONS::redo ) ) )
  2327. {
  2328. wxBell();
  2329. }
  2330. else
  2331. {
  2332. evt->SetPassEvent();
  2333. }
  2334. if( arcManager.IsComplete() )
  2335. {
  2336. break;
  2337. }
  2338. else if( arcManager.HasGeometryChanged() )
  2339. {
  2340. updateArcFromConstructionMgr( arcManager, *graphic );
  2341. m_view->Update( &preview );
  2342. m_view->Update( &arcAsst );
  2343. if( started )
  2344. frame()->SetMsgPanel( graphic );
  2345. else
  2346. frame()->SetMsgPanel( board() );
  2347. }
  2348. }
  2349. preview.Remove( graphic );
  2350. m_view->Remove( &arcAsst );
  2351. m_view->Remove( &preview );
  2352. if( selection().Empty() )
  2353. m_frame->SetMsgPanel( board() );
  2354. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  2355. m_controls->SetAutoPan( false );
  2356. m_controls->CaptureCursor( false );
  2357. m_controls->ForceCursorPosition( false );
  2358. return !cancelled;
  2359. }
  2360. /**
  2361. * Update a bezier PCB_SHAPE from the current state of a Bezier Geometry Manager.
  2362. */
  2363. static void updateBezierFromConstructionMgr( const KIGFX::PREVIEW::BEZIER_GEOM_MANAGER& aMgr,
  2364. PCB_SHAPE& aBezier )
  2365. {
  2366. VECTOR2I vec = aMgr.GetStart();
  2367. aBezier.SetStart( vec );
  2368. aBezier.SetBezierC1( aMgr.GetControlC1() );
  2369. aBezier.SetEnd( aMgr.GetEnd() );
  2370. aBezier.SetBezierC2( aMgr.GetControlC2() );
  2371. // Need this for the length preview to work
  2372. aBezier.RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
  2373. }
  2374. std::unique_ptr<PCB_SHAPE> DRAWING_TOOL::drawOneBezier( const TOOL_EVENT& aTool,
  2375. const OPT_VECTOR2I& aStartingPoint,
  2376. const OPT_VECTOR2I& aStartingControl1Point,
  2377. DRAW_ONE_RESULT& aResult )
  2378. {
  2379. std::unique_ptr<PCB_SHAPE> bezier = std::make_unique<PCB_SHAPE>( m_frame->GetModel() );
  2380. bezier->SetShape( SHAPE_T::BEZIER );
  2381. bezier->SetFlags( IS_NEW );
  2382. if( m_layer != m_frame->GetActiveLayer() )
  2383. {
  2384. m_layer = m_frame->GetActiveLayer();
  2385. m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
  2386. m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
  2387. m_stroke.SetColor( COLOR4D::UNSPECIFIED );
  2388. }
  2389. // Turn shapes on if they are off, so that the created object will be visible after completion
  2390. m_frame->SetObjectVisible( LAYER_SHAPES );
  2391. // Arc geometric construction manager
  2392. KIGFX::PREVIEW::BEZIER_GEOM_MANAGER bezierManager;
  2393. // Arc drawing assistant overlay
  2394. KIGFX::PREVIEW::BEZIER_ASSISTANT bezierAsst( bezierManager, pcbIUScale,
  2395. m_frame->GetUserUnits() );
  2396. // Add a VIEW_GROUP that serves as a preview for the new item
  2397. PCB_SELECTION preview;
  2398. m_view->Add( &preview );
  2399. m_view->Add( &bezierAsst );
  2400. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  2401. const auto setCursor = [&]()
  2402. {
  2403. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  2404. };
  2405. const auto resetProgress = [&]()
  2406. {
  2407. preview.Clear();
  2408. bezier.reset();
  2409. };
  2410. m_controls->ShowCursor( true );
  2411. m_controls->ForceCursorPosition( false );
  2412. // Set initial cursor
  2413. setCursor();
  2414. const auto started = [&]()
  2415. {
  2416. return bezierManager.GetStep() > KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_START;
  2417. };
  2418. aResult = DRAW_ONE_RESULT::ACCEPTED;
  2419. bool priming = false;
  2420. m_toolMgr->PostAction( ACTIONS::refreshPreview );
  2421. // Load in one or two points if they were passed in
  2422. if( aStartingPoint )
  2423. {
  2424. priming = true;
  2425. if( aStartingControl1Point )
  2426. {
  2427. bezierManager.AddPoint( *aStartingPoint, true );
  2428. bezierManager.AddPoint( *aStartingControl1Point, true );
  2429. m_toolMgr->PrimeTool( *aStartingControl1Point );
  2430. }
  2431. else
  2432. {
  2433. bezierManager.AddPoint( *aStartingPoint, true );
  2434. m_toolMgr->PrimeTool( *aStartingPoint );
  2435. }
  2436. }
  2437. // Main loop: keep receiving events
  2438. while( TOOL_EVENT* evt = Wait() )
  2439. {
  2440. if( started() )
  2441. m_frame->SetMsgPanel( bezier.get() );
  2442. setCursor();
  2443. // Init the new item attributes
  2444. // (non-geometric, those are handled by the manager)
  2445. bezier->SetShape( SHAPE_T::BEZIER );
  2446. bezier->SetStroke( m_stroke );
  2447. bezier->SetLayer( m_layer );
  2448. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  2449. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  2450. VECTOR2I cursorPos = GetClampedCoords(
  2451. grid.BestSnapAnchor( m_controls->GetMousePosition(), bezier.get(), GRID_GRAPHICS ),
  2452. COORDS_PADDING );
  2453. m_controls->ForceCursorPosition( true, cursorPos );
  2454. if( evt->IsCancelInteractive() || ( started() && evt->IsAction( &ACTIONS::undo ) ) )
  2455. {
  2456. resetProgress();
  2457. if( !started() )
  2458. {
  2459. // We've handled the cancel event. Don't cancel other tools
  2460. evt->SetPassEvent( false );
  2461. m_frame->PopTool( aTool );
  2462. aResult = DRAW_ONE_RESULT::CANCELLED;
  2463. }
  2464. else
  2465. {
  2466. // We're not cancelling, but we're also not returning a finished bezier
  2467. // So we'll be called again.
  2468. aResult = DRAW_ONE_RESULT::RESET;
  2469. }
  2470. break;
  2471. }
  2472. else if( evt->IsActivate() )
  2473. {
  2474. if( evt->IsPointEditor() )
  2475. {
  2476. // don't exit (the point editor runs in the background)
  2477. }
  2478. else if( evt->IsMoveTool() )
  2479. {
  2480. resetProgress();
  2481. // leave ourselves on the stack so we come back after the move
  2482. aResult = DRAW_ONE_RESULT::CANCELLED;
  2483. break;
  2484. }
  2485. else
  2486. {
  2487. resetProgress();
  2488. m_frame->PopTool( aTool );
  2489. aResult = DRAW_ONE_RESULT::CANCELLED;
  2490. break;
  2491. }
  2492. }
  2493. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  2494. {
  2495. if( !started() )
  2496. {
  2497. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  2498. m_controls->SetAutoPan( true );
  2499. m_controls->CaptureCursor( true );
  2500. if( !m_view->IsLayerVisible( m_layer ) )
  2501. {
  2502. m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
  2503. m_frame->GetCanvas()->Refresh();
  2504. }
  2505. frame()->SetMsgPanel( bezier.get() );
  2506. }
  2507. if( !priming )
  2508. bezierManager.AddPoint( cursorPos, true );
  2509. else
  2510. priming = false;
  2511. const bool doubleClick = evt->IsDblClick( BUT_LEFT );
  2512. if( doubleClick )
  2513. {
  2514. // Use the current point for all remaining points
  2515. while( bezierManager.GetStep() < KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END )
  2516. {
  2517. bezierManager.AddPoint( cursorPos, true );
  2518. }
  2519. }
  2520. if( bezierManager.GetStep() == KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END )
  2521. {
  2522. preview.Add( bezier.get() );
  2523. }
  2524. // Return to the caller for a reset
  2525. if( doubleClick )
  2526. {
  2527. // Don't chain to this one
  2528. aResult = DRAW_ONE_RESULT::ACCEPTED_AND_RESET;
  2529. break;
  2530. }
  2531. }
  2532. else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
  2533. {
  2534. bezierManager.RemoveLastPoint();
  2535. if( bezierManager.GetStep() < KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END )
  2536. {
  2537. preview.Remove( bezier.get() );
  2538. }
  2539. }
  2540. else if( evt->IsMotion() )
  2541. {
  2542. // set angle snap
  2543. // bezierManager.SetAngleSnap( Is45Limited() );
  2544. // update, but don't step the manager state
  2545. bezierManager.AddPoint( cursorPos, false );
  2546. }
  2547. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  2548. {
  2549. if( m_layer != m_frame->GetActiveLayer() )
  2550. {
  2551. m_layer = m_frame->GetActiveLayer();
  2552. m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
  2553. m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
  2554. m_stroke.SetColor( COLOR4D::UNSPECIFIED );
  2555. }
  2556. if( bezier )
  2557. {
  2558. if( !m_view->IsLayerVisible( m_layer ) )
  2559. {
  2560. m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
  2561. m_frame->GetCanvas()->Refresh();
  2562. }
  2563. bezier->SetLayer( m_layer );
  2564. bezier->SetStroke( m_stroke );
  2565. m_view->Update( &preview );
  2566. frame()->SetMsgPanel( bezier.get() );
  2567. }
  2568. else
  2569. {
  2570. evt->SetPassEvent();
  2571. }
  2572. }
  2573. else if( evt->IsAction( &PCB_ACTIONS::properties ) )
  2574. {
  2575. // Don't show the edit panel if we can't represent the arc with it
  2576. if( ( bezierManager.GetStep() >= KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END ) )
  2577. {
  2578. frame()->OnEditItemRequest( bezier.get() );
  2579. m_view->Update( &preview );
  2580. frame()->SetMsgPanel( bezier.get() );
  2581. break;
  2582. }
  2583. else
  2584. {
  2585. evt->SetPassEvent();
  2586. }
  2587. }
  2588. else if( evt->IsClick( BUT_RIGHT ) )
  2589. {
  2590. if( !bezier )
  2591. m_toolMgr->VetoContextMenuMouseWarp();
  2592. m_menu->ShowContextMenu( selection() );
  2593. }
  2594. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
  2595. {
  2596. m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
  2597. if( bezier )
  2598. {
  2599. bezier->SetStroke( m_stroke );
  2600. m_view->Update( &preview );
  2601. frame()->SetMsgPanel( bezier.get() );
  2602. }
  2603. }
  2604. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
  2605. {
  2606. if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
  2607. {
  2608. m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
  2609. if( bezier )
  2610. {
  2611. bezier->SetStroke( m_stroke );
  2612. m_view->Update( &preview );
  2613. frame()->SetMsgPanel( bezier.get() );
  2614. }
  2615. }
  2616. }
  2617. else if( evt->IsAction( &ACTIONS::updateUnits ) )
  2618. {
  2619. bezierAsst.SetUnits( frame()->GetUserUnits() );
  2620. m_view->Update( &bezierAsst );
  2621. evt->SetPassEvent();
  2622. }
  2623. else if( started()
  2624. && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
  2625. || evt->IsAction( &ACTIONS::redo ) ) )
  2626. {
  2627. wxBell();
  2628. }
  2629. else
  2630. {
  2631. evt->SetPassEvent();
  2632. }
  2633. if( bezierManager.IsComplete() )
  2634. {
  2635. break;
  2636. }
  2637. else if( bezierManager.HasGeometryChanged() )
  2638. {
  2639. updateBezierFromConstructionMgr( bezierManager, *bezier );
  2640. m_view->Update( &preview );
  2641. m_view->Update( &bezierAsst );
  2642. // Once we are receiving end points, we can show the bezier in the preview
  2643. if( bezierManager.GetStep() >= KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END )
  2644. frame()->SetMsgPanel( bezier.get() );
  2645. else
  2646. frame()->SetMsgPanel( board() );
  2647. }
  2648. }
  2649. preview.Remove( bezier.get() );
  2650. m_view->Remove( &bezierAsst );
  2651. m_view->Remove( &preview );
  2652. if( selection().Empty() )
  2653. m_frame->SetMsgPanel( board() );
  2654. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  2655. m_controls->SetAutoPan( false );
  2656. m_controls->CaptureCursor( false );
  2657. m_controls->ForceCursorPosition( false );
  2658. return bezier;
  2659. };
  2660. bool DRAWING_TOOL::getSourceZoneForAction( ZONE_MODE aMode, ZONE** aZone )
  2661. {
  2662. bool clearSelection = false;
  2663. *aZone = nullptr;
  2664. // not an action that needs a source zone
  2665. if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON )
  2666. return true;
  2667. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  2668. const PCB_SELECTION& selection = selTool->GetSelection();
  2669. if( selection.Empty() )
  2670. {
  2671. clearSelection = true;
  2672. m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor );
  2673. }
  2674. // we want a single zone
  2675. if( selection.Size() == 1 && selection[0]->Type() == PCB_ZONE_T )
  2676. *aZone = static_cast<ZONE*>( selection[0] );
  2677. // expected a zone, but didn't get one
  2678. if( !*aZone )
  2679. {
  2680. if( clearSelection )
  2681. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  2682. return false;
  2683. }
  2684. return true;
  2685. }
  2686. int DRAWING_TOOL::DrawZone( const TOOL_EVENT& aEvent )
  2687. {
  2688. if( m_isFootprintEditor && !m_frame->GetModel() )
  2689. return 0;
  2690. ZONE_MODE zoneMode = aEvent.Parameter<ZONE_MODE>();
  2691. MODE drawMode = MODE::ZONE;
  2692. if( aEvent.IsAction( &PCB_ACTIONS::drawRuleArea ) )
  2693. drawMode = MODE::KEEPOUT;
  2694. if( aEvent.IsAction( &PCB_ACTIONS::drawPolygon ) )
  2695. drawMode = MODE::GRAPHIC_POLYGON;
  2696. SCOPED_DRAW_MODE scopedDrawMode( m_mode, drawMode );
  2697. // get a source zone, if we need one. We need it for:
  2698. // ZONE_MODE::CUTOUT (adding a hole to the source zone)
  2699. // ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
  2700. ZONE* sourceZone = nullptr;
  2701. if( !getSourceZoneForAction( zoneMode, &sourceZone ) )
  2702. return 0;
  2703. // Turn zones on if they are off, so that the created object will be visible after completion
  2704. m_frame->SetObjectVisible( LAYER_ZONES );
  2705. ZONE_CREATE_HELPER::PARAMS params;
  2706. params.m_keepout = drawMode == MODE::KEEPOUT;
  2707. params.m_mode = zoneMode;
  2708. params.m_sourceZone = sourceZone;
  2709. params.m_layer = m_frame->GetActiveLayer();
  2710. if( zoneMode == ZONE_MODE::SIMILAR && !sourceZone->IsOnLayer( params.m_layer ) )
  2711. params.m_layer = sourceZone->GetFirstLayer();
  2712. ZONE_CREATE_HELPER zoneTool( *this, params );
  2713. // the geometry manager which handles the zone geometry, and hands the calculated points
  2714. // over to the zone creator tool
  2715. POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
  2716. bool started = false;
  2717. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  2718. m_frame->PushTool( aEvent );
  2719. auto setCursor =
  2720. [&]()
  2721. {
  2722. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  2723. };
  2724. auto cleanup =
  2725. [&] ()
  2726. {
  2727. polyGeomMgr.Reset();
  2728. started = false;
  2729. grid.ClearSkipPoint();
  2730. m_controls->SetAutoPan( false );
  2731. m_controls->CaptureCursor( false );
  2732. };
  2733. Activate();
  2734. // Must be done after Activate() so that it gets set into the correct context
  2735. m_controls->ShowCursor( true );
  2736. m_controls->ForceCursorPosition( false );
  2737. // Set initial cursor
  2738. setCursor();
  2739. if( aEvent.HasPosition() )
  2740. m_toolMgr->PrimeTool( aEvent.Position() );
  2741. // Main loop: keep receiving events
  2742. while( TOOL_EVENT* evt = Wait() )
  2743. {
  2744. setCursor();
  2745. LSET layers( { m_frame->GetActiveLayer() } );
  2746. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  2747. bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
  2748. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  2749. VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
  2750. cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, layers, GRID_GRAPHICS ),
  2751. COORDS_PADDING );
  2752. m_controls->ForceCursorPosition( true, cursorPos );
  2753. polyGeomMgr.SetLeaderMode( is45Limited ? POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45
  2754. : POLYGON_GEOM_MANAGER::LEADER_MODE::DIRECT );
  2755. if( evt->IsCancelInteractive() )
  2756. {
  2757. if( started )
  2758. {
  2759. cleanup();
  2760. }
  2761. else
  2762. {
  2763. m_frame->PopTool( aEvent );
  2764. // We've handled the cancel event. Don't cancel other tools
  2765. evt->SetPassEvent( false );
  2766. break;
  2767. }
  2768. }
  2769. else if( evt->IsActivate() )
  2770. {
  2771. if( started )
  2772. cleanup();
  2773. if( evt->IsPointEditor() )
  2774. {
  2775. // don't exit (the point editor runs in the background)
  2776. }
  2777. else if( evt->IsMoveTool() )
  2778. {
  2779. // leave ourselves on the stack so we come back after the move
  2780. break;
  2781. }
  2782. else
  2783. {
  2784. m_frame->PopTool( aEvent );
  2785. break;
  2786. }
  2787. }
  2788. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  2789. {
  2790. if( zoneMode != ZONE_MODE::SIMILAR )
  2791. params.m_layer = frame()->GetActiveLayer();
  2792. if( !m_view->IsLayerVisible( params.m_layer ) )
  2793. {
  2794. m_frame->GetAppearancePanel()->SetLayerVisible( params.m_layer, true );
  2795. m_frame->GetCanvas()->Refresh();
  2796. }
  2797. }
  2798. else if( evt->IsClick( BUT_RIGHT ) )
  2799. {
  2800. if( !started )
  2801. m_toolMgr->VetoContextMenuMouseWarp();
  2802. m_menu->ShowContextMenu( selection() );
  2803. }
  2804. // events that lock in nodes
  2805. else if( evt->IsClick( BUT_LEFT )
  2806. || evt->IsDblClick( BUT_LEFT )
  2807. || evt->IsAction( &PCB_ACTIONS::closeOutline ) )
  2808. {
  2809. // Check if it is double click / closing line (so we have to finish the zone)
  2810. const bool endPolygon = evt->IsDblClick( BUT_LEFT )
  2811. || evt->IsAction( &PCB_ACTIONS::closeOutline )
  2812. || polyGeomMgr.NewPointClosesOutline( cursorPos );
  2813. if( endPolygon )
  2814. {
  2815. polyGeomMgr.SetFinished();
  2816. polyGeomMgr.Reset();
  2817. started = false;
  2818. m_controls->SetAutoPan( false );
  2819. m_controls->CaptureCursor( false );
  2820. }
  2821. // adding a corner
  2822. else if( polyGeomMgr.AddPoint( cursorPos ) )
  2823. {
  2824. if( !started )
  2825. {
  2826. started = true;
  2827. m_controls->SetAutoPan( true );
  2828. m_controls->CaptureCursor( true );
  2829. if( !m_view->IsLayerVisible( params.m_layer ) )
  2830. {
  2831. m_frame->GetAppearancePanel()->SetLayerVisible( params.m_layer, true );
  2832. m_frame->GetCanvas()->Refresh();
  2833. }
  2834. }
  2835. }
  2836. }
  2837. else if( started && ( evt->IsAction( &PCB_ACTIONS::deleteLastPoint )
  2838. || evt->IsAction( &ACTIONS::doDelete )
  2839. || evt->IsAction( &ACTIONS::undo ) ) )
  2840. {
  2841. if( std::optional<VECTOR2I> last = polyGeomMgr.DeleteLastCorner() )
  2842. {
  2843. cursorPos = last.value();
  2844. getViewControls()->WarpMouseCursor( cursorPos, true );
  2845. m_controls->ForceCursorPosition( true, cursorPos );
  2846. polyGeomMgr.SetCursorPosition( cursorPos );
  2847. }
  2848. else
  2849. {
  2850. cleanup();
  2851. }
  2852. }
  2853. else if( started && ( evt->IsMotion()
  2854. || evt->IsDrag( BUT_LEFT ) ) )
  2855. {
  2856. polyGeomMgr.SetCursorPosition( cursorPos );
  2857. }
  2858. else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
  2859. || evt->IsAction( &ACTIONS::redo ) ) )
  2860. {
  2861. wxBell();
  2862. }
  2863. else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
  2864. {
  2865. frame()->OnEditItemRequest( zoneTool.GetZone() );
  2866. zoneTool.OnGeometryChange( polyGeomMgr );
  2867. frame()->SetMsgPanel( zoneTool.GetZone() );
  2868. }
  2869. /*else if( evt->IsAction( &ACTIONS::updateUnits ) )
  2870. {
  2871. // If we ever have an assistant here that reports dimensions, we'll want to
  2872. // update its units here....
  2873. // zoneAsst.SetUnits( frame()->GetUserUnits() );
  2874. // m_view->Update( &zoneAsst );
  2875. evt->SetPassEvent();
  2876. }*/
  2877. else
  2878. {
  2879. evt->SetPassEvent();
  2880. }
  2881. } // end while
  2882. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  2883. m_controls->ForceCursorPosition( false );
  2884. controls()->SetAutoPan( false );
  2885. m_controls->CaptureCursor( false );
  2886. return 0;
  2887. }
  2888. int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
  2889. {
  2890. if( m_isFootprintEditor )
  2891. return 0;
  2892. struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
  2893. {
  2894. PCB_BASE_EDIT_FRAME* m_frame;
  2895. PCB_GRID_HELPER m_gridHelper;
  2896. std::shared_ptr<DRC_ENGINE> m_drcEngine;
  2897. int m_drcEpsilon;
  2898. int m_worstClearance;
  2899. bool m_allowDRCViolations;
  2900. VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) :
  2901. m_frame( aFrame ),
  2902. m_gridHelper( aFrame->GetToolManager(), aFrame->GetMagneticItemsSettings() ),
  2903. m_drcEngine( aFrame->GetBoard()->GetDesignSettings().m_DRCEngine ),
  2904. m_drcEpsilon( aFrame->GetBoard()->GetDesignSettings().GetDRCEpsilon() ),
  2905. m_worstClearance( 0 )
  2906. {
  2907. ROUTER_TOOL* router = m_frame->GetToolManager()->GetTool<ROUTER_TOOL>();
  2908. if( router )
  2909. m_allowDRCViolations = router->Router()->Settings().AllowDRCViolations();
  2910. try
  2911. {
  2912. if( aFrame )
  2913. m_drcEngine->InitEngine( aFrame->GetDesignRulesPath() );
  2914. DRC_CONSTRAINT constraint;
  2915. if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, constraint ) )
  2916. m_worstClearance = constraint.GetValue().Min();
  2917. if( m_drcEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, constraint ) )
  2918. m_worstClearance = std::max( m_worstClearance, constraint.GetValue().Min() );
  2919. for( FOOTPRINT* footprint : aFrame->GetBoard()->Footprints() )
  2920. {
  2921. for( PAD* pad : footprint->Pads() )
  2922. {
  2923. std::optional<int> padOverride = pad->GetClearanceOverrides( nullptr );
  2924. if( padOverride.has_value() )
  2925. m_worstClearance = std::max( m_worstClearance, padOverride.value() );
  2926. }
  2927. }
  2928. }
  2929. catch( PARSE_ERROR& )
  2930. {
  2931. }
  2932. }
  2933. virtual ~VIA_PLACER()
  2934. {
  2935. }
  2936. PCB_TRACK* findTrack( PCB_VIA* aVia )
  2937. {
  2938. const LSET lset = aVia->GetLayerSet();
  2939. VECTOR2I position = aVia->GetPosition();
  2940. BOX2I bbox = aVia->GetBoundingBox();
  2941. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
  2942. KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
  2943. std::vector<PCB_TRACK*> possible_tracks;
  2944. wxCHECK( view, nullptr );
  2945. view->Query( bbox, items );
  2946. for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
  2947. {
  2948. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
  2949. if( !( item->GetLayerSet() & lset ).any() )
  2950. continue;
  2951. if( item->Type() == PCB_TRACE_T )
  2952. {
  2953. PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
  2954. if( TestSegmentHit( position, track->GetStart(), track->GetEnd(),
  2955. ( track->GetWidth()
  2956. + aVia->GetWidth( track->GetLayer() ) ) / 2 ) )
  2957. {
  2958. possible_tracks.push_back( track );
  2959. }
  2960. }
  2961. else if( item->Type() == PCB_ARC_T )
  2962. {
  2963. PCB_ARC* arc = static_cast<PCB_ARC*>( item );
  2964. if( arc->HitTest( position, aVia->GetWidth( arc->GetLayer() ) / 2 ) )
  2965. possible_tracks.push_back( arc );
  2966. }
  2967. }
  2968. PCB_TRACK* return_track = nullptr;
  2969. int min_d = std::numeric_limits<int>::max();
  2970. for( PCB_TRACK* track : possible_tracks )
  2971. {
  2972. SEG test( track->GetStart(), track->GetEnd() );
  2973. int dist = ( test.NearestPoint( position ) - position ).EuclideanNorm();
  2974. if( dist < min_d )
  2975. {
  2976. min_d = dist;
  2977. return_track = track;
  2978. }
  2979. }
  2980. return return_track;
  2981. }
  2982. bool hasDRCViolation( PCB_VIA* aVia, BOARD_ITEM* aOther )
  2983. {
  2984. DRC_CONSTRAINT constraint;
  2985. int clearance;
  2986. BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aOther );
  2987. ZONE* zone = dynamic_cast<ZONE*>( aOther );
  2988. if( zone && zone->GetIsRuleArea() )
  2989. {
  2990. if( zone->GetDoNotAllowVias() )
  2991. {
  2992. bool hit = false;
  2993. aVia->Padstack().ForEachUniqueLayer(
  2994. [&]( PCB_LAYER_ID aLayer )
  2995. {
  2996. if( hit )
  2997. return;
  2998. if( zone->Outline()->Collide( aVia->GetPosition(),
  2999. aVia->GetWidth( aLayer ) / 2 ) )
  3000. {
  3001. hit = true;
  3002. }
  3003. } );
  3004. return hit;
  3005. }
  3006. return false;
  3007. }
  3008. if( connectedItem )
  3009. {
  3010. int connectedItemNet = connectedItem->GetNetCode();
  3011. if( connectedItemNet == 0 || connectedItemNet == aVia->GetNetCode() )
  3012. return false;
  3013. }
  3014. for( PCB_LAYER_ID layer : aOther->GetLayerSet().Seq() )
  3015. {
  3016. // Reference images are "on" a copper layer but are not actually part of it
  3017. if( !IsCopperLayer( layer ) || aOther->Type() == PCB_REFERENCE_IMAGE_T )
  3018. continue;
  3019. constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aVia, aOther, layer );
  3020. clearance = constraint.GetValue().Min();
  3021. if( clearance >= 0 )
  3022. {
  3023. std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( layer );
  3024. std::shared_ptr<SHAPE> otherShape = aOther->GetEffectiveShape( layer );
  3025. if( viaShape->Collide( otherShape.get(), clearance - m_drcEpsilon ) )
  3026. return true;
  3027. }
  3028. }
  3029. if( aOther->HasHole() )
  3030. {
  3031. constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aVia, aOther,
  3032. UNDEFINED_LAYER );
  3033. clearance = constraint.GetValue().Min();
  3034. if( clearance >= 0 )
  3035. {
  3036. std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( UNDEFINED_LAYER );
  3037. if( viaShape->Collide( aOther->GetEffectiveHoleShape().get(),
  3038. clearance - m_drcEpsilon ) )
  3039. {
  3040. return true;
  3041. }
  3042. }
  3043. }
  3044. return false;
  3045. }
  3046. bool checkDRCViolation( PCB_VIA* aVia )
  3047. {
  3048. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
  3049. std::set<BOARD_ITEM*> checkedItems;
  3050. BOX2I bbox = aVia->GetBoundingBox();
  3051. bbox.Inflate( m_worstClearance );
  3052. m_frame->GetCanvas()->GetView()->Query( bbox, items );
  3053. for( std::pair<KIGFX::VIEW_ITEM*, int> it : items )
  3054. {
  3055. if( !it.first->IsBOARD_ITEM() )
  3056. continue;
  3057. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
  3058. if( item->Type() == PCB_ZONE_T && !static_cast<ZONE*>( item )->GetIsRuleArea() )
  3059. {
  3060. continue; // stitching vias bind to zones, so ignore them
  3061. }
  3062. else if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_GROUP_T )
  3063. {
  3064. continue; // check against children, but not against footprint itself
  3065. }
  3066. else if( ( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
  3067. && !static_cast<PCB_TEXT*>( item )->IsVisible() )
  3068. {
  3069. continue; // ignore hidden items
  3070. }
  3071. else if( checkedItems.count( item ) )
  3072. {
  3073. continue; // already checked
  3074. }
  3075. if( hasDRCViolation( aVia, item ) )
  3076. return true;
  3077. checkedItems.insert( item );
  3078. }
  3079. DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( DISALLOW_CONSTRAINT, aVia, nullptr,
  3080. UNDEFINED_LAYER );
  3081. if( constraint.m_DisallowFlags && constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
  3082. return true;
  3083. return false;
  3084. }
  3085. PAD* findPad( PCB_VIA* aVia )
  3086. {
  3087. const VECTOR2I position = aVia->GetPosition();
  3088. const LSET lset = aVia->GetLayerSet();
  3089. for( FOOTPRINT* fp : m_board->Footprints() )
  3090. {
  3091. for( PAD* pad : fp->Pads() )
  3092. {
  3093. if( pad->HitTest( position ) && ( pad->GetLayerSet() & lset ).any() )
  3094. {
  3095. return pad;
  3096. }
  3097. }
  3098. }
  3099. return nullptr;
  3100. }
  3101. PCB_SHAPE* findGraphic( const PCB_VIA* aVia ) const
  3102. {
  3103. const LSET lset = aVia->GetLayerSet() & LSET::AllCuMask();
  3104. VECTOR2I position = aVia->GetPosition();
  3105. BOX2I bbox = aVia->GetBoundingBox();
  3106. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
  3107. KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
  3108. PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
  3109. std::vector<PCB_SHAPE*> possible_shapes;
  3110. view->Query( bbox, items );
  3111. for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
  3112. {
  3113. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
  3114. if( !( item->GetLayerSet() & lset ).any() )
  3115. continue;
  3116. if( item->Type() == PCB_SHAPE_T )
  3117. {
  3118. PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
  3119. if( shape->HitTest( position, aVia->GetWidth( activeLayer ) / 2 ) )
  3120. possible_shapes.push_back( shape );
  3121. }
  3122. }
  3123. PCB_SHAPE* return_shape = nullptr;
  3124. int min_d = std::numeric_limits<int>::max();
  3125. for( PCB_SHAPE* shape : possible_shapes )
  3126. {
  3127. int dist = ( shape->GetPosition() - position ).EuclideanNorm();
  3128. if( dist < min_d )
  3129. {
  3130. min_d = dist;
  3131. return_shape = shape;
  3132. }
  3133. }
  3134. return return_shape;
  3135. }
  3136. std::optional<int> selectPossibleNetsByPopupMenu( std::set<int>& aNetcodeList )
  3137. {
  3138. ACTION_MENU menu( true );
  3139. const NETINFO_LIST& netInfo = m_board->GetNetInfo();
  3140. std::map<int, int> menuIDNetCodeMap;
  3141. int menuID = 1;
  3142. for( int netcode : aNetcodeList )
  3143. {
  3144. wxString menuText;
  3145. if( menuID < 10 )
  3146. {
  3147. #ifdef __WXMAC__
  3148. menuText = wxString::Format( "%s\t",
  3149. netInfo.GetNetItem( netcode )->GetNetname() );
  3150. #else
  3151. menuText = wxString::Format( "&%d %s\t",
  3152. menuID,
  3153. netInfo.GetNetItem( netcode )->GetNetname() );
  3154. #endif
  3155. }
  3156. else
  3157. {
  3158. menuText = netInfo.GetNetItem( netcode )->GetNetname();
  3159. }
  3160. menu.Add( menuText, menuID, BITMAPS::INVALID_BITMAP );
  3161. menuIDNetCodeMap[ menuID ] = netcode;
  3162. menuID++;
  3163. }
  3164. menu.SetTitle( _( "Select Net:" ) );
  3165. menu.DisplayTitle( true );
  3166. DRAWING_TOOL* drawingTool = m_frame->GetToolManager()->GetTool<DRAWING_TOOL>();
  3167. drawingTool->SetContextMenu( &menu, CMENU_NOW );
  3168. int selectedNetCode = -1;
  3169. bool cancelled = false;
  3170. while( TOOL_EVENT* evt = drawingTool->Wait() )
  3171. {
  3172. if( evt->Action() == TA_CHOICE_MENU_UPDATE )
  3173. {
  3174. evt->SetPassEvent();
  3175. }
  3176. else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
  3177. {
  3178. std::optional<int> id = evt->GetCommandId();
  3179. // User has selected an item, so this one will be returned
  3180. if( id && ( *id > 0 ) && ( *id < menuID ) )
  3181. {
  3182. selectedNetCode = menuIDNetCodeMap.at( *id );
  3183. }
  3184. // User has cancelled the menu (either by <esc> or clicking out of it),
  3185. else
  3186. {
  3187. cancelled = true;
  3188. }
  3189. }
  3190. else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
  3191. {
  3192. break;
  3193. }
  3194. }
  3195. if( cancelled )
  3196. return std::optional<int>();
  3197. else
  3198. return selectedNetCode;
  3199. }
  3200. std::optional<int> findStitchedZoneNet( PCB_VIA* aVia )
  3201. {
  3202. const VECTOR2I position = aVia->GetPosition();
  3203. PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
  3204. std::set<int> netcodeList;
  3205. // See if there are any connections available on a high-contrast layer
  3206. if( opts.m_ContrastModeDisplay == HIGH_CONTRAST_MODE::DIMMED
  3207. || opts.m_ContrastModeDisplay == HIGH_CONTRAST_MODE::HIDDEN )
  3208. {
  3209. if( aVia->GetLayerSet().test( m_frame->GetActiveLayer() ) )
  3210. {
  3211. for( ZONE* z : m_board->Zones() )
  3212. {
  3213. if( z->IsOnLayer( m_frame->GetActiveLayer() ) )
  3214. {
  3215. if( z->HitTestFilledArea( m_frame->GetActiveLayer(), position ) )
  3216. netcodeList.insert( z->GetNetCode() );
  3217. }
  3218. }
  3219. }
  3220. }
  3221. // If there's only one, return it.
  3222. if( netcodeList.size() == 1 )
  3223. return *netcodeList.begin();
  3224. // See if there are any connections available on a visible layer
  3225. LSET lset = LSET( m_board->GetVisibleLayers() & aVia->GetLayerSet() );
  3226. for( ZONE* z : m_board->Zones() )
  3227. {
  3228. for( PCB_LAYER_ID layer : lset.Seq() )
  3229. {
  3230. if( z->IsOnLayer( layer ) )
  3231. {
  3232. if( z->HitTestFilledArea( layer, position ) )
  3233. netcodeList.insert( z->GetNetCode() );
  3234. }
  3235. }
  3236. }
  3237. // If there's only one, return it.
  3238. if( netcodeList.size() == 1 )
  3239. return *netcodeList.begin();
  3240. if( netcodeList.size() > 1 )
  3241. {
  3242. // The net assignment is ambiguous. Let the user decide.
  3243. return selectPossibleNetsByPopupMenu( netcodeList );
  3244. }
  3245. else
  3246. {
  3247. return NETINFO_LIST::ORPHANED;
  3248. }
  3249. }
  3250. void SnapItem( BOARD_ITEM *aItem ) override
  3251. {
  3252. m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
  3253. MAGNETIC_SETTINGS* settings = m_frame->GetMagneticItemsSettings();
  3254. PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
  3255. VECTOR2I position = via->GetPosition();
  3256. if( settings->tracks != MAGNETIC_OPTIONS::NO_EFFECT && m_gridHelper.GetSnap() )
  3257. {
  3258. if( PCB_TRACK* track = findTrack( via ) )
  3259. {
  3260. SEG trackSeg( track->GetStart(), track->GetEnd() );
  3261. VECTOR2I snap = m_gridHelper.AlignToSegment( position, trackSeg );
  3262. aItem->SetPosition( snap );
  3263. return;
  3264. }
  3265. }
  3266. if( settings->pads != MAGNETIC_OPTIONS::NO_EFFECT && m_gridHelper.GetSnap() )
  3267. {
  3268. if( PAD* pad = findPad( via ) )
  3269. {
  3270. aItem->SetPosition( pad->GetPosition() );
  3271. return;
  3272. }
  3273. }
  3274. if( settings->graphics && m_gridHelper.GetSnap() )
  3275. {
  3276. if( PCB_SHAPE* shape = findGraphic( via ) )
  3277. {
  3278. if( shape->IsAnyFill() )
  3279. {
  3280. aItem->SetPosition( shape->GetPosition() );
  3281. }
  3282. else
  3283. {
  3284. switch( shape->GetShape() )
  3285. {
  3286. case SHAPE_T::SEGMENT:
  3287. {
  3288. SEG seg( shape->GetStart(), shape->GetEnd() );
  3289. VECTOR2I snap = m_gridHelper.AlignToSegment( position, seg );
  3290. aItem->SetPosition( snap );
  3291. break;
  3292. }
  3293. case SHAPE_T::ARC:
  3294. {
  3295. if( ( shape->GetEnd() - position ).SquaredEuclideanNorm() <
  3296. ( shape->GetStart() - position ).SquaredEuclideanNorm() )
  3297. {
  3298. aItem->SetPosition( shape->GetEnd() );
  3299. }
  3300. else
  3301. {
  3302. aItem->SetPosition( shape->GetStart() );
  3303. }
  3304. break;
  3305. }
  3306. case SHAPE_T::POLY:
  3307. {
  3308. if( !shape->IsPolyShapeValid() )
  3309. {
  3310. aItem->SetPosition( shape->GetPosition() );
  3311. break;
  3312. }
  3313. const SHAPE_POLY_SET& polySet = shape->GetPolyShape();
  3314. std::optional<SEG> nearestSeg;
  3315. int minDist = std::numeric_limits<int>::max();
  3316. for( int ii = 0; ii < polySet.OutlineCount(); ++ii )
  3317. {
  3318. const SHAPE_LINE_CHAIN& poly = polySet.Outline( ii );
  3319. for( int jj = 0; jj < poly.SegmentCount(); ++jj )
  3320. {
  3321. const SEG& seg = poly.GetSegment( jj );
  3322. int dist = seg.Distance( position );
  3323. if( dist < minDist )
  3324. {
  3325. minDist = dist;
  3326. nearestSeg = seg;
  3327. }
  3328. }
  3329. }
  3330. if( nearestSeg )
  3331. {
  3332. VECTOR2I snap = m_gridHelper.AlignToSegment( position, *nearestSeg );
  3333. aItem->SetPosition( snap );
  3334. }
  3335. break;
  3336. }
  3337. default:
  3338. aItem->SetPosition( shape->GetPosition() );
  3339. }
  3340. }
  3341. }
  3342. }
  3343. }
  3344. bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
  3345. {
  3346. WX_INFOBAR* infobar = m_frame->GetInfoBar();
  3347. PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
  3348. VECTOR2I viaPos = via->GetPosition();
  3349. PCB_TRACK* track = findTrack( via );
  3350. PAD* pad = findPad( via );
  3351. if( track )
  3352. {
  3353. via->SetNetCode( track->GetNetCode() );
  3354. via->SetIsFree( false );
  3355. }
  3356. else if( pad )
  3357. {
  3358. via->SetNetCode( pad->GetNetCode() );
  3359. via->SetIsFree( false );
  3360. }
  3361. else
  3362. {
  3363. std::optional<int> netcode = findStitchedZoneNet( via );
  3364. if( !netcode.has_value() ) // user cancelled net disambiguation menu
  3365. return false;
  3366. via->SetNetCode( netcode.value() );
  3367. via->SetIsFree( via->GetNetCode() > 0 );
  3368. }
  3369. if( checkDRCViolation( via ) )
  3370. {
  3371. m_frame->ShowInfoBarError( _( "Via location violates DRC." ), true,
  3372. WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION );
  3373. if( !m_allowDRCViolations )
  3374. return false;
  3375. }
  3376. else
  3377. {
  3378. if( infobar->GetMessageType() == WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION )
  3379. infobar->Dismiss();
  3380. }
  3381. aCommit.Add( via );
  3382. // If the user explicitly disables snap (using shift), then don't break the tracks.
  3383. // This will prevent PNS from being able to connect the via and track but
  3384. // it is explicitly requested by the user
  3385. if( track && m_gridHelper.GetSnap() )
  3386. {
  3387. VECTOR2I trackStart = track->GetStart();
  3388. VECTOR2I trackEnd = track->GetEnd();
  3389. SEG trackSeg( trackStart, trackEnd );
  3390. if( viaPos == trackStart || viaPos == trackEnd )
  3391. return true;
  3392. if( !trackSeg.Contains( viaPos ) )
  3393. return true;
  3394. aCommit.Modify( track );
  3395. track->SetStart( trackStart );
  3396. track->SetEnd( viaPos );
  3397. PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
  3398. const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
  3399. newTrack->SetStart( viaPos );
  3400. newTrack->SetEnd( trackEnd );
  3401. aCommit.Add( newTrack );
  3402. }
  3403. return true;
  3404. }
  3405. std::unique_ptr<BOARD_ITEM> CreateItem() override
  3406. {
  3407. BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
  3408. PCB_VIA* via = new PCB_VIA( m_board );
  3409. via->SetNetCode( 0 );
  3410. via->SetViaType( bds.m_CurrentViaType );
  3411. if( via->GetViaType() == VIATYPE::THROUGH )
  3412. {
  3413. via->SetLayerPair( B_Cu, F_Cu );
  3414. }
  3415. else
  3416. {
  3417. PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
  3418. PCB_LAYER_ID last_layer;
  3419. // prepare switch to new active layer:
  3420. if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
  3421. last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
  3422. else
  3423. last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
  3424. via->SetLayerPair( first_layer, last_layer );
  3425. }
  3426. if( via->GetViaType() == VIATYPE::MICROVIA )
  3427. {
  3428. via->SetWidth( PADSTACK::ALL_LAYERS,
  3429. via->GetEffectiveNetClass()->GetuViaDiameter() );
  3430. via->SetDrill( via->GetEffectiveNetClass()->GetuViaDrill() );
  3431. }
  3432. else
  3433. {
  3434. via->SetWidth( PADSTACK::ALL_LAYERS, bds.GetCurrentViaSize() );
  3435. via->SetDrill( bds.GetCurrentViaDrill() );
  3436. }
  3437. return std::unique_ptr<BOARD_ITEM>( via );
  3438. }
  3439. };
  3440. VIA_PLACER placer( frame() );
  3441. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
  3442. doInteractiveItemPlacement( aEvent, &placer, _( "Place via" ), IPO_REPEAT | IPO_SINGLE_CLICK );
  3443. return 0;
  3444. }
  3445. const unsigned int DRAWING_TOOL::WIDTH_STEP = pcbIUScale.mmToIU( 0.1 );
  3446. void DRAWING_TOOL::setTransitions()
  3447. {
  3448. // clang-format off
  3449. Go( &DRAWING_TOOL::PlaceStackup, PCB_ACTIONS::placeStackup.MakeEvent() );
  3450. Go( &DRAWING_TOOL::PlaceCharacteristics, PCB_ACTIONS::placeCharacteristics.MakeEvent() );
  3451. Go( &DRAWING_TOOL::DrawLine, PCB_ACTIONS::drawLine.MakeEvent() );
  3452. Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawPolygon.MakeEvent() );
  3453. Go( &DRAWING_TOOL::DrawRectangle, PCB_ACTIONS::drawRectangle.MakeEvent() );
  3454. Go( &DRAWING_TOOL::DrawCircle, PCB_ACTIONS::drawCircle.MakeEvent() );
  3455. Go( &DRAWING_TOOL::DrawArc, PCB_ACTIONS::drawArc.MakeEvent() );
  3456. Go( &DRAWING_TOOL::DrawBezier, PCB_ACTIONS::drawBezier.MakeEvent() );
  3457. Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawAlignedDimension.MakeEvent() );
  3458. Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawOrthogonalDimension.MakeEvent() );
  3459. Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawCenterDimension.MakeEvent() );
  3460. Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawRadialDimension.MakeEvent() );
  3461. Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawLeader.MakeEvent() );
  3462. Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawZone.MakeEvent() );
  3463. Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawRuleArea.MakeEvent() );
  3464. Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawZoneCutout.MakeEvent() );
  3465. Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawSimilarZone.MakeEvent() );
  3466. Go( &DRAWING_TOOL::DrawVia, PCB_ACTIONS::drawVia.MakeEvent() );
  3467. Go( &DRAWING_TOOL::PlaceReferenceImage, PCB_ACTIONS::placeReferenceImage.MakeEvent() );
  3468. Go( &DRAWING_TOOL::PlaceText, PCB_ACTIONS::placeText.MakeEvent() );
  3469. Go( &DRAWING_TOOL::DrawTable, PCB_ACTIONS::drawTable.MakeEvent() );
  3470. Go( &DRAWING_TOOL::DrawRectangle, PCB_ACTIONS::drawTextBox.MakeEvent() );
  3471. Go( &DRAWING_TOOL::PlaceImportedGraphics, PCB_ACTIONS::placeImportedGraphics.MakeEvent() );
  3472. Go( &DRAWING_TOOL::SetAnchor, PCB_ACTIONS::setAnchor.MakeEvent() );
  3473. Go( &DRAWING_TOOL::ToggleHV45Mode, PCB_ACTIONS::toggleHV45Mode.MakeEvent() );
  3474. Go( &DRAWING_TOOL::PlaceTuningPattern, PCB_ACTIONS::tuneSingleTrack.MakeEvent() );
  3475. Go( &DRAWING_TOOL::PlaceTuningPattern, PCB_ACTIONS::tuneDiffPair.MakeEvent() );
  3476. Go( &DRAWING_TOOL::PlaceTuningPattern, PCB_ACTIONS::tuneSkew.MakeEvent() );
  3477. // clang-format on
  3478. }