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.

942 lines
32 KiB

7 years ago
5 years ago
5 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
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-2021 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_symbol.h>
  33. #include <sch_sheet.h>
  34. #include <sch_sheet_pin.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_isDrag( false ),
  47. m_moveOffset( 0, 0 )
  48. {
  49. }
  50. bool SCH_MOVE_TOOL::Init()
  51. {
  52. EE_TOOL_BASE::Init();
  53. auto moveCondition =
  54. []( const SELECTION& aSel )
  55. {
  56. if( aSel.Empty() || SELECTION_CONDITIONS::OnlyType( SCH_MARKER_T )( aSel ) )
  57. return false;
  58. if( SCH_LINE_WIRE_BUS_TOOL::IsDrawingLineWireOrBus( aSel ) )
  59. return false;
  60. return true;
  61. };
  62. // Add move actions to the selection tool menu
  63. //
  64. CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
  65. selToolMenu.AddItem( EE_ACTIONS::move, moveCondition, 150 );
  66. selToolMenu.AddItem( EE_ACTIONS::drag, moveCondition, 150 );
  67. selToolMenu.AddItem( EE_ACTIONS::alignToGrid, moveCondition, 150 );
  68. return true;
  69. }
  70. int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
  71. {
  72. EESCHEMA_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
  73. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  74. EE_GRID_HELPER grid( m_toolMgr );
  75. bool wasDragging = m_moveInProgress && m_isDrag;
  76. m_anchorPos.reset();
  77. if( aEvent.IsAction( &EE_ACTIONS::move ) )
  78. m_isDrag = false;
  79. else if( aEvent.IsAction( &EE_ACTIONS::drag ) )
  80. m_isDrag = true;
  81. else if( aEvent.IsAction( &EE_ACTIONS::moveActivate ) )
  82. m_isDrag = !cfg->m_Input.drag_is_move;
  83. else
  84. return 0;
  85. if( m_moveInProgress )
  86. {
  87. if( m_isDrag != wasDragging )
  88. {
  89. EDA_ITEM* sel = m_selectionTool->GetSelection().Front();
  90. if( sel && !sel->IsNew() )
  91. {
  92. // Reset the selected items so we can start again with the current m_isDrag
  93. // state.
  94. m_frame->RollbackSchematicFromUndo();
  95. m_selectionTool->RemoveItemsFromSel( &m_dragAdditions, QUIET_MODE );
  96. m_anchorPos = m_cursor - m_moveOffset;
  97. m_moveInProgress = false;
  98. controls->SetAutoPan( false );
  99. // And give it a kick so it doesn't have to wait for the first mouse movement
  100. // to refresh.
  101. m_toolMgr->RunAction( EE_ACTIONS::restartMove );
  102. }
  103. }
  104. return 0;
  105. }
  106. // Be sure that there is at least one item that we can move. If there's no selection try
  107. // looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection).
  108. EE_SELECTION& selection = m_selectionTool->RequestSelection( EE_COLLECTOR::MovableItems );
  109. bool unselect = selection.IsHover();
  110. // Keep an original copy of the starting points for cleanup after the move
  111. std::vector<DANGLING_END_ITEM> internalPoints;
  112. Activate();
  113. // Must be done after Activate() so that it gets set into the correct context
  114. controls->ShowCursor( true );
  115. std::string tool = aEvent.GetCommandStr().get();
  116. m_frame->PushTool( tool );
  117. if( selection.Empty() )
  118. {
  119. // Note that it's important to go through push/pop even when the selection is empty.
  120. // This keeps other tools from having to special-case an empty move.
  121. m_frame->PopTool( tool );
  122. return 0;
  123. }
  124. bool restore_state = false;
  125. bool chain_commands = false;
  126. TOOL_EVENT* evt = const_cast<TOOL_EVENT*>( &aEvent );
  127. VECTOR2I prevPos;
  128. int snapLayer = UNDEFINED_LAYER;
  129. m_cursor = controls->GetCursorPosition();
  130. // Main loop: keep receiving events
  131. do
  132. {
  133. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
  134. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  135. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  136. if( evt->IsAction( &EE_ACTIONS::moveActivate )
  137. || evt->IsAction( &EE_ACTIONS::restartMove )
  138. || evt->IsAction( &EE_ACTIONS::move )
  139. || evt->IsAction( &EE_ACTIONS::drag )
  140. || evt->IsMotion()
  141. || evt->IsDrag( BUT_LEFT )
  142. || evt->IsAction( &ACTIONS::refreshPreview ) )
  143. {
  144. if( !m_moveInProgress ) // Prepare to start moving/dragging
  145. {
  146. SCH_ITEM* sch_item = (SCH_ITEM*) selection.Front();
  147. bool appendUndo = sch_item && sch_item->IsNew();
  148. bool placingNewItems = sch_item && sch_item->IsNew();
  149. //------------------------------------------------------------------------
  150. // Setup a drag or a move
  151. //
  152. m_dragAdditions.clear();
  153. m_specialCaseLabels.clear();
  154. internalPoints.clear();
  155. for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
  156. {
  157. it->ClearFlags( TEMP_SELECTED );
  158. if( !it->IsSelected() )
  159. it->ClearFlags( STARTPOINT | ENDPOINT );
  160. if( !selection.IsHover() && it->IsSelected() )
  161. it->SetFlags( STARTPOINT | ENDPOINT );
  162. }
  163. if( m_isDrag )
  164. {
  165. EDA_ITEMS connectedDragItems;
  166. // Add connections to the selection for a drag.
  167. //
  168. for( EDA_ITEM* edaItem : selection )
  169. {
  170. SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
  171. std::vector<wxPoint> connections;
  172. if( item->Type() == SCH_LINE_T )
  173. static_cast<SCH_LINE*>( item )->GetSelectedPoints( connections );
  174. else
  175. connections = item->GetConnectionPoints();
  176. for( wxPoint point : connections )
  177. getConnectedDragItems( item, point, connectedDragItems );
  178. }
  179. for( EDA_ITEM* item : connectedDragItems )
  180. {
  181. m_dragAdditions.push_back( item->m_Uuid );
  182. m_selectionTool->AddItemToSel( item, QUIET_MODE );
  183. }
  184. }
  185. else
  186. {
  187. // Mark the edges of the block with dangling flags for a move.
  188. for( EDA_ITEM* item : selection )
  189. static_cast<SCH_ITEM*>( item )->GetEndPoints( internalPoints );
  190. for( EDA_ITEM* item : selection )
  191. static_cast<SCH_ITEM*>( item )->UpdateDanglingState( internalPoints );
  192. }
  193. // Generic setup
  194. //
  195. for( EDA_ITEM* item : selection )
  196. {
  197. if( static_cast<SCH_ITEM*>( item )->IsConnectable() )
  198. {
  199. if( snapLayer == LAYER_GRAPHICS )
  200. snapLayer = LAYER_ANY;
  201. else
  202. snapLayer = LAYER_CONNECTABLE;
  203. }
  204. else
  205. {
  206. if( snapLayer == LAYER_CONNECTABLE )
  207. snapLayer = LAYER_ANY;
  208. else
  209. snapLayer = LAYER_GRAPHICS;
  210. }
  211. if( item->IsNew() )
  212. {
  213. if( item->HasFlag( TEMP_SELECTED ) && m_isDrag )
  214. {
  215. // Item was added in getConnectedDragItems
  216. saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::NEWITEM, appendUndo );
  217. appendUndo = true;
  218. }
  219. else
  220. {
  221. // Item was added in a previous command (and saved to undo by
  222. // that command)
  223. }
  224. }
  225. else if( item->GetParent() && item->GetParent()->IsSelected() )
  226. {
  227. // Item will be (or has been) saved to undo by parent
  228. }
  229. else
  230. {
  231. saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::CHANGED, appendUndo );
  232. appendUndo = true;
  233. }
  234. SCH_ITEM* schItem = (SCH_ITEM*) item;
  235. schItem->SetStoredPos( schItem->GetPosition() );
  236. }
  237. // Set up the starting position and move/drag offset
  238. //
  239. m_cursor = controls->GetCursorPosition();
  240. if( evt->IsAction( &EE_ACTIONS::restartMove ) )
  241. {
  242. wxASSERT_MSG( m_anchorPos, "Should be already set from previous cmd" );
  243. }
  244. else if( placingNewItems )
  245. {
  246. m_anchorPos = selection.GetReferencePoint();
  247. }
  248. if( m_anchorPos )
  249. {
  250. VECTOR2I delta = m_cursor - (*m_anchorPos);
  251. bool isPasted = false;
  252. // Drag items to the current cursor position
  253. for( EDA_ITEM* item : selection )
  254. {
  255. // Don't double move pins, fields, etc.
  256. if( item->GetParent() && item->GetParent()->IsSelected() )
  257. continue;
  258. moveItem( item, delta );
  259. updateItem( item, false );
  260. isPasted |= item->GetFlags() & IS_PASTED;
  261. item->ClearFlags( IS_PASTED );
  262. }
  263. // The first time pasted items are moved we need to store
  264. // the position of the cursor so that rotate while moving
  265. // works as expected (instead of around the original anchor
  266. // point
  267. if( isPasted )
  268. selection.SetReferencePoint( m_cursor );
  269. m_anchorPos = m_cursor;
  270. }
  271. // For some items, moving the cursor to anchor is not good (for instance large
  272. // hierarchical sheets or symbols can have the anchor outside the view)
  273. else if( selection.Size() == 1 && !sch_item->IsMovableFromAnchorPoint() )
  274. {
  275. m_cursor = getViewControls()->GetCursorPosition( true );
  276. m_anchorPos = m_cursor;
  277. }
  278. else
  279. {
  280. if( m_frame->GetMoveWarpsCursor() )
  281. {
  282. // User wants to warp the mouse
  283. m_cursor = grid.BestDragOrigin( m_cursor, snapLayer, selection );
  284. selection.SetReferencePoint( m_cursor );
  285. }
  286. else
  287. {
  288. // User does not want to warp the mouse
  289. m_cursor = getViewControls()->GetCursorPosition( true );
  290. }
  291. }
  292. controls->SetCursorPosition( m_cursor, false );
  293. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  294. prevPos = m_cursor;
  295. controls->SetAutoPan( true );
  296. m_moveInProgress = true;
  297. }
  298. //------------------------------------------------------------------------
  299. // Follow the mouse
  300. //
  301. m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ),
  302. snapLayer, selection );
  303. VECTOR2I delta( m_cursor - prevPos );
  304. m_anchorPos = m_cursor;
  305. m_moveOffset += delta;
  306. prevPos = m_cursor;
  307. for( EDA_ITEM* item : selection )
  308. {
  309. // Don't double move pins, fields, etc.
  310. if( item->GetParent() && item->GetParent()->IsSelected() )
  311. continue;
  312. moveItem( item, delta );
  313. updateItem( item, false );
  314. }
  315. if( selection.HasReferencePoint() )
  316. selection.SetReferencePoint( selection.GetReferencePoint() + delta );
  317. m_toolMgr->PostEvent( EVENTS::SelectedItemsMoved );
  318. }
  319. //------------------------------------------------------------------------
  320. // Handle cancel
  321. //
  322. else if( evt->IsCancelInteractive() || evt->IsActivate() )
  323. {
  324. if( m_moveInProgress )
  325. {
  326. if( evt->IsActivate() )
  327. {
  328. // Allowing other tools to activate during a move runs the risk of race
  329. // conditions in which we try to spool up both event loops at once.
  330. if( m_isDrag )
  331. m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel drag." ) );
  332. else
  333. m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel move." ) );
  334. evt->SetPassEvent( false );
  335. continue;
  336. }
  337. evt->SetPassEvent( false );
  338. restore_state = true;
  339. }
  340. break;
  341. }
  342. //------------------------------------------------------------------------
  343. // Handle TOOL_ACTION special cases
  344. //
  345. else if( evt->Action() == TA_UNDO_REDO_PRE )
  346. {
  347. unselect = true;
  348. break;
  349. }
  350. else if( evt->IsAction( &ACTIONS::doDelete ) )
  351. {
  352. evt->SetPassEvent();
  353. // Exit on a delete; there will no longer be anything to drag.
  354. break;
  355. }
  356. else if( evt->IsAction( &ACTIONS::duplicate ) )
  357. {
  358. if( selection.Front()->IsNew() )
  359. {
  360. // This doesn't really make sense; we'll just end up dragging a stack of
  361. // objects so we ignore the duplicate and just carry on.
  362. continue;
  363. }
  364. // Move original back and exit. The duplicate will run in its own loop.
  365. restore_state = true;
  366. unselect = false;
  367. chain_commands = true;
  368. break;
  369. }
  370. else if( evt->IsAction( &EE_ACTIONS::rotateCW )
  371. || evt->IsAction( &EE_ACTIONS::rotateCCW )
  372. || evt->IsAction( &EE_ACTIONS::mirrorH )
  373. || evt->IsAction( &EE_ACTIONS::mirrorV ) )
  374. {
  375. evt->SetPassEvent();
  376. }
  377. else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
  378. {
  379. if( evt->GetCommandId().get() >= ID_POPUP_SCH_SELECT_UNIT_CMP
  380. && evt->GetCommandId().get() <= ID_POPUP_SCH_SELECT_UNIT_SYM_MAX )
  381. {
  382. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
  383. int unit = evt->GetCommandId().get() - ID_POPUP_SCH_SELECT_UNIT_CMP;
  384. if( symbol )
  385. {
  386. m_frame->SelectUnit( symbol, unit );
  387. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  388. }
  389. }
  390. }
  391. //------------------------------------------------------------------------
  392. // Handle context menu
  393. //
  394. else if( evt->IsClick( BUT_RIGHT ) )
  395. {
  396. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  397. }
  398. //------------------------------------------------------------------------
  399. // Handle drop
  400. //
  401. else if( evt->IsMouseUp( BUT_LEFT )
  402. || evt->IsClick( BUT_LEFT )
  403. || evt->IsDblClick( BUT_LEFT ) )
  404. {
  405. break; // Finish
  406. }
  407. else
  408. {
  409. evt->SetPassEvent();
  410. }
  411. controls->SetAutoPan( m_moveInProgress );
  412. } while( ( evt = Wait() ) ); //Should be assignment not equality test
  413. controls->ForceCursorPosition( false );
  414. controls->ShowCursor( false );
  415. controls->SetAutoPan( false );
  416. if( !chain_commands )
  417. m_moveOffset = { 0, 0 };
  418. m_anchorPos.reset();
  419. for( EDA_ITEM* item : selection )
  420. item->ClearEditFlags();
  421. if( restore_state )
  422. {
  423. m_selectionTool->RemoveItemsFromSel( &m_dragAdditions, QUIET_MODE );
  424. m_frame->RollbackSchematicFromUndo();
  425. }
  426. else
  427. {
  428. // One last update after exiting loop (for slower stuff, such as updating SCREEN's RTree).
  429. for( EDA_ITEM* item : selection )
  430. updateItem( item, true );
  431. EE_SELECTION selectionCopy( selection );
  432. m_selectionTool->RemoveItemsFromSel( &m_dragAdditions, QUIET_MODE );
  433. // If we move items away from a junction, we _may_ want to add a junction there
  434. // to denote the state.
  435. for( const DANGLING_END_ITEM& it : internalPoints )
  436. {
  437. if( m_frame->GetScreen()->IsExplicitJunctionNeeded( it.GetPosition()) )
  438. m_frame->AddJunction( m_frame->GetScreen(), it.GetPosition(), true, false );
  439. }
  440. m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selectionCopy );
  441. m_frame->RecalculateConnections( LOCAL_CLEANUP );
  442. m_frame->TestDanglingEnds();
  443. m_frame->OnModify();
  444. }
  445. if( unselect )
  446. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  447. else
  448. m_selectionTool->RebuildSelection(); // Schematic cleanup might have merged lines, etc.
  449. m_dragAdditions.clear();
  450. m_moveInProgress = false;
  451. m_frame->PopTool( tool );
  452. return 0;
  453. }
  454. void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const wxPoint& aPoint,
  455. EDA_ITEMS& aList )
  456. {
  457. EE_RTREE& items = m_frame->GetScreen()->Items();
  458. EE_RTREE::EE_TYPE itemsOverlapping = items.Overlapping( aOriginalItem->GetBoundingBox() );
  459. bool ptHasUnselectedJunction = false;
  460. for( SCH_ITEM* item : itemsOverlapping )
  461. {
  462. if( item->Type() == SCH_JUNCTION_T && item->IsConnected( aPoint ) && !item->IsSelected() )
  463. {
  464. ptHasUnselectedJunction = true;
  465. break;
  466. }
  467. }
  468. for( SCH_ITEM* test : itemsOverlapping )
  469. {
  470. if( test == aOriginalItem || test->IsSelected() || !test->CanConnect( aOriginalItem ) )
  471. continue;
  472. KICAD_T testType = test->Type();
  473. switch( testType )
  474. {
  475. case SCH_LINE_T:
  476. {
  477. // Select the connected end of wires/bus connections that don't have an unselected
  478. // junction isolating them from the drag
  479. if( ptHasUnselectedJunction )
  480. break;
  481. SCH_LINE* line = static_cast<SCH_LINE*>( test );
  482. if( line->GetStartPoint() == aPoint )
  483. {
  484. if( !line->HasFlag(TEMP_SELECTED ) )
  485. aList.push_back( line );
  486. line->SetFlags(STARTPOINT | TEMP_SELECTED );
  487. }
  488. else if( line->GetEndPoint() == aPoint )
  489. {
  490. if( !line->HasFlag(TEMP_SELECTED ) )
  491. aList.push_back( line );
  492. line->SetFlags(ENDPOINT | TEMP_SELECTED );
  493. }
  494. else
  495. {
  496. break;
  497. }
  498. // Since only one end is going to move, the movement vector of any labels attached
  499. // to it is scaled by the proportion of the line length the label is from the moving
  500. // end.
  501. for( SCH_ITEM* item : itemsOverlapping )
  502. {
  503. if( item->Type() == SCH_LABEL_T )
  504. {
  505. SCH_TEXT* label = static_cast<SCH_TEXT*>( item );
  506. if( label->IsSelected() )
  507. continue; // These will be moved on their own because they're selected
  508. if( label->HasFlag( TEMP_SELECTED ) )
  509. continue;
  510. if( label->CanConnect( line ) && line->HitTest( label->GetPosition(), 1 ) )
  511. {
  512. label->SetFlags( TEMP_SELECTED );
  513. aList.push_back( label );
  514. SPECIAL_CASE_LABEL_INFO info;
  515. info.attachedLine = line;
  516. info.originalLabelPos = label->GetPosition();
  517. m_specialCaseLabels[label] = info;
  518. }
  519. }
  520. }
  521. break;
  522. }
  523. case SCH_SHEET_T:
  524. case SCH_SYMBOL_T:
  525. case SCH_JUNCTION_T:
  526. if( test->IsConnected( aPoint ) )
  527. {
  528. // Add a new wire between the symbol or junction and the selected item so
  529. // the selected item can be dragged.
  530. SCH_LINE* newWire = nullptr;
  531. if( test->GetLayer() == LAYER_BUS_JUNCTION ||
  532. aOriginalItem->GetLayer() == LAYER_BUS )
  533. {
  534. newWire = new SCH_LINE( aPoint, LAYER_BUS );
  535. }
  536. else
  537. newWire = new SCH_LINE( aPoint, LAYER_WIRE );
  538. newWire->SetFlags( IS_NEW );
  539. m_frame->AddToScreen( newWire, m_frame->GetScreen() );
  540. newWire->SetFlags( TEMP_SELECTED | STARTPOINT );
  541. aList.push_back( newWire );
  542. }
  543. break;
  544. case SCH_NO_CONNECT_T:
  545. // Select no-connects that are connected to items being moved.
  546. if( !test->HasFlag( TEMP_SELECTED ) && test->IsConnected( aPoint ) )
  547. {
  548. aList.push_back( test );
  549. test->SetFlags( TEMP_SELECTED );
  550. }
  551. break;
  552. case SCH_LABEL_T:
  553. case SCH_GLOBAL_LABEL_T:
  554. case SCH_HIER_LABEL_T:
  555. // Performance optimization:
  556. if( test->HasFlag( TEMP_SELECTED ) )
  557. break;
  558. // Select labels that are connected to a wire (or bus) being moved.
  559. if( aOriginalItem->Type() == SCH_LINE_T && test->CanConnect( aOriginalItem ) )
  560. {
  561. SCH_TEXT* label = static_cast<SCH_TEXT*>( test );
  562. SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
  563. bool oneEndFixed = !line->HasFlag( STARTPOINT ) || !line->HasFlag( ENDPOINT );
  564. if( line->HitTest( label->GetTextPos(), 1 ) )
  565. {
  566. label->SetFlags( TEMP_SELECTED );
  567. aList.push_back( label );
  568. if( oneEndFixed )
  569. {
  570. SPECIAL_CASE_LABEL_INFO info;
  571. info.attachedLine = line;
  572. info.originalLabelPos = label->GetPosition();
  573. m_specialCaseLabels[ label ] = info;
  574. }
  575. }
  576. }
  577. break;
  578. case SCH_BUS_WIRE_ENTRY_T:
  579. case SCH_BUS_BUS_ENTRY_T:
  580. // Performance optimization:
  581. if( test->HasFlag( TEMP_SELECTED ) )
  582. break;
  583. // Select bus entries that are connected to a bus being moved.
  584. if( aOriginalItem->Type() == SCH_LINE_T && test->CanConnect( aOriginalItem ) )
  585. {
  586. SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
  587. bool oneEndFixed = !line->HasFlag( STARTPOINT ) || !line->HasFlag( ENDPOINT );
  588. if( oneEndFixed )
  589. {
  590. // This is only going to end in tears, so don't go there
  591. continue;
  592. }
  593. for( wxPoint& point : test->GetConnectionPoints() )
  594. {
  595. if( line->HitTest( point, 1 ) )
  596. {
  597. test->SetFlags( TEMP_SELECTED );
  598. aList.push_back( test );
  599. // A bus entry needs its wire & label as well
  600. std::vector<wxPoint> ends = test->GetConnectionPoints();
  601. wxPoint otherEnd;
  602. if( ends[0] == point )
  603. otherEnd = ends[1];
  604. else
  605. otherEnd = ends[0];
  606. getConnectedDragItems( test, otherEnd, aList );
  607. // No need to test the other end of the bus entry
  608. break;
  609. }
  610. }
  611. }
  612. break;
  613. default:
  614. break;
  615. }
  616. }
  617. }
  618. void SCH_MOVE_TOOL::moveItem( EDA_ITEM* aItem, const VECTOR2I& aDelta )
  619. {
  620. switch( aItem->Type() )
  621. {
  622. case SCH_LINE_T:
  623. {
  624. SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
  625. if( aItem->HasFlag( STARTPOINT ) )
  626. line->MoveStart( (wxPoint) aDelta );
  627. if( aItem->HasFlag( ENDPOINT ) )
  628. line->MoveEnd( (wxPoint) aDelta );
  629. }
  630. break;
  631. case SCH_PIN_T:
  632. case SCH_FIELD_T:
  633. {
  634. SCH_ITEM* parent = (SCH_ITEM*) aItem->GetParent();
  635. wxPoint delta( aDelta );
  636. if( parent && parent->Type() == SCH_SYMBOL_T )
  637. {
  638. SCH_SYMBOL* symbol = (SCH_SYMBOL*) aItem->GetParent();
  639. TRANSFORM transform = symbol->GetTransform().InverseTransform();
  640. delta = transform.TransformCoordinate( delta );
  641. }
  642. static_cast<SCH_ITEM*>( aItem )->Move( delta );
  643. // If we're moving a field with respect to its parent then it's no longer auto-placed
  644. if( aItem->Type() == SCH_FIELD_T && parent && !parent->IsSelected() )
  645. parent->ClearFieldsAutoplaced();
  646. break;
  647. }
  648. case SCH_SHEET_PIN_T:
  649. {
  650. SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) aItem;
  651. pin->SetStoredPos( pin->GetStoredPos() + (wxPoint) aDelta );
  652. pin->ConstrainOnEdge( pin->GetStoredPos() );
  653. break;
  654. }
  655. case SCH_LABEL_T:
  656. {
  657. SCH_TEXT* label = static_cast<SCH_TEXT*>( aItem );
  658. if( m_specialCaseLabels.count( label ) )
  659. {
  660. SPECIAL_CASE_LABEL_INFO info = m_specialCaseLabels[ label ];
  661. SEG currentLine( info.attachedLine->GetStartPoint(), info.attachedLine->GetEndPoint() );
  662. label->SetPosition( (wxPoint) currentLine.NearestPoint( info.originalLabelPos ) );
  663. }
  664. else
  665. {
  666. label->Move( (wxPoint) aDelta );
  667. }
  668. break;
  669. }
  670. default:
  671. static_cast<SCH_ITEM*>( aItem )->Move( (wxPoint) aDelta );
  672. break;
  673. }
  674. getView()->Hide( aItem, false );
  675. aItem->SetFlags( IS_MOVING );
  676. }
  677. int SCH_MOVE_TOOL::AlignElements( const TOOL_EVENT& aEvent )
  678. {
  679. EE_GRID_HELPER grid( m_toolMgr);
  680. EE_SELECTION& selection = m_selectionTool->RequestSelection( EE_COLLECTOR::MovableItems );
  681. bool append_undo = false;
  682. for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
  683. {
  684. if( !it->IsSelected() )
  685. it->ClearFlags( STARTPOINT | ENDPOINT );
  686. if( !selection.IsHover() && it->IsSelected() )
  687. it->SetFlags( STARTPOINT | ENDPOINT );
  688. it->SetStoredPos( it->GetPosition() );
  689. if( it->Type() == SCH_SHEET_T )
  690. {
  691. for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( it )->GetPins() )
  692. pin->SetStoredPos( pin->GetPosition() );
  693. }
  694. }
  695. for( EDA_ITEM* item : selection )
  696. {
  697. if( item->Type() == SCH_LINE_T )
  698. {
  699. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  700. std::vector<int> flags{ STARTPOINT, ENDPOINT };
  701. std::vector<wxPoint> pts{ line->GetStartPoint(), line->GetEndPoint() };
  702. for( int ii = 0; ii < 2; ++ii )
  703. {
  704. EDA_ITEMS drag_items{ item };
  705. line->ClearFlags();
  706. line->SetFlags( flags[ii] );
  707. getConnectedDragItems( line, pts[ii], drag_items );
  708. std::set<EDA_ITEM*> unique_items( drag_items.begin(), drag_items.end() );
  709. VECTOR2I gridpt = grid.AlignGrid( pts[ii] ) - pts[ii];
  710. if( gridpt != VECTOR2I( 0, 0 ) )
  711. {
  712. for( EDA_ITEM* dragItem : unique_items )
  713. {
  714. if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
  715. continue;
  716. saveCopyInUndoList( dragItem, UNDO_REDO::CHANGED, append_undo );
  717. append_undo = true;
  718. moveItem( dragItem, gridpt );
  719. dragItem->ClearFlags( IS_MOVING );
  720. updateItem( dragItem, true );
  721. }
  722. }
  723. }
  724. }
  725. else if( item->Type() == SCH_FIELD_T )
  726. {
  727. VECTOR2I gridpt = grid.AlignGrid( item->GetPosition() ) - item->GetPosition();
  728. if( gridpt != VECTOR2I( 0, 0 ) )
  729. {
  730. saveCopyInUndoList( item, UNDO_REDO::CHANGED, append_undo );
  731. append_undo = true;
  732. moveItem( item, gridpt );
  733. updateItem( item, true );
  734. item->ClearFlags( IS_MOVING );
  735. }
  736. }
  737. else
  738. {
  739. std::vector<wxPoint> connections;
  740. EDA_ITEMS drag_items{ item };
  741. connections = static_cast<SCH_ITEM*>( item )->GetConnectionPoints();
  742. for( const wxPoint& point : connections )
  743. getConnectedDragItems( static_cast<SCH_ITEM*>( item ), point, drag_items );
  744. std::map<VECTOR2I, int> shifts;
  745. VECTOR2I most_common( 0, 0 );
  746. int max_count = 0;
  747. for( const wxPoint& conn : connections )
  748. {
  749. VECTOR2I gridpt = grid.AlignGrid( conn ) - conn;
  750. shifts[gridpt]++;
  751. if( shifts[gridpt] > max_count )
  752. {
  753. most_common = gridpt;
  754. max_count = shifts[most_common];
  755. }
  756. }
  757. if( most_common != VECTOR2I( 0, 0 ) )
  758. {
  759. for( EDA_ITEM* dragItem : drag_items )
  760. {
  761. if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
  762. continue;
  763. saveCopyInUndoList( dragItem, UNDO_REDO::CHANGED, append_undo );
  764. append_undo = true;
  765. moveItem( dragItem, most_common );
  766. dragItem->ClearFlags( IS_MOVING );
  767. updateItem( dragItem, true );
  768. }
  769. }
  770. }
  771. }
  772. m_toolMgr->PostEvent( EVENTS::SelectedItemsMoved );
  773. m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selection );
  774. m_frame->RecalculateConnections( LOCAL_CLEANUP );
  775. m_frame->TestDanglingEnds();
  776. m_frame->OnModify();
  777. return 0;
  778. }
  779. void SCH_MOVE_TOOL::setTransitions()
  780. {
  781. Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::moveActivate.MakeEvent() );
  782. Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::move.MakeEvent() );
  783. Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::drag.MakeEvent() );
  784. Go( &SCH_MOVE_TOOL::AlignElements, EE_ACTIONS::alignToGrid.MakeEvent() );
  785. }