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.

3433 lines
115 KiB

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