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.

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