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.

1417 lines
45 KiB

11 years ago
9 years ago
9 years ago
11 years ago
9 years ago
12 years ago
9 years ago
9 years ago
9 years ago
9 years ago
10 years ago
11 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2013-2017 CERN
  5. * @author Maciej Suminski <maciej.suminski@cern.ch>
  6. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  7. * Copyright (C) 2017 KiCad Developers, see CHANGELOG.TXT for contributors.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include <limits>
  27. #include <class_board.h>
  28. #include <class_module.h>
  29. #include <class_edge_mod.h>
  30. #include <class_zone.h>
  31. #include <collectors.h>
  32. #include <pcb_edit_frame.h>
  33. #include <kiway.h>
  34. #include <class_draw_panel_gal.h>
  35. #include <footprint_edit_frame.h>
  36. #include <array_creator.h>
  37. #include <pcbnew_id.h>
  38. #include <tool/tool_manager.h>
  39. #include <view/view_controls.h>
  40. #include <view/view.h>
  41. #include <gal/graphics_abstraction_layer.h>
  42. #include <connectivity_data.h>
  43. #include <confirm.h>
  44. #include <bitmaps.h>
  45. #include <hotkeys.h>
  46. #include <cassert>
  47. #include <functional>
  48. using namespace std::placeholders;
  49. #include "pcb_actions.h"
  50. #include "selection_tool.h"
  51. #include "edit_tool.h"
  52. #include "picker_tool.h"
  53. #include "grid_helper.h"
  54. #include "kicad_clipboard.h"
  55. #include "pcbnew_control.h"
  56. #include <router/router_tool.h>
  57. #include <dialogs/dialog_move_exact.h>
  58. #include <dialogs/dialog_track_via_properties.h>
  59. #include <dialogs/dialog_exchange_footprints.h>
  60. #include <tools/tool_event_utils.h>
  61. #include <preview_items/ruler_item.h>
  62. #include <board_commit.h>
  63. extern bool Magnetize( PCB_BASE_EDIT_FRAME* frame, int aCurrentTool,
  64. wxSize aGridSize, wxPoint on_grid, wxPoint* curpos );
  65. // Edit tool actions
  66. TOOL_ACTION PCB_ACTIONS::editFootprintInFpEditor( "pcbnew.InteractiveEdit.editFootprintInFpEditor",
  67. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_EDIT_MODULE_WITH_MODEDIT ),
  68. _( "Open in Footprint Editor" ),
  69. _( "Opens the selected footprint in the Footprint Editor" ),
  70. module_editor_xpm );
  71. TOOL_ACTION PCB_ACTIONS::copyPadToSettings( "pcbnew.InteractiveEdit.copyPadToSettings",
  72. AS_GLOBAL, 0,
  73. _( "Copy Pad Properties to Default Pad Properties" ),
  74. _( "Copies the properties of the selected pad to the default pad properties." ) );
  75. TOOL_ACTION PCB_ACTIONS::copySettingsToPads( "pcbnew.InteractiveEdit.copySettingsToPads",
  76. AS_GLOBAL, 0,
  77. _( "Copy Default Pad Properties to Pads" ),
  78. _( "Copies the default pad properties to the selected pad(s)." ) );
  79. TOOL_ACTION PCB_ACTIONS::globalEditPads( "pcbnew.InteractiveEdit.globalPadEdit",
  80. AS_GLOBAL, 0,
  81. _( "Push Pad Settings..." ),
  82. _( "Copies the selected pad's properties to all pads in its footprint (or similar footprints)." ),
  83. push_pad_settings_xpm );
  84. TOOL_ACTION PCB_ACTIONS::editActivate( "pcbnew.InteractiveEdit",
  85. AS_GLOBAL, 0,
  86. _( "Edit Activate" ), "", move_xpm, AF_ACTIVATE );
  87. TOOL_ACTION PCB_ACTIONS::move( "pcbnew.InteractiveEdit.move",
  88. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_MOVE_ITEM ),
  89. _( "Move" ), _( "Moves the selected item(s)" ), move_xpm, AF_ACTIVATE );
  90. TOOL_ACTION PCB_ACTIONS::duplicate( "pcbnew.InteractiveEdit.duplicate",
  91. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_DUPLICATE_ITEM ),
  92. _( "Duplicate" ), _( "Duplicates the selected item(s)" ), duplicate_xpm );
  93. TOOL_ACTION PCB_ACTIONS::duplicateIncrement( "pcbnew.InteractiveEdit.duplicateIncrementPads",
  94. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_DUPLICATE_ITEM_AND_INCREMENT ),
  95. _( "Duplicate" ), _( "Duplicates the selected item(s), incrementing pad numbers" ) );
  96. TOOL_ACTION PCB_ACTIONS::moveExact( "pcbnew.InteractiveEdit.moveExact",
  97. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_MOVE_ITEM_EXACT ),
  98. _( "Move Exactly..." ), _( "Moves the selected item(s) by an exact amount" ),
  99. move_exactly_xpm );
  100. TOOL_ACTION PCB_ACTIONS::createArray( "pcbnew.InteractiveEdit.createArray",
  101. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_CREATE_ARRAY ),
  102. _( "Create Array..." ), _( "Create array" ), array_xpm, AF_ACTIVATE );
  103. TOOL_ACTION PCB_ACTIONS::rotateCw( "pcbnew.InteractiveEdit.rotateCw",
  104. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ROTATE_ITEM_CLOCKWISE ),
  105. _( "Rotate Clockwise" ), _( "Rotates selected item(s) clockwise" ),
  106. rotate_cw_xpm, AF_NONE, (void*) -1 );
  107. TOOL_ACTION PCB_ACTIONS::rotateCcw( "pcbnew.InteractiveEdit.rotateCcw",
  108. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ROTATE_ITEM ),
  109. _( "Rotate Counterclockwise" ), _( "Rotates selected item(s) counterclockwise" ),
  110. rotate_ccw_xpm, AF_NONE, (void*) 1 );
  111. TOOL_ACTION PCB_ACTIONS::flip( "pcbnew.InteractiveEdit.flip",
  112. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_FLIP_ITEM ),
  113. _( "Flip" ), _( "Flips selected item(s)" ), swap_layer_xpm );
  114. TOOL_ACTION PCB_ACTIONS::mirror( "pcbnew.InteractiveEdit.mirror",
  115. AS_GLOBAL, 0,
  116. _( "Mirror" ), _( "Mirrors selected item" ), mirror_h_xpm );
  117. TOOL_ACTION PCB_ACTIONS::remove( "pcbnew.InteractiveEdit.remove",
  118. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_BACK_SPACE ),
  119. _( "Delete" ), _( "Deletes selected item(s)" ), delete_xpm,
  120. AF_NONE, (void*) REMOVE_FLAGS::NORMAL );
  121. TOOL_ACTION PCB_ACTIONS::removeAlt( "pcbnew.InteractiveEdit.removeAlt",
  122. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_DELETE ),
  123. _( "Delete (Alternative)" ), _( "Deletes selected item(s)" ), delete_xpm,
  124. AF_NONE, (void*) REMOVE_FLAGS::ALT );
  125. TOOL_ACTION PCB_ACTIONS::updateFootprints( "pcbnew.InteractiveEdit.updateFootprints",
  126. AS_GLOBAL, 0,
  127. _( "Update Footprint..." ), _( "Update the footprint from the library" ),
  128. reload_xpm );
  129. TOOL_ACTION PCB_ACTIONS::exchangeFootprints( "pcbnew.InteractiveEdit.ExchangeFootprints",
  130. AS_GLOBAL, 0,
  131. _( "Change Footprint..." ), _( "Assign a different footprint from the library" ),
  132. exchange_xpm );
  133. TOOL_ACTION PCB_ACTIONS::properties( "pcbnew.InteractiveEdit.properties",
  134. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_EDIT_ITEM ),
  135. _( "Properties..." ), _( "Displays item properties dialog" ), config_xpm );
  136. TOOL_ACTION PCB_ACTIONS::selectionModified( "pcbnew.InteractiveEdit.ModifiedSelection",
  137. AS_GLOBAL, 0,
  138. "", "", nullptr, AF_NOTIFY );
  139. TOOL_ACTION PCB_ACTIONS::measureTool( "pcbnew.InteractiveEdit.measureTool",
  140. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_MEASURE_TOOL ),
  141. _( "Measuring Tool" ), _( "Interactively measure distance between points" ),
  142. nullptr, AF_ACTIVATE );
  143. TOOL_ACTION PCB_ACTIONS::copyToClipboard( "pcbnew.InteractiveEdit.CopyToClipboard",
  144. AS_GLOBAL, 0, // do not define a hotkey and let TranslateLegacyId() handle the event
  145. _( "Copy" ), _( "Copy selected content to clipboard" ),
  146. copy_xpm );
  147. TOOL_ACTION PCB_ACTIONS::cutToClipboard( "pcbnew.InteractiveEdit.CutToClipboard",
  148. AS_GLOBAL, 0, // do not define a hotkey and let TranslateLegacyId() handle the event
  149. _( "Cut" ), _( "Cut selected content to clipboard" ),
  150. cut_xpm );
  151. static wxPoint getAnchorPoint( const SELECTION &selection, const MOVE_PARAMETERS &params )
  152. {
  153. wxPoint anchorPoint;
  154. if( params.origin == RELATIVE_TO_CURRENT_POSITION )
  155. {
  156. return wxPoint( 0, 0 );
  157. }
  158. // set default anchor
  159. VECTOR2I rp = selection.GetCenter();
  160. anchorPoint = wxPoint( rp.x, rp.y );
  161. // If the anchor is not ANCHOR_FROM_LIBRARY then the user applied an override.
  162. // Also run through this block if only one item is slected because it may be a module,
  163. // in which case we want something different than the center of the selection
  164. if( ( params.anchor != ANCHOR_FROM_LIBRARY ) || ( selection.GetSize() == 1 ) )
  165. {
  166. BOARD_ITEM* topLeftItem = static_cast<BOARD_ITEM*>( selection.GetTopLeftModule() );
  167. // no module found if the GetTopLeftModule() returns null
  168. if( topLeftItem != nullptr )
  169. {
  170. if( topLeftItem->Type() == PCB_MODULE_T )
  171. {
  172. // Cast to module to allow access to the pads
  173. MODULE* mod = static_cast<MODULE*>( topLeftItem );
  174. switch( params.anchor )
  175. {
  176. case ANCHOR_FROM_LIBRARY:
  177. anchorPoint = mod->GetPosition();
  178. break;
  179. case ANCHOR_TOP_LEFT_PAD:
  180. topLeftItem = mod->GetTopLeftPad();
  181. break;
  182. case ANCHOR_CENTER_FOOTPRINT:
  183. anchorPoint = mod->GetFootprintRect().GetCenter();
  184. break;
  185. }
  186. }
  187. if( topLeftItem->Type() == PCB_PAD_T )
  188. {
  189. if( static_cast<D_PAD*>( topLeftItem )->GetAttribute() == PAD_ATTRIB_SMD )
  190. {
  191. // Use the top left corner of SMD pads as an anchor instead of the center
  192. anchorPoint = topLeftItem->GetBoundingBox().GetPosition();
  193. }
  194. else
  195. {
  196. anchorPoint = topLeftItem->GetPosition();
  197. }
  198. }
  199. }
  200. else // no module found in the selection
  201. {
  202. // in a selection of non-modules
  203. if( params.anchor == ANCHOR_TOP_LEFT_PAD )
  204. {
  205. // approach the top left pad override for non-modules by using the position of
  206. // the topleft item as an anchor
  207. topLeftItem = static_cast<BOARD_ITEM*>( selection.GetTopLeftItem() );
  208. anchorPoint = topLeftItem->GetPosition();
  209. }
  210. }
  211. }
  212. return anchorPoint;
  213. }
  214. EDIT_TOOL::EDIT_TOOL() :
  215. PCB_TOOL( "pcbnew.InteractiveEdit" ), m_selectionTool( NULL ),
  216. m_dragging( false )
  217. {
  218. }
  219. void EDIT_TOOL::Reset( RESET_REASON aReason )
  220. {
  221. m_dragging = false;
  222. if( aReason != RUN )
  223. m_commit.reset( new BOARD_COMMIT( this ) );
  224. }
  225. bool EDIT_TOOL::Init()
  226. {
  227. // Find the selection tool, so they can cooperate
  228. m_selectionTool = static_cast<SELECTION_TOOL*>( m_toolMgr->FindTool( "pcbnew.InteractiveSelection" ) );
  229. if( !m_selectionTool )
  230. {
  231. DisplayError( NULL, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
  232. return false;
  233. }
  234. auto editingModuleCondition = [ this ] ( const SELECTION& aSelection ) {
  235. return m_editModules;
  236. };
  237. auto singleModuleCondition = SELECTION_CONDITIONS::OnlyType( PCB_MODULE_T )
  238. && SELECTION_CONDITIONS::Count( 1 );
  239. auto noActiveToolCondition = [ this ] ( const SELECTION& aSelection ) {
  240. return ( frame()->GetToolId() == ID_NO_TOOL_SELECTED );
  241. };
  242. // Add context menu entries that are displayed when selection tool is active
  243. CONDITIONAL_MENU& menu = m_selectionTool->GetToolMenu().GetMenu();
  244. menu.AddItem( PCB_ACTIONS::move, SELECTION_CONDITIONS::NotEmpty );
  245. menu.AddItem( PCB_ACTIONS::drag45Degree, SELECTION_CONDITIONS::OnlyTypes( GENERAL_COLLECTOR::Tracks ) );
  246. menu.AddItem( PCB_ACTIONS::dragFreeAngle, SELECTION_CONDITIONS::OnlyTypes( GENERAL_COLLECTOR::Tracks ) );
  247. menu.AddItem( PCB_ACTIONS::rotateCcw, SELECTION_CONDITIONS::NotEmpty );
  248. menu.AddItem( PCB_ACTIONS::rotateCw, SELECTION_CONDITIONS::NotEmpty );
  249. menu.AddItem( PCB_ACTIONS::flip, SELECTION_CONDITIONS::NotEmpty );
  250. menu.AddItem( PCB_ACTIONS::remove, SELECTION_CONDITIONS::NotEmpty );
  251. menu.AddItem( PCB_ACTIONS::properties, SELECTION_CONDITIONS::Count( 1 )
  252. || SELECTION_CONDITIONS::OnlyTypes( GENERAL_COLLECTOR::Tracks ) );
  253. menu.AddItem( PCB_ACTIONS::moveExact, SELECTION_CONDITIONS::NotEmpty );
  254. menu.AddItem( PCB_ACTIONS::positionRelative, SELECTION_CONDITIONS::NotEmpty );
  255. menu.AddItem( PCB_ACTIONS::duplicate, SELECTION_CONDITIONS::NotEmpty );
  256. menu.AddItem( PCB_ACTIONS::createArray, SELECTION_CONDITIONS::NotEmpty );
  257. menu.AddItem( PCB_ACTIONS::copyToClipboard, SELECTION_CONDITIONS::NotEmpty );
  258. menu.AddItem( PCB_ACTIONS::cutToClipboard, SELECTION_CONDITIONS::NotEmpty );
  259. // Selection tool handles the context menu for some other tools, such as the Picker.
  260. // Don't add things like Paste when another tool is active.
  261. menu.AddItem( PCB_ACTIONS::pasteFromClipboard, noActiveToolCondition );
  262. menu.AddSeparator( noActiveToolCondition );
  263. // Mirror only available in modedit
  264. menu.AddItem( PCB_ACTIONS::mirror, editingModuleCondition && SELECTION_CONDITIONS::NotEmpty );
  265. menu.AddItem( PCB_ACTIONS::createPadFromShapes, editingModuleCondition && SELECTION_CONDITIONS::NotEmpty );
  266. menu.AddItem( PCB_ACTIONS::explodePadToShapes, editingModuleCondition && SELECTION_CONDITIONS::NotEmpty );
  267. // Footprint actions
  268. menu.AddItem( PCB_ACTIONS::editFootprintInFpEditor, singleModuleCondition );
  269. menu.AddItem( PCB_ACTIONS::updateFootprints, singleModuleCondition );
  270. menu.AddItem( PCB_ACTIONS::exchangeFootprints, singleModuleCondition );
  271. return true;
  272. }
  273. bool EDIT_TOOL::invokeInlineRouter( int aDragMode )
  274. {
  275. auto theRouter = static_cast<ROUTER_TOOL*>( m_toolMgr->FindTool( "pcbnew.InteractiveRouter" ) );
  276. if( !theRouter )
  277. return false;
  278. // make sure we don't accidentally invoke inline routing mode while the router is already active!
  279. if( theRouter->IsToolActive() )
  280. return false;
  281. if( theRouter->CanInlineDrag() )
  282. {
  283. m_toolMgr->RunAction( PCB_ACTIONS::routerInlineDrag, true, aDragMode );
  284. return true;
  285. }
  286. return false;
  287. }
  288. bool EDIT_TOOL::isInteractiveDragEnabled() const
  289. {
  290. auto theRouter = static_cast<ROUTER_TOOL*>( m_toolMgr->FindTool( "pcbnew.InteractiveRouter" ) );
  291. return theRouter ? theRouter->Router()->Settings().InlineDragEnabled() : false;
  292. }
  293. int EDIT_TOOL::Drag( const TOOL_EVENT& aEvent )
  294. {
  295. int mode = PNS::DM_ANY;
  296. if( aEvent.IsAction( &PCB_ACTIONS::dragFreeAngle ) )
  297. mode |= PNS::DM_FREE_ANGLE;
  298. invokeInlineRouter( mode );
  299. return 0;
  300. }
  301. int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
  302. {
  303. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  304. PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
  305. VECTOR2I originalCursorPos = controls->GetCursorPosition();
  306. // Be sure that there is at least one item that we can modify. If nothing was selected before,
  307. // try looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection)
  308. auto& selection = m_selectionTool->RequestSelection( SELECTION_DEFAULT );
  309. if( selection.Empty() )
  310. return 0;
  311. bool unselect = selection.IsHover();
  312. if( m_dragging )
  313. return 0;
  314. Activate();
  315. m_dragging = false; // Are selected items being dragged?
  316. bool restore = false; // Should items' state be restored when finishing the tool?
  317. controls->ShowCursor( true );
  318. controls->SetSnapping( true );
  319. controls->SetAutoPan( true );
  320. // cumulative translation
  321. VECTOR2I totalMovement;
  322. GRID_HELPER grid( editFrame );
  323. OPT_TOOL_EVENT evt = aEvent;
  324. VECTOR2I prevPos;
  325. // Main loop: keep receiving events
  326. do
  327. {
  328. if( evt->IsAction( &PCB_ACTIONS::editActivate ) ||
  329. evt->IsAction( &PCB_ACTIONS::move ) ||
  330. evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
  331. {
  332. if( selection.Empty() )
  333. break;
  334. auto curr_item = static_cast<BOARD_ITEM*>( selection.Front() );
  335. if( m_dragging && evt->Category() == TC_MOUSE )
  336. {
  337. m_cursor = grid.BestSnapAnchor( evt->Position(), curr_item );
  338. controls->ForceCursorPosition( true, m_cursor );
  339. VECTOR2I movement( m_cursor - prevPos );
  340. selection.SetReferencePoint( m_cursor );
  341. totalMovement += movement;
  342. prevPos = m_cursor;
  343. // Drag items to the current cursor position
  344. for( auto item : selection )
  345. {
  346. // Don't double move footprint pads, fields, etc.
  347. if( item->GetParent() && item->GetParent()->IsSelected() )
  348. continue;
  349. static_cast<BOARD_ITEM*>( item )->Move( movement );
  350. }
  351. frame()->UpdateMsgPanel();
  352. }
  353. else if( !m_dragging ) // Prepare to start dragging
  354. {
  355. bool invokedRouter = false;
  356. if ( !evt->IsAction( &PCB_ACTIONS::move ) && isInteractiveDragEnabled() )
  357. invokedRouter = invokeInlineRouter( PNS::DM_ANY );
  358. if( !invokedRouter )
  359. {
  360. // deal with locked items (override lock or abort the operation)
  361. SELECTION_LOCK_FLAGS lockFlags = m_selectionTool->CheckLock();
  362. if( lockFlags == SELECTION_LOCKED )
  363. break;
  364. // Save items, so changes can be undone
  365. for( auto item : selection )
  366. {
  367. // Don't double move footprint pads, fields, etc.
  368. if( item->GetParent() && item->GetParent()->IsSelected() )
  369. continue;
  370. m_commit->Modify( item );
  371. }
  372. m_cursor = controls->GetCursorPosition();
  373. if ( selection.HasReferencePoint() )
  374. {
  375. // start moving with the reference point attached to the cursor
  376. grid.SetAuxAxes( false );
  377. auto delta = m_cursor - selection.GetReferencePoint();
  378. // Drag items to the current cursor position
  379. for( auto item : selection )
  380. {
  381. // Don't double move footprint pads, fields, etc.
  382. if( item->GetParent() && item->GetParent()->IsSelected() )
  383. continue;
  384. static_cast<BOARD_ITEM*>( item )->Move( delta );
  385. }
  386. selection.SetReferencePoint( m_cursor );
  387. }
  388. else if( selection.Size() == 1 )
  389. {
  390. // Set the current cursor position to the first dragged item origin, so the
  391. // movement vector could be computed later
  392. updateModificationPoint( selection );
  393. m_cursor = grid.BestDragOrigin( originalCursorPos, curr_item );
  394. grid.SetAuxAxes( true, m_cursor );
  395. }
  396. else
  397. {
  398. updateModificationPoint( selection );
  399. m_cursor = grid.Align( m_cursor );
  400. }
  401. controls->SetCursorPosition( m_cursor, false );
  402. prevPos = m_cursor;
  403. controls->SetAutoPan( true );
  404. m_dragging = true;
  405. }
  406. }
  407. m_toolMgr->RunAction( PCB_ACTIONS::selectionModified, false );
  408. }
  409. else if( evt->IsCancel() || evt->IsActivate() )
  410. {
  411. restore = true; // Cancelling the tool means that items have to be restored
  412. break; // Finish
  413. }
  414. else if( evt->Action() == TA_UNDO_REDO_PRE )
  415. {
  416. unselect = true;
  417. break;
  418. }
  419. // Dispatch TOOL_ACTIONs
  420. else if( evt->Category() == TC_COMMAND )
  421. {
  422. if( evt->IsAction( &PCB_ACTIONS::remove ) )
  423. {
  424. // exit the loop, as there is no further processing for removed items
  425. break;
  426. }
  427. else if( evt->IsAction( &PCB_ACTIONS::duplicate ) )
  428. {
  429. // On duplicate, stop moving this item
  430. // The duplicate tool should then select the new item and start
  431. // a new move procedure
  432. break;
  433. }
  434. else if( evt->IsAction( &PCB_ACTIONS::moveExact ) )
  435. {
  436. // Can't do this, because the selection will then contain
  437. // stale pointers and it will all go horribly wrong...
  438. //editFrame->RestoreCopyFromUndoList( dummy );
  439. //
  440. // So, instead, reset the position manually
  441. for( auto item : selection )
  442. {
  443. BOARD_ITEM* i = static_cast<BOARD_ITEM*>( item );
  444. auto delta = VECTOR2I( i->GetPosition() ) - totalMovement;
  445. i->SetPosition( wxPoint( delta.x, delta.y ) );
  446. // And what about flipping and rotation?
  447. // for now, they won't be undone, but maybe that is how
  448. // it should be, so you can flip and move exact in the
  449. // same action?
  450. }
  451. // This causes a double event, so we will get the dialogue
  452. // correctly, somehow - why does Rotate not?
  453. //MoveExact( aEvent );
  454. break; // exit the loop - we move exactly, so we have finished moving
  455. }
  456. }
  457. else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
  458. {
  459. break; // Finish
  460. }
  461. } while( ( evt = Wait() ) ); //Should be assignment not equality test
  462. controls->ForceCursorPosition( false );
  463. controls->ShowCursor( false );
  464. controls->SetSnapping( false );
  465. controls->SetAutoPan( false );
  466. m_dragging = false;
  467. // Discard reference point when selection is "dropped" onto the board (ie: not dragging anymore)
  468. selection.ClearReferencePoint();
  469. if( unselect || restore )
  470. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  471. if( restore )
  472. m_commit->Revert();
  473. else
  474. m_commit->Push( _( "Drag" ) );
  475. return 0;
  476. }
  477. bool EDIT_TOOL::changeTrackWidthOnClick( const SELECTION& selection )
  478. {
  479. if ( selection.Size() == 1 && frame()->Settings().m_editActionChangesTrackWidth )
  480. {
  481. auto item = static_cast<BOARD_ITEM *>( selection[0] );
  482. m_commit->Modify( item );
  483. if( auto via = dyn_cast<VIA*>( item ) )
  484. {
  485. int new_width, new_drill;
  486. if( via->GetViaType() == VIA_MICROVIA )
  487. {
  488. auto net = via->GetNet();
  489. new_width = net->GetMicroViaSize();
  490. new_drill = net->GetMicroViaDrillSize();
  491. }
  492. else
  493. {
  494. new_width = board()->GetDesignSettings().GetCurrentViaSize();
  495. new_drill = board()->GetDesignSettings().GetCurrentViaDrill();
  496. }
  497. via->SetDrill( new_drill );
  498. via->SetWidth( new_width );
  499. }
  500. else if ( auto track = dyn_cast<TRACK*>( item ) )
  501. {
  502. int new_width = board()->GetDesignSettings().GetCurrentTrackWidth();
  503. track->SetWidth( new_width );
  504. }
  505. m_commit->Push( _("Edit track width/via size") );
  506. return true;
  507. }
  508. return false;
  509. }
  510. int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
  511. {
  512. PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
  513. const auto& selection = m_selectionTool->RequestSelection(
  514. SELECTION_EDITABLE | SELECTION_DELETABLE | SELECTION_FORCE_UNLOCK );
  515. if( selection.Empty() )
  516. return 0;
  517. // Tracks & vias are treated in a special way:
  518. if( ( SELECTION_CONDITIONS::OnlyTypes( GENERAL_COLLECTOR::Tracks ) )( selection ) )
  519. {
  520. if ( !changeTrackWidthOnClick( selection ) )
  521. {
  522. DIALOG_TRACK_VIA_PROPERTIES dlg( editFrame, selection, *m_commit );
  523. dlg.ShowModal();
  524. }
  525. }
  526. else if( selection.Size() == 1 ) // Properties are displayed when there is only one item selected
  527. {
  528. // Display properties dialog
  529. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
  530. // Some of properties dialogs alter pointers, so we should deselect them
  531. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  532. // Store flags, so they can be restored later
  533. STATUS_FLAGS flags = item->GetFlags();
  534. item->ClearFlags();
  535. // Do not handle undo buffer, it is done by the properties dialogs @todo LEGACY
  536. // Display properties dialog provided by the legacy canvas frame
  537. editFrame->OnEditItemRequest( NULL, item );
  538. m_toolMgr->RunAction( PCB_ACTIONS::selectionModified, true );
  539. item->SetFlags( flags );
  540. }
  541. if( selection.IsHover() )
  542. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  543. return 0;
  544. }
  545. int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
  546. {
  547. PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
  548. auto& selection = m_selectionTool->RequestSelection();
  549. if( selection.Empty() )
  550. return 0;
  551. if( m_selectionTool->CheckLock() == SELECTION_LOCKED )
  552. return 0;
  553. updateModificationPoint( selection );
  554. const int rotateAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *editFrame, aEvent );
  555. for( auto item : selection )
  556. {
  557. if( !item->IsNew() )
  558. m_commit->Modify( item );
  559. static_cast<BOARD_ITEM*>( item )->Rotate( selection.GetReferencePoint(), rotateAngle );
  560. }
  561. if( !m_dragging )
  562. m_commit->Push( _( "Rotate" ) );
  563. if( selection.IsHover() && !m_dragging )
  564. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  565. m_toolMgr->RunAction( PCB_ACTIONS::selectionModified, true );
  566. return 0;
  567. }
  568. /*!
  569. * Mirror a point about the vertical axis passing through another point
  570. */
  571. static wxPoint mirrorPointX( const wxPoint& aPoint, const wxPoint& aMirrorPoint )
  572. {
  573. wxPoint mirrored = aPoint;
  574. mirrored.x -= aMirrorPoint.x;
  575. mirrored.x = -mirrored.x;
  576. mirrored.x += aMirrorPoint.x;
  577. return mirrored;
  578. }
  579. /**
  580. * Mirror a pad in the vertical axis passing through a point
  581. */
  582. static void mirrorPadX( D_PAD& aPad, const wxPoint& aMirrorPoint )
  583. {
  584. wxPoint tmpPt = mirrorPointX( aPad.GetPosition(), aMirrorPoint );
  585. aPad.SetPosition( tmpPt );
  586. aPad.SetX0( aPad.GetPosition().x );
  587. tmpPt = aPad.GetOffset();
  588. tmpPt.x = -tmpPt.x;
  589. aPad.SetOffset( tmpPt );
  590. auto tmpz = aPad.GetDelta();
  591. tmpz.x = -tmpz.x;
  592. aPad.SetDelta( tmpz );
  593. aPad.SetOrientation( -aPad.GetOrientation() );
  594. }
  595. int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
  596. {
  597. auto& selection = m_selectionTool->RequestSelection();
  598. if( m_selectionTool->CheckLock() == SELECTION_LOCKED )
  599. return 0;
  600. if( selection.Empty() )
  601. return 0;
  602. updateModificationPoint( selection );
  603. auto refPoint = selection.GetReferencePoint();
  604. wxPoint mirrorPoint( refPoint.x, refPoint.y );
  605. for( auto item : selection )
  606. {
  607. // only modify items we can mirror
  608. switch( item->Type() )
  609. {
  610. case PCB_MODULE_EDGE_T:
  611. case PCB_MODULE_TEXT_T:
  612. case PCB_PAD_T:
  613. // Only create undo entry for items on the board
  614. if( !item->IsNew() )
  615. m_commit->Modify( item );
  616. break;
  617. default:
  618. continue;
  619. }
  620. // modify each object as necessary
  621. switch( item->Type() )
  622. {
  623. case PCB_MODULE_EDGE_T:
  624. {
  625. auto& edge = static_cast<EDGE_MODULE&>( *item );
  626. edge.Mirror( mirrorPoint, false );
  627. break;
  628. }
  629. case PCB_MODULE_TEXT_T:
  630. {
  631. auto& modText = static_cast<TEXTE_MODULE&>( *item );
  632. modText.Mirror( mirrorPoint, false );
  633. break;
  634. }
  635. case PCB_PAD_T:
  636. {
  637. auto& pad = static_cast<D_PAD&>( *item );
  638. mirrorPadX( pad, mirrorPoint );
  639. break;
  640. }
  641. default:
  642. // it's likely the commit object is wrong if you get here
  643. assert( false );
  644. break;
  645. }
  646. }
  647. if( !m_dragging )
  648. m_commit->Push( _( "Mirror" ) );
  649. if( selection.IsHover() && !m_dragging )
  650. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  651. m_toolMgr->RunAction( PCB_ACTIONS::selectionModified, true );
  652. return 0;
  653. }
  654. int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
  655. {
  656. auto& selection = m_selectionTool->RequestSelection();
  657. if( m_selectionTool->CheckLock() == SELECTION_LOCKED )
  658. return 0;
  659. if( selection.Empty() )
  660. return 0;
  661. updateModificationPoint( selection );
  662. auto modPoint = selection.GetReferencePoint();
  663. for( auto item : selection )
  664. {
  665. if( !item->IsNew() )
  666. m_commit->Modify( item );
  667. static_cast<BOARD_ITEM*>( item )->Flip( modPoint );
  668. }
  669. if( !m_dragging )
  670. m_commit->Push( _( "Flip" ) );
  671. if( selection.IsHover() && !m_dragging )
  672. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  673. m_toolMgr->RunAction( PCB_ACTIONS::selectionModified, true );
  674. return 0;
  675. }
  676. int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
  677. {
  678. ROUTER_TOOL* routerTool = static_cast<ROUTER_TOOL*>
  679. ( m_toolMgr->FindTool( "pcbnew.InteractiveRouter" ) );
  680. // Do not delete items while actively routing.
  681. if( routerTool && routerTool->Router() && routerTool->Router()->RoutingInProgress() )
  682. return 0;
  683. // get a copy instead of reference (as we're going to clear the selectio before removing items)
  684. auto selection = m_selectionTool->RequestSelection( SELECTION_DELETABLE | SELECTION_SANITIZE_PADS );
  685. if( m_selectionTool->CheckLock() == SELECTION_LOCKED )
  686. return 0;
  687. // is this "alternative" remove?
  688. const bool isAlt = aEvent.Parameter<intptr_t>() == (int) PCB_ACTIONS::REMOVE_FLAGS::ALT;
  689. // in "alternative" mode, deletion is not just a simple list of selected items,
  690. // it removes whole tracks, not just segments
  691. if( isAlt && selection.IsHover()
  692. && ( selection.HasType( PCB_TRACE_T ) || selection.HasType( PCB_VIA_T ) ) )
  693. {
  694. m_toolMgr->RunAction( PCB_ACTIONS::expandSelectedConnection, true );
  695. selection = m_selectionTool->GetSelection();
  696. }
  697. if( selection.Empty() )
  698. return 0;
  699. // As we are about to remove items, they have to be removed from the selection first
  700. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  701. for( auto item : selection )
  702. m_commit->Remove( item );
  703. m_commit->Push( _( "Delete" ) );
  704. return 0;
  705. }
  706. int EDIT_TOOL::MoveExact( const TOOL_EVENT& aEvent )
  707. {
  708. const auto& selection = m_selectionTool->RequestSelection();
  709. if( m_selectionTool->CheckLock() == SELECTION_LOCKED )
  710. return 0;
  711. if( selection.Empty() )
  712. return 0;
  713. PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
  714. MOVE_PARAMETERS params;
  715. params.editingFootprint = m_editModules;
  716. DIALOG_MOVE_EXACT dialog( editFrame, params );
  717. int ret = dialog.ShowModal();
  718. if( ret == wxID_OK )
  719. {
  720. VECTOR2I rp = selection.GetCenter();
  721. wxPoint rotPoint( rp.x, rp.y );
  722. wxPoint anchorPoint = getAnchorPoint( selection, params );
  723. wxPoint finalMoveVector = params.translation - anchorPoint;
  724. // Make sure the rotation is from the right reference point
  725. rotPoint += finalMoveVector;
  726. for( auto item : selection )
  727. {
  728. if( !item->IsNew() )
  729. m_commit->Modify( item );
  730. static_cast<BOARD_ITEM*>( item )->Move( finalMoveVector );
  731. static_cast<BOARD_ITEM*>( item )->Rotate( rotPoint, params.rotation );
  732. if( !m_dragging )
  733. getView()->Update( item );
  734. }
  735. m_commit->Push( _( "Move exact" ) );
  736. if( selection.IsHover() )
  737. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  738. m_toolMgr->RunAction( PCB_ACTIONS::selectionModified, true );
  739. }
  740. return 0;
  741. }
  742. int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
  743. {
  744. bool increment = aEvent.IsAction( &PCB_ACTIONS::duplicateIncrement );
  745. // Be sure that there is at least one item that we can modify
  746. const auto& selection = m_selectionTool->RequestSelection( SELECTION_DELETABLE | SELECTION_SANITIZE_PADS );
  747. if( selection.Empty() )
  748. return 0;
  749. // we have a selection to work on now, so start the tool process
  750. PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
  751. std::vector<BOARD_ITEM*> new_items;
  752. new_items.reserve( selection.Size() );
  753. BOARD_ITEM* orig_item = nullptr;
  754. BOARD_ITEM* dupe_item = nullptr;
  755. // Each selected item is duplicated and pushed to new_items list
  756. // Old selection is cleared, and new items are then selected.
  757. for( auto item : selection )
  758. {
  759. if( !item )
  760. continue;
  761. orig_item = static_cast<BOARD_ITEM*>( item );
  762. if( m_editModules )
  763. {
  764. dupe_item = editFrame->GetBoard()->m_Modules->Duplicate( orig_item, increment );
  765. }
  766. else
  767. {
  768. #if 0
  769. // @TODO: see if we allow zone duplication here
  770. // Duplicate zones is especially tricky (overlaping zones must be merged)
  771. // so zones are not duplicated
  772. if( item->Type() != PCB_ZONE_AREA_T )
  773. #endif
  774. dupe_item = editFrame->GetBoard()->Duplicate( orig_item );
  775. }
  776. if( dupe_item )
  777. {
  778. // Clear the selection flag here, otherwise the SELECTION_TOOL
  779. // will not properly select it later on
  780. dupe_item->ClearSelected();
  781. new_items.push_back( dupe_item );
  782. m_commit->Add( dupe_item );
  783. }
  784. }
  785. // Clear the old selection first
  786. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  787. // Select the new items
  788. m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &new_items );
  789. // record the new items as added
  790. if( !selection.Empty() )
  791. {
  792. editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ),
  793. (int) new_items.size() ) );
  794. // If items were duplicated, pick them up
  795. // this works well for "dropping" copies around and pushes the commit
  796. TOOL_EVENT evt = PCB_ACTIONS::move.MakeEvent();
  797. Main( evt );
  798. }
  799. return 0;
  800. }
  801. class GAL_ARRAY_CREATOR: public ARRAY_CREATOR
  802. {
  803. public:
  804. GAL_ARRAY_CREATOR( PCB_BASE_FRAME& editFrame, bool editModules,
  805. const SELECTION& selection ):
  806. ARRAY_CREATOR( editFrame ),
  807. m_editModules( editModules ),
  808. m_selection( selection )
  809. {}
  810. private:
  811. int getNumberOfItemsToArray() const override
  812. {
  813. // only handle single items
  814. return m_selection.Size();
  815. }
  816. BOARD_ITEM* getNthItemToArray( int n ) const override
  817. {
  818. return static_cast<BOARD_ITEM*>( m_selection[n] );
  819. }
  820. BOARD* getBoard() const override
  821. {
  822. return m_parent.GetBoard();
  823. }
  824. MODULE* getModule() const override
  825. {
  826. // Remember this is valid and used only in the module editor.
  827. // in board editor, the parent of items is usually the board.
  828. return m_editModules ? m_parent.GetBoard()->m_Modules.GetFirst() : NULL;
  829. }
  830. wxPoint getRotationCentre() const override
  831. {
  832. const VECTOR2I rp = m_selection.GetCenter();
  833. return wxPoint( rp.x, rp.y );
  834. }
  835. void prePushAction( BOARD_ITEM* aItem ) override
  836. {
  837. // Because aItem is/can be created from a selected item, and inherits from
  838. // it this state, reset the selected stated of aItem:
  839. aItem->ClearSelected();
  840. if( aItem->Type() == PCB_MODULE_T )
  841. {
  842. static_cast<MODULE*>( aItem )->RunOnChildren( [&] ( BOARD_ITEM* item )
  843. {
  844. item->ClearSelected();
  845. }
  846. );
  847. }
  848. }
  849. void postPushAction( BOARD_ITEM* new_item ) override
  850. {
  851. }
  852. void finalise() override
  853. {
  854. }
  855. bool m_editModules;
  856. const SELECTION& m_selection;
  857. };
  858. int EDIT_TOOL::CreateArray( const TOOL_EVENT& aEvent )
  859. {
  860. const auto& selection = m_selectionTool->RequestSelection();
  861. if( selection.Empty() )
  862. return 0;
  863. // we have a selection to work on now, so start the tool process
  864. PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
  865. GAL_ARRAY_CREATOR array_creator( *editFrame, m_editModules, selection );
  866. array_creator.Invoke();
  867. return 0;
  868. }
  869. void EDIT_TOOL::FootprintFilter( const VECTOR2I&, GENERAL_COLLECTOR& aCollector )
  870. {
  871. for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
  872. {
  873. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( aCollector[i] );
  874. if( item->Type() != PCB_MODULE_T )
  875. aCollector.Remove( i );
  876. }
  877. }
  878. int EDIT_TOOL::ExchangeFootprints( const TOOL_EVENT& aEvent )
  879. {
  880. const auto& selection = m_selectionTool->RequestSelection( 0, FootprintFilter );
  881. bool updateMode = aEvent.IsAction( &PCB_ACTIONS::updateFootprints );
  882. MODULE* mod = (selection.Empty() ? nullptr : selection.FirstOfKind<MODULE> () );
  883. frame()->SetCurItem( mod );
  884. // Footprint exchange could remove modules, so they have to be
  885. // removed from the selection first
  886. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  887. // invoke the exchange dialog process
  888. {
  889. DIALOG_EXCHANGE_FOOTPRINTS dialog( frame(), mod, updateMode );
  890. dialog.ShowQuasiModal();
  891. }
  892. return 0;
  893. }
  894. int EDIT_TOOL::MeasureTool( const TOOL_EVENT& aEvent )
  895. {
  896. if( EditingModules() && !frame()->GetModel())
  897. return 0;
  898. auto& view = *getView();
  899. auto& controls = *getViewControls();
  900. int toolID = EditingModules() ? ID_MODEDIT_MEASUREMENT_TOOL : ID_PCB_MEASUREMENT_TOOL;
  901. Activate();
  902. frame()->SetToolID( toolID, wxCURSOR_PENCIL, _( "Measure distance" ) );
  903. KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER twoPtMgr;
  904. KIGFX::PREVIEW::RULER_ITEM ruler( twoPtMgr );
  905. view.Add( &ruler );
  906. view.SetVisible( &ruler, false );
  907. bool originSet = false;
  908. controls.ShowCursor( true );
  909. controls.SetSnapping( true );
  910. controls.SetAutoPan( false );
  911. while( auto evt = Wait() )
  912. {
  913. // TODO: magnetic pad & track processing needs to move to VIEW_CONTROLS.
  914. wxPoint pos( controls.GetMousePosition().x, controls.GetMousePosition().y );
  915. frame()->SetMousePosition( pos );
  916. wxRealPoint gridSize = frame()->GetScreen()->GetGridSize();
  917. wxSize igridsize;
  918. igridsize.x = KiROUND( gridSize.x );
  919. igridsize.y = KiROUND( gridSize.y );
  920. if( Magnetize( frame(), toolID, igridsize, pos, &pos ) )
  921. controls.ForceCursorPosition( true, pos );
  922. else
  923. controls.ForceCursorPosition( false );
  924. const VECTOR2I cursorPos = controls.GetCursorPosition();
  925. if( evt->IsCancel() || evt->IsActivate() )
  926. {
  927. break;
  928. }
  929. // click or drag starts
  930. else if( !originSet &&
  931. ( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
  932. {
  933. if( !evt->IsDrag( BUT_LEFT ) )
  934. {
  935. twoPtMgr.SetOrigin( cursorPos );
  936. twoPtMgr.SetEnd( cursorPos );
  937. }
  938. controls.CaptureCursor( true );
  939. controls.SetAutoPan( true );
  940. originSet = true;
  941. }
  942. else if( !originSet && evt->IsMotion() )
  943. {
  944. // make sure the origin is set before a drag starts
  945. // otherwise you can miss a step
  946. twoPtMgr.SetOrigin( cursorPos );
  947. twoPtMgr.SetEnd( cursorPos );
  948. }
  949. // second click or mouse up after drag ends
  950. else if( originSet &&
  951. ( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
  952. {
  953. originSet = false;
  954. controls.SetAutoPan( false );
  955. controls.CaptureCursor( false );
  956. view.SetVisible( &ruler, false );
  957. }
  958. // move or drag when origin set updates rules
  959. else if( originSet &&
  960. ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
  961. {
  962. twoPtMgr.SetAngleSnap( evt->Modifier( MD_CTRL ) );
  963. twoPtMgr.SetEnd( cursorPos );
  964. view.SetVisible( &ruler, true );
  965. view.Update( &ruler, KIGFX::GEOMETRY );
  966. }
  967. else if( evt->IsClick( BUT_RIGHT ) )
  968. {
  969. GetManager()->PassEvent();
  970. }
  971. }
  972. view.SetVisible( &ruler, false );
  973. view.Remove( &ruler );
  974. frame()->SetNoToolSelected();
  975. return 0;
  976. }
  977. void EDIT_TOOL::setTransitions()
  978. {
  979. Go( &EDIT_TOOL::Main, PCB_ACTIONS::editActivate.MakeEvent() );
  980. Go( &EDIT_TOOL::Main, PCB_ACTIONS::move.MakeEvent() );
  981. Go( &EDIT_TOOL::Drag, PCB_ACTIONS::drag45Degree.MakeEvent() );
  982. Go( &EDIT_TOOL::Drag, PCB_ACTIONS::dragFreeAngle.MakeEvent() );
  983. Go( &EDIT_TOOL::Rotate, PCB_ACTIONS::rotateCw.MakeEvent() );
  984. Go( &EDIT_TOOL::Rotate, PCB_ACTIONS::rotateCcw.MakeEvent() );
  985. Go( &EDIT_TOOL::Flip, PCB_ACTIONS::flip.MakeEvent() );
  986. Go( &EDIT_TOOL::Remove, PCB_ACTIONS::remove.MakeEvent() );
  987. Go( &EDIT_TOOL::Remove, PCB_ACTIONS::removeAlt.MakeEvent() );
  988. Go( &EDIT_TOOL::Properties, PCB_ACTIONS::properties.MakeEvent() );
  989. Go( &EDIT_TOOL::MoveExact, PCB_ACTIONS::moveExact.MakeEvent() );
  990. Go( &EDIT_TOOL::Duplicate, PCB_ACTIONS::duplicate.MakeEvent() );
  991. Go( &EDIT_TOOL::Duplicate, PCB_ACTIONS::duplicateIncrement.MakeEvent() );
  992. Go( &EDIT_TOOL::CreateArray,PCB_ACTIONS::createArray.MakeEvent() );
  993. Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirror.MakeEvent() );
  994. Go( &EDIT_TOOL::editFootprintInFpEditor, PCB_ACTIONS::editFootprintInFpEditor.MakeEvent() );
  995. Go( &EDIT_TOOL::ExchangeFootprints, PCB_ACTIONS::updateFootprints.MakeEvent() );
  996. Go( &EDIT_TOOL::ExchangeFootprints, PCB_ACTIONS::exchangeFootprints.MakeEvent() );
  997. Go( &EDIT_TOOL::MeasureTool, PCB_ACTIONS::measureTool.MakeEvent() );
  998. Go( &EDIT_TOOL::copyToClipboard, PCB_ACTIONS::copyToClipboard.MakeEvent() );
  999. Go( &EDIT_TOOL::cutToClipboard, PCB_ACTIONS::cutToClipboard.MakeEvent() );
  1000. }
  1001. bool EDIT_TOOL::updateModificationPoint( SELECTION& aSelection )
  1002. {
  1003. if( m_dragging && aSelection.HasReferencePoint() )
  1004. return false;
  1005. // When there is only one item selected, the reference point is its position...
  1006. if( aSelection.Size() == 1 )
  1007. {
  1008. auto item = static_cast<BOARD_ITEM*>( aSelection.Front() );
  1009. auto pos = item->GetPosition();
  1010. aSelection.SetReferencePoint( VECTOR2I( pos.x, pos.y ) );
  1011. }
  1012. // ...otherwise modify items with regard to the cursor position
  1013. else
  1014. {
  1015. m_cursor = getViewControls()->GetCursorPosition();
  1016. aSelection.SetReferencePoint( m_cursor );
  1017. }
  1018. return true;
  1019. }
  1020. int EDIT_TOOL::editFootprintInFpEditor( const TOOL_EVENT& aEvent )
  1021. {
  1022. const auto& selection = m_selectionTool->RequestSelection( 0, FootprintFilter );
  1023. if( selection.Empty() )
  1024. return 0;
  1025. MODULE* mod = selection.FirstOfKind<MODULE>();
  1026. if( !mod )
  1027. return 0;
  1028. PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
  1029. editFrame->SetCurItem( mod );
  1030. if( editFrame->GetCurItem()->GetTimeStamp() == 0 ) // Module Editor needs a non null timestamp
  1031. {
  1032. editFrame->GetCurItem()->SetTimeStamp( GetNewTimeStamp() );
  1033. editFrame->OnModify();
  1034. }
  1035. FOOTPRINT_EDIT_FRAME* editor = (FOOTPRINT_EDIT_FRAME*) editFrame->Kiway().Player( FRAME_PCB_MODULE_EDITOR, true );
  1036. editor->Load_Module_From_BOARD( (MODULE*) editFrame->GetCurItem() );
  1037. editFrame->SetCurItem( NULL ); // the current module could be deleted by
  1038. editor->Show( true );
  1039. editor->Raise(); // Iconize( false );
  1040. if( selection.IsHover() )
  1041. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1042. return 0;
  1043. }
  1044. bool EDIT_TOOL::pickCopyReferencePoint( VECTOR2I& aP )
  1045. {
  1046. PICKER_TOOL* picker = m_toolMgr->GetTool<PICKER_TOOL>();
  1047. assert( picker );
  1048. picker->Activate();
  1049. while ( picker->IsPicking() )
  1050. Wait();
  1051. if( !picker->GetPoint() )
  1052. return false;
  1053. aP = *picker->GetPoint();
  1054. return true;
  1055. }
  1056. int EDIT_TOOL::copyToClipboard( const TOOL_EVENT& aEvent )
  1057. {
  1058. CLIPBOARD_IO io;
  1059. VECTOR2I refPoint;
  1060. Activate();
  1061. auto item1 = MSG_PANEL_ITEM( "", _( "Select reference point for the block being copied..." ),
  1062. COLOR4D::BLACK );
  1063. std::vector<MSG_PANEL_ITEM> msgItems = { item1 };
  1064. SELECTION& selection = m_selectionTool->RequestSelection();
  1065. if( selection.Empty() )
  1066. return 1;
  1067. frame()->SetMsgPanel( msgItems );
  1068. bool rv = pickCopyReferencePoint( refPoint );
  1069. frame()->SetMsgPanel( board() );
  1070. if( !rv )
  1071. return 1;
  1072. selection.SetReferencePoint( refPoint );
  1073. io.SetBoard( board() );
  1074. io.SaveSelection( selection );
  1075. return 0;
  1076. }
  1077. int EDIT_TOOL::cutToClipboard( const TOOL_EVENT& aEvent )
  1078. {
  1079. if( !copyToClipboard( aEvent ) )
  1080. Remove( aEvent );
  1081. return 0;
  1082. }