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.

866 lines
29 KiB

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