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.

1795 lines
58 KiB

12 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 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 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 "pcb_actions.h"
  27. #include <pcb_edit_frame.h>
  28. #include <class_draw_panel_gal.h>
  29. #include <project.h>
  30. #include <id.h>
  31. #include <pcbnew_id.h>
  32. #include <confirm.h>
  33. #include <import_dxf/dialog_dxf_import.h>
  34. #include <view/view_group.h>
  35. #include <view/view_controls.h>
  36. #include <view/view.h>
  37. #include <gal/graphics_abstraction_layer.h>
  38. #include <tool/tool_manager.h>
  39. #include <geometry/direction45.h>
  40. #include <geometry/geometry_utils.h>
  41. #include <ratsnest_data.h>
  42. #include <board_commit.h>
  43. #include <scoped_set_reset.h>
  44. #include <bitmaps.h>
  45. #include <hotkeys.h>
  46. #include <painter.h>
  47. #include <status_popup.h>
  48. #include "grid_helper.h"
  49. #include <dialogs/dialog_text_properties.h>
  50. #include <preview_items/arc_assistant.h>
  51. #include <class_board.h>
  52. #include <class_edge_mod.h>
  53. #include <class_pcb_text.h>
  54. #include <class_dimension.h>
  55. #include <class_zone.h>
  56. #include <class_module.h>
  57. #include <tools/selection_tool.h>
  58. #include <tools/tool_event_utils.h>
  59. #include <tools/zone_create_helper.h>
  60. using SCOPED_DRAW_MODE = SCOPED_SET_RESET<DRAWING_TOOL::MODE>;
  61. // Drawing tool actions
  62. TOOL_ACTION PCB_ACTIONS::drawLine( "pcbnew.InteractiveDrawing.line",
  63. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_LINE ),
  64. _( "Draw Line" ), _( "Draw a line" ), NULL, AF_ACTIVATE );
  65. TOOL_ACTION PCB_ACTIONS::drawGraphicPolygon( "pcbnew.InteractiveDrawing.graphicPolygon",
  66. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_POLYGON ),
  67. _( "Draw Graphic Polygon" ), _( "Draw a graphic polygon" ), NULL, AF_ACTIVATE );
  68. TOOL_ACTION PCB_ACTIONS::drawCircle( "pcbnew.InteractiveDrawing.circle",
  69. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_CIRCLE ),
  70. _( "Draw Circle" ), _( "Draw a circle" ), NULL, AF_ACTIVATE );
  71. TOOL_ACTION PCB_ACTIONS::drawArc( "pcbnew.InteractiveDrawing.arc",
  72. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_ARC ),
  73. _( "Draw Arc" ), _( "Draw an arc" ), NULL, AF_ACTIVATE );
  74. TOOL_ACTION PCB_ACTIONS::placeText( "pcbnew.InteractiveDrawing.text",
  75. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_TEXT ),
  76. _( "Add Text" ), _( "Add a text" ), NULL, AF_ACTIVATE );
  77. TOOL_ACTION PCB_ACTIONS::drawDimension( "pcbnew.InteractiveDrawing.dimension",
  78. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_DIMENSION ),
  79. _( "Add Dimension" ), _( "Add a dimension" ), NULL, AF_ACTIVATE );
  80. TOOL_ACTION PCB_ACTIONS::drawZone( "pcbnew.InteractiveDrawing.zone",
  81. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_ZONE ),
  82. _( "Add Filled Zone" ), _( "Add a filled zone" ), NULL, AF_ACTIVATE );
  83. TOOL_ACTION PCB_ACTIONS::drawVia( "pcbnew.InteractiveDrawing.via",
  84. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_FREE_VIA ),
  85. _( "Add Vias" ), _( "Add free-standing vias" ), NULL, AF_ACTIVATE );
  86. TOOL_ACTION PCB_ACTIONS::drawZoneKeepout( "pcbnew.InteractiveDrawing.keepout",
  87. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_KEEPOUT ),
  88. _( "Add Keepout Area" ), _( "Add a keepout area" ), NULL, AF_ACTIVATE );
  89. TOOL_ACTION PCB_ACTIONS::drawZoneCutout( "pcbnew.InteractiveDrawing.zoneCutout",
  90. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_CUTOUT ),
  91. _( "Add a Zone Cutout" ), _( "Add a cutout area of an existing zone" ),
  92. add_zone_cutout_xpm, AF_ACTIVATE );
  93. TOOL_ACTION PCB_ACTIONS::drawSimilarZone( "pcbnew.InteractiveDrawing.similarZone",
  94. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_SIMILAR_ZONE ),
  95. _( "Add a Similar Zone" ), _( "Add a zone with the same settings as an existing zone" ),
  96. add_zone_xpm, AF_ACTIVATE );
  97. TOOL_ACTION PCB_ACTIONS::placeDXF( "pcbnew.InteractiveDrawing.placeDXF",
  98. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_DXF ),
  99. "Place DXF", "", NULL, AF_ACTIVATE );
  100. TOOL_ACTION PCB_ACTIONS::setAnchor( "pcbnew.InteractiveDrawing.setAnchor",
  101. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_ANCHOR ),
  102. _( "Place the Footprint Anchor" ), _( "Place the footprint anchor" ),
  103. NULL, AF_ACTIVATE );
  104. TOOL_ACTION PCB_ACTIONS::incWidth( "pcbnew.InteractiveDrawing.incWidth",
  105. AS_CONTEXT, TOOL_ACTION::LegacyHotKey( HK_INC_LINE_WIDTH ),
  106. _( "Increase Line Width" ), _( "Increase the line width" ) );
  107. TOOL_ACTION PCB_ACTIONS::decWidth( "pcbnew.InteractiveDrawing.decWidth",
  108. AS_CONTEXT, TOOL_ACTION::LegacyHotKey( HK_DEC_LINE_WIDTH ),
  109. _( "Decrease Line Width" ), _( "Decrease the line width" ) );
  110. TOOL_ACTION PCB_ACTIONS::arcPosture( "pcbnew.InteractiveDrawing.arcPosture",
  111. AS_CONTEXT, TOOL_ACTION::LegacyHotKey( HK_SWITCH_TRACK_POSTURE ),
  112. _( "Switch Arc Posture" ), _( "Switch the arc posture" ) );
  113. /*
  114. * Contextual actions
  115. */
  116. static TOOL_ACTION deleteLastPoint( "pcbnew.InteractiveDrawing.deleteLastPoint",
  117. AS_CONTEXT, WXK_BACK,
  118. _( "Delete Last Point" ), _( "Delete the last point added to the current item" ),
  119. undo_xpm );
  120. static TOOL_ACTION closeZoneOutline( "pcbnew.InteractiveDrawing.closeZoneOutline",
  121. AS_CONTEXT, 0,
  122. _( "Close Zone Outline" ), _( "Close the outline of a zone in progress" ),
  123. checked_ok_xpm );
  124. DRAWING_TOOL::DRAWING_TOOL() :
  125. PCB_TOOL( "pcbnew.InteractiveDrawing" ),
  126. m_view( nullptr ), m_controls( nullptr ),
  127. m_board( nullptr ), m_frame( nullptr ), m_mode( MODE::NONE ),
  128. m_lineWidth( 1 )
  129. {
  130. }
  131. DRAWING_TOOL::~DRAWING_TOOL()
  132. {
  133. }
  134. bool DRAWING_TOOL::Init()
  135. {
  136. auto activeToolFunctor = [ this ] ( const SELECTION& aSel ) {
  137. return m_mode != MODE::NONE;
  138. };
  139. // some interactive drawing tools can undo the last point
  140. auto canUndoPoint = [ this ] ( const SELECTION& aSel ) {
  141. return m_mode == MODE::ARC || m_mode == MODE::ZONE;
  142. };
  143. // functor for zone-only actions
  144. auto zoneActiveFunctor = [this ] ( const SELECTION& aSel ) {
  145. return m_mode == MODE::ZONE;
  146. };
  147. auto& ctxMenu = m_menu.GetMenu();
  148. // cancel current tool goes in main context menu at the top if present
  149. ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
  150. ctxMenu.AddSeparator( activeToolFunctor, 1 );
  151. // tool-specific actions
  152. ctxMenu.AddItem( closeZoneOutline, zoneActiveFunctor, 200 );
  153. ctxMenu.AddItem( deleteLastPoint, canUndoPoint, 200 );
  154. ctxMenu.AddSeparator( canUndoPoint, 500 );
  155. // Type-specific sub-menus will be added for us by other tools
  156. // For example, zone fill/unfill is provided by the PCB control tool
  157. // Finally, add the standard zoom/grid items
  158. m_menu.AddStandardSubMenus( *getEditFrame<PCB_BASE_FRAME>() );
  159. return true;
  160. }
  161. void DRAWING_TOOL::Reset( RESET_REASON aReason )
  162. {
  163. // Init variables used by every drawing tool
  164. m_view = getView();
  165. m_controls = getViewControls();
  166. m_board = getModel<BOARD>();
  167. m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
  168. }
  169. DRAWING_TOOL::MODE DRAWING_TOOL::GetDrawingMode() const
  170. {
  171. return m_mode;
  172. }
  173. int DRAWING_TOOL::DrawLine( const TOOL_EVENT& aEvent )
  174. {
  175. if( m_editModules && !m_frame->GetModel() )
  176. return 0;
  177. BOARD_ITEM_CONTAINER* parent = m_frame->GetModel();
  178. DRAWSEGMENT* line = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
  179. OPT<VECTOR2D> startingPoint;
  180. BOARD_COMMIT commit( m_frame );
  181. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
  182. m_frame->SetToolID( m_editModules ? ID_MODEDIT_LINE_TOOL : ID_PCB_ADD_LINE_BUTT,
  183. wxCURSOR_PENCIL, _( "Add graphic line" ) );
  184. m_lineWidth = getSegmentWidth( getDrawingLayer() );
  185. while( drawSegment( S_SEGMENT, line, startingPoint ) )
  186. {
  187. if( line )
  188. {
  189. commit.Add( line );
  190. commit.Push( _( "Draw a line segment" ) );
  191. startingPoint = VECTOR2D( line->GetEnd() );
  192. }
  193. else
  194. {
  195. startingPoint = NULLOPT;
  196. }
  197. line = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
  198. }
  199. m_frame->SetNoToolSelected();
  200. return 0;
  201. }
  202. int DRAWING_TOOL::DrawCircle( const TOOL_EVENT& aEvent )
  203. {
  204. if( m_editModules && !m_frame->GetModel() )
  205. return 0;
  206. BOARD_ITEM_CONTAINER* parent = m_frame->GetModel();
  207. DRAWSEGMENT* circle = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
  208. BOARD_COMMIT commit( m_frame );
  209. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::CIRCLE );
  210. m_frame->SetToolID( m_editModules ? ID_MODEDIT_CIRCLE_TOOL : ID_PCB_CIRCLE_BUTT,
  211. wxCURSOR_PENCIL, _( "Add graphic circle" ) );
  212. m_lineWidth = getSegmentWidth( getDrawingLayer() );
  213. while( drawSegment( S_CIRCLE, circle ) )
  214. {
  215. if( circle )
  216. {
  217. commit.Add( circle );
  218. commit.Push( _( "Draw a circle" ) );
  219. }
  220. circle = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
  221. }
  222. m_frame->SetNoToolSelected();
  223. return 0;
  224. }
  225. int DRAWING_TOOL::DrawArc( const TOOL_EVENT& aEvent )
  226. {
  227. if( m_editModules && !m_frame->GetModel() )
  228. return 0;
  229. BOARD_ITEM_CONTAINER* parent = m_frame->GetModel();
  230. DRAWSEGMENT* arc = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
  231. BOARD_COMMIT commit( m_frame );
  232. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
  233. m_frame->SetToolID( m_editModules ? ID_MODEDIT_ARC_TOOL : ID_PCB_ARC_BUTT,
  234. wxCURSOR_PENCIL, _( "Add graphic arc" ) );
  235. m_lineWidth = getSegmentWidth( getDrawingLayer() );
  236. while( drawArc( arc ) )
  237. {
  238. if( arc )
  239. {
  240. commit.Add( arc );
  241. commit.Push( _( "Draw an arc" ) );
  242. }
  243. arc = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
  244. }
  245. m_frame->SetNoToolSelected();
  246. return 0;
  247. }
  248. int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
  249. {
  250. if( m_editModules && !m_frame->GetModel() )
  251. return 0;
  252. BOARD_ITEM* text = NULL;
  253. const BOARD_DESIGN_SETTINGS& dsnSettings = m_frame->GetDesignSettings();
  254. SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  255. SELECTION& selection = selTool->GetSelection();
  256. BOARD_COMMIT commit( m_frame );
  257. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  258. m_controls->ShowCursor( true );
  259. m_controls->SetSnapping( true );
  260. // do not capture or auto-pan until we start placing some text
  261. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
  262. Activate();
  263. m_frame->SetToolID( m_editModules ? ID_MODEDIT_TEXT_TOOL : ID_PCB_ADD_TEXT_BUTT,
  264. wxCURSOR_PENCIL, _( "Add text" ) );
  265. bool reselect = false;
  266. // Main loop: keep receiving events
  267. while( OPT_TOOL_EVENT evt = Wait() )
  268. {
  269. VECTOR2I cursorPos = m_controls->GetCursorPosition();
  270. if( reselect && text )
  271. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, text );
  272. if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  273. {
  274. if( text )
  275. {
  276. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  277. // Delete the old text and have another try
  278. delete text;
  279. text = NULL;
  280. m_controls->SetAutoPan( false );
  281. m_controls->CaptureCursor( false );
  282. m_controls->ShowCursor( true );
  283. }
  284. else
  285. break;
  286. if( evt->IsActivate() ) // now finish unconditionally
  287. break;
  288. }
  289. else if( evt->IsClick( BUT_RIGHT ) )
  290. {
  291. m_menu.ShowContextMenu();
  292. }
  293. else if( evt->IsClick( BUT_LEFT ) )
  294. {
  295. if( !text )
  296. {
  297. PCB_LAYER_ID layer = m_frame->GetActiveLayer();
  298. // Init the new item attributes
  299. if( m_editModules )
  300. {
  301. TEXTE_MODULE* textMod = new TEXTE_MODULE( (MODULE*) m_frame->GetModel() );
  302. textMod->SetLayer( layer );
  303. textMod->SetTextSize( dsnSettings.GetTextSize( layer ) );
  304. textMod->SetThickness( dsnSettings.GetTextThickness( layer ) );
  305. textMod->SetItalic( dsnSettings.GetTextItalic( layer ) );
  306. textMod->SetKeepUpright( dsnSettings.GetTextUpright( layer ) );
  307. textMod->SetTextPos( wxPoint( cursorPos.x, cursorPos.y ) );
  308. DIALOG_TEXT_PROPERTIES textDialog( m_frame, textMod, NULL );
  309. bool placing;
  310. RunMainStack([&]() {
  311. placing = textDialog.ShowModal() && ( textMod->GetText().Length() > 0 );
  312. } );
  313. if( placing )
  314. text = textMod;
  315. else
  316. delete textMod;
  317. }
  318. else
  319. {
  320. TEXTE_PCB* textPcb = new TEXTE_PCB( m_frame->GetModel() );
  321. // TODO we have to set IS_NEW, otherwise InstallTextPCB.. creates an undo entry :| LEGACY_CLEANUP
  322. textPcb->SetFlags( IS_NEW );
  323. textPcb->SetLayer( layer );
  324. // Set the mirrored option for layers on the BACK side of the board
  325. if( IsBackLayer( layer ) )
  326. textPcb->SetMirrored( true );
  327. textPcb->SetTextSize( dsnSettings.GetTextSize( layer ) );
  328. textPcb->SetThickness( dsnSettings.GetTextThickness( layer ) );
  329. textPcb->SetItalic( dsnSettings.GetTextItalic( layer ) );
  330. textPcb->SetTextPos( wxPoint( cursorPos.x, cursorPos.y ) );
  331. RunMainStack([&]() {
  332. m_frame->InstallTextOptionsFrame( textPcb, NULL );
  333. } );
  334. if( textPcb->GetText().IsEmpty() )
  335. delete textPcb;
  336. else
  337. text = textPcb;
  338. }
  339. if( text == NULL )
  340. continue;
  341. m_controls->CaptureCursor( true );
  342. m_controls->SetAutoPan( true );
  343. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, text );
  344. }
  345. else
  346. {
  347. text->ClearFlags();
  348. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  349. commit.Add( text );
  350. commit.Push( _( "Place a text" ) );
  351. m_controls->CaptureCursor( false );
  352. m_controls->SetAutoPan( false );
  353. m_controls->ShowCursor( true );
  354. text = NULL;
  355. }
  356. }
  357. else if( text && evt->IsMotion() )
  358. {
  359. text->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
  360. selection.SetReferencePoint( cursorPos );
  361. m_view->Update( &selection );
  362. frame()->SetMsgPanel( text );
  363. }
  364. else if( text && evt->IsAction( &PCB_ACTIONS::properties ) )
  365. {
  366. // Calling 'Properties' action clears the selection, so we need to restore it
  367. reselect = true;
  368. }
  369. }
  370. frame()->SetMsgPanel( board() );
  371. frame()->SetNoToolSelected();
  372. return 0;
  373. }
  374. void DRAWING_TOOL::constrainDimension( DIMENSION* dimension )
  375. {
  376. const VECTOR2I lineVector{ dimension->GetEnd() - dimension->GetOrigin() };
  377. dimension->SetEnd( wxPoint(
  378. VECTOR2I( dimension->GetOrigin() ) + GetVectorSnapped45( lineVector ) ) );
  379. }
  380. int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
  381. {
  382. if( m_editModules && !m_frame->GetModel() )
  383. return 0;
  384. DIMENSION* dimension = NULL;
  385. BOARD_COMMIT commit( m_frame );
  386. GRID_HELPER grid( m_frame );
  387. // Add a VIEW_GROUP that serves as a preview for the new item
  388. SELECTION preview;
  389. m_view->Add( &preview );
  390. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  391. m_controls->ShowCursor( true );
  392. m_controls->SetSnapping( true );
  393. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
  394. Activate();
  395. m_frame->SetToolID( ID_PCB_DIMENSION_BUTT, wxCURSOR_PENCIL, _( "Add dimension" ) );
  396. m_lineWidth = getSegmentWidth( getDrawingLayer() );
  397. enum DIMENSION_STEPS
  398. {
  399. SET_ORIGIN = 0,
  400. SET_END,
  401. SET_HEIGHT,
  402. FINISHED
  403. };
  404. int step = SET_ORIGIN;
  405. // Main loop: keep receiving events
  406. while( OPT_TOOL_EVENT evt = Wait() )
  407. {
  408. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  409. grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
  410. m_controls->SetSnapping( !evt->Modifier( MD_ALT ) );
  411. VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), nullptr );
  412. if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  413. {
  414. m_controls->SetAutoPan( false );
  415. if( step != SET_ORIGIN ) // start from the beginning
  416. {
  417. preview.Clear();
  418. delete dimension;
  419. step = SET_ORIGIN;
  420. }
  421. else
  422. break;
  423. if( evt->IsActivate() ) // now finish unconditionally
  424. break;
  425. }
  426. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
  427. {
  428. m_lineWidth += WIDTH_STEP;
  429. dimension->SetWidth( m_lineWidth );
  430. m_view->Update( &preview );
  431. frame()->SetMsgPanel( dimension );
  432. }
  433. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
  434. {
  435. if( m_lineWidth > WIDTH_STEP )
  436. {
  437. m_lineWidth -= WIDTH_STEP;
  438. dimension->SetWidth( m_lineWidth );
  439. m_view->Update( &preview );
  440. frame()->SetMsgPanel( dimension );
  441. }
  442. }
  443. else if( evt->IsClick( BUT_RIGHT ) )
  444. {
  445. m_menu.ShowContextMenu();
  446. }
  447. else if( evt->IsClick( BUT_LEFT ) )
  448. {
  449. switch( step )
  450. {
  451. case SET_ORIGIN:
  452. {
  453. PCB_LAYER_ID layer = getDrawingLayer();
  454. const BOARD_DESIGN_SETTINGS& boardSettings = m_board->GetDesignSettings();
  455. if( layer == Edge_Cuts ) // dimensions are not allowed on EdgeCuts
  456. layer = Dwgs_User;
  457. // Init the new item attributes
  458. dimension = new DIMENSION( m_board );
  459. dimension->SetLayer( layer );
  460. dimension->SetOrigin( wxPoint( cursorPos.x, cursorPos.y ) );
  461. dimension->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
  462. dimension->Text().SetTextSize( boardSettings.GetTextSize( layer ) );
  463. dimension->Text().SetThickness( boardSettings.GetTextThickness( layer ) );
  464. dimension->Text().SetItalic( boardSettings.GetTextItalic( layer ) );
  465. dimension->SetWidth( boardSettings.GetLineThickness( layer ) );
  466. dimension->SetUnits( m_frame->GetUserUnits(), false );
  467. dimension->AdjustDimensionDetails();
  468. preview.Add( dimension );
  469. frame()->SetMsgPanel( dimension );
  470. m_controls->SetAutoPan( true );
  471. m_controls->CaptureCursor( true );
  472. }
  473. break;
  474. case SET_END:
  475. dimension->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
  476. if( !!evt->Modifier( MD_CTRL ) )
  477. constrainDimension( dimension );
  478. // Dimensions that have origin and end in the same spot are not valid
  479. if( dimension->GetOrigin() == dimension->GetEnd() )
  480. --step;
  481. break;
  482. case SET_HEIGHT:
  483. {
  484. if( wxPoint( cursorPos.x, cursorPos.y ) != dimension->GetPosition() )
  485. {
  486. assert( dimension->GetOrigin() != dimension->GetEnd() );
  487. assert( dimension->GetWidth() > 0 );
  488. preview.Remove( dimension );
  489. commit.Add( dimension );
  490. commit.Push( _( "Draw a dimension" ) );
  491. }
  492. }
  493. break;
  494. }
  495. if( ++step == FINISHED )
  496. {
  497. step = SET_ORIGIN;
  498. m_controls->SetAutoPan( false );
  499. m_controls->CaptureCursor( false );
  500. }
  501. }
  502. else if( evt->IsMotion() )
  503. {
  504. switch( step )
  505. {
  506. case SET_END:
  507. dimension->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
  508. if( !!evt->Modifier( MD_CTRL ) )
  509. constrainDimension( dimension );
  510. break;
  511. case SET_HEIGHT:
  512. {
  513. // Calculating the direction of travel perpendicular to the selected axis
  514. double angle = dimension->GetAngle() + ( M_PI / 2 );
  515. wxPoint pos( cursorPos.x, cursorPos.y );
  516. wxPoint delta( pos - dimension->m_featureLineDO );
  517. double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
  518. dimension->SetHeight( height );
  519. }
  520. break;
  521. }
  522. // Show a preview of the item
  523. m_view->Update( &preview );
  524. if( step )
  525. frame()->SetMsgPanel( dimension );
  526. else
  527. frame()->SetMsgPanel( board() );
  528. }
  529. }
  530. if( step != SET_ORIGIN )
  531. delete dimension;
  532. m_controls->SetAutoPan( false );
  533. m_view->Remove( &preview );
  534. frame()->SetMsgPanel( board() );
  535. m_frame->SetNoToolSelected();
  536. return 0;
  537. }
  538. int DRAWING_TOOL::DrawZone( const TOOL_EVENT& aEvent )
  539. {
  540. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ZONE );
  541. m_frame->SetToolID( ID_PCB_ZONES_BUTT, wxCURSOR_PENCIL, _( "Add zones" ) );
  542. return drawZone( false, ZONE_MODE::ADD );
  543. }
  544. int DRAWING_TOOL::DrawZoneKeepout( const TOOL_EVENT& aEvent )
  545. {
  546. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::KEEPOUT );
  547. m_frame->SetToolID( ID_PCB_KEEPOUT_AREA_BUTT, wxCURSOR_PENCIL, _( "Add keepout" ) );
  548. return drawZone( true, ZONE_MODE::ADD );
  549. }
  550. int DRAWING_TOOL::DrawZoneCutout( const TOOL_EVENT& aEvent )
  551. {
  552. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ZONE );
  553. m_frame->SetToolID( ID_PCB_ZONES_BUTT, wxCURSOR_PENCIL, _( "Add zone cutout" ) );
  554. return drawZone( false, ZONE_MODE::CUTOUT );
  555. }
  556. int DRAWING_TOOL::DrawGraphicPolygon( const TOOL_EVENT& aEvent )
  557. {
  558. if( m_editModules && !m_frame->GetModel() )
  559. return 0;
  560. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::GRAPHIC_POLYGON );
  561. m_frame->SetToolID( m_editModules ? ID_MODEDIT_POLYGON_TOOL : ID_PCB_ADD_POLYGON_BUTT,
  562. wxCURSOR_PENCIL, _( "Add graphic polygon" ) );
  563. return drawZone( false, ZONE_MODE::GRAPHIC_POLYGON );
  564. }
  565. int DRAWING_TOOL::DrawSimilarZone( const TOOL_EVENT& aEvent )
  566. {
  567. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ZONE );
  568. m_frame->SetToolID( ID_PCB_ZONES_BUTT, wxCURSOR_PENCIL, _( "Add similar zone" ) );
  569. return drawZone( false, ZONE_MODE::SIMILAR );
  570. }
  571. int DRAWING_TOOL::PlaceDXF( const TOOL_EVENT& aEvent )
  572. {
  573. if( !m_frame->GetModel() )
  574. return 0;
  575. DIALOG_DXF_IMPORT dlg( m_frame );
  576. int dlgResult = dlg.ShowModal();
  577. const std::list<BOARD_ITEM*>& list = dlg.GetImportedItems();
  578. if( dlgResult != wxID_OK || list.empty() )
  579. return 0;
  580. VECTOR2I cursorPos = m_controls->GetCursorPosition();
  581. VECTOR2I delta = cursorPos - list.front()->GetPosition();
  582. // Add a VIEW_GROUP that serves as a preview for the new item
  583. SELECTION preview;
  584. BOARD_COMMIT commit( m_frame );
  585. // Build the undo list & add items to the current view
  586. for( auto item : list )
  587. {
  588. assert( item->Type() == PCB_LINE_T || item->Type() == PCB_TEXT_T );
  589. preview.Add( item );
  590. }
  591. BOARD_ITEM* firstItem = static_cast<BOARD_ITEM*>( preview.Front() );
  592. m_view->Add( &preview );
  593. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  594. m_controls->ShowCursor( true );
  595. m_controls->SetSnapping( true );
  596. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
  597. // Now move the new items to the current cursor position:
  598. cursorPos = m_controls->GetCursorPosition();
  599. delta = cursorPos - firstItem->GetPosition();
  600. for( auto item : preview )
  601. static_cast<BOARD_ITEM*>( item )->Move( wxPoint( delta.x, delta.y ) );
  602. m_view->Update( &preview );
  603. Activate();
  604. // Main loop: keep receiving events
  605. while( OPT_TOOL_EVENT evt = Wait() )
  606. {
  607. cursorPos = m_controls->GetCursorPosition();
  608. if( evt->IsMotion() )
  609. {
  610. delta = cursorPos - firstItem->GetPosition();
  611. for( auto item : preview )
  612. static_cast<BOARD_ITEM*>( item )->Move( wxPoint( delta.x, delta.y ) );
  613. m_view->Update( &preview );
  614. }
  615. else if( evt->Category() == TC_COMMAND )
  616. {
  617. // TODO it should be handled by EDIT_TOOL, so add items and select?
  618. if( TOOL_EVT_UTILS::IsRotateToolEvt( *evt ) )
  619. {
  620. const auto rotationPoint = wxPoint( cursorPos.x, cursorPos.y );
  621. const auto rotationAngle = TOOL_EVT_UTILS::GetEventRotationAngle(
  622. *m_frame, *evt );
  623. for( auto item : preview )
  624. {
  625. static_cast<BOARD_ITEM*>( item )->Rotate( rotationPoint, rotationAngle );
  626. }
  627. m_view->Update( &preview );
  628. }
  629. else if( evt->IsAction( &PCB_ACTIONS::flip ) )
  630. {
  631. for( auto item : preview )
  632. static_cast<BOARD_ITEM*>( item )->Flip( wxPoint( cursorPos.x, cursorPos.y ) );
  633. m_view->Update( &preview );
  634. }
  635. else if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  636. {
  637. preview.FreeItems();
  638. break;
  639. }
  640. }
  641. else if( evt->IsClick( BUT_RIGHT ) )
  642. {
  643. m_menu.ShowContextMenu();
  644. }
  645. else if( evt->IsClick( BUT_LEFT ) )
  646. {
  647. // Place the drawing
  648. BOARD_ITEM_CONTAINER* parent = m_frame->GetModel();
  649. for( auto item : preview )
  650. {
  651. if( m_editModules )
  652. {
  653. // Modules use different types for the same things,
  654. // so we need to convert imported items to appropriate classes.
  655. BOARD_ITEM* converted = NULL;
  656. switch( item->Type() )
  657. {
  658. case PCB_TEXT_T:
  659. {
  660. TEXTE_PCB* text = static_cast<TEXTE_PCB*>( item );
  661. TEXTE_MODULE* textMod = new TEXTE_MODULE( (MODULE*) parent );
  662. // Assignment operator also copies the item PCB_TEXT_T type,
  663. // so it cannot be added to a module which handles PCB_MODULE_TEXT_T
  664. textMod->SetText( text->GetText() );
  665. #if 0
  666. textMod->SetTextSize( text->GetTextSize() );
  667. textMod->SetThickness( text->GetThickness() );
  668. textMod->SetOrientation( text->GetTextAngle() );
  669. textMod->SetTextPos( text->GetTextPos() );
  670. textMod->SetTextSize( text->GetTextSize() );
  671. textMod->SetVisible( text->GetVisible() );
  672. textMod->SetMirrored( text->IsMirrored() );
  673. textMod->SetItalic( text->IsItalic() );
  674. textMod->SetBold( text->IsBold() );
  675. textMod->SetHorizJustify( text->GetHorizJustify() );
  676. textMod->SetVertJustify( text->GetVertJustify() );
  677. textMod->SetMultilineAllowed( text->IsMultilineAllowed() );
  678. #else
  679. textMod->EDA_TEXT::SetEffects( *text );
  680. textMod->SetLocalCoord(); // using changed SetTexPos() via SetEffects()
  681. #endif
  682. converted = textMod;
  683. break;
  684. }
  685. case PCB_LINE_T:
  686. {
  687. DRAWSEGMENT* seg = static_cast<DRAWSEGMENT*>( item );
  688. EDGE_MODULE* modSeg = new EDGE_MODULE( (MODULE*) parent );
  689. // Assignment operator also copies the item PCB_LINE_T type,
  690. // so it cannot be added to a module which handles PCB_MODULE_EDGE_T
  691. modSeg->SetWidth( seg->GetWidth() );
  692. modSeg->SetStart( seg->GetStart() );
  693. modSeg->SetEnd( seg->GetEnd() );
  694. modSeg->SetAngle( seg->GetAngle() );
  695. modSeg->SetShape( seg->GetShape() );
  696. modSeg->SetType( seg->GetType() );
  697. modSeg->SetBezControl1( seg->GetBezControl1() );
  698. modSeg->SetBezControl2( seg->GetBezControl2() );
  699. modSeg->SetBezierPoints( seg->GetBezierPoints() );
  700. modSeg->SetPolyShape( seg->GetPolyShape() );
  701. modSeg->SetLocalCoord();
  702. converted = modSeg;
  703. break;
  704. }
  705. default:
  706. assert( false );
  707. break;
  708. }
  709. if( converted )
  710. converted->SetLayer( static_cast<BOARD_ITEM*>( item )->GetLayer() );
  711. delete item;
  712. item = converted;
  713. }
  714. if( item )
  715. commit.Add( item );
  716. }
  717. commit.Push( _( "Place a DXF drawing" ) );
  718. break;
  719. }
  720. }
  721. preview.Clear();
  722. m_view->Remove( &preview );
  723. return 0;
  724. }
  725. int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent )
  726. {
  727. assert( m_editModules );
  728. if( !m_frame->GetModel() )
  729. return 0;
  730. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
  731. Activate();
  732. m_frame->SetToolID( ID_MODEDIT_ANCHOR_TOOL, wxCURSOR_PENCIL,
  733. _( "Place the footprint anchor" ) );
  734. m_controls->ShowCursor( true );
  735. m_controls->SetSnapping( true );
  736. m_controls->SetAutoPan( true );
  737. m_controls->CaptureCursor( false );
  738. while( OPT_TOOL_EVENT evt = Wait() )
  739. {
  740. if( evt->IsClick( BUT_LEFT ) )
  741. {
  742. MODULE* module = (MODULE*) m_frame->GetModel();
  743. BOARD_COMMIT commit( m_frame );
  744. commit.Modify( module );
  745. // set the new relative internal local coordinates of footprint items
  746. VECTOR2I cursorPos = m_controls->GetCursorPosition();
  747. wxPoint moveVector = module->GetPosition() - wxPoint( cursorPos.x, cursorPos.y );
  748. module->MoveAnchorPosition( moveVector );
  749. commit.Push( _( "Move the footprint reference anchor" ) );
  750. // Usually, we do not need to change twice the anchor position,
  751. // so deselect the active tool
  752. break;
  753. }
  754. else if( evt->IsClick( BUT_RIGHT ) )
  755. {
  756. m_menu.ShowContextMenu();
  757. }
  758. else if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  759. break;
  760. }
  761. m_frame->SetNoToolSelected();
  762. return 0;
  763. }
  764. bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic,
  765. OPT<VECTOR2D> aStartingPoint )
  766. {
  767. // Only two shapes are currently supported
  768. assert( aShape == S_SEGMENT || aShape == S_CIRCLE );
  769. DRAWSEGMENT line45;
  770. GRID_HELPER grid( m_frame );
  771. m_frame->SetActiveLayer( getDrawingLayer() );
  772. // Add a VIEW_GROUP that serves as a preview for the new item
  773. SELECTION preview;
  774. m_view->Add( &preview );
  775. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  776. m_controls->ShowCursor( true );
  777. Activate();
  778. bool direction45 = false; // 45 degrees only mode
  779. bool started = false;
  780. bool IsOCurseurSet = ( m_frame->GetScreen()->m_O_Curseur != wxPoint( 0, 0 ) );
  781. VECTOR2I cursorPos = m_controls->GetMousePosition();
  782. if( aStartingPoint )
  783. {
  784. // Init the new item attributes
  785. aGraphic->SetShape( (STROKE_T) aShape );
  786. aGraphic->SetWidth( m_lineWidth );
  787. aGraphic->SetLayer( getDrawingLayer() );
  788. aGraphic->SetStart( wxPoint( aStartingPoint->x, aStartingPoint->y ) );
  789. cursorPos = grid.BestSnapAnchor( cursorPos, aGraphic );
  790. aGraphic->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
  791. if( aShape == S_SEGMENT )
  792. line45 = *aGraphic; // used only for direction 45 mode with lines
  793. preview.Add( aGraphic );
  794. m_controls->SetAutoPan( true );
  795. m_controls->CaptureCursor( true );
  796. if( !IsOCurseurSet )
  797. m_frame->GetScreen()->m_O_Curseur = wxPoint( aStartingPoint->x, aStartingPoint->y );
  798. started = true;
  799. }
  800. // Main loop: keep receiving events
  801. while( OPT_TOOL_EVENT evt = Wait() )
  802. {
  803. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  804. grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
  805. m_controls->SetSnapping( !evt->Modifier( MD_ALT ) );
  806. cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), getDrawingLayer() );
  807. // 45 degree angle constraint enabled with an option and toggled with Ctrl
  808. const bool limit45 = ( frame()->Settings().m_use45DegreeGraphicSegments != !!( evt->Modifier( MD_CTRL ) ) );
  809. if( direction45 != limit45 && started && aShape == S_SEGMENT )
  810. {
  811. direction45 = limit45;
  812. if( direction45 )
  813. {
  814. preview.Add( &line45 );
  815. make45DegLine( aGraphic, &line45, cursorPos );
  816. }
  817. else
  818. {
  819. preview.Remove( &line45 );
  820. aGraphic->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
  821. }
  822. m_view->Update( &preview );
  823. frame()->SetMsgPanel( aGraphic );
  824. }
  825. if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  826. {
  827. preview.Clear();
  828. m_view->Update( &preview );
  829. delete aGraphic;
  830. aGraphic = NULL;
  831. if( !IsOCurseurSet )
  832. m_frame->GetScreen()->m_O_Curseur = wxPoint( 0, 0 );
  833. break;
  834. }
  835. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  836. {
  837. m_lineWidth = getSegmentWidth( getDrawingLayer() );
  838. aGraphic->SetLayer( getDrawingLayer() );
  839. aGraphic->SetWidth( m_lineWidth );
  840. m_view->Update( &preview );
  841. frame()->SetMsgPanel( aGraphic );
  842. }
  843. else if( evt->IsClick( BUT_RIGHT ) )
  844. {
  845. m_menu.ShowContextMenu();
  846. }
  847. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  848. {
  849. if( !started )
  850. {
  851. // Init the new item attributes
  852. aGraphic->SetShape( (STROKE_T) aShape );
  853. aGraphic->SetWidth( m_lineWidth );
  854. aGraphic->SetStart( wxPoint( cursorPos.x, cursorPos.y ) );
  855. aGraphic->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
  856. aGraphic->SetLayer( getDrawingLayer() );
  857. if( !IsOCurseurSet )
  858. m_frame->GetScreen()->m_O_Curseur = wxPoint( cursorPos.x, cursorPos.y );
  859. if( aShape == S_SEGMENT )
  860. line45 = *aGraphic; // used only for direction 45 mode with lines
  861. preview.Add( aGraphic );
  862. frame()->SetMsgPanel( aGraphic );
  863. m_controls->SetAutoPan( true );
  864. m_controls->CaptureCursor( true );
  865. started = true;
  866. }
  867. else
  868. {
  869. if( aGraphic->GetEnd() == aGraphic->GetStart()
  870. || ( evt->IsDblClick( BUT_LEFT ) && aShape == S_SEGMENT ) )
  871. // User has clicked twice in the same spot
  872. {
  873. // a clear sign that the current drawing is finished
  874. // Now we have to add the helper line as well, unless it is zero-length
  875. if( direction45 && line45.GetStart() != aGraphic->GetStart() )
  876. {
  877. BOARD_ITEM_CONTAINER* parent = m_frame->GetModel();
  878. DRAWSEGMENT* l = m_editModules ? new EDGE_MODULE( (MODULE*) parent )
  879. : new DRAWSEGMENT;
  880. // Copy coordinates, layer, etc.
  881. *static_cast<DRAWSEGMENT*>( l ) = line45;
  882. l->SetEnd( aGraphic->GetStart() );
  883. BOARD_COMMIT commit( m_frame );
  884. commit.Add( l );
  885. commit.Push( _( "Draw a line" ) );
  886. }
  887. delete aGraphic;
  888. aGraphic = NULL;
  889. }
  890. preview.Clear();
  891. break;
  892. }
  893. }
  894. else if( evt->IsMotion() )
  895. {
  896. // 45 degree lines
  897. if( direction45 && aShape == S_SEGMENT )
  898. make45DegLine( aGraphic, &line45, cursorPos );
  899. else
  900. aGraphic->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
  901. m_view->Update( &preview );
  902. if( started )
  903. frame()->SetMsgPanel( aGraphic );
  904. else
  905. frame()->SetMsgPanel( board() );
  906. }
  907. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
  908. {
  909. m_lineWidth += WIDTH_STEP;
  910. aGraphic->SetWidth( m_lineWidth );
  911. line45.SetWidth( m_lineWidth );
  912. m_view->Update( &preview );
  913. frame()->SetMsgPanel( aGraphic );
  914. }
  915. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && ( m_lineWidth > WIDTH_STEP ) )
  916. {
  917. m_lineWidth -= WIDTH_STEP;
  918. aGraphic->SetWidth( m_lineWidth );
  919. line45.SetWidth( m_lineWidth );
  920. m_view->Update( &preview );
  921. frame()->SetMsgPanel( aGraphic );
  922. }
  923. else if( evt->IsAction( &PCB_ACTIONS::resetCoords ) )
  924. {
  925. IsOCurseurSet = true;
  926. }
  927. }
  928. if( !IsOCurseurSet ) // reset the relative coordinte if it was not set before
  929. m_frame->GetScreen()->m_O_Curseur = wxPoint( 0, 0 );
  930. m_view->Remove( &preview );
  931. frame()->SetMsgPanel( board() );
  932. m_controls->SetAutoPan( false );
  933. m_controls->CaptureCursor( false );
  934. return started;
  935. }
  936. /**
  937. * Update an arc DRAWSEGMENT from the current state
  938. * of an Arc Geometry Manager
  939. */
  940. static void updateArcFromConstructionMgr( const KIGFX::PREVIEW::ARC_GEOM_MANAGER& aMgr,
  941. DRAWSEGMENT& aArc )
  942. {
  943. auto vec = aMgr.GetOrigin();
  944. aArc.SetCenter( { vec.x, vec.y } );
  945. vec = aMgr.GetStartRadiusEnd();
  946. aArc.SetArcStart( { vec.x, vec.y } );
  947. aArc.SetAngle( RAD2DECIDEG( -aMgr.GetSubtended() ) );
  948. }
  949. bool DRAWING_TOOL::drawArc( DRAWSEGMENT*& aGraphic )
  950. {
  951. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  952. // Arc geometric construction manager
  953. KIGFX::PREVIEW::ARC_GEOM_MANAGER arcManager;
  954. // Arc drawing assistant overlay
  955. KIGFX::PREVIEW::ARC_ASSISTANT arcAsst( arcManager, m_frame->GetUserUnits() );
  956. // Add a VIEW_GROUP that serves as a preview for the new item
  957. SELECTION preview;
  958. m_view->Add( &preview );
  959. m_view->Add( &arcAsst );
  960. GRID_HELPER grid( m_frame );
  961. m_controls->ShowCursor( true );
  962. m_controls->SetSnapping( true );
  963. Activate();
  964. bool firstPoint = false;
  965. // Main loop: keep receiving events
  966. while( OPT_TOOL_EVENT evt = Wait() )
  967. {
  968. PCB_LAYER_ID layer = getDrawingLayer();
  969. aGraphic->SetLayer( layer );
  970. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  971. grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
  972. m_controls->SetSnapping( !evt->Modifier( MD_ALT ) );
  973. VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), aGraphic );
  974. if( evt->IsClick( BUT_LEFT ) )
  975. {
  976. if( !firstPoint )
  977. {
  978. m_controls->SetAutoPan( true );
  979. m_controls->CaptureCursor( true );
  980. // Init the new item attributes
  981. // (non-geometric, those are handled by the manager)
  982. aGraphic->SetShape( S_ARC );
  983. aGraphic->SetWidth( m_lineWidth );
  984. preview.Add( aGraphic );
  985. firstPoint = true;
  986. }
  987. arcManager.AddPoint( cursorPos, true );
  988. }
  989. else if( evt->IsAction( &deleteLastPoint ) )
  990. {
  991. arcManager.RemoveLastPoint();
  992. }
  993. else if( evt->IsMotion() )
  994. {
  995. // set angle snap
  996. arcManager.SetAngleSnap( evt->Modifier( MD_CTRL ) );
  997. // update, but don't step the manager state
  998. arcManager.AddPoint( cursorPos, false );
  999. }
  1000. else if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  1001. {
  1002. preview.Clear();
  1003. delete aGraphic;
  1004. aGraphic = nullptr;
  1005. break;
  1006. }
  1007. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  1008. {
  1009. m_lineWidth = getSegmentWidth( getDrawingLayer() );
  1010. aGraphic->SetLayer( getDrawingLayer() );
  1011. aGraphic->SetWidth( m_lineWidth );
  1012. m_view->Update( &preview );
  1013. frame()->SetMsgPanel( aGraphic );
  1014. }
  1015. else if( evt->IsClick( BUT_RIGHT ) )
  1016. {
  1017. m_menu.ShowContextMenu();
  1018. }
  1019. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
  1020. {
  1021. m_lineWidth += WIDTH_STEP;
  1022. aGraphic->SetWidth( m_lineWidth );
  1023. m_view->Update( &preview );
  1024. frame()->SetMsgPanel( aGraphic );
  1025. }
  1026. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && m_lineWidth > WIDTH_STEP )
  1027. {
  1028. m_lineWidth -= WIDTH_STEP;
  1029. aGraphic->SetWidth( m_lineWidth );
  1030. m_view->Update( &preview );
  1031. frame()->SetMsgPanel( aGraphic );
  1032. }
  1033. else if( evt->IsAction( &PCB_ACTIONS::arcPosture ) )
  1034. {
  1035. arcManager.ToggleClockwise();
  1036. }
  1037. if( arcManager.IsComplete() )
  1038. {
  1039. break;
  1040. }
  1041. else if( arcManager.HasGeometryChanged() )
  1042. {
  1043. updateArcFromConstructionMgr( arcManager, *aGraphic );
  1044. m_view->Update( &preview );
  1045. m_view->Update( &arcAsst );
  1046. if(firstPoint)
  1047. frame()->SetMsgPanel( aGraphic );
  1048. else
  1049. frame()->SetMsgPanel( board() );
  1050. }
  1051. }
  1052. preview.Remove( aGraphic );
  1053. m_view->Remove( &arcAsst );
  1054. m_view->Remove( &preview );
  1055. frame()->SetMsgPanel( board() );
  1056. m_controls->SetAutoPan( false );
  1057. m_controls->CaptureCursor( false );
  1058. return !arcManager.IsReset();
  1059. }
  1060. bool DRAWING_TOOL::getSourceZoneForAction( ZONE_MODE aMode, ZONE_CONTAINER*& aZone )
  1061. {
  1062. bool clearSelection = false;
  1063. aZone = nullptr;
  1064. // not an action that needs a source zone
  1065. if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON )
  1066. return true;
  1067. SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  1068. const SELECTION& selection = selTool->GetSelection();
  1069. if( selection.Empty() )
  1070. {
  1071. clearSelection = true;
  1072. m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true );
  1073. }
  1074. // we want a single zone
  1075. if( selection.Size() == 1 )
  1076. aZone = dyn_cast<ZONE_CONTAINER*>( selection[0] );
  1077. // expected a zone, but didn't get one
  1078. if( !aZone )
  1079. {
  1080. if( clearSelection )
  1081. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1082. return false;
  1083. }
  1084. return true;
  1085. }
  1086. void DRAWING_TOOL::runPolygonEventLoop( POLYGON_GEOM_MANAGER& polyGeomMgr )
  1087. {
  1088. auto& controls = *getViewControls();
  1089. bool started = false;
  1090. GRID_HELPER grid( m_frame );
  1091. STATUS_TEXT_POPUP status( m_frame );
  1092. status.SetTextColor( wxColour( 255, 0, 0 ) );
  1093. status.SetText( _( "Self-intersecting polygons are not allowed" ) );
  1094. m_controls->SetSnapping( true );
  1095. while( OPT_TOOL_EVENT evt = Wait() )
  1096. {
  1097. LSET layers( m_frame->GetActiveLayer() );
  1098. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  1099. grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
  1100. m_controls->SetSnapping( !evt->Modifier( MD_ALT ) );
  1101. VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), layers );
  1102. if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  1103. {
  1104. // pre-empted by another tool, give up
  1105. // cancelled without an inprogress polygon, give up
  1106. if( !polyGeomMgr.IsPolygonInProgress() || evt->IsActivate() )
  1107. {
  1108. break;
  1109. }
  1110. polyGeomMgr.Reset();
  1111. // start again
  1112. started = false;
  1113. controls.SetAutoPan( false );
  1114. controls.CaptureCursor( false );
  1115. }
  1116. else if( evt->IsClick( BUT_RIGHT ) )
  1117. {
  1118. m_menu.ShowContextMenu();
  1119. }
  1120. // events that lock in nodes
  1121. else if( evt->IsClick( BUT_LEFT )
  1122. || evt->IsDblClick( BUT_LEFT )
  1123. || evt->IsAction( &closeZoneOutline ) )
  1124. {
  1125. // Check if it is double click / closing line (so we have to finish the zone)
  1126. const bool endPolygon = evt->IsDblClick( BUT_LEFT )
  1127. || evt->IsAction( &closeZoneOutline )
  1128. || polyGeomMgr.NewPointClosesOutline( cursorPos );
  1129. if( endPolygon )
  1130. {
  1131. polyGeomMgr.SetFinished();
  1132. polyGeomMgr.Reset();
  1133. // ready to start again
  1134. started = false;
  1135. controls.SetAutoPan( false );
  1136. controls.CaptureCursor( false );
  1137. }
  1138. // adding a corner
  1139. else if( polyGeomMgr.AddPoint( cursorPos ) )
  1140. {
  1141. if( !started )
  1142. {
  1143. started = true;
  1144. controls.SetAutoPan( true );
  1145. controls.CaptureCursor( true );
  1146. }
  1147. }
  1148. }
  1149. else if( evt->IsAction( &deleteLastPoint ) )
  1150. {
  1151. polyGeomMgr.DeleteLastCorner();
  1152. if( !polyGeomMgr.IsPolygonInProgress() )
  1153. {
  1154. // report finished as an empty shape
  1155. polyGeomMgr.SetFinished();
  1156. // start again
  1157. started = false;
  1158. controls.SetAutoPan( false );
  1159. controls.CaptureCursor( false );
  1160. }
  1161. }
  1162. else if( polyGeomMgr.IsPolygonInProgress()
  1163. && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
  1164. {
  1165. polyGeomMgr.SetCursorPosition( cursorPos, evt->Modifier( MD_CTRL )
  1166. ? POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45
  1167. : POLYGON_GEOM_MANAGER::LEADER_MODE::DIRECT );
  1168. if( polyGeomMgr.IsSelfIntersecting( true ) )
  1169. {
  1170. wxPoint p = wxGetMousePosition() + wxPoint( 20, 20 );
  1171. status.Move( p );
  1172. status.Popup( m_frame );
  1173. status.Expire( 1500 );
  1174. }
  1175. else
  1176. {
  1177. status.Hide();
  1178. }
  1179. }
  1180. } // end while
  1181. }
  1182. int DRAWING_TOOL::drawZone( bool aKeepout, ZONE_MODE aMode )
  1183. {
  1184. // get a source zone, if we need one. We need it for:
  1185. // ZONE_MODE::CUTOUT (adding a hole to the source zone)
  1186. // ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
  1187. ZONE_CONTAINER* sourceZone = nullptr;
  1188. if( !getSourceZoneForAction( aMode, sourceZone ) )
  1189. {
  1190. m_frame->SetNoToolSelected();
  1191. return 0;
  1192. }
  1193. ZONE_CREATE_HELPER::PARAMS params;
  1194. params.m_keepout = aKeepout;
  1195. params.m_mode = aMode;
  1196. params.m_sourceZone = sourceZone;
  1197. if( aMode == ZONE_MODE::GRAPHIC_POLYGON )
  1198. params.m_layer = getDrawingLayer();
  1199. else if( aMode == ZONE_MODE::SIMILAR )
  1200. params.m_layer = sourceZone->GetLayer();
  1201. else
  1202. params.m_layer = m_frame->GetActiveLayer();
  1203. ZONE_CREATE_HELPER zoneTool( *this, params );
  1204. // the geometry manager which handles the zone geometry, and
  1205. // hands the calculated points over to the zone creator tool
  1206. POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
  1207. Activate(); // register for events
  1208. auto& controls = *getViewControls();
  1209. controls.ShowCursor( true );
  1210. controls.SetSnapping( true );
  1211. runPolygonEventLoop( polyGeomMgr );
  1212. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1213. m_frame->SetNoToolSelected();
  1214. return 0;
  1215. }
  1216. void DRAWING_TOOL::make45DegLine( DRAWSEGMENT* aSegment, DRAWSEGMENT* aHelper,
  1217. VECTOR2I& aPos ) const
  1218. {
  1219. VECTOR2I origin( aSegment->GetStart() );
  1220. DIRECTION_45 direction( origin - aPos );
  1221. SHAPE_LINE_CHAIN newChain = direction.BuildInitialTrace( origin, aPos );
  1222. if( newChain.PointCount() > 2 )
  1223. {
  1224. aSegment->SetEnd( wxPoint( newChain.Point( -2 ).x, newChain.Point( -2 ).y ) );
  1225. aHelper->SetStart( wxPoint( newChain.Point( -2 ).x, newChain.Point( -2 ).y ) );
  1226. aHelper->SetEnd( wxPoint( newChain.Point( -1 ).x, newChain.Point( -1 ).y ) );
  1227. }
  1228. else
  1229. {
  1230. aSegment->SetEnd( wxPoint( aPos.x, aPos.y ) );
  1231. aHelper->SetStart( wxPoint( aPos.x, aPos.y ) );
  1232. aHelper->SetEnd( wxPoint( aPos.x, aPos.y ) );
  1233. }
  1234. }
  1235. int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
  1236. {
  1237. struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
  1238. {
  1239. GRID_HELPER m_gridHelper;
  1240. VIA_PLACER( PCB_EDIT_FRAME* aFrame ) : m_gridHelper( aFrame )
  1241. {}
  1242. TRACK* findTrack( VIA* aVia )
  1243. {
  1244. const LSET lset = aVia->GetLayerSet();
  1245. for( TRACK* track : m_board->Tracks() )
  1246. {
  1247. if( !(track->GetLayerSet() & lset ).any() )
  1248. continue;
  1249. if( TestSegmentHit( aVia->GetPosition(), track->GetStart(), track->GetEnd(),
  1250. ( track->GetWidth() + aVia->GetWidth() ) / 2 ) )
  1251. return track;
  1252. }
  1253. return nullptr;
  1254. }
  1255. int findStitchedZoneNet( VIA* aVia )
  1256. {
  1257. const auto pos = aVia->GetPosition();
  1258. const auto lset = aVia->GetLayerSet();
  1259. for( auto mod : m_board->Modules() )
  1260. {
  1261. for( D_PAD* pad : mod->Pads() )
  1262. {
  1263. if( pad->HitTest( pos ) && ( pad->GetLayerSet() & lset ).any() )
  1264. return -1;
  1265. }
  1266. }
  1267. std::vector<ZONE_CONTAINER*> foundZones;
  1268. for( auto zone : m_board->Zones() )
  1269. {
  1270. if( zone->HitTestFilledArea( pos ) )
  1271. {
  1272. foundZones.push_back( zone );
  1273. }
  1274. }
  1275. std::sort( foundZones.begin(), foundZones.end(),
  1276. [] ( const ZONE_CONTAINER* a, const ZONE_CONTAINER* b ) {
  1277. return a->GetLayer() < b->GetLayer();
  1278. } );
  1279. // first take the net of the active layer
  1280. for( auto z : foundZones )
  1281. {
  1282. if( m_frame->GetActiveLayer() == z->GetLayer() )
  1283. return z->GetNetCode();
  1284. }
  1285. // none? take the topmost visible layer
  1286. for( auto z : foundZones )
  1287. {
  1288. if( m_board->IsLayerVisible( z->GetLayer() ) )
  1289. return z->GetNetCode();
  1290. }
  1291. return -1;
  1292. }
  1293. void SnapItem( BOARD_ITEM *aItem ) override
  1294. {
  1295. // If you place a Via on a track but not on its centerline, the current
  1296. // connectivity algorithm will require us to put a kink in the track when
  1297. // we break it (so that each of the two segments ends on the via center).
  1298. // That's not ideal, and is in fact probably worse than forcing snap in
  1299. // this situation.
  1300. // bool do_snap = ( m_frame->Settings().m_magneticTracks == CAPTURE_CURSOR_IN_TRACK_TOOL
  1301. // || m_frame->Settings().m_magneticTracks == CAPTURE_ALWAYS );
  1302. m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
  1303. auto via = static_cast<VIA*>( aItem );
  1304. wxPoint pos = via->GetPosition();
  1305. TRACK* track = findTrack( via );
  1306. if( track )
  1307. {
  1308. SEG trackSeg( track->GetStart(), track->GetEnd() );
  1309. VECTOR2I snap = m_gridHelper.AlignToSegment( pos, trackSeg );
  1310. aItem->SetPosition( wxPoint( snap.x, snap.y ) );
  1311. }
  1312. }
  1313. void PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
  1314. {
  1315. auto via = static_cast<VIA*>( aItem );
  1316. int newNet;
  1317. TRACK* track = findTrack( via );
  1318. if( track )
  1319. {
  1320. aCommit.Modify( track );
  1321. TRACK* newTrack = dynamic_cast<TRACK*>( track->Clone() );
  1322. track->SetEnd( via->GetPosition() );
  1323. newTrack->SetStart( via->GetPosition() );
  1324. aCommit.Add( newTrack );
  1325. newNet = track->GetNetCode();
  1326. }
  1327. else
  1328. newNet = findStitchedZoneNet( via );
  1329. if( newNet > 0 )
  1330. via->SetNetCode( newNet );
  1331. aCommit.Add( aItem );
  1332. }
  1333. std::unique_ptr<BOARD_ITEM> CreateItem() override
  1334. {
  1335. auto& ds = m_board->GetDesignSettings();
  1336. VIA* via = new VIA( m_board );
  1337. via->SetNetCode( 0 );
  1338. via->SetViaType( ds.m_CurrentViaType );
  1339. // for microvias, the size and hole will be changed later.
  1340. via->SetWidth( ds.GetCurrentViaSize() );
  1341. via->SetDrill( ds.GetCurrentViaDrill() );
  1342. // Usual via is from copper to component.
  1343. // layer pair is B_Cu and F_Cu.
  1344. via->SetLayerPair( B_Cu, F_Cu );
  1345. PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
  1346. PCB_LAYER_ID last_layer;
  1347. // prepare switch to new active layer:
  1348. if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
  1349. last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
  1350. else
  1351. last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
  1352. // Adjust the actual via layer pair
  1353. switch( via->GetViaType() )
  1354. {
  1355. case VIA_BLIND_BURIED:
  1356. via->SetLayerPair( first_layer, last_layer );
  1357. break;
  1358. case VIA_MICROVIA: // from external to the near neighbor inner layer
  1359. {
  1360. PCB_LAYER_ID last_inner_layer =
  1361. ToLAYER_ID( ( m_board->GetCopperLayerCount() - 2 ) );
  1362. if( first_layer == B_Cu )
  1363. last_layer = last_inner_layer;
  1364. else if( first_layer == F_Cu )
  1365. last_layer = In1_Cu;
  1366. else if( first_layer == last_inner_layer )
  1367. last_layer = B_Cu;
  1368. else if( first_layer == In1_Cu )
  1369. last_layer = F_Cu;
  1370. // else error: will be removed later
  1371. via->SetLayerPair( first_layer, last_layer );
  1372. // Update diameter and hole size, which where set previously
  1373. // for normal vias
  1374. NETINFO_ITEM* net = via->GetNet();
  1375. if( net )
  1376. {
  1377. via->SetWidth( net->GetMicroViaSize() );
  1378. via->SetDrill( net->GetMicroViaDrillSize() );
  1379. }
  1380. }
  1381. break;
  1382. default:
  1383. break;
  1384. }
  1385. return std::unique_ptr<BOARD_ITEM>( via );
  1386. }
  1387. };
  1388. VIA_PLACER placer( frame() );
  1389. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
  1390. frame()->SetToolID( ID_PCB_DRAW_VIA_BUTT, wxCURSOR_PENCIL, _( "Add vias" ) );
  1391. doInteractiveItemPlacement( &placer, _( "Place via" ),
  1392. IPO_REPEAT | IPO_SINGLE_CLICK | IPO_ROTATE | IPO_FLIP );
  1393. frame()->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
  1394. return 0;
  1395. }
  1396. void DRAWING_TOOL::setTransitions()
  1397. {
  1398. Go( &DRAWING_TOOL::DrawLine, PCB_ACTIONS::drawLine.MakeEvent() );
  1399. Go( &DRAWING_TOOL::DrawGraphicPolygon, PCB_ACTIONS::drawGraphicPolygon.MakeEvent() );
  1400. Go( &DRAWING_TOOL::DrawCircle, PCB_ACTIONS::drawCircle.MakeEvent() );
  1401. Go( &DRAWING_TOOL::DrawArc, PCB_ACTIONS::drawArc.MakeEvent() );
  1402. Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawDimension.MakeEvent() );
  1403. Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawZone.MakeEvent() );
  1404. Go( &DRAWING_TOOL::DrawZoneKeepout, PCB_ACTIONS::drawZoneKeepout.MakeEvent() );
  1405. Go( &DRAWING_TOOL::DrawZoneCutout, PCB_ACTIONS::drawZoneCutout.MakeEvent() );
  1406. Go( &DRAWING_TOOL::DrawSimilarZone, PCB_ACTIONS::drawSimilarZone.MakeEvent() );
  1407. Go( &DRAWING_TOOL::DrawVia, PCB_ACTIONS::drawVia.MakeEvent() );
  1408. Go( &DRAWING_TOOL::PlaceText, PCB_ACTIONS::placeText.MakeEvent() );
  1409. Go( &DRAWING_TOOL::PlaceDXF, PCB_ACTIONS::placeDXF.MakeEvent() );
  1410. Go( &DRAWING_TOOL::SetAnchor, PCB_ACTIONS::setAnchor.MakeEvent() );
  1411. }
  1412. int DRAWING_TOOL::getSegmentWidth( PCB_LAYER_ID aLayer ) const
  1413. {
  1414. assert( m_board );
  1415. return m_board->GetDesignSettings().GetLineThickness( aLayer );
  1416. }
  1417. PCB_LAYER_ID DRAWING_TOOL::getDrawingLayer() const
  1418. {
  1419. PCB_LAYER_ID layer = m_frame->GetActiveLayer();
  1420. if( IsCopperLayer( layer ) )
  1421. {
  1422. if( layer == F_Cu )
  1423. layer = F_SilkS;
  1424. else if( layer == B_Cu )
  1425. layer = B_SilkS;
  1426. else
  1427. layer = Dwgs_User;
  1428. m_frame->SetActiveLayer( layer );
  1429. }
  1430. return layer;
  1431. }
  1432. const unsigned int DRAWING_TOOL::WIDTH_STEP = 100000;