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.

862 lines
29 KiB

7 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 CERN
  5. * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include <tool/tool_manager.h>
  25. #include <tools/ee_grid_helper.h>
  26. #include <tools/ee_selection_tool.h>
  27. #include <tools/sch_line_wire_bus_tool.h>
  28. #include <ee_actions.h>
  29. #include <bitmaps.h>
  30. #include <eda_item.h>
  31. #include <sch_item.h>
  32. #include <sch_component.h>
  33. #include <sch_sheet.h>
  34. #include <sch_line.h>
  35. #include <sch_edit_frame.h>
  36. #include <eeschema_id.h>
  37. #include <pgm_base.h>
  38. #include <settings/settings_manager.h>
  39. #include "sch_move_tool.h"
  40. // For adding to or removing from selections
  41. #define QUIET_MODE true
  42. SCH_MOVE_TOOL::SCH_MOVE_TOOL() :
  43. EE_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.InteractiveMove" ),
  44. m_moveInProgress( false ),
  45. m_isDragOperation( false ),
  46. m_moveOffset( 0, 0 )
  47. {
  48. }
  49. bool SCH_MOVE_TOOL::Init()
  50. {
  51. EE_TOOL_BASE::Init();
  52. auto moveCondition =
  53. []( const SELECTION& aSel )
  54. {
  55. if( aSel.Empty() )
  56. return false;
  57. if( SCH_LINE_WIRE_BUS_TOOL::IsDrawingLineWireOrBus( aSel ) )
  58. return false;
  59. return true;
  60. };
  61. // Add move actions to the selection tool menu
  62. //
  63. CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
  64. selToolMenu.AddItem( EE_ACTIONS::move, moveCondition, 150 );
  65. selToolMenu.AddItem( EE_ACTIONS::drag, moveCondition, 150 );
  66. selToolMenu.AddItem( EE_ACTIONS::alignToGrid, moveCondition, 150 );
  67. return true;
  68. }
  69. static const KICAD_T movableItems[] =
  70. {
  71. SCH_MARKER_T,
  72. SCH_JUNCTION_T,
  73. SCH_NO_CONNECT_T,
  74. SCH_BUS_BUS_ENTRY_T,
  75. SCH_BUS_WIRE_ENTRY_T,
  76. SCH_LINE_T,
  77. SCH_BITMAP_T,
  78. SCH_TEXT_T,
  79. SCH_LABEL_T,
  80. SCH_GLOBAL_LABEL_T,
  81. SCH_HIER_LABEL_T,
  82. SCH_FIELD_T,
  83. SCH_COMPONENT_T,
  84. SCH_SHEET_PIN_T,
  85. SCH_SHEET_T,
  86. EOT
  87. };
  88. /* TODO - Tom/Jeff
  89. - add preferences option "Move origin: always cursor / item origin"
  90. - add preferences option "Default drag action: drag items / move"
  91. - add preferences option "Drag always selects"
  92. */
  93. int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
  94. {
  95. EESCHEMA_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
  96. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  97. EE_GRID_HELPER grid( m_toolMgr );
  98. m_anchorPos.reset();
  99. if( aEvent.IsAction( &EE_ACTIONS::move ) )
  100. m_isDragOperation = false;
  101. else if( aEvent.IsAction( &EE_ACTIONS::drag ) )
  102. m_isDragOperation = true;
  103. else if( aEvent.IsAction( &EE_ACTIONS::moveActivate ) )
  104. m_isDragOperation = !cfg->m_Input.drag_is_move;
  105. else
  106. return 0;
  107. if( m_moveInProgress )
  108. {
  109. auto sel = m_selectionTool->GetSelection().Front();
  110. if( sel && !sel->IsNew() )
  111. {
  112. // User must have switched from move to drag or vice-versa. Reset the selected
  113. // items so we can start again with the current m_isDragOperation.
  114. m_frame->RollbackSchematicFromUndo();
  115. m_selectionTool->RemoveItemsFromSel( &m_dragAdditions, QUIET_MODE );
  116. m_anchorPos = m_cursor - m_moveOffset;
  117. m_moveInProgress = false;
  118. controls->SetAutoPan( false );
  119. // And give it a kick so it doesn't have to wait for the first mouse movement to
  120. // refresh.
  121. m_toolMgr->RunAction( EE_ACTIONS::restartMove );
  122. }
  123. return 0;
  124. }
  125. // Be sure that there is at least one item that we can move. If there's no selection try
  126. // looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection).
  127. EE_SELECTION& selection = m_selectionTool->RequestSelection( movableItems );
  128. bool unselect = selection.IsHover();
  129. // Keep an original copy of the starting points for cleanup after the move
  130. std::vector<DANGLING_END_ITEM> internalPoints;
  131. Activate();
  132. controls->ShowCursor( true );
  133. std::string tool = aEvent.GetCommandStr().get();
  134. m_frame->PushTool( tool );
  135. if( selection.Empty() )
  136. {
  137. // Note that it's important to go through push/pop even when the selection is empty.
  138. // This keeps other tools from having to special-case an empty move.
  139. m_frame->PopTool( tool );
  140. return 0;
  141. }
  142. bool restore_state = false;
  143. bool chain_commands = false;
  144. TOOL_EVENT* evt = const_cast<TOOL_EVENT*>( &aEvent );
  145. VECTOR2I prevPos;
  146. int snapLayer = UNDEFINED_LAYER;
  147. m_cursor = controls->GetCursorPosition();
  148. // Main loop: keep receiving events
  149. do
  150. {
  151. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
  152. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  153. grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
  154. if( evt->IsAction( &EE_ACTIONS::moveActivate )
  155. || evt->IsAction( &EE_ACTIONS::restartMove )
  156. || evt->IsAction( &EE_ACTIONS::move )
  157. || evt->IsAction( &EE_ACTIONS::drag )
  158. || evt->IsMotion()
  159. || evt->IsDrag( BUT_LEFT )
  160. || evt->IsAction( &ACTIONS::refreshPreview ) )
  161. {
  162. if( !m_moveInProgress ) // Prepare to start moving/dragging
  163. {
  164. SCH_ITEM* sch_item = (SCH_ITEM*) selection.Front();
  165. bool appendUndo = sch_item && sch_item->IsNew();
  166. bool placingNewItems = sch_item && sch_item->IsNew();
  167. //------------------------------------------------------------------------
  168. // Setup a drag or a move
  169. //
  170. m_dragAdditions.clear();
  171. m_specialCaseLabels.clear();
  172. internalPoints.clear();
  173. for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
  174. {
  175. it->ClearFlags( TEMP_SELECTED );
  176. if( !it->IsSelected() )
  177. it->ClearFlags( STARTPOINT | ENDPOINT );
  178. if( !selection.IsHover() && it->IsSelected() )
  179. it->SetFlags( STARTPOINT | ENDPOINT );
  180. }
  181. if( m_isDragOperation )
  182. {
  183. // Add connections to the selection for a drag.
  184. //
  185. for( EDA_ITEM* edaItem : selection )
  186. {
  187. SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
  188. std::vector<wxPoint> connections;
  189. if( item->Type() == SCH_LINE_T )
  190. static_cast<SCH_LINE*>( item )->GetSelectedPoints( connections );
  191. else
  192. connections = item->GetConnectionPoints();
  193. for( wxPoint point : connections )
  194. getConnectedDragItems( item, point, m_dragAdditions );
  195. }
  196. m_selectionTool->AddItemsToSel( &m_dragAdditions, QUIET_MODE );
  197. }
  198. else
  199. {
  200. // Mark the edges of the block with dangling flags for a move.
  201. for( EDA_ITEM* item : selection )
  202. static_cast<SCH_ITEM*>( item )->GetEndPoints( internalPoints );
  203. for( EDA_ITEM* item : selection )
  204. static_cast<SCH_ITEM*>( item )->UpdateDanglingState( internalPoints );
  205. }
  206. // Generic setup
  207. //
  208. for( EDA_ITEM* item : selection )
  209. {
  210. if( static_cast<SCH_ITEM*>( item )->IsConnectable() )
  211. {
  212. if( snapLayer == LAYER_GRAPHICS )
  213. snapLayer = LAYER_ANY;
  214. else
  215. snapLayer = LAYER_CONNECTABLE;
  216. }
  217. else
  218. {
  219. if( snapLayer == LAYER_CONNECTABLE )
  220. snapLayer = LAYER_ANY;
  221. else
  222. snapLayer = LAYER_GRAPHICS;
  223. }
  224. if( item->IsNew() )
  225. {
  226. if( item->HasFlag( TEMP_SELECTED ) && m_isDragOperation )
  227. {
  228. // Item was added in getConnectedDragItems
  229. saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::NEWITEM, appendUndo );
  230. appendUndo = true;
  231. }
  232. else
  233. {
  234. // Item was added in a previous command (and saved to undo by
  235. // that command)
  236. }
  237. }
  238. else if( item->GetParent() && item->GetParent()->IsSelected() )
  239. {
  240. // Item will be (or has been) saved to undo by parent
  241. }
  242. else
  243. {
  244. saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::CHANGED, appendUndo );
  245. appendUndo = true;
  246. }
  247. SCH_ITEM* schItem = (SCH_ITEM*) item;
  248. schItem->SetStoredPos( schItem->GetPosition() );
  249. }
  250. // Set up the starting position and move/drag offset
  251. //
  252. m_cursor = controls->GetCursorPosition();
  253. if( evt->IsAction( &EE_ACTIONS::restartMove ) )
  254. {
  255. wxASSERT_MSG( m_anchorPos, "Should be already set from previous cmd" );
  256. }
  257. else if( placingNewItems )
  258. {
  259. m_anchorPos = selection.GetReferencePoint();
  260. }
  261. if( m_anchorPos )
  262. {
  263. VECTOR2I delta = m_cursor - (*m_anchorPos);
  264. // Drag items to the current cursor position
  265. for( EDA_ITEM* item : selection )
  266. {
  267. // Don't double move pins, fields, etc.
  268. if( item->GetParent() && item->GetParent()->IsSelected() )
  269. continue;
  270. moveItem( item, delta );
  271. updateItem( item, false );
  272. }
  273. m_anchorPos = m_cursor;
  274. }
  275. // For some items, moving the cursor to anchor is not good (for instance large
  276. // hierarchical sheets or components can have the anchor outside the view)
  277. else if( selection.Size() == 1 && !sch_item->IsMovableFromAnchorPoint() )
  278. {
  279. m_cursor = getViewControls()->GetCursorPosition( true );
  280. m_anchorPos = m_cursor;
  281. }
  282. else
  283. {
  284. if( m_frame->GetMoveWarpsCursor() )
  285. {
  286. // User wants to warp the mouse
  287. m_cursor = grid.BestDragOrigin( m_cursor, snapLayer, selection );
  288. }
  289. else
  290. {
  291. // User does not want to warp the mouse
  292. m_cursor = getViewControls()->GetCursorPosition( true );
  293. }
  294. }
  295. controls->SetCursorPosition( m_cursor, false );
  296. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  297. prevPos = m_cursor;
  298. controls->SetAutoPan( true );
  299. m_moveInProgress = true;
  300. }
  301. //------------------------------------------------------------------------
  302. // Follow the mouse
  303. //
  304. m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ),
  305. snapLayer, selection );
  306. VECTOR2I delta( m_cursor - prevPos );
  307. m_anchorPos = m_cursor;
  308. m_moveOffset += delta;
  309. prevPos = m_cursor;
  310. for( EDA_ITEM* item : selection )
  311. {
  312. // Don't double move pins, fields, etc.
  313. if( item->GetParent() && item->GetParent()->IsSelected() )
  314. continue;
  315. moveItem( item, delta );
  316. updateItem( item, false );
  317. }
  318. m_toolMgr->PostEvent( EVENTS::SelectedItemsMoved );
  319. }
  320. //------------------------------------------------------------------------
  321. // Handle cancel
  322. //
  323. else if( evt->IsCancelInteractive() )
  324. {
  325. if( m_moveInProgress )
  326. {
  327. evt->SetPassEvent( false );
  328. restore_state = true;
  329. }
  330. break;
  331. }
  332. //------------------------------------------------------------------------
  333. // Handle TOOL_ACTION special cases
  334. //
  335. else if( evt->Action() == TA_UNDO_REDO_PRE )
  336. {
  337. unselect = true;
  338. break;
  339. }
  340. else if( evt->IsAction( &ACTIONS::doDelete ) )
  341. {
  342. // Exit on a remove operation; there is no further processing for removed items.
  343. break;
  344. }
  345. else if( evt->IsAction( &ACTIONS::duplicate ) )
  346. {
  347. if( selection.Front()->IsNew() )
  348. {
  349. // This doesn't really make sense; we'll just end up dragging a stack of
  350. // objects so Duplicate() is going to ignore this and we'll just carry on.
  351. continue;
  352. }
  353. // Move original back and exit. The duplicate will run in its own loop.
  354. restore_state = true;
  355. unselect = false;
  356. chain_commands = true;
  357. break;
  358. }
  359. else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
  360. {
  361. if( evt->GetCommandId().get() >= ID_POPUP_SCH_SELECT_UNIT_CMP
  362. && evt->GetCommandId().get() <= ID_POPUP_SCH_SELECT_UNIT_CMP_MAX )
  363. {
  364. SCH_COMPONENT* component = dynamic_cast<SCH_COMPONENT*>( selection.Front() );
  365. int unit = evt->GetCommandId().get() - ID_POPUP_SCH_SELECT_UNIT_CMP;
  366. if( component )
  367. {
  368. m_frame->SelectUnit( component, unit );
  369. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  370. }
  371. }
  372. }
  373. //------------------------------------------------------------------------
  374. // Handle context menu
  375. //
  376. else if( evt->IsClick( BUT_RIGHT ) )
  377. {
  378. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  379. }
  380. //------------------------------------------------------------------------
  381. // Handle drop
  382. //
  383. else if( evt->IsMouseUp( BUT_LEFT )
  384. || evt->IsClick( BUT_LEFT )
  385. || evt->IsDblClick( BUT_LEFT ) )
  386. {
  387. break; // Finish
  388. }
  389. else
  390. evt->SetPassEvent();
  391. controls->SetAutoPan( m_moveInProgress );
  392. } while( ( evt = Wait() ) ); //Should be assignment not equality test
  393. controls->ForceCursorPosition( false );
  394. controls->ShowCursor( false );
  395. controls->SetAutoPan( false );
  396. if( !chain_commands )
  397. m_moveOffset = { 0, 0 };
  398. m_anchorPos.reset();
  399. for( EDA_ITEM* item : selection )
  400. item->ClearEditFlags();
  401. if( restore_state )
  402. {
  403. m_selectionTool->RemoveItemsFromSel( &m_dragAdditions, QUIET_MODE );
  404. m_frame->RollbackSchematicFromUndo();
  405. }
  406. else
  407. {
  408. // One last update after exiting loop (for slower stuff, such as updating SCREEN's RTree).
  409. for( EDA_ITEM* item : selection )
  410. updateItem( item, true );
  411. EE_SELECTION selectionCopy( selection );
  412. m_selectionTool->RemoveItemsFromSel( &m_dragAdditions, QUIET_MODE );
  413. // If we move items away from a junction, we _may_ want to add a junction there
  414. // to denote the state.
  415. for( const DANGLING_END_ITEM& it : internalPoints )
  416. {
  417. if( m_frame->GetScreen()->IsJunctionNeeded( it.GetPosition(), true ) )
  418. m_frame->AddJunction( m_frame->GetScreen(), it.GetPosition(), true, false );
  419. }
  420. m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selectionCopy );
  421. m_frame->SchematicCleanUp();
  422. m_frame->TestDanglingEnds();
  423. m_frame->OnModify();
  424. }
  425. if( unselect )
  426. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  427. else
  428. m_selectionTool->RebuildSelection(); // Schematic cleanup might have merged lines, etc.
  429. m_dragAdditions.clear();
  430. m_moveInProgress = false;
  431. m_frame->PopTool( tool );
  432. return 0;
  433. }
  434. void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, wxPoint aPoint,
  435. EDA_ITEMS& aList )
  436. {
  437. EE_RTREE& items = m_frame->GetScreen()->Items();
  438. EE_RTREE::EE_TYPE itemsOverlapping = items.Overlapping( aOriginalItem->GetBoundingBox() );
  439. bool ptHasUnselectedJunction = false;
  440. for( SCH_ITEM* item : itemsOverlapping )
  441. {
  442. if( item->Type() == SCH_JUNCTION_T && item->IsConnected( aPoint ) && !item->IsSelected() )
  443. {
  444. ptHasUnselectedJunction = true;
  445. break;
  446. }
  447. }
  448. for( SCH_ITEM *test : itemsOverlapping )
  449. {
  450. if( test == aOriginalItem || test->IsSelected() || !test->CanConnect( aOriginalItem ) )
  451. continue;
  452. KICAD_T testType = test->Type();
  453. switch( testType )
  454. {
  455. case SCH_LINE_T:
  456. {
  457. // Select the connected end of wires/bus connections that don't have an unselected
  458. // junction isolating them from the drag
  459. if( ptHasUnselectedJunction )
  460. break;
  461. SCH_LINE* testLine = static_cast<SCH_LINE*>( test );
  462. if( testLine->GetStartPoint() == aPoint )
  463. {
  464. if( !testLine->HasFlag( TEMP_SELECTED ) )
  465. aList.push_back( testLine );
  466. testLine->SetFlags( STARTPOINT | TEMP_SELECTED );
  467. }
  468. else if( testLine->GetEndPoint() == aPoint )
  469. {
  470. if( !testLine->HasFlag( TEMP_SELECTED ) )
  471. aList.push_back( testLine );
  472. testLine->SetFlags( ENDPOINT | TEMP_SELECTED );
  473. }
  474. else
  475. {
  476. break;
  477. }
  478. // Since only one end is going to move, the movement vector of any labels attached
  479. // to it is scaled by the proportion of the line length the label is from the moving
  480. // end.
  481. for( SCH_ITEM* item : itemsOverlapping )
  482. {
  483. if( item->Type() == SCH_LABEL_T )
  484. {
  485. SCH_TEXT* label = static_cast<SCH_TEXT*>( item );
  486. if( label->IsSelected() )
  487. continue; // These will be moved on their own because they're selected
  488. if( label->CanConnect( testLine )
  489. && testLine->HitTest( label->GetPosition(), 1 ) )
  490. {
  491. if( !label->HasFlag( TEMP_SELECTED ) )
  492. aList.push_back( label );
  493. SPECIAL_CASE_LABEL_INFO info;
  494. info.attachedLine = testLine;
  495. info.originalLabelPos = label->GetPosition();
  496. m_specialCaseLabels[label] = info;
  497. }
  498. }
  499. }
  500. break;
  501. }
  502. case SCH_SHEET_T:
  503. // Dragging a sheet just because it's connected to something else feels a bit like
  504. // the tail wagging the dog, but this could be moved down to the next case.
  505. break;
  506. case SCH_COMPONENT_T:
  507. case SCH_JUNCTION_T:
  508. if( test->IsConnected( aPoint ) )
  509. {
  510. // Add a new wire between the component or junction and the selected item so
  511. // the selected item can be dragged.
  512. SCH_LINE* newWire = new SCH_LINE( aPoint, LAYER_WIRE );
  513. newWire->SetFlags( IS_NEW );
  514. m_frame->AddToScreen( newWire, m_frame->GetScreen() );
  515. newWire->SetFlags( TEMP_SELECTED | STARTPOINT );
  516. aList.push_back( newWire );
  517. }
  518. break;
  519. case SCH_NO_CONNECT_T:
  520. // Select no-connects that are connected to items being moved.
  521. if( !test->HasFlag( TEMP_SELECTED ) && test->IsConnected( aPoint ) )
  522. {
  523. aList.push_back( test );
  524. test->SetFlags( TEMP_SELECTED );
  525. }
  526. break;
  527. case SCH_LABEL_T:
  528. case SCH_GLOBAL_LABEL_T:
  529. case SCH_HIER_LABEL_T:
  530. case SCH_BUS_WIRE_ENTRY_T:
  531. case SCH_BUS_BUS_ENTRY_T:
  532. // Performance optimization:
  533. if( test->HasFlag( TEMP_SELECTED ) )
  534. break;
  535. // Select labels and bus entries that are connected to a wire being moved.
  536. if( aOriginalItem->Type() == SCH_LINE_T )
  537. {
  538. std::vector<wxPoint> connections = test->GetConnectionPoints();
  539. for( wxPoint& point : connections )
  540. {
  541. if( aOriginalItem->HitTest( point, 1 ) )
  542. {
  543. test->SetFlags( TEMP_SELECTED );
  544. aList.push_back( test );
  545. // A bus entry needs its wire & label as well
  546. if( testType == SCH_BUS_WIRE_ENTRY_T || testType == SCH_BUS_BUS_ENTRY_T )
  547. {
  548. std::vector<wxPoint> ends = test->GetConnectionPoints();
  549. wxPoint otherEnd;
  550. if( ends[0] == point )
  551. otherEnd = ends[1];
  552. else
  553. otherEnd = ends[0];
  554. getConnectedDragItems( test, otherEnd, aList );
  555. }
  556. break;
  557. }
  558. }
  559. }
  560. break;
  561. default:
  562. break;
  563. }
  564. }
  565. }
  566. void SCH_MOVE_TOOL::moveItem( EDA_ITEM* aItem, const VECTOR2I& aDelta )
  567. {
  568. switch( aItem->Type() )
  569. {
  570. case SCH_LINE_T:
  571. {
  572. SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
  573. if( aItem->HasFlag( STARTPOINT ) )
  574. line->MoveStart( (wxPoint) aDelta );
  575. if( aItem->HasFlag( ENDPOINT ) )
  576. line->MoveEnd( (wxPoint) aDelta );
  577. }
  578. break;
  579. case SCH_PIN_T:
  580. case SCH_FIELD_T:
  581. {
  582. SCH_ITEM* parent = (SCH_ITEM*) aItem->GetParent();
  583. wxPoint delta( aDelta );
  584. if( parent && parent->Type() == SCH_COMPONENT_T )
  585. {
  586. SCH_COMPONENT* component = (SCH_COMPONENT*) aItem->GetParent();
  587. TRANSFORM transform = component->GetTransform().InverseTransform();
  588. delta = transform.TransformCoordinate( delta );
  589. }
  590. static_cast<SCH_ITEM*>( aItem )->Move( delta );
  591. // If we're moving a field with respect to its parent then it's no longer auto-placed
  592. if( aItem->Type() == SCH_FIELD_T && parent && !parent->IsSelected() )
  593. parent->ClearFieldsAutoplaced();
  594. break;
  595. }
  596. case SCH_SHEET_PIN_T:
  597. {
  598. SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) aItem;
  599. pin->SetStoredPos( pin->GetStoredPos() + (wxPoint) aDelta );
  600. pin->ConstrainOnEdge( pin->GetStoredPos() );
  601. break;
  602. }
  603. case SCH_LABEL_T:
  604. {
  605. SCH_TEXT* label = static_cast<SCH_TEXT*>( aItem );
  606. if( m_specialCaseLabels.count( label ) )
  607. {
  608. SPECIAL_CASE_LABEL_INFO info = m_specialCaseLabels[ label ];
  609. SEG currentLine( info.attachedLine->GetStartPoint(), info.attachedLine->GetEndPoint() );
  610. label->SetPosition( (wxPoint) currentLine.NearestPoint( info.originalLabelPos ) );
  611. }
  612. else
  613. {
  614. label->Move( (wxPoint) aDelta );
  615. }
  616. break;
  617. }
  618. default:
  619. static_cast<SCH_ITEM*>( aItem )->Move( (wxPoint) aDelta );
  620. break;
  621. }
  622. getView()->Hide( aItem, false );
  623. aItem->SetFlags( IS_MOVED );
  624. }
  625. int SCH_MOVE_TOOL::AlignElements( const TOOL_EVENT& aEvent )
  626. {
  627. EE_GRID_HELPER grid( m_toolMgr);
  628. EE_SELECTION& selection = m_selectionTool->RequestSelection( movableItems );
  629. bool append_undo = false;
  630. for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
  631. {
  632. if( !it->IsSelected() )
  633. it->ClearFlags( STARTPOINT | ENDPOINT );
  634. if( !selection.IsHover() && it->IsSelected() )
  635. it->SetFlags( STARTPOINT | ENDPOINT );
  636. it->SetStoredPos( it->GetPosition() );
  637. if( it->Type() == SCH_SHEET_T )
  638. {
  639. for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( it )->GetPins() )
  640. pin->SetStoredPos( pin->GetPosition() );
  641. }
  642. }
  643. for( EDA_ITEM* item : selection )
  644. {
  645. if( item->Type() == SCH_LINE_T )
  646. {
  647. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  648. std::vector<int> flags{ STARTPOINT, ENDPOINT };
  649. std::vector<wxPoint> pts{ line->GetStartPoint(), line->GetEndPoint() };
  650. for( int ii = 0; ii < 2; ++ii )
  651. {
  652. EDA_ITEMS drag_items{ item };
  653. line->ClearFlags();
  654. line->SetFlags( flags[ii] );
  655. getConnectedDragItems( line, pts[ii], drag_items );
  656. std::set<EDA_ITEM*> unique_items( drag_items.begin(), drag_items.end() );
  657. VECTOR2I gridpt = grid.AlignGrid( pts[ii] ) - pts[ii];
  658. if( gridpt != VECTOR2I( 0, 0 ) )
  659. {
  660. for( auto dritem : unique_items )
  661. {
  662. if( dritem->GetParent() && dritem->GetParent()->IsSelected() )
  663. continue;
  664. saveCopyInUndoList( dritem, UNDO_REDO::CHANGED, append_undo );
  665. append_undo = true;
  666. moveItem( dritem, gridpt );
  667. updateItem( dritem, true );
  668. }
  669. }
  670. }
  671. }
  672. else
  673. {
  674. std::vector<wxPoint> connections;
  675. EDA_ITEMS drag_items{ item };
  676. connections = static_cast<SCH_ITEM*>( item )->GetConnectionPoints();
  677. for( const wxPoint& point : connections )
  678. getConnectedDragItems( static_cast<SCH_ITEM*>( item ), point, drag_items );
  679. std::map<VECTOR2I, int> shifts;
  680. VECTOR2I most_common( 0, 0 );
  681. int max_count = 0;
  682. for( const wxPoint& conn : connections )
  683. {
  684. VECTOR2I gridpt = grid.AlignGrid( conn ) - conn;
  685. shifts[gridpt]++;
  686. if( shifts[gridpt] > max_count )
  687. {
  688. most_common = gridpt;
  689. max_count = shifts[most_common];
  690. }
  691. }
  692. if( most_common != VECTOR2I( 0, 0 ) )
  693. {
  694. for( EDA_ITEM* dritem : drag_items )
  695. {
  696. if( dritem->GetParent() && dritem->GetParent()->IsSelected() )
  697. continue;
  698. saveCopyInUndoList( dritem, UNDO_REDO::CHANGED, append_undo );
  699. append_undo = true;
  700. moveItem( dritem, most_common );
  701. updateItem( dritem, true );
  702. }
  703. }
  704. }
  705. }
  706. m_toolMgr->PostEvent( EVENTS::SelectedItemsMoved );
  707. m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selection );
  708. m_frame->SchematicCleanUp();
  709. m_frame->TestDanglingEnds();
  710. m_frame->OnModify();
  711. return 0;
  712. }
  713. void SCH_MOVE_TOOL::setTransitions()
  714. {
  715. Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::moveActivate.MakeEvent() );
  716. Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::move.MakeEvent() );
  717. Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::drag.MakeEvent() );
  718. Go( &SCH_MOVE_TOOL::AlignElements, EE_ACTIONS::alignToGrid.MakeEvent() );
  719. }