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.

2967 lines
98 KiB

9 years ago
5 years ago
9 years ago
6 years ago
6 years ago
9 years ago
9 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 (C) 2018-2022 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 <pgm_base.h>
  28. #include <settings/settings_manager.h>
  29. #include <pcbnew_settings.h>
  30. #include <footprint_editor_settings.h>
  31. #include <dialogs/dialog_text_properties.h>
  32. #include <dialogs/dialog_track_via_size.h>
  33. #include <geometry/geometry_utils.h>
  34. #include <geometry/shape_segment.h>
  35. #include <import_gfx/dialog_import_gfx.h>
  36. #include <preview_items/two_point_assistant.h>
  37. #include <preview_items/two_point_geom_manager.h>
  38. #include <ratsnest/ratsnest_data.h>
  39. #include <router/router_tool.h>
  40. #include <tool/tool_manager.h>
  41. #include <tools/pcb_actions.h>
  42. #include <tools/pcb_grid_helper.h>
  43. #include <tools/pcb_selection_tool.h>
  44. #include <tools/tool_event_utils.h>
  45. #include <tools/zone_create_helper.h>
  46. #include <view/view.h>
  47. #include <widgets/appearance_controls.h>
  48. #include <widgets/infobar.h>
  49. #include <bitmaps.h>
  50. #include <board.h>
  51. #include <board_commit.h>
  52. #include <board_design_settings.h>
  53. #include <confirm.h>
  54. #include <footprint.h>
  55. #include <fp_shape.h>
  56. #include <fp_textbox.h>
  57. #include <macros.h>
  58. #include <painter.h>
  59. #include <pcb_edit_frame.h>
  60. #include <pcb_group.h>
  61. #include <pcb_text.h>
  62. #include <pcb_textbox.h>
  63. #include <pcb_dimension.h>
  64. #include <pcbnew_id.h>
  65. #include <preview_items/arc_assistant.h>
  66. #include <scoped_set_reset.h>
  67. #include <status_popup.h>
  68. #include <string_utils.h>
  69. #include <zone.h>
  70. using SCOPED_DRAW_MODE = SCOPED_SET_RESET<DRAWING_TOOL::MODE>;
  71. class VIA_SIZE_MENU : public ACTION_MENU
  72. {
  73. public:
  74. VIA_SIZE_MENU() :
  75. ACTION_MENU( true )
  76. {
  77. SetIcon( BITMAPS::width_track_via );
  78. SetTitle( _( "Select Via Size" ) );
  79. }
  80. protected:
  81. ACTION_MENU* create() const override
  82. {
  83. return new VIA_SIZE_MENU();
  84. }
  85. void update() override
  86. {
  87. PCB_EDIT_FRAME* frame = (PCB_EDIT_FRAME*) getToolManager()->GetToolHolder();
  88. EDA_UNITS units = frame->GetUserUnits();
  89. BOARD_DESIGN_SETTINGS& bds = frame->GetBoard()->GetDesignSettings();
  90. bool useIndex = !bds.m_UseConnectedTrackWidth
  91. && !bds.UseCustomTrackViaSize();
  92. wxString msg;
  93. Clear();
  94. Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
  95. _( "Specify custom track and via sizes" ), wxITEM_CHECK );
  96. Check( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, bds.UseCustomTrackViaSize() );
  97. AppendSeparator();
  98. for( unsigned i = 1; i < bds.m_ViasDimensionsList.size(); i++ )
  99. {
  100. VIA_DIMENSION via = bds.m_ViasDimensionsList[i];
  101. if( via.m_Drill > 0 )
  102. {
  103. msg.Printf( _("Via %s, hole %s" ),
  104. MessageTextFromValue( units, via.m_Diameter ),
  105. MessageTextFromValue( units, via.m_Drill ) );
  106. }
  107. else
  108. {
  109. msg.Printf( _( "Via %s" ), MessageTextFromValue( units, via.m_Diameter ) );
  110. }
  111. int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
  112. Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
  113. Check( menuIdx, useIndex && bds.GetViaSizeIndex() == i );
  114. }
  115. }
  116. OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
  117. {
  118. PCB_EDIT_FRAME* frame = (PCB_EDIT_FRAME*) getToolManager()->GetToolHolder();
  119. BOARD_DESIGN_SETTINGS& bds = frame->GetBoard()->GetDesignSettings();
  120. int id = aEvent.GetId();
  121. // On Windows, this handler can be called with an event ID not existing in any
  122. // menuitem, so only set flags when we have an ID match.
  123. if( id == ID_POPUP_PCB_SELECT_CUSTOM_WIDTH )
  124. {
  125. DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds );
  126. if( sizeDlg.ShowModal() == wxID_OK )
  127. {
  128. bds.UseCustomTrackViaSize( true );
  129. bds.m_UseConnectedTrackWidth = false;
  130. }
  131. }
  132. else if( id >= ID_POPUP_PCB_SELECT_VIASIZE1 && id <= ID_POPUP_PCB_SELECT_VIASIZE16 )
  133. {
  134. bds.UseCustomTrackViaSize( false );
  135. bds.m_UseConnectedTrackWidth = false;
  136. bds.SetViaSizeIndex( id - ID_POPUP_PCB_SELECT_VIASIZE1 );
  137. }
  138. return OPT_TOOL_EVENT( PCB_ACTIONS::trackViaSizeChanged.MakeEvent() );
  139. }
  140. };
  141. DRAWING_TOOL::DRAWING_TOOL() :
  142. PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ),
  143. m_view( nullptr ),
  144. m_controls( nullptr ),
  145. m_board( nullptr ),
  146. m_frame( nullptr ),
  147. m_mode( MODE::NONE ),
  148. m_inDrawingTool( false ),
  149. m_layer( UNDEFINED_LAYER ),
  150. m_stroke( 1, PLOT_DASH_TYPE::DEFAULT, COLOR4D::UNSPECIFIED )
  151. {
  152. }
  153. DRAWING_TOOL::~DRAWING_TOOL()
  154. {
  155. }
  156. bool DRAWING_TOOL::Init()
  157. {
  158. auto activeToolFunctor = [this]( const SELECTION& aSel )
  159. {
  160. return m_mode != MODE::NONE;
  161. };
  162. // some interactive drawing tools can undo the last point
  163. auto canUndoPoint = [this]( const SELECTION& aSel )
  164. {
  165. return ( m_mode == MODE::ARC
  166. || m_mode == MODE::ZONE
  167. || m_mode == MODE::KEEPOUT
  168. || m_mode == MODE::GRAPHIC_POLYGON );
  169. };
  170. // functor for tools that can automatically close the outline
  171. auto canCloseOutline = [this]( const SELECTION& aSel )
  172. {
  173. return ( m_mode == MODE::ZONE
  174. || m_mode == MODE::KEEPOUT
  175. || m_mode == MODE::GRAPHIC_POLYGON );
  176. };
  177. auto viaToolActive = [this]( const SELECTION& aSel )
  178. {
  179. return m_mode == MODE::VIA;
  180. };
  181. auto& ctxMenu = m_menu.GetMenu();
  182. // cancel current tool goes in main context menu at the top if present
  183. ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
  184. ctxMenu.AddSeparator( 1 );
  185. // tool-specific actions
  186. ctxMenu.AddItem( PCB_ACTIONS::closeOutline, canCloseOutline, 200 );
  187. ctxMenu.AddItem( PCB_ACTIONS::deleteLastPoint, canUndoPoint, 200 );
  188. ctxMenu.AddCheckItem( PCB_ACTIONS::toggleHV45Mode, SELECTION_CONDITIONS::ShowAlways, 250 );
  189. ctxMenu.AddSeparator( 500 );
  190. std::shared_ptr<VIA_SIZE_MENU> viaSizeMenu = std::make_shared<VIA_SIZE_MENU>();
  191. viaSizeMenu->SetTool( this );
  192. m_menu.AddSubMenu( viaSizeMenu );
  193. ctxMenu.AddMenu( viaSizeMenu.get(), viaToolActive, 500 );
  194. ctxMenu.AddSeparator( 500 );
  195. // Type-specific sub-menus will be added for us by other tools
  196. // For example, zone fill/unfill is provided by the PCB control tool
  197. // Finally, add the standard zoom/grid items
  198. getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( m_menu );
  199. return true;
  200. }
  201. void DRAWING_TOOL::Reset( RESET_REASON aReason )
  202. {
  203. // Init variables used by every drawing tool
  204. m_view = getView();
  205. m_controls = getViewControls();
  206. m_board = getModel<BOARD>();
  207. m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
  208. updateStatusBar();
  209. }
  210. DRAWING_TOOL::MODE DRAWING_TOOL::GetDrawingMode() const
  211. {
  212. return m_mode;
  213. }
  214. void DRAWING_TOOL::updateStatusBar() const
  215. {
  216. if( m_frame )
  217. {
  218. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  219. bool constrained;
  220. if( m_frame->IsType( FRAME_PCB_EDITOR ) )
  221. constrained = mgr.GetAppSettings<PCBNEW_SETTINGS>()->m_Use45DegreeLimit;
  222. else
  223. constrained = mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>()->m_Use45Limit;
  224. m_frame->DisplayConstraintsMsg(
  225. constrained ? _( "Constrain to H, V, 45" ) : wxString( "" ) );
  226. }
  227. }
  228. int DRAWING_TOOL::DrawLine( const TOOL_EVENT& aEvent )
  229. {
  230. if( m_isFootprintEditor && !m_frame->GetModel() )
  231. return 0;
  232. if( m_inDrawingTool )
  233. return 0;
  234. REENTRANCY_GUARD guard( &m_inDrawingTool );
  235. FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
  236. PCB_SHAPE* line = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
  237. BOARD_COMMIT commit( m_frame );
  238. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
  239. OPT<VECTOR2D> startingPoint = boost::make_optional<VECTOR2D>( false, VECTOR2D( 0, 0 ) );
  240. line->SetShape( SHAPE_T::SEGMENT );
  241. line->SetFlags( IS_NEW );
  242. if( aEvent.HasPosition() )
  243. startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
  244. std::string tool = aEvent.GetCommandStr().get();
  245. m_frame->PushTool( tool );
  246. Activate();
  247. while( drawSegment( tool, &line, startingPoint ) )
  248. {
  249. if( line )
  250. {
  251. if( m_isFootprintEditor )
  252. static_cast<FP_SHAPE*>( line )->SetLocalCoord();
  253. commit.Add( line );
  254. commit.Push( _( "Draw a line segment" ) );
  255. startingPoint = VECTOR2D( line->GetEnd() );
  256. }
  257. else
  258. {
  259. startingPoint = NULLOPT;
  260. }
  261. line = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
  262. line->SetShape( SHAPE_T::SEGMENT );
  263. line->SetFlags( IS_NEW );
  264. }
  265. return 0;
  266. }
  267. int DRAWING_TOOL::DrawRectangle( const TOOL_EVENT& aEvent )
  268. {
  269. if( m_isFootprintEditor && !m_frame->GetModel() )
  270. return 0;
  271. if( m_inDrawingTool )
  272. return 0;
  273. REENTRANCY_GUARD guard( &m_inDrawingTool );
  274. bool isTextBox = aEvent.IsAction( &PCB_ACTIONS::drawTextBox );
  275. PCB_SHAPE* rect = nullptr;
  276. BOARD_COMMIT commit( m_frame );
  277. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::RECTANGLE );
  278. OPT<VECTOR2D> startingPoint = boost::make_optional<VECTOR2D>( false, VECTOR2D( 0, 0 ) );
  279. auto makeNew =
  280. [&]() -> PCB_SHAPE*
  281. {
  282. if( m_isFootprintEditor )
  283. {
  284. FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
  285. if( isTextBox )
  286. return new FP_TEXTBOX( parentFootprint );
  287. else
  288. return new FP_SHAPE( parentFootprint );
  289. }
  290. else
  291. {
  292. if( isTextBox )
  293. return new PCB_TEXTBOX( m_frame->GetModel() );
  294. else
  295. return new PCB_SHAPE();
  296. }
  297. };
  298. rect = makeNew();
  299. rect->SetShape( SHAPE_T::RECT );
  300. rect->SetFilled( false );
  301. rect->SetFlags(IS_NEW );
  302. if( aEvent.HasPosition() )
  303. startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
  304. std::string tool = aEvent.GetCommandStr().get();
  305. m_frame->PushTool( tool );
  306. Activate();
  307. while( drawSegment( tool, &rect, startingPoint ) )
  308. {
  309. if( rect )
  310. {
  311. if( m_isFootprintEditor )
  312. static_cast<FP_SHAPE*>( rect )->SetLocalCoord();
  313. if( isTextBox && m_frame->ShowTextBoxPropertiesDialog( rect ) != wxID_OK )
  314. {
  315. delete rect;
  316. rect = nullptr;
  317. }
  318. else
  319. {
  320. commit.Add( rect );
  321. commit.Push( isTextBox ? _( "Draw a text box" ) : _( "Draw a rectangle" ) );
  322. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, rect );
  323. }
  324. }
  325. rect = makeNew();
  326. rect->SetShape( SHAPE_T::RECT );
  327. rect->SetFilled( false );
  328. rect->SetFlags(IS_NEW );
  329. startingPoint = NULLOPT;
  330. }
  331. return 0;
  332. }
  333. int DRAWING_TOOL::DrawCircle( const TOOL_EVENT& aEvent )
  334. {
  335. if( m_isFootprintEditor && !m_frame->GetModel() )
  336. return 0;
  337. if( m_inDrawingTool )
  338. return 0;
  339. REENTRANCY_GUARD guard( &m_inDrawingTool );
  340. FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
  341. PCB_SHAPE* circle = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
  342. BOARD_COMMIT commit( m_frame );
  343. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::CIRCLE );
  344. OPT<VECTOR2D> startingPoint = boost::make_optional<VECTOR2D>( false, VECTOR2D( 0, 0 ) );
  345. circle->SetShape( SHAPE_T::CIRCLE );
  346. circle->SetFilled( false );
  347. circle->SetFlags( IS_NEW );
  348. if( aEvent.HasPosition() )
  349. startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
  350. std::string tool = aEvent.GetCommandStr().get();
  351. m_frame->PushTool( tool );
  352. Activate();
  353. while( drawSegment( tool, &circle, startingPoint ) )
  354. {
  355. if( circle )
  356. {
  357. if( m_isFootprintEditor )
  358. static_cast<FP_SHAPE*>( circle )->SetLocalCoord();
  359. commit.Add( circle );
  360. commit.Push( _( "Draw a circle" ) );
  361. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, circle );
  362. }
  363. circle = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
  364. circle->SetShape( SHAPE_T::CIRCLE );
  365. circle->SetFilled( false );
  366. circle->SetFlags( IS_NEW );
  367. startingPoint = NULLOPT;
  368. }
  369. return 0;
  370. }
  371. int DRAWING_TOOL::DrawArc( const TOOL_EVENT& aEvent )
  372. {
  373. if( m_isFootprintEditor && !m_frame->GetModel() )
  374. return 0;
  375. if( m_inDrawingTool )
  376. return 0;
  377. REENTRANCY_GUARD guard( &m_inDrawingTool );
  378. FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
  379. PCB_SHAPE* arc = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
  380. BOARD_COMMIT commit( m_frame );
  381. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
  382. OPT<VECTOR2D> startingPoint = boost::make_optional<VECTOR2D>( false, { 0, 0 } );
  383. arc->SetShape( SHAPE_T::ARC );
  384. arc->SetFlags( IS_NEW );
  385. std::string tool = aEvent.GetCommandStr().get();
  386. m_frame->PushTool( tool );
  387. Activate();
  388. if( aEvent.HasPosition() )
  389. startingPoint = aEvent.Position();
  390. while( drawArc( tool, &arc, startingPoint ) )
  391. {
  392. if( arc )
  393. {
  394. if( m_isFootprintEditor )
  395. static_cast<FP_SHAPE*>( arc )->SetLocalCoord();
  396. commit.Add( arc );
  397. commit.Push( _( "Draw an arc" ) );
  398. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, arc );
  399. }
  400. arc = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
  401. arc->SetShape( SHAPE_T::ARC );
  402. arc->SetFlags( IS_NEW );
  403. startingPoint = NULLOPT;
  404. }
  405. return 0;
  406. }
  407. int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
  408. {
  409. if( m_isFootprintEditor && !m_frame->GetModel() )
  410. return 0;
  411. if( m_inDrawingTool )
  412. return 0;
  413. REENTRANCY_GUARD guard( &m_inDrawingTool );
  414. COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
  415. BOARD_ITEM* text = nullptr;
  416. bool ignorePrimePosition = false;
  417. const BOARD_DESIGN_SETTINGS& dsnSettings = m_frame->GetDesignSettings();
  418. BOARD_COMMIT commit( m_frame );
  419. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
  420. auto cleanup =
  421. [&]()
  422. {
  423. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  424. m_controls->ForceCursorPosition( false );
  425. m_controls->ShowCursor( true );
  426. m_controls->SetAutoPan( false );
  427. m_controls->CaptureCursor( false );
  428. delete text;
  429. text = nullptr;
  430. };
  431. auto setCursor =
  432. [&]()
  433. {
  434. if( text )
  435. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
  436. else
  437. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
  438. };
  439. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  440. std::string tool = aEvent.GetCommandStr().get();
  441. m_frame->PushTool( tool );
  442. Activate();
  443. // Must be done after Activate() so that it gets set into the correct context
  444. m_controls->ShowCursor( true );
  445. m_controls->ForceCursorPosition( false );
  446. // do not capture or auto-pan until we start placing some text
  447. // Set initial cursor
  448. setCursor();
  449. if( aEvent.HasPosition() )
  450. {
  451. m_toolMgr->PrimeTool( aEvent.Position() );
  452. }
  453. else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
  454. {
  455. m_toolMgr->PrimeTool( { 0, 0 } );
  456. ignorePrimePosition = true;
  457. }
  458. // Main loop: keep receiving events
  459. while( TOOL_EVENT* evt = Wait() )
  460. {
  461. setCursor();
  462. VECTOR2I cursorPos = m_controls->GetCursorPosition();
  463. if( evt->IsCancelInteractive() )
  464. {
  465. if( text )
  466. {
  467. cleanup();
  468. }
  469. else
  470. {
  471. m_frame->PopTool( tool );
  472. break;
  473. }
  474. }
  475. else if( evt->IsActivate() )
  476. {
  477. if( text )
  478. cleanup();
  479. if( evt->IsMoveTool() )
  480. {
  481. // leave ourselves on the stack so we come back after the move
  482. break;
  483. }
  484. else
  485. {
  486. m_frame->PopTool( tool );
  487. break;
  488. }
  489. }
  490. else if( evt->IsClick( BUT_RIGHT ) )
  491. {
  492. m_menu.ShowContextMenu( selection() );
  493. }
  494. else if( evt->IsClick( BUT_LEFT ) )
  495. {
  496. bool placing = text != nullptr;
  497. if( !text )
  498. {
  499. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  500. m_controls->ForceCursorPosition( true, m_controls->GetCursorPosition() );
  501. PCB_LAYER_ID layer = m_frame->GetActiveLayer();
  502. wxSize textSize = dsnSettings.GetTextSize( layer );
  503. int thickness = dsnSettings.GetTextThickness( layer );
  504. // Init the new item attributes
  505. if( m_isFootprintEditor )
  506. {
  507. FP_TEXT* fpText = new FP_TEXT( (FOOTPRINT*) m_frame->GetModel() );
  508. fpText->SetLayer( layer );
  509. fpText->SetTextSize( textSize );
  510. fpText->SetTextThickness( thickness );
  511. fpText->SetBold( abs( thickness - GetPenSizeForBold( textSize ) ) <
  512. abs( thickness - GetPenSizeForNormal( textSize ) ) );
  513. fpText->SetItalic( dsnSettings.GetTextItalic( layer ) );
  514. fpText->SetKeepUpright( dsnSettings.GetTextUpright( layer ) );
  515. fpText->SetTextPos( cursorPos );
  516. text = fpText;
  517. DIALOG_TEXT_PROPERTIES textDialog( m_frame, fpText );
  518. bool cancelled;
  519. RunMainStack( [&]()
  520. {
  521. cancelled = !textDialog.ShowModal();
  522. } );
  523. if( cancelled || NoPrintableChars( fpText->GetText() ) )
  524. {
  525. delete text;
  526. text = nullptr;
  527. }
  528. else if( fpText->GetTextPos() != cursorPos )
  529. {
  530. // If the user modified the location then go ahead and place it there.
  531. // Otherwise we'll drag.
  532. placing = true;
  533. }
  534. }
  535. else
  536. {
  537. PCB_TEXT* pcbText = new PCB_TEXT( m_frame->GetModel() );
  538. pcbText->SetFlags( IS_NEW );
  539. pcbText->SetLayer( layer );
  540. // Set the mirrored option for layers on the BACK side of the board
  541. if( IsBackLayer( layer ) )
  542. pcbText->SetMirrored( true );
  543. pcbText->SetTextSize( textSize );
  544. pcbText->SetTextThickness( thickness );
  545. pcbText->SetBold( abs( thickness - GetPenSizeForBold( textSize ) ) <
  546. abs( thickness - GetPenSizeForNormal( textSize ) ) );
  547. pcbText->SetItalic( dsnSettings.GetTextItalic( layer ) );
  548. pcbText->SetTextPos( cursorPos );
  549. RunMainStack( [&]()
  550. {
  551. m_frame->ShowTextPropertiesDialog( pcbText );
  552. } );
  553. if( NoPrintableChars( pcbText->GetText() ) )
  554. delete pcbText;
  555. else
  556. text = pcbText;
  557. }
  558. if( text )
  559. {
  560. if( !m_view->IsLayerVisible( text->GetLayer() ) )
  561. {
  562. m_frame->GetAppearancePanel()->SetLayerVisible( text->GetLayer(), true );
  563. m_frame->GetCanvas()->Refresh();
  564. }
  565. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, text );
  566. m_view->Update( &selection() );
  567. // update the cursor so it looks correct before another event
  568. setCursor();
  569. }
  570. }
  571. if( placing )
  572. {
  573. text->ClearFlags();
  574. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  575. commit.Add( text );
  576. commit.Push( _( "Place a text" ) );
  577. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, text );
  578. text = nullptr;
  579. }
  580. m_controls->ForceCursorPosition( false );
  581. // If we started with a hotkey which has a position then warp back to that.
  582. // Otherwise update to the current mouse position pinned inside the autoscroll
  583. // boundaries.
  584. if( evt->IsPrime() && !ignorePrimePosition )
  585. {
  586. cursorPos = evt->Position();
  587. m_controls->WarpMouseCursor( cursorPos, true );
  588. }
  589. else
  590. {
  591. m_controls->PinCursorInsideNonAutoscrollArea( true );
  592. cursorPos = m_controls->GetMousePosition();
  593. }
  594. m_toolMgr->RunAction( PCB_ACTIONS::refreshPreview );
  595. m_controls->ShowCursor( true );
  596. m_controls->CaptureCursor( text != nullptr );
  597. m_controls->SetAutoPan( text != nullptr );
  598. }
  599. else if( text && ( evt->IsMotion() || evt->IsAction( &PCB_ACTIONS::refreshPreview ) ) )
  600. {
  601. text->SetPosition( cursorPos );
  602. selection().SetReferencePoint( cursorPos );
  603. m_view->Update( &selection() );
  604. }
  605. else if( evt->IsAction( &PCB_ACTIONS::properties ) )
  606. {
  607. if( text )
  608. {
  609. frame()->OnEditItemRequest( text );
  610. m_view->Update( &selection() );
  611. frame()->SetMsgPanel( text );
  612. }
  613. else
  614. {
  615. evt->SetPassEvent();
  616. }
  617. }
  618. else
  619. {
  620. evt->SetPassEvent();
  621. }
  622. }
  623. m_controls->SetAutoPan( false );
  624. m_controls->CaptureCursor( false );
  625. m_controls->ForceCursorPosition( false );
  626. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  627. if( selection().Empty() )
  628. m_frame->SetMsgPanel( board() );
  629. return 0;
  630. }
  631. void DRAWING_TOOL::constrainDimension( PCB_DIMENSION_BASE* aDim )
  632. {
  633. const VECTOR2I lineVector{ aDim->GetEnd() - aDim->GetStart() };
  634. aDim->SetEnd( aDim->GetStart() + GetVectorSnapped45( lineVector ) );
  635. aDim->Update();
  636. }
  637. int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
  638. {
  639. if( m_isFootprintEditor && !m_frame->GetModel() )
  640. return 0;
  641. if( m_inDrawingTool )
  642. return 0;
  643. REENTRANCY_GUARD guard( &m_inDrawingTool );
  644. enum DIMENSION_STEPS
  645. {
  646. SET_ORIGIN = 0,
  647. SET_END,
  648. SET_HEIGHT,
  649. FINISHED
  650. };
  651. TOOL_EVENT originalEvent = aEvent;
  652. PCB_DIMENSION_BASE* dimension = nullptr;
  653. BOARD_COMMIT commit( m_frame );
  654. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  655. BOARD_DESIGN_SETTINGS& boardSettings = m_board->GetDesignSettings();
  656. PCB_SELECTION preview; // A VIEW_GROUP that serves as a preview for the new item(s)
  657. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
  658. int step = SET_ORIGIN;
  659. KICAD_T t = PCB_DIMENSION_T;
  660. m_view->Add( &preview );
  661. auto cleanup =
  662. [&]()
  663. {
  664. m_controls->SetAutoPan( false );
  665. m_controls->CaptureCursor( false );
  666. m_controls->ForceCursorPosition( false );
  667. preview.Clear();
  668. m_view->Update( &preview );
  669. delete dimension;
  670. dimension = nullptr;
  671. step = SET_ORIGIN;
  672. };
  673. auto setCursor =
  674. [&]()
  675. {
  676. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
  677. };
  678. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  679. std::string tool = aEvent.GetCommandStr().get();
  680. m_frame->PushTool( tool );
  681. Activate();
  682. // Must be done after Activate() so that it gets set into the correct context
  683. m_controls->ShowCursor( true );
  684. m_controls->ForceCursorPosition( false );
  685. // Set initial cursor
  686. setCursor();
  687. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  688. if( aEvent.HasPosition() )
  689. m_toolMgr->PrimeTool( aEvent.Position() );
  690. // Main loop: keep receiving events
  691. while( TOOL_EVENT* evt = Wait() )
  692. {
  693. if( step > SET_ORIGIN )
  694. frame()->SetMsgPanel( dimension );
  695. setCursor();
  696. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  697. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  698. if( step == SET_HEIGHT )
  699. {
  700. if( dimension->GetStart().x != dimension->GetEnd().x
  701. && dimension->GetStart().y != dimension->GetEnd().y )
  702. {
  703. // Not cardinal. Grid snapping doesn't make sense for height.
  704. grid.SetUseGrid( false );
  705. }
  706. }
  707. VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
  708. cursorPos = grid.BestSnapAnchor( cursorPos, nullptr );
  709. m_controls->ForceCursorPosition( true, cursorPos );
  710. if( evt->IsCancelInteractive() )
  711. {
  712. m_controls->SetAutoPan( false );
  713. if( step != SET_ORIGIN ) // start from the beginning
  714. {
  715. cleanup();
  716. }
  717. else
  718. {
  719. m_frame->PopTool( tool );
  720. break;
  721. }
  722. }
  723. else if( evt->IsActivate() )
  724. {
  725. if( step != SET_ORIGIN )
  726. cleanup();
  727. if( evt->IsPointEditor() )
  728. {
  729. // don't exit (the point editor runs in the background)
  730. }
  731. else if( evt->IsMoveTool() )
  732. {
  733. // leave ourselves on the stack so we come back after the move
  734. break;
  735. }
  736. else
  737. {
  738. m_frame->PopTool( tool );
  739. break;
  740. }
  741. }
  742. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
  743. {
  744. m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
  745. dimension->SetLineThickness( m_stroke.GetWidth() );
  746. m_view->Update( &preview );
  747. frame()->SetMsgPanel( dimension );
  748. }
  749. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
  750. {
  751. if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
  752. {
  753. m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
  754. dimension->SetLineThickness( m_stroke.GetWidth() );
  755. m_view->Update( &preview );
  756. frame()->SetMsgPanel( dimension );
  757. }
  758. }
  759. else if( evt->IsClick( BUT_RIGHT ) )
  760. {
  761. m_menu.ShowContextMenu( selection() );
  762. }
  763. else if( evt->IsClick( BUT_LEFT ) )
  764. {
  765. switch( step )
  766. {
  767. case SET_ORIGIN:
  768. {
  769. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  770. PCB_LAYER_ID layer = m_frame->GetActiveLayer();
  771. // Init the new item attributes
  772. auto setMeasurementAttributes =
  773. [&]( PCB_DIMENSION_BASE* aDim )
  774. {
  775. aDim->SetUnitsMode( boardSettings.m_DimensionUnitsMode );
  776. aDim->SetUnitsFormat( boardSettings.m_DimensionUnitsFormat );
  777. aDim->SetPrecision( boardSettings.m_DimensionPrecision );
  778. aDim->SetSuppressZeroes( boardSettings.m_DimensionSuppressZeroes );
  779. aDim->SetTextPositionMode( boardSettings.m_DimensionTextPosition );
  780. aDim->SetKeepTextAligned( boardSettings.m_DimensionKeepTextAligned );
  781. if( boardSettings.m_DimensionUnitsMode == DIM_UNITS_MODE::AUTOMATIC )
  782. aDim->SetUnits( m_frame->GetUserUnits() );
  783. };
  784. if( originalEvent.IsAction( &PCB_ACTIONS::drawAlignedDimension ) )
  785. {
  786. dimension = new PCB_DIM_ALIGNED( m_frame->GetModel(),
  787. m_isFootprintEditor ? PCB_FP_DIM_ALIGNED_T
  788. : PCB_DIM_ALIGNED_T );
  789. setMeasurementAttributes( dimension );
  790. }
  791. else if( originalEvent.IsAction( &PCB_ACTIONS::drawOrthogonalDimension ) )
  792. {
  793. dimension = new PCB_DIM_ORTHOGONAL( m_frame->GetModel(), m_isFootprintEditor );
  794. setMeasurementAttributes( dimension );
  795. }
  796. else if( originalEvent.IsAction( &PCB_ACTIONS::drawCenterDimension ) )
  797. {
  798. dimension = new PCB_DIM_CENTER( m_frame->GetModel(), m_isFootprintEditor );
  799. }
  800. else if( originalEvent.IsAction( &PCB_ACTIONS::drawRadialDimension ) )
  801. {
  802. dimension = new PCB_DIM_RADIAL( m_frame->GetModel(), m_isFootprintEditor );
  803. setMeasurementAttributes( dimension );
  804. }
  805. else if( originalEvent.IsAction( &PCB_ACTIONS::drawLeader ) )
  806. {
  807. dimension = new PCB_DIM_LEADER( m_frame->GetModel(), m_isFootprintEditor );
  808. dimension->Text().SetPosition( cursorPos );
  809. }
  810. else
  811. {
  812. wxFAIL_MSG( wxT( "Unhandled action in DRAWING_TOOL::DrawDimension" ) );
  813. }
  814. t = dimension->Type();
  815. dimension->SetLayer( layer );
  816. dimension->Text().SetTextSize( boardSettings.GetTextSize( layer ) );
  817. dimension->Text().SetTextThickness( boardSettings.GetTextThickness( layer ) );
  818. dimension->Text().SetItalic( boardSettings.GetTextItalic( layer ) );
  819. dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
  820. dimension->SetArrowLength( boardSettings.m_DimensionArrowLength );
  821. dimension->SetExtensionOffset( boardSettings.m_DimensionExtensionOffset );
  822. dimension->SetStart( cursorPos );
  823. dimension->SetEnd( cursorPos );
  824. dimension->Update();
  825. if( !m_view->IsLayerVisible( layer ) )
  826. {
  827. m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
  828. m_frame->GetCanvas()->Refresh();
  829. }
  830. preview.Add( dimension );
  831. frame()->SetMsgPanel( dimension );
  832. m_controls->SetAutoPan( true );
  833. m_controls->CaptureCursor( true );
  834. break;
  835. }
  836. case SET_END:
  837. // Dimensions that have origin and end in the same spot are not valid
  838. if( dimension->GetStart() == dimension->GetEnd() )
  839. {
  840. --step;
  841. break;
  842. }
  843. if( t == PCB_DIM_CENTER_T || t == PCB_DIM_RADIAL_T || t == PCB_DIM_LEADER_T
  844. || t == PCB_FP_DIM_CENTER_T || t == PCB_FP_DIM_RADIAL_T || t == PCB_FP_DIM_LEADER_T )
  845. {
  846. // No separate height step
  847. ++step;
  848. KI_FALLTHROUGH;
  849. }
  850. else
  851. {
  852. break;
  853. }
  854. case SET_HEIGHT:
  855. assert( dimension->GetStart() != dimension->GetEnd() );
  856. assert( dimension->GetLineThickness() > 0 );
  857. preview.Remove( dimension );
  858. commit.Add( dimension );
  859. commit.Push( _( "Draw a dimension" ) );
  860. if( t == PCB_DIM_LEADER_T || t == PCB_FP_DIM_LEADER_T )
  861. {
  862. // Run the edit immediately to set the leader text
  863. m_toolMgr->RunAction( PCB_ACTIONS::properties, true, dimension );
  864. }
  865. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, dimension );
  866. break;
  867. }
  868. if( ++step == FINISHED )
  869. {
  870. step = SET_ORIGIN;
  871. m_controls->SetAutoPan( false );
  872. m_controls->CaptureCursor( false );
  873. }
  874. }
  875. else if( evt->IsMotion() )
  876. {
  877. switch( step )
  878. {
  879. case SET_END:
  880. dimension->SetEnd( cursorPos );
  881. if( Is45Limited() || t == PCB_DIM_CENTER_T || t == PCB_FP_DIM_CENTER_T )
  882. constrainDimension( dimension );
  883. if( t == PCB_DIM_ORTHOGONAL_T || t == PCB_FP_DIM_ORTHOGONAL_T )
  884. {
  885. PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
  886. BOX2I bounds( dimension->GetStart(),
  887. dimension->GetEnd() - dimension->GetStart() );
  888. // Create a nice preview by measuring the longer dimension
  889. bool vert = bounds.GetWidth() < bounds.GetHeight();
  890. ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
  891. : PCB_DIM_ORTHOGONAL::DIR::HORIZONTAL );
  892. }
  893. else if( t == PCB_DIM_RADIAL_T || t == PCB_FP_DIM_RADIAL_T )
  894. {
  895. PCB_DIM_RADIAL* radialDim = static_cast<PCB_DIM_RADIAL*>( dimension );
  896. VECTOR2I textOffset( radialDim->GetArrowLength() * 10, 0 );
  897. if( radialDim->GetEnd().x < radialDim->GetStart().x )
  898. textOffset = -textOffset;
  899. radialDim->Text().SetPosition( radialDim->GetKnee() + textOffset );
  900. }
  901. else if( t == PCB_DIM_LEADER_T || t == PCB_FP_DIM_LEADER_T )
  902. {
  903. VECTOR2I textOffset( dimension->GetArrowLength() * 10, 0 );
  904. if( dimension->GetEnd().x < dimension->GetStart().x )
  905. textOffset = -textOffset;
  906. dimension->Text().SetPosition( dimension->GetEnd() + textOffset );
  907. }
  908. dimension->Update();
  909. break;
  910. case SET_HEIGHT:
  911. if( t == PCB_DIM_ALIGNED_T || t == PCB_FP_DIM_ALIGNED_T )
  912. {
  913. PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dimension );
  914. // Calculating the direction of travel perpendicular to the selected axis
  915. double angle = aligned->GetAngle() + ( M_PI / 2 );
  916. VECTOR2I delta( (VECTOR2I) cursorPos - dimension->GetEnd() );
  917. double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
  918. aligned->SetHeight( height );
  919. aligned->Update();
  920. }
  921. else if( t == PCB_DIM_ORTHOGONAL_T || t == PCB_FP_DIM_ORTHOGONAL_T )
  922. {
  923. PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
  924. BOX2I bbox( dimension->GetStart(),
  925. dimension->GetEnd() - dimension->GetStart() );
  926. VECTOR2I direction( cursorPos - bbox.Centre() );
  927. bool vert;
  928. // Only change the orientation when we move outside the bbox
  929. if( !bbox.Contains( cursorPos ) )
  930. {
  931. // If the dimension is horizontal or vertical, set correct orientation
  932. // otherwise, test if we're left/right of the bounding box or above/below it
  933. if( bbox.GetWidth() == 0 )
  934. vert = true;
  935. else if( bbox.GetHeight() == 0 )
  936. vert = false;
  937. else if( cursorPos.x > bbox.GetLeft() && cursorPos.x < bbox.GetRight() )
  938. vert = false;
  939. else if( cursorPos.y > bbox.GetTop() && cursorPos.y < bbox.GetBottom() )
  940. vert = true;
  941. else
  942. vert = std::abs( direction.y ) < std::abs( direction.x );
  943. ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
  944. : PCB_DIM_ORTHOGONAL::DIR::HORIZONTAL );
  945. }
  946. else
  947. {
  948. vert = ortho->GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
  949. }
  950. VECTOR2I heightVector( cursorPos - dimension->GetStart() );
  951. ortho->SetHeight( vert ? heightVector.x : heightVector.y );
  952. ortho->Update();
  953. }
  954. break;
  955. }
  956. // Show a preview of the item
  957. m_view->Update( &preview );
  958. }
  959. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  960. {
  961. if( dimension )
  962. {
  963. PCB_LAYER_ID layer = m_frame->GetActiveLayer();
  964. if( !m_view->IsLayerVisible( layer ) )
  965. {
  966. m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
  967. m_frame->GetCanvas()->Refresh();
  968. }
  969. dimension->SetLayer( layer );
  970. dimension->Text().SetTextSize( boardSettings.GetTextSize( layer ) );
  971. dimension->Text().SetTextThickness( boardSettings.GetTextThickness( layer ) );
  972. dimension->Text().SetItalic( boardSettings.GetTextItalic( layer ) );
  973. dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
  974. dimension->Update();
  975. m_view->Update( &preview );
  976. frame()->SetMsgPanel( dimension );
  977. }
  978. else
  979. {
  980. evt->SetPassEvent();
  981. }
  982. }
  983. else if( evt->IsAction( &PCB_ACTIONS::properties ) )
  984. {
  985. if( step == SET_END || step == SET_HEIGHT )
  986. {
  987. frame()->OnEditItemRequest( dimension );
  988. dimension->Update();
  989. frame()->SetMsgPanel( dimension );
  990. break;
  991. }
  992. else
  993. {
  994. evt->SetPassEvent();
  995. }
  996. }
  997. else
  998. {
  999. evt->SetPassEvent();
  1000. }
  1001. }
  1002. if( step != SET_ORIGIN )
  1003. delete dimension;
  1004. m_controls->SetAutoPan( false );
  1005. m_controls->ForceCursorPosition( false );
  1006. m_controls->CaptureCursor( false );
  1007. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  1008. m_view->Remove( &preview );
  1009. if( selection().Empty() )
  1010. m_frame->SetMsgPanel( board() );
  1011. return 0;
  1012. }
  1013. int DRAWING_TOOL::PlaceImportedGraphics( const TOOL_EVENT& aEvent )
  1014. {
  1015. if( !m_frame->GetModel() )
  1016. return 0;
  1017. if( m_inDrawingTool )
  1018. return 0;
  1019. REENTRANCY_GUARD guard( &m_inDrawingTool );
  1020. // Note: PlaceImportedGraphics() will convert PCB_SHAPE_T and PCB_TEXT_T to footprint
  1021. // items if needed
  1022. DIALOG_IMPORT_GFX dlg( m_frame, m_isFootprintEditor );
  1023. int dlgResult = dlg.ShowModal();
  1024. std::list<std::unique_ptr<EDA_ITEM>>& list = dlg.GetImportedItems();
  1025. if( dlgResult != wxID_OK )
  1026. return 0;
  1027. // Ensure the list is not empty:
  1028. if( list.empty() )
  1029. {
  1030. wxMessageBox( _( "No graphic items found in file.") );
  1031. return 0;
  1032. }
  1033. m_toolMgr->RunAction( ACTIONS::cancelInteractive, true );
  1034. std::vector<BOARD_ITEM*> newItems; // all new items, including group
  1035. std::vector<BOARD_ITEM*> selectedItems; // the group, or newItems if no group
  1036. PCB_SELECTION preview;
  1037. BOARD_COMMIT commit( m_frame );
  1038. PCB_GROUP* group = nullptr;
  1039. if( dlg.ShouldGroupItems() )
  1040. {
  1041. if( m_isFootprintEditor )
  1042. group = new PCB_GROUP( m_frame->GetBoard()->GetFirstFootprint() );
  1043. else
  1044. group = new PCB_GROUP( m_frame->GetBoard() );
  1045. newItems.push_back( group );
  1046. selectedItems.push_back( group );
  1047. preview.Add( group );
  1048. }
  1049. for( std::unique_ptr<EDA_ITEM>& ptr : list )
  1050. {
  1051. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( ptr.get() );
  1052. if( m_isFootprintEditor )
  1053. wxASSERT( item->Type() == PCB_FP_SHAPE_T || item->Type() == PCB_FP_TEXT_T );
  1054. else
  1055. wxASSERT( item->Type() == PCB_SHAPE_T || item->Type() == PCB_TEXT_T );
  1056. newItems.push_back( item );
  1057. if( group )
  1058. group->AddItem( item );
  1059. else
  1060. selectedItems.push_back( item );
  1061. preview.Add( item );
  1062. ptr.release();
  1063. }
  1064. if( !dlg.IsPlacementInteractive() )
  1065. {
  1066. for( BOARD_ITEM* item : newItems )
  1067. commit.Add( item );
  1068. commit.Push( _( "Place a DXF_SVG drawing" ) );
  1069. return 0;
  1070. }
  1071. m_view->Add( &preview );
  1072. // Clear the current selection then select the drawings so that edit tools work on them
  1073. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1074. m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &selectedItems );
  1075. std::string tool = aEvent.GetCommandStr().get();
  1076. m_frame->PushTool( tool );
  1077. auto setCursor =
  1078. [&]()
  1079. {
  1080. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
  1081. };
  1082. Activate();
  1083. // Must be done after Activate() so that it gets set into the correct context
  1084. m_controls->ShowCursor( true );
  1085. m_controls->ForceCursorPosition( false );
  1086. // Set initial cursor
  1087. setCursor();
  1088. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
  1089. // Now move the new items to the current cursor position:
  1090. VECTOR2I cursorPos = m_controls->GetCursorPosition();
  1091. VECTOR2I delta = cursorPos - static_cast<BOARD_ITEM*>( preview.Front() )->GetPosition();
  1092. for( BOARD_ITEM* item : selectedItems )
  1093. item->Move( delta );
  1094. m_view->Update( &preview );
  1095. // Main loop: keep receiving events
  1096. while( TOOL_EVENT* evt = Wait() )
  1097. {
  1098. setCursor();
  1099. cursorPos = m_controls->GetCursorPosition();
  1100. if( evt->IsCancelInteractive() || evt->IsActivate() )
  1101. {
  1102. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1103. for( BOARD_ITEM* item : newItems )
  1104. delete item;
  1105. break;
  1106. }
  1107. else if( evt->IsMotion() )
  1108. {
  1109. delta = cursorPos - static_cast<BOARD_ITEM*>( preview.Front() )->GetPosition();
  1110. for( BOARD_ITEM* item : selectedItems )
  1111. item->Move( delta );
  1112. m_view->Update( &preview );
  1113. }
  1114. else if( evt->IsClick( BUT_RIGHT ) )
  1115. {
  1116. m_menu.ShowContextMenu( selection() );
  1117. }
  1118. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  1119. {
  1120. // Place the imported drawings
  1121. for( BOARD_ITEM* item : newItems )
  1122. commit.Add( item );
  1123. commit.Push( _( "Place a DXF_SVG drawing" ) );
  1124. break; // This is a one-shot command, not a tool
  1125. }
  1126. else
  1127. {
  1128. evt->SetPassEvent();
  1129. }
  1130. }
  1131. preview.Clear();
  1132. m_view->Remove( &preview );
  1133. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  1134. m_controls->ForceCursorPosition( false );
  1135. m_frame->PopTool( tool );
  1136. return 0;
  1137. }
  1138. int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent )
  1139. {
  1140. wxASSERT( m_isFootprintEditor );
  1141. if( !m_frame->GetModel() )
  1142. return 0;
  1143. if( m_inDrawingTool )
  1144. return 0;
  1145. REENTRANCY_GUARD guard( &m_inDrawingTool );
  1146. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
  1147. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  1148. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1149. std::string tool = aEvent.GetCommandStr().get();
  1150. m_frame->PushTool( tool );
  1151. auto setCursor =
  1152. [&]()
  1153. {
  1154. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
  1155. };
  1156. Activate();
  1157. // Must be done after Activate() so that it gets set into the correct context
  1158. m_controls->ShowCursor( true );
  1159. m_controls->SetAutoPan( true );
  1160. m_controls->CaptureCursor( false );
  1161. m_controls->ForceCursorPosition( false );
  1162. // Set initial cursor
  1163. setCursor();
  1164. while( TOOL_EVENT* evt = Wait() )
  1165. {
  1166. setCursor();
  1167. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  1168. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  1169. VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(),
  1170. LSET::AllLayersMask() );
  1171. m_controls->ForceCursorPosition( true, cursorPos );
  1172. if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  1173. {
  1174. FOOTPRINT* footprint = (FOOTPRINT*) m_frame->GetModel();
  1175. BOARD_COMMIT commit( m_frame );
  1176. commit.Modify( footprint );
  1177. // set the new relative internal local coordinates of footprint items
  1178. VECTOR2I moveVector = footprint->GetPosition() - cursorPos;
  1179. footprint->MoveAnchorPosition( moveVector );
  1180. commit.Push( _( "Move the footprint reference anchor" ) );
  1181. // Usually, we do not need to change twice the anchor position,
  1182. // so deselect the active tool
  1183. m_frame->PopTool( tool );
  1184. break;
  1185. }
  1186. else if( evt->IsClick( BUT_RIGHT ) )
  1187. {
  1188. m_menu.ShowContextMenu( selection() );
  1189. }
  1190. else if( evt->IsCancelInteractive() || evt->IsActivate() )
  1191. {
  1192. m_frame->PopTool( tool );
  1193. break;
  1194. }
  1195. else
  1196. {
  1197. evt->SetPassEvent();
  1198. }
  1199. }
  1200. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  1201. m_controls->ForceCursorPosition( false );
  1202. return 0;
  1203. }
  1204. int DRAWING_TOOL::ToggleHV45Mode( const TOOL_EVENT& toolEvent )
  1205. {
  1206. #define TOGGLE( a ) a = !a
  1207. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  1208. if( frame()->IsType( FRAME_PCB_EDITOR ) )
  1209. TOGGLE( mgr.GetAppSettings<PCBNEW_SETTINGS>()->m_Use45DegreeLimit );
  1210. else
  1211. TOGGLE( mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>()->m_Use45Limit );
  1212. updateStatusBar();
  1213. return 0;
  1214. #undef TOGGLE
  1215. }
  1216. /**
  1217. * Update a #PCB_SHAPE from the current state of a #TWO_POINT_GEOMETRY_MANAGER.
  1218. */
  1219. static void updateSegmentFromGeometryMgr( const KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER& aMgr,
  1220. PCB_SHAPE* aGraphic )
  1221. {
  1222. if( !aMgr.IsReset() )
  1223. {
  1224. aGraphic->SetStart( aMgr.GetOrigin() );
  1225. aGraphic->SetEnd( aMgr.GetEnd() );
  1226. }
  1227. }
  1228. bool DRAWING_TOOL::drawSegment( const std::string& aTool, PCB_SHAPE** aGraphic,
  1229. OPT<VECTOR2D> aStartingPoint )
  1230. {
  1231. SHAPE_T shape = ( *aGraphic )->GetShape();
  1232. // Only three shapes are currently supported
  1233. wxASSERT( shape == SHAPE_T::SEGMENT || shape == SHAPE_T::CIRCLE || shape == SHAPE_T::RECT );
  1234. EDA_UNITS userUnits = m_frame->GetUserUnits();
  1235. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  1236. PCB_SHAPE*& graphic = *aGraphic;
  1237. if( m_layer != m_frame->GetActiveLayer() )
  1238. {
  1239. m_layer = m_frame->GetActiveLayer();
  1240. m_stroke.SetWidth( getSegmentWidth( m_layer ) );
  1241. m_stroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT );
  1242. m_stroke.SetColor( COLOR4D::UNSPECIFIED );
  1243. }
  1244. // geometric construction manager
  1245. KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER twoPointManager;
  1246. // drawing assistant overlay
  1247. // TODO: workaround because EDA_SHAPE_TYPE_T is not visible from commons.
  1248. KIGFX::PREVIEW::GEOM_SHAPE geomShape( static_cast<KIGFX::PREVIEW::GEOM_SHAPE>( shape ) );
  1249. KIGFX::PREVIEW::TWO_POINT_ASSISTANT twoPointAsst( twoPointManager, userUnits, geomShape );
  1250. // Add a VIEW_GROUP that serves as a preview for the new item
  1251. PCB_SELECTION preview;
  1252. m_view->Add( &preview );
  1253. m_view->Add( &twoPointAsst );
  1254. bool started = false;
  1255. bool cancelled = false;
  1256. bool isLocalOriginSet = ( m_frame->GetScreen()->m_LocalOrigin != VECTOR2D( 0, 0 ) );
  1257. VECTOR2I cursorPos = m_controls->GetMousePosition();
  1258. auto setCursor =
  1259. [&]()
  1260. {
  1261. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  1262. };
  1263. auto cleanup =
  1264. [&]()
  1265. {
  1266. preview.Clear();
  1267. m_view->Update( &preview );
  1268. delete graphic;
  1269. graphic = nullptr;
  1270. if( !isLocalOriginSet )
  1271. m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
  1272. };
  1273. m_controls->ShowCursor( true );
  1274. m_controls->ForceCursorPosition( false );
  1275. // Set initial cursor
  1276. setCursor();
  1277. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  1278. if( aStartingPoint )
  1279. m_toolMgr->PrimeTool( aStartingPoint.get() );
  1280. // Main loop: keep receiving events
  1281. while( TOOL_EVENT* evt = Wait() )
  1282. {
  1283. setCursor();
  1284. if( started )
  1285. m_frame->SetMsgPanel( graphic );
  1286. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  1287. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  1288. cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), m_layer );
  1289. m_controls->ForceCursorPosition( true, cursorPos );
  1290. if( evt->IsCancelInteractive() )
  1291. {
  1292. cleanup();
  1293. if( !started )
  1294. {
  1295. // We've handled the cancel event. Don't cancel other tools
  1296. evt->SetPassEvent( false );
  1297. m_frame->PopTool( aTool );
  1298. cancelled = true;
  1299. }
  1300. break;
  1301. }
  1302. else if( evt->IsActivate() )
  1303. {
  1304. if( evt->IsPointEditor() )
  1305. {
  1306. // don't exit (the point editor runs in the background)
  1307. }
  1308. else if( evt->IsMoveTool() )
  1309. {
  1310. cleanup();
  1311. // leave ourselves on the stack so we come back after the move
  1312. cancelled = true;
  1313. break;
  1314. }
  1315. else
  1316. {
  1317. cleanup();
  1318. m_frame->PopTool( aTool );
  1319. cancelled = true;
  1320. break;
  1321. }
  1322. }
  1323. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  1324. {
  1325. if( m_layer != m_frame->GetActiveLayer() )
  1326. {
  1327. m_layer = m_frame->GetActiveLayer();
  1328. m_stroke.SetWidth( getSegmentWidth( m_layer ) );
  1329. m_stroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT );
  1330. m_stroke.SetColor( COLOR4D::UNSPECIFIED );
  1331. }
  1332. if( graphic )
  1333. {
  1334. if( !m_view->IsLayerVisible( m_layer ) )
  1335. {
  1336. m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
  1337. m_frame->GetCanvas()->Refresh();
  1338. }
  1339. graphic->SetLayer( m_layer );
  1340. graphic->SetStroke( m_stroke );
  1341. m_view->Update( &preview );
  1342. frame()->SetMsgPanel( graphic );
  1343. }
  1344. else
  1345. {
  1346. evt->SetPassEvent();
  1347. }
  1348. }
  1349. else if( evt->IsAction( &PCB_ACTIONS::properties ) )
  1350. {
  1351. if( started )
  1352. {
  1353. frame()->OnEditItemRequest( graphic );
  1354. m_view->Update( &preview );
  1355. frame()->SetMsgPanel( graphic );
  1356. break;
  1357. }
  1358. else
  1359. {
  1360. evt->SetPassEvent();
  1361. }
  1362. }
  1363. else if( evt->IsClick( BUT_RIGHT ) )
  1364. {
  1365. m_menu.ShowContextMenu( selection() );
  1366. }
  1367. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  1368. {
  1369. if( !started )
  1370. {
  1371. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1372. if( aStartingPoint )
  1373. {
  1374. cursorPos = aStartingPoint.get();
  1375. aStartingPoint = NULLOPT;
  1376. }
  1377. // Init the new item attributes
  1378. graphic->SetShape( static_cast<SHAPE_T>( shape ) );
  1379. graphic->SetFilled( false );
  1380. graphic->SetStroke( m_stroke );
  1381. graphic->SetLayer( m_layer );
  1382. grid.SetSkipPoint( cursorPos );
  1383. twoPointManager.SetOrigin( cursorPos );
  1384. twoPointManager.SetEnd( cursorPos );
  1385. if( !isLocalOriginSet )
  1386. m_frame->GetScreen()->m_LocalOrigin = cursorPos;
  1387. preview.Add( graphic );
  1388. frame()->SetMsgPanel( graphic );
  1389. m_controls->SetAutoPan( true );
  1390. m_controls->CaptureCursor( true );
  1391. if( !m_view->IsLayerVisible( m_layer ) )
  1392. {
  1393. m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
  1394. m_frame->GetCanvas()->Refresh();
  1395. }
  1396. updateSegmentFromGeometryMgr( twoPointManager, graphic );
  1397. started = true;
  1398. }
  1399. else if( shape == SHAPE_T::CIRCLE )
  1400. {
  1401. // No clever logic if drawing a circle
  1402. preview.Clear();
  1403. twoPointManager.Reset();
  1404. break;
  1405. }
  1406. else
  1407. {
  1408. PCB_SHAPE* snapItem = dyn_cast<PCB_SHAPE*>( grid.GetSnapped() );
  1409. if( twoPointManager.GetOrigin() == twoPointManager.GetEnd()
  1410. || ( evt->IsDblClick( BUT_LEFT ) && shape == SHAPE_T::SEGMENT )
  1411. || snapItem )
  1412. // User has clicked twice in the same spot
  1413. // or clicked on the end of an existing segment (closing a path)
  1414. {
  1415. BOARD_COMMIT commit( m_frame );
  1416. // If the user clicks on an existing snap point from a drawsegment
  1417. // we finish the segment as they are likely closing a path
  1418. if( snapItem && ( shape == SHAPE_T::RECT || graphic->GetLength() > 0.0 ) )
  1419. {
  1420. commit.Add( graphic );
  1421. commit.Push( _( "Draw a line segment" ) );
  1422. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, graphic );
  1423. }
  1424. else
  1425. {
  1426. delete graphic;
  1427. }
  1428. graphic = nullptr;
  1429. }
  1430. preview.Clear();
  1431. twoPointManager.Reset();
  1432. break;
  1433. }
  1434. twoPointManager.SetEnd( cursorPos );
  1435. }
  1436. else if( evt->IsMotion() )
  1437. {
  1438. // 45 degree lines
  1439. if( started && Is45Limited() )
  1440. {
  1441. const VECTOR2I lineVector( cursorPos - VECTOR2I( twoPointManager.GetOrigin() ) );
  1442. // get a restricted 45/H/V line from the last fixed point to the cursor
  1443. auto newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECT ) );
  1444. m_controls->ForceCursorPosition( true, VECTOR2I( twoPointManager.GetEnd() ) );
  1445. twoPointManager.SetEnd( twoPointManager.GetOrigin() + newEnd );
  1446. twoPointManager.SetAngleSnap( true );
  1447. }
  1448. else
  1449. {
  1450. twoPointManager.SetEnd( cursorPos );
  1451. twoPointManager.SetAngleSnap( false );
  1452. }
  1453. updateSegmentFromGeometryMgr( twoPointManager, graphic );
  1454. m_view->Update( &preview );
  1455. m_view->Update( &twoPointAsst );
  1456. }
  1457. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
  1458. {
  1459. m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
  1460. graphic->SetStroke( m_stroke );
  1461. m_view->Update( &preview );
  1462. frame()->SetMsgPanel( graphic );
  1463. }
  1464. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
  1465. {
  1466. if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
  1467. {
  1468. m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
  1469. graphic->SetStroke( m_stroke );
  1470. m_view->Update( &preview );
  1471. frame()->SetMsgPanel( graphic );
  1472. }
  1473. }
  1474. else if( evt->IsAction( &ACTIONS::resetLocalCoords ) )
  1475. {
  1476. isLocalOriginSet = true;
  1477. evt->SetPassEvent();
  1478. }
  1479. else if( evt->IsAction( &ACTIONS::updateUnits ) )
  1480. {
  1481. if( frame()->GetUserUnits() != userUnits )
  1482. {
  1483. userUnits = frame()->GetUserUnits();
  1484. twoPointAsst.SetUnits( userUnits );
  1485. m_view->Update( &twoPointAsst );
  1486. }
  1487. evt->SetPassEvent();
  1488. }
  1489. else
  1490. {
  1491. evt->SetPassEvent();
  1492. }
  1493. }
  1494. if( !isLocalOriginSet ) // reset the relative coordinate if it was not set before
  1495. m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
  1496. m_view->Remove( &twoPointAsst );
  1497. m_view->Remove( &preview );
  1498. if( selection().Empty() )
  1499. m_frame->SetMsgPanel( board() );
  1500. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  1501. m_controls->SetAutoPan( false );
  1502. m_controls->CaptureCursor( false );
  1503. m_controls->ForceCursorPosition( false );
  1504. return !cancelled;
  1505. }
  1506. /**
  1507. * Update an arc PCB_SHAPE from the current state of an Arc Geometry Manager.
  1508. */
  1509. static void updateArcFromConstructionMgr( const KIGFX::PREVIEW::ARC_GEOM_MANAGER& aMgr,
  1510. PCB_SHAPE& aArc )
  1511. {
  1512. VECTOR2I vec = aMgr.GetOrigin();
  1513. aArc.SetCenter( vec );
  1514. vec = aMgr.GetStartRadiusEnd();
  1515. aArc.SetStart( vec );
  1516. vec = aMgr.GetEndRadiusEnd();
  1517. aArc.SetEnd( vec );
  1518. }
  1519. bool DRAWING_TOOL::drawArc( const std::string& aTool, PCB_SHAPE** aGraphic,
  1520. OPT<VECTOR2D> aStartingPoint )
  1521. {
  1522. PCB_SHAPE*& graphic = *aGraphic;
  1523. if( m_layer != m_frame->GetActiveLayer() )
  1524. {
  1525. m_layer = m_frame->GetActiveLayer();
  1526. m_stroke.SetWidth( getSegmentWidth( m_layer ) );
  1527. m_stroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT );
  1528. m_stroke.SetColor( COLOR4D::UNSPECIFIED );
  1529. }
  1530. // Arc geometric construction manager
  1531. KIGFX::PREVIEW::ARC_GEOM_MANAGER arcManager;
  1532. // Arc drawing assistant overlay
  1533. KIGFX::PREVIEW::ARC_ASSISTANT arcAsst( arcManager, m_frame->GetUserUnits() );
  1534. // Add a VIEW_GROUP that serves as a preview for the new item
  1535. PCB_SELECTION preview;
  1536. m_view->Add( &preview );
  1537. m_view->Add( &arcAsst );
  1538. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  1539. auto setCursor =
  1540. [&]()
  1541. {
  1542. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  1543. };
  1544. auto cleanup =
  1545. [&] ()
  1546. {
  1547. preview.Clear();
  1548. delete *aGraphic;
  1549. *aGraphic = nullptr;
  1550. };
  1551. m_controls->ShowCursor( true );
  1552. m_controls->ForceCursorPosition( false );
  1553. // Set initial cursor
  1554. setCursor();
  1555. bool firstPoint = false;
  1556. bool cancelled = false;
  1557. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  1558. if( aStartingPoint )
  1559. m_toolMgr->PrimeTool(aStartingPoint.get() );
  1560. // Main loop: keep receiving events
  1561. while( TOOL_EVENT* evt = Wait() )
  1562. {
  1563. if( firstPoint )
  1564. m_frame->SetMsgPanel( graphic );
  1565. setCursor();
  1566. graphic->SetLayer( m_layer );
  1567. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  1568. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  1569. VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), graphic );
  1570. m_controls->ForceCursorPosition( true, cursorPos );
  1571. if( evt->IsCancelInteractive() )
  1572. {
  1573. cleanup();
  1574. if( !firstPoint )
  1575. {
  1576. // We've handled the cancel event. Don't cancel other tools
  1577. evt->SetPassEvent( false );
  1578. m_frame->PopTool( aTool );
  1579. cancelled = true;
  1580. }
  1581. break;
  1582. }
  1583. else if( evt->IsActivate() )
  1584. {
  1585. if( evt->IsPointEditor() )
  1586. {
  1587. // don't exit (the point editor runs in the background)
  1588. }
  1589. else if( evt->IsMoveTool() )
  1590. {
  1591. cleanup();
  1592. // leave ourselves on the stack so we come back after the move
  1593. cancelled = true;
  1594. break;
  1595. }
  1596. else
  1597. {
  1598. cleanup();
  1599. m_frame->PopTool( aTool );
  1600. cancelled = true;
  1601. break;
  1602. }
  1603. }
  1604. else if( evt->IsClick( BUT_LEFT ) )
  1605. {
  1606. if( !firstPoint )
  1607. {
  1608. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1609. m_controls->SetAutoPan( true );
  1610. m_controls->CaptureCursor( true );
  1611. // Init the new item attributes
  1612. // (non-geometric, those are handled by the manager)
  1613. graphic->SetShape( SHAPE_T::ARC );
  1614. graphic->SetStroke( m_stroke );
  1615. if( !m_view->IsLayerVisible( m_layer ) )
  1616. {
  1617. m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
  1618. m_frame->GetCanvas()->Refresh();
  1619. }
  1620. preview.Add( graphic );
  1621. frame()->SetMsgPanel( graphic );
  1622. firstPoint = true;
  1623. }
  1624. arcManager.AddPoint( cursorPos, true );
  1625. }
  1626. else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
  1627. {
  1628. arcManager.RemoveLastPoint();
  1629. }
  1630. else if( evt->IsMotion() )
  1631. {
  1632. // set angle snap
  1633. arcManager.SetAngleSnap( Is45Limited() );
  1634. // update, but don't step the manager state
  1635. arcManager.AddPoint( cursorPos, false );
  1636. }
  1637. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  1638. {
  1639. if( m_layer != m_frame->GetActiveLayer() )
  1640. {
  1641. m_layer = m_frame->GetActiveLayer();
  1642. m_stroke.SetWidth( getSegmentWidth( m_layer ) );
  1643. m_stroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT );
  1644. m_stroke.SetColor( COLOR4D::UNSPECIFIED );
  1645. }
  1646. if( graphic )
  1647. {
  1648. if( !m_view->IsLayerVisible( m_layer ) )
  1649. {
  1650. m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
  1651. m_frame->GetCanvas()->Refresh();
  1652. }
  1653. graphic->SetLayer( m_layer );
  1654. graphic->SetStroke( m_stroke );
  1655. m_view->Update( &preview );
  1656. frame()->SetMsgPanel( graphic );
  1657. }
  1658. else
  1659. {
  1660. evt->SetPassEvent();
  1661. }
  1662. }
  1663. else if( evt->IsAction( &PCB_ACTIONS::properties ) )
  1664. {
  1665. if( arcManager.GetStep() == KIGFX::PREVIEW::ARC_GEOM_MANAGER::SET_START )
  1666. {
  1667. graphic->SetArcAngleAndEnd( ANGLE_90 );
  1668. frame()->OnEditItemRequest( graphic );
  1669. m_view->Update( &preview );
  1670. frame()->SetMsgPanel( graphic );
  1671. break;
  1672. }
  1673. // Don't show the edit panel if we can't represent the arc with it
  1674. else if( ( arcManager.GetStep() == KIGFX::PREVIEW::ARC_GEOM_MANAGER::SET_ANGLE )
  1675. && ( arcManager.GetStartRadiusEnd() != arcManager.GetEndRadiusEnd() ) )
  1676. {
  1677. frame()->OnEditItemRequest( graphic );
  1678. m_view->Update( &preview );
  1679. frame()->SetMsgPanel( graphic );
  1680. break;
  1681. }
  1682. else
  1683. {
  1684. evt->SetPassEvent();
  1685. }
  1686. }
  1687. else if( evt->IsClick( BUT_RIGHT ) )
  1688. {
  1689. m_menu.ShowContextMenu( selection() );
  1690. }
  1691. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
  1692. {
  1693. m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
  1694. graphic->SetStroke( m_stroke );
  1695. m_view->Update( &preview );
  1696. frame()->SetMsgPanel( graphic );
  1697. }
  1698. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
  1699. {
  1700. if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
  1701. {
  1702. m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
  1703. graphic->SetStroke( m_stroke );
  1704. m_view->Update( &preview );
  1705. frame()->SetMsgPanel( graphic );
  1706. }
  1707. }
  1708. else if( evt->IsAction( &PCB_ACTIONS::arcPosture ) )
  1709. {
  1710. arcManager.ToggleClockwise();
  1711. }
  1712. else if( evt->IsAction( &ACTIONS::updateUnits ) )
  1713. {
  1714. arcAsst.SetUnits( frame()->GetUserUnits() );
  1715. m_view->Update( &arcAsst );
  1716. evt->SetPassEvent();
  1717. }
  1718. else
  1719. {
  1720. evt->SetPassEvent();
  1721. }
  1722. if( arcManager.IsComplete() )
  1723. {
  1724. break;
  1725. }
  1726. else if( arcManager.HasGeometryChanged() )
  1727. {
  1728. updateArcFromConstructionMgr( arcManager, *graphic );
  1729. m_view->Update( &preview );
  1730. m_view->Update( &arcAsst );
  1731. if( firstPoint )
  1732. frame()->SetMsgPanel( graphic );
  1733. else
  1734. frame()->SetMsgPanel( board() );
  1735. }
  1736. }
  1737. preview.Remove( graphic );
  1738. m_view->Remove( &arcAsst );
  1739. m_view->Remove( &preview );
  1740. if( selection().Empty() )
  1741. m_frame->SetMsgPanel( board() );
  1742. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  1743. m_controls->SetAutoPan( false );
  1744. m_controls->CaptureCursor( false );
  1745. m_controls->ForceCursorPosition( false );
  1746. return !cancelled;
  1747. }
  1748. bool DRAWING_TOOL::getSourceZoneForAction( ZONE_MODE aMode, ZONE** aZone )
  1749. {
  1750. bool clearSelection = false;
  1751. *aZone = nullptr;
  1752. // not an action that needs a source zone
  1753. if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON )
  1754. return true;
  1755. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  1756. const PCB_SELECTION& selection = selTool->GetSelection();
  1757. if( selection.Empty() )
  1758. {
  1759. clearSelection = true;
  1760. m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true );
  1761. }
  1762. // we want a single zone
  1763. if( selection.Size() == 1 )
  1764. *aZone = dyn_cast<ZONE*>( selection[0] );
  1765. // expected a zone, but didn't get one
  1766. if( !*aZone )
  1767. {
  1768. if( clearSelection )
  1769. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1770. return false;
  1771. }
  1772. return true;
  1773. }
  1774. int DRAWING_TOOL::DrawZone( const TOOL_EVENT& aEvent )
  1775. {
  1776. if( m_isFootprintEditor && !m_frame->GetModel() )
  1777. return 0;
  1778. ZONE_MODE zoneMode = aEvent.Parameter<ZONE_MODE>();
  1779. MODE drawMode = MODE::ZONE;
  1780. if( aEvent.IsAction( &PCB_ACTIONS::drawRuleArea ) )
  1781. drawMode = MODE::KEEPOUT;
  1782. if( aEvent.IsAction( &PCB_ACTIONS::drawPolygon ) )
  1783. drawMode = MODE::GRAPHIC_POLYGON;
  1784. SCOPED_DRAW_MODE scopedDrawMode( m_mode, drawMode );
  1785. // get a source zone, if we need one. We need it for:
  1786. // ZONE_MODE::CUTOUT (adding a hole to the source zone)
  1787. // ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
  1788. ZONE* sourceZone = nullptr;
  1789. if( !getSourceZoneForAction( zoneMode, &sourceZone ) )
  1790. return 0;
  1791. // Turn zones on if they are off, so that the created object will be visible after completion
  1792. m_frame->SetObjectVisible( LAYER_ZONES );
  1793. ZONE_CREATE_HELPER::PARAMS params;
  1794. params.m_keepout = drawMode == MODE::KEEPOUT;
  1795. params.m_mode = zoneMode;
  1796. params.m_sourceZone = sourceZone;
  1797. if( zoneMode == ZONE_MODE::SIMILAR )
  1798. params.m_layer = sourceZone->GetLayer();
  1799. else
  1800. params.m_layer = m_frame->GetActiveLayer();
  1801. ZONE_CREATE_HELPER zoneTool( *this, params );
  1802. // the geometry manager which handles the zone geometry, and hands the calculated points
  1803. // over to the zone creator tool
  1804. POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
  1805. bool started = false;
  1806. PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
  1807. STATUS_TEXT_POPUP status( m_frame );
  1808. status.SetTextColor( wxColour( 255, 0, 0 ) );
  1809. status.SetText( _( "Self-intersecting polygons are not allowed" ) );
  1810. std::string tool = aEvent.GetCommandStr().get();
  1811. m_frame->PushTool( tool );
  1812. auto setCursor =
  1813. [&]()
  1814. {
  1815. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  1816. };
  1817. auto cleanup =
  1818. [&] ()
  1819. {
  1820. polyGeomMgr.Reset();
  1821. started = false;
  1822. grid.ClearSkipPoint();
  1823. m_controls->SetAutoPan( false );
  1824. m_controls->CaptureCursor( false );
  1825. };
  1826. Activate();
  1827. // Must be done after Activate() so that it gets set into the correct context
  1828. m_controls->ShowCursor( true );
  1829. m_controls->ForceCursorPosition( false );
  1830. // Set initial cursor
  1831. setCursor();
  1832. if( aEvent.HasPosition() )
  1833. m_toolMgr->PrimeTool( aEvent.Position() );
  1834. // Main loop: keep receiving events
  1835. while( TOOL_EVENT* evt = Wait() )
  1836. {
  1837. setCursor();
  1838. LSET layers( m_frame->GetActiveLayer() );
  1839. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  1840. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  1841. VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
  1842. cursorPos = grid.BestSnapAnchor( cursorPos, layers );
  1843. m_controls->ForceCursorPosition( true, cursorPos );
  1844. polyGeomMgr.SetLeaderMode( Is45Limited() ? POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45
  1845. : POLYGON_GEOM_MANAGER::LEADER_MODE::DIRECT );
  1846. if( evt->IsCancelInteractive() )
  1847. {
  1848. if( polyGeomMgr.IsPolygonInProgress() )
  1849. {
  1850. cleanup();
  1851. }
  1852. else
  1853. {
  1854. // We've handled the cancel event. Don't cancel other tools
  1855. evt->SetPassEvent( false );
  1856. m_frame->PopTool( tool );
  1857. break;
  1858. }
  1859. }
  1860. else if( evt->IsActivate() )
  1861. {
  1862. if( polyGeomMgr.IsPolygonInProgress() )
  1863. cleanup();
  1864. if( evt->IsPointEditor() )
  1865. {
  1866. // don't exit (the point editor runs in the background)
  1867. }
  1868. else if( evt->IsMoveTool() )
  1869. {
  1870. // leave ourselves on the stack so we come back after the move
  1871. break;
  1872. }
  1873. else
  1874. {
  1875. m_frame->PopTool( tool );
  1876. break;
  1877. }
  1878. }
  1879. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  1880. {
  1881. if( zoneMode != ZONE_MODE::SIMILAR )
  1882. params.m_layer = frame()->GetActiveLayer();
  1883. if( !m_view->IsLayerVisible( params.m_layer ) )
  1884. {
  1885. m_frame->GetAppearancePanel()->SetLayerVisible( params.m_layer, true );
  1886. m_frame->GetCanvas()->Refresh();
  1887. }
  1888. }
  1889. else if( evt->IsClick( BUT_RIGHT ) )
  1890. {
  1891. m_menu.ShowContextMenu( selection() );
  1892. }
  1893. // events that lock in nodes
  1894. else if( evt->IsClick( BUT_LEFT )
  1895. || evt->IsDblClick( BUT_LEFT )
  1896. || evt->IsAction( &PCB_ACTIONS::closeOutline ) )
  1897. {
  1898. // Check if it is double click / closing line (so we have to finish the zone)
  1899. const bool endPolygon = evt->IsDblClick( BUT_LEFT )
  1900. || evt->IsAction( &PCB_ACTIONS::closeOutline )
  1901. || polyGeomMgr.NewPointClosesOutline( cursorPos );
  1902. if( endPolygon )
  1903. {
  1904. polyGeomMgr.SetFinished();
  1905. polyGeomMgr.Reset();
  1906. started = false;
  1907. m_controls->SetAutoPan( false );
  1908. m_controls->CaptureCursor( false );
  1909. }
  1910. // adding a corner
  1911. else if( polyGeomMgr.AddPoint( cursorPos ) )
  1912. {
  1913. if( !started )
  1914. {
  1915. started = true;
  1916. m_controls->SetAutoPan( true );
  1917. m_controls->CaptureCursor( true );
  1918. if( !m_view->IsLayerVisible( params.m_layer ) )
  1919. {
  1920. m_frame->GetAppearancePanel()->SetLayerVisible( params.m_layer, true );
  1921. m_frame->GetCanvas()->Refresh();
  1922. }
  1923. }
  1924. }
  1925. }
  1926. else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
  1927. {
  1928. polyGeomMgr.DeleteLastCorner();
  1929. if( !polyGeomMgr.IsPolygonInProgress() )
  1930. {
  1931. // report finished as an empty shape
  1932. polyGeomMgr.SetFinished();
  1933. // start again
  1934. started = false;
  1935. m_controls->SetAutoPan( false );
  1936. m_controls->CaptureCursor( false );
  1937. }
  1938. }
  1939. else if( polyGeomMgr.IsPolygonInProgress()
  1940. && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
  1941. {
  1942. polyGeomMgr.SetCursorPosition( cursorPos );
  1943. if( polyGeomMgr.IsSelfIntersecting( true ) )
  1944. {
  1945. VECTOR2I p = wxGetMousePosition() + wxPoint( 20, 20 );
  1946. status.Move( p );
  1947. status.PopupFor( 1500 );
  1948. }
  1949. else
  1950. {
  1951. status.Hide();
  1952. }
  1953. }
  1954. else if( evt->IsAction( &PCB_ACTIONS::properties ) )
  1955. {
  1956. if( started )
  1957. {
  1958. frame()->OnEditItemRequest( zoneTool.GetZone() );
  1959. zoneTool.OnGeometryChange( polyGeomMgr );
  1960. frame()->SetMsgPanel( zoneTool.GetZone() );
  1961. }
  1962. else
  1963. {
  1964. evt->SetPassEvent();
  1965. }
  1966. }
  1967. /*else if( evt->IsAction( &ACTIONS::updateUnits ) )
  1968. {
  1969. // If we ever have an assistant here that reports dimensions, we'll want to
  1970. // update its units here....
  1971. // zoneAsst.SetUnits( frame()->GetUserUnits() );
  1972. // m_view->Update( &zoneAsst );
  1973. evt->SetPassEvent();
  1974. }*/
  1975. else
  1976. {
  1977. evt->SetPassEvent();
  1978. }
  1979. } // end while
  1980. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  1981. m_controls->ForceCursorPosition( false );
  1982. controls()->SetAutoPan( false );
  1983. m_controls->CaptureCursor( false );
  1984. return 0;
  1985. }
  1986. int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
  1987. {
  1988. if( m_isFootprintEditor )
  1989. return 0;
  1990. struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
  1991. {
  1992. PCB_BASE_EDIT_FRAME* m_frame;
  1993. PCB_GRID_HELPER m_gridHelper;
  1994. std::shared_ptr<DRC_ENGINE> m_drcEngine;
  1995. int m_drcEpsilon;
  1996. int m_worstClearance;
  1997. bool m_allowDRCViolations;
  1998. VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) :
  1999. m_frame( aFrame ),
  2000. m_gridHelper( aFrame->GetToolManager(), aFrame->GetMagneticItemsSettings() ),
  2001. m_drcEngine( aFrame->GetBoard()->GetDesignSettings().m_DRCEngine ),
  2002. m_drcEpsilon( aFrame->GetBoard()->GetDesignSettings().GetDRCEpsilon() ),
  2003. m_worstClearance( 0 )
  2004. {
  2005. ROUTER_TOOL* router = m_frame->GetToolManager()->GetTool<ROUTER_TOOL>();
  2006. m_allowDRCViolations = router->Router()->Settings().AllowDRCViolations();
  2007. try
  2008. {
  2009. m_drcEngine->InitEngine( aFrame->GetDesignRulesPath() );
  2010. DRC_CONSTRAINT constraint;
  2011. if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, constraint ) )
  2012. m_worstClearance = constraint.GetValue().Min();
  2013. if( m_drcEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, constraint ) )
  2014. m_worstClearance = std::max( m_worstClearance, constraint.GetValue().Min() );
  2015. for( FOOTPRINT* footprint : aFrame->GetBoard()->Footprints() )
  2016. {
  2017. for( PAD* pad : footprint->Pads() )
  2018. m_worstClearance = std::max( m_worstClearance, pad->GetLocalClearance() );
  2019. }
  2020. }
  2021. catch( PARSE_ERROR& )
  2022. {
  2023. }
  2024. }
  2025. virtual ~VIA_PLACER()
  2026. {
  2027. }
  2028. PCB_TRACK* findTrack( PCB_VIA* aVia )
  2029. {
  2030. const LSET lset = aVia->GetLayerSet();
  2031. VECTOR2I position = aVia->GetPosition();
  2032. BOX2I bbox = aVia->GetBoundingBox();
  2033. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
  2034. KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
  2035. std::vector<PCB_TRACK*> possible_tracks;
  2036. view->Query( bbox, items );
  2037. for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
  2038. {
  2039. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
  2040. if( !(item->GetLayerSet() & lset ).any() )
  2041. continue;
  2042. if( PCB_TRACK* track = dyn_cast<PCB_TRACK*>( item ) )
  2043. {
  2044. if( TestSegmentHit( position, track->GetStart(), track->GetEnd(),
  2045. ( track->GetWidth() + aVia->GetWidth() ) / 2 ) )
  2046. {
  2047. possible_tracks.push_back( track );
  2048. }
  2049. }
  2050. }
  2051. PCB_TRACK* return_track = nullptr;
  2052. int min_d = std::numeric_limits<int>::max();
  2053. for( PCB_TRACK* track : possible_tracks )
  2054. {
  2055. SEG test( track->GetStart(), track->GetEnd() );
  2056. int dist = ( test.NearestPoint( position ) - position ).EuclideanNorm();
  2057. if( dist < min_d )
  2058. {
  2059. min_d = dist;
  2060. return_track = track;
  2061. }
  2062. }
  2063. return return_track;
  2064. }
  2065. bool hasDRCViolation( PCB_VIA* aVia, BOARD_ITEM* aOther )
  2066. {
  2067. // It would really be better to know what particular nets a nettie should allow,
  2068. // but for now it is what it is.
  2069. if( DRC_ENGINE::IsNetTie( aOther ) )
  2070. return false;
  2071. DRC_CONSTRAINT constraint;
  2072. if( ( aOther->Type() == PCB_ZONE_T || aOther->Type() == PCB_FP_ZONE_T )
  2073. && static_cast<ZONE*>( aOther )->GetIsRuleArea() )
  2074. {
  2075. ZONE* zone = static_cast<ZONE*>( aOther );
  2076. if( zone->GetDoNotAllowVias() )
  2077. return true;
  2078. constraint = m_drcEngine->EvalRules( DISALLOW_CONSTRAINT, aVia, nullptr,
  2079. UNDEFINED_LAYER );
  2080. if( constraint.m_DisallowFlags && constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
  2081. return true;
  2082. return false;
  2083. }
  2084. BOARD_CONNECTED_ITEM* cItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aOther );
  2085. if( cItem && cItem->GetNetCode() == aVia->GetNetCode() )
  2086. return false;
  2087. int clearance;
  2088. for( PCB_LAYER_ID layer : aOther->GetLayerSet().Seq() )
  2089. {
  2090. if( !IsCopperLayer( layer ) )
  2091. continue;
  2092. constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aVia, aOther, layer );
  2093. clearance = constraint.GetValue().Min();
  2094. if( clearance >= 0 )
  2095. {
  2096. std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( layer );
  2097. std::shared_ptr<SHAPE> otherShape = aOther->GetEffectiveShape( layer );
  2098. if( viaShape->Collide( otherShape.get(), clearance - m_drcEpsilon ) )
  2099. return true;
  2100. }
  2101. }
  2102. std::unique_ptr<SHAPE_SEGMENT> holeShape;
  2103. if( aOther->Type() == PCB_VIA_T )
  2104. {
  2105. PCB_VIA* via = static_cast<PCB_VIA*>( aOther );
  2106. VECTOR2I pos = via->GetPosition();
  2107. holeShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
  2108. }
  2109. else if( aOther->Type() == PCB_PAD_T )
  2110. {
  2111. PAD* pad = static_cast<PAD*>( aOther );
  2112. if( pad->GetDrillSize().x )
  2113. holeShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
  2114. }
  2115. if( holeShape )
  2116. {
  2117. constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aVia, aOther,
  2118. UNDEFINED_LAYER );
  2119. clearance = constraint.GetValue().Min();
  2120. if( clearance >= 0 )
  2121. {
  2122. std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( UNDEFINED_LAYER );
  2123. if( viaShape->Collide( holeShape.get(), clearance - m_drcEpsilon ) )
  2124. return true;
  2125. }
  2126. }
  2127. return false;
  2128. }
  2129. bool checkDRCViolation( PCB_VIA* aVia )
  2130. {
  2131. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
  2132. std::set<BOARD_ITEM*> checkedItems;
  2133. BOX2I bbox = aVia->GetBoundingBox();
  2134. bbox.Inflate( m_worstClearance );
  2135. m_frame->GetCanvas()->GetView()->Query( bbox, items );
  2136. for( std::pair<KIGFX::VIEW_ITEM*, int> it : items )
  2137. {
  2138. BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( it.first );
  2139. if( !item )
  2140. continue;
  2141. if( ( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
  2142. && !static_cast<ZONE*>( item )->GetIsRuleArea() )
  2143. {
  2144. continue; // stitching vias bind to zones, so ignore them
  2145. }
  2146. else if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_GROUP_T )
  2147. {
  2148. continue; // check against children, but not against footprint itself
  2149. }
  2150. else if( item->Type() == PCB_FP_TEXT_T
  2151. && !static_cast<FP_TEXT*>( item )->IsVisible() )
  2152. {
  2153. continue; // ignore hidden items
  2154. }
  2155. else if( checkedItems.count( item ) )
  2156. {
  2157. continue; // already checked
  2158. }
  2159. if( hasDRCViolation( aVia, item ) )
  2160. return true;
  2161. checkedItems.insert( item );
  2162. }
  2163. return false;
  2164. }
  2165. PAD* findPad( PCB_VIA* aVia )
  2166. {
  2167. const VECTOR2I position = aVia->GetPosition();
  2168. const LSET lset = aVia->GetLayerSet();
  2169. for( FOOTPRINT* fp : m_board->Footprints() )
  2170. {
  2171. for( PAD* pad : fp->Pads() )
  2172. {
  2173. if( pad->HitTest( position ) && ( pad->GetLayerSet() & lset ).any() )
  2174. {
  2175. if( pad->GetNetCode() > 0 )
  2176. return pad;
  2177. }
  2178. }
  2179. }
  2180. return nullptr;
  2181. }
  2182. int findStitchedZoneNet( PCB_VIA* aVia )
  2183. {
  2184. const VECTOR2I position = aVia->GetPosition();
  2185. const LSET lset = aVia->GetLayerSet();
  2186. // first take the net of the active layer
  2187. if( lset.test( m_frame->GetActiveLayer() ) )
  2188. {
  2189. for( ZONE* z : m_board->Zones() )
  2190. {
  2191. if( z->IsOnLayer( m_frame->GetActiveLayer() ) )
  2192. {
  2193. if( z->HitTestFilledArea( m_frame->GetActiveLayer(), position ) )
  2194. return z->GetNetCode();
  2195. }
  2196. }
  2197. }
  2198. // none? take the topmost visible layer
  2199. for( PCB_LAYER_ID layer : LSET( m_board->GetVisibleLayers() & lset ).Seq() )
  2200. {
  2201. for( ZONE* z : m_board->Zones() )
  2202. {
  2203. if( z->IsOnLayer( m_frame->GetActiveLayer() ) )
  2204. {
  2205. if( z->HitTestFilledArea( layer, position ) )
  2206. return z->GetNetCode();
  2207. }
  2208. }
  2209. }
  2210. return -1;
  2211. }
  2212. void SnapItem( BOARD_ITEM *aItem ) override
  2213. {
  2214. m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
  2215. MAGNETIC_SETTINGS* settings = m_frame->GetMagneticItemsSettings();
  2216. PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
  2217. VECTOR2I position = via->GetPosition();
  2218. if( settings->tracks == MAGNETIC_OPTIONS::CAPTURE_ALWAYS && m_gridHelper.GetSnap() )
  2219. {
  2220. PCB_TRACK* track = findTrack( via );
  2221. if( track )
  2222. {
  2223. SEG trackSeg( track->GetStart(), track->GetEnd() );
  2224. VECTOR2I snap = m_gridHelper.AlignToSegment( position, trackSeg );
  2225. aItem->SetPosition( snap );
  2226. }
  2227. }
  2228. else if( settings->pads == MAGNETIC_OPTIONS::CAPTURE_ALWAYS && m_gridHelper.GetSnap() )
  2229. {
  2230. PAD* pad = findPad( via );
  2231. if( pad )
  2232. {
  2233. aItem->SetPosition( pad->GetPosition() );
  2234. }
  2235. }
  2236. }
  2237. bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
  2238. {
  2239. WX_INFOBAR* infobar = m_frame->GetInfoBar();
  2240. PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
  2241. VECTOR2I viaPos = via->GetPosition();
  2242. PCB_TRACK* track = findTrack( via );
  2243. PAD* pad = findPad( via );
  2244. if( track )
  2245. via->SetNetCode( track->GetNetCode() );
  2246. else if( pad )
  2247. via->SetNetCode( pad->GetNetCode() );
  2248. else
  2249. via->SetNetCode( findStitchedZoneNet( via ) );
  2250. if( !m_allowDRCViolations && checkDRCViolation( via ) )
  2251. {
  2252. m_frame->ShowInfoBarError( _( "Via location violates DRC." ), true,
  2253. WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION );
  2254. via->SetNetCode( 0 );
  2255. return false;
  2256. }
  2257. else
  2258. {
  2259. if( infobar->GetMessageType() == WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION )
  2260. infobar->Dismiss();
  2261. }
  2262. if( !track && !pad )
  2263. {
  2264. via->SetNetCode( findStitchedZoneNet( via ) );
  2265. via->SetIsFree();
  2266. }
  2267. aCommit.Add( via );
  2268. if( track )
  2269. {
  2270. VECTOR2I trackStart = track->GetStart();
  2271. VECTOR2I trackEnd = track->GetEnd();
  2272. SEG trackSeg( trackStart, trackEnd );
  2273. VECTOR2I joint1;
  2274. VECTOR2I joint2;
  2275. auto insertChevron =
  2276. [&]()
  2277. {
  2278. if( ( trackStart - joint1 ).SquaredEuclideanNorm()
  2279. > ( trackStart - joint2 ).SquaredEuclideanNorm() )
  2280. {
  2281. std::swap( joint1, joint2 );
  2282. }
  2283. aCommit.Modify( track );
  2284. track->SetStart( trackStart );
  2285. track->SetEnd( joint1 );
  2286. PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
  2287. const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
  2288. newTrack->SetStart( joint1 );
  2289. newTrack->SetEnd( viaPos );
  2290. aCommit.Add( newTrack );
  2291. newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
  2292. const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
  2293. newTrack->SetStart( viaPos );
  2294. newTrack->SetEnd( joint2 );
  2295. aCommit.Add( newTrack );
  2296. newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
  2297. const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
  2298. newTrack->SetStart( joint2 );
  2299. newTrack->SetEnd( trackEnd );
  2300. aCommit.Add( newTrack );
  2301. };
  2302. if( viaPos == trackStart || viaPos == trackEnd )
  2303. return true;
  2304. if( trackStart.x == trackEnd.x )
  2305. {
  2306. VECTOR2I splitPt = trackSeg.NearestPoint( viaPos );
  2307. if( splitPt.x != viaPos.x
  2308. && abs( splitPt.x - viaPos.x ) < abs( splitPt.y - trackStart.y )
  2309. && abs( splitPt.x - viaPos.x ) < abs( splitPt.y - trackEnd.y ) )
  2310. {
  2311. int offset = abs( splitPt.x - viaPos.x );
  2312. joint1 = VECTOR2I( splitPt.x, splitPt.y - offset );
  2313. joint2 = VECTOR2I( splitPt.x, splitPt.y + offset );
  2314. insertChevron();
  2315. return true;
  2316. }
  2317. }
  2318. else if( trackStart.y == trackEnd.y )
  2319. {
  2320. VECTOR2I splitPt = trackSeg.NearestPoint( viaPos );
  2321. if( splitPt.y != viaPos.y
  2322. && abs( trackStart.y - viaPos.y ) < abs( trackStart.x - viaPos.x )
  2323. && abs( trackEnd.y - viaPos.y ) < abs( trackEnd.x - viaPos.x ) )
  2324. {
  2325. int offset = abs( splitPt.y - viaPos.y );
  2326. joint1 = VECTOR2I( splitPt.x - offset, splitPt.y );
  2327. joint2 = VECTOR2I( splitPt.x + offset, splitPt.y );
  2328. insertChevron();
  2329. return true;
  2330. }
  2331. }
  2332. else if( abs( trackStart.y - trackEnd.y ) == abs( trackStart.x - trackEnd.x ) )
  2333. {
  2334. SEG horiz( VECTOR2I( -INT_MAX, viaPos.y ), VECTOR2I( INT_MAX, viaPos.y ) );
  2335. SEG vert( VECTOR2I( viaPos.x, -INT_MAX ), VECTOR2I( viaPos.x, INT_MAX ) );
  2336. if( track->GetBoundingBox().Contains( viaPos ) )
  2337. {
  2338. joint1 = trackSeg.Intersect( horiz, true, true ).get();
  2339. joint2 = trackSeg.Intersect( vert, true, true ).get();
  2340. insertChevron();
  2341. return true;
  2342. }
  2343. }
  2344. aCommit.Modify( track );
  2345. track->SetStart( trackStart );
  2346. track->SetEnd( viaPos );
  2347. PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
  2348. const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
  2349. newTrack->SetStart( viaPos );
  2350. newTrack->SetEnd( trackEnd );
  2351. aCommit.Add( newTrack );
  2352. }
  2353. return true;
  2354. }
  2355. std::unique_ptr<BOARD_ITEM> CreateItem() override
  2356. {
  2357. BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
  2358. PCB_VIA* via = new PCB_VIA( m_board );
  2359. via->SetNetCode( 0 );
  2360. via->SetViaType( bds.m_CurrentViaType );
  2361. // for microvias, the size and hole will be changed later.
  2362. via->SetWidth( bds.GetCurrentViaSize() );
  2363. via->SetDrill( bds.GetCurrentViaDrill() );
  2364. // Usual via is from copper to component.
  2365. // layer pair is B_Cu and F_Cu.
  2366. via->SetLayerPair( B_Cu, F_Cu );
  2367. PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
  2368. PCB_LAYER_ID last_layer;
  2369. // prepare switch to new active layer:
  2370. if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
  2371. last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
  2372. else
  2373. last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
  2374. // Adjust the actual via layer pair
  2375. switch( via->GetViaType() )
  2376. {
  2377. case VIATYPE::BLIND_BURIED:
  2378. via->SetLayerPair( first_layer, last_layer );
  2379. break;
  2380. case VIATYPE::MICROVIA: // from external to the near neighbor inner layer
  2381. {
  2382. PCB_LAYER_ID last_inner_layer =
  2383. ToLAYER_ID( ( m_board->GetCopperLayerCount() - 2 ) );
  2384. if( first_layer == B_Cu )
  2385. last_layer = last_inner_layer;
  2386. else if( first_layer == F_Cu )
  2387. last_layer = In1_Cu;
  2388. else if( first_layer == last_inner_layer )
  2389. last_layer = B_Cu;
  2390. else if( first_layer == In1_Cu )
  2391. last_layer = F_Cu;
  2392. // else error: will be removed later
  2393. via->SetLayerPair( first_layer, last_layer );
  2394. // Update diameter and hole size, which where set previously for normal vias
  2395. NETCLASS* netClass = via->GetNetClass();
  2396. via->SetWidth( netClass->GetuViaDiameter() );
  2397. via->SetDrill( netClass->GetuViaDrill() );
  2398. }
  2399. break;
  2400. default:
  2401. break;
  2402. }
  2403. return std::unique_ptr<BOARD_ITEM>( via );
  2404. }
  2405. };
  2406. VIA_PLACER placer( frame() );
  2407. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
  2408. doInteractiveItemPlacement( aEvent.GetCommandStr().get(), &placer, _( "Place via" ),
  2409. IPO_REPEAT | IPO_SINGLE_CLICK );
  2410. return 0;
  2411. }
  2412. int DRAWING_TOOL::getSegmentWidth( PCB_LAYER_ID aLayer ) const
  2413. {
  2414. assert( m_board );
  2415. return m_board->GetDesignSettings().GetLineThickness( aLayer );
  2416. }
  2417. const unsigned int DRAWING_TOOL::WIDTH_STEP = Millimeter2iu( 0.1 );
  2418. void DRAWING_TOOL::setTransitions()
  2419. {
  2420. Go( &DRAWING_TOOL::PlaceStackup, PCB_ACTIONS::placeStackup.MakeEvent() );
  2421. Go( &DRAWING_TOOL::PlaceCharacteristics, PCB_ACTIONS::placeCharacteristics.MakeEvent() );
  2422. Go( &DRAWING_TOOL::DrawLine, PCB_ACTIONS::drawLine.MakeEvent() );
  2423. Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawPolygon.MakeEvent() );
  2424. Go( &DRAWING_TOOL::DrawRectangle, PCB_ACTIONS::drawRectangle.MakeEvent() );
  2425. Go( &DRAWING_TOOL::DrawCircle, PCB_ACTIONS::drawCircle.MakeEvent() );
  2426. Go( &DRAWING_TOOL::DrawArc, PCB_ACTIONS::drawArc.MakeEvent() );
  2427. Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawAlignedDimension.MakeEvent() );
  2428. Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawOrthogonalDimension.MakeEvent() );
  2429. Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawCenterDimension.MakeEvent() );
  2430. Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawRadialDimension.MakeEvent() );
  2431. Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawLeader.MakeEvent() );
  2432. Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawZone.MakeEvent() );
  2433. Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawRuleArea.MakeEvent() );
  2434. Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawZoneCutout.MakeEvent() );
  2435. Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawSimilarZone.MakeEvent() );
  2436. Go( &DRAWING_TOOL::DrawVia, PCB_ACTIONS::drawVia.MakeEvent() );
  2437. Go( &DRAWING_TOOL::PlaceText, PCB_ACTIONS::placeText.MakeEvent() );
  2438. Go( &DRAWING_TOOL::DrawRectangle, PCB_ACTIONS::drawTextBox.MakeEvent() );
  2439. Go( &DRAWING_TOOL::PlaceImportedGraphics, PCB_ACTIONS::placeImportedGraphics.MakeEvent() );
  2440. Go( &DRAWING_TOOL::SetAnchor, PCB_ACTIONS::setAnchor.MakeEvent() );
  2441. Go( &DRAWING_TOOL::ToggleHV45Mode, PCB_ACTIONS::toggleHV45Mode.MakeEvent() );
  2442. }