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.

380 lines
13 KiB

  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_selection_tool.h>
  26. #include <ee_actions.h>
  27. #include <bitmaps.h>
  28. #include <eda_item.h>
  29. #include <wx/log.h>
  30. #include "symbol_editor_move_tool.h"
  31. #include "symbol_editor_pin_tool.h"
  32. SYMBOL_EDITOR_MOVE_TOOL::SYMBOL_EDITOR_MOVE_TOOL() :
  33. EE_TOOL_BASE( "eeschema.SymbolMoveTool" ),
  34. m_moveInProgress( false ),
  35. m_moveOffset( 0, 0 )
  36. {
  37. }
  38. bool SYMBOL_EDITOR_MOVE_TOOL::Init()
  39. {
  40. EE_TOOL_BASE::Init();
  41. //
  42. // Add move actions to the selection tool menu
  43. //
  44. CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
  45. auto canMove =
  46. [&]( const SELECTION& sel )
  47. {
  48. SYMBOL_EDIT_FRAME* editor = static_cast<SYMBOL_EDIT_FRAME*>( m_frame );
  49. wxCHECK( editor, false );
  50. if( !editor->IsSymbolEditable() )
  51. return false;
  52. if( editor->IsSymbolAlias() )
  53. {
  54. for( EDA_ITEM* item : sel )
  55. {
  56. if( item->Type() != LIB_FIELD_T )
  57. return false;
  58. }
  59. }
  60. return true;
  61. };
  62. selToolMenu.AddItem( EE_ACTIONS::move, canMove && EE_CONDITIONS::IdleSelection, 150 );
  63. return true;
  64. }
  65. void SYMBOL_EDITOR_MOVE_TOOL::Reset( RESET_REASON aReason )
  66. {
  67. EE_TOOL_BASE::Reset( aReason );
  68. if( aReason == MODEL_RELOAD )
  69. {
  70. m_moveInProgress = false;
  71. m_moveOffset = { 0, 0 };
  72. }
  73. }
  74. int SYMBOL_EDITOR_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
  75. {
  76. static KICAD_T fieldsOnly[] = { LIB_FIELD_T, EOT };
  77. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  78. m_anchorPos = { 0, 0 };
  79. // Be sure that there is at least one item that we can move. If there's no selection try
  80. // looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection).
  81. EE_SELECTION& selection = m_frame->IsSymbolAlias()
  82. ? m_selectionTool->RequestSelection( fieldsOnly )
  83. : m_selectionTool->RequestSelection();
  84. bool unselect = selection.IsHover();
  85. if( !m_frame->IsSymbolEditable() || selection.Empty() || m_moveInProgress )
  86. return 0;
  87. std::string tool = aEvent.GetCommandStr().get();
  88. m_frame->PushTool( tool );
  89. Activate();
  90. // Must be done after Activate() so that it gets set into the correct context
  91. controls->ShowCursor( true );
  92. controls->SetAutoPan( true );
  93. bool restore_state = false;
  94. bool chain_commands = false;
  95. TOOL_EVENT* evt = const_cast<TOOL_EVENT*>( &aEvent );
  96. VECTOR2I prevPos;
  97. if( !selection.Front()->IsNew() )
  98. saveCopyInUndoList( m_frame->GetCurSymbol(), UNDO_REDO::LIBEDIT );
  99. m_cursor = controls->GetCursorPosition();
  100. // Main loop: keep receiving events
  101. do
  102. {
  103. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
  104. if( evt->IsAction( &EE_ACTIONS::move )
  105. || evt->IsMotion()
  106. || evt->IsDrag( BUT_LEFT )
  107. || evt->IsAction( &ACTIONS::refreshPreview )
  108. || evt->IsAction( &EE_ACTIONS::symbolMoveActivate ) )
  109. {
  110. if( !m_moveInProgress ) // Prepare to start moving/dragging
  111. {
  112. LIB_ITEM* lib_item = static_cast<LIB_ITEM*>( selection.Front() );
  113. // Pick up any synchronized pins
  114. //
  115. // Careful when pasting. The pasted pin will be at the same location as it
  116. // was copied from, leading us to believe it's a synchronized pin. It's not.
  117. if( m_frame->SynchronizePins()
  118. && ( lib_item->GetEditFlags() & IS_PASTED ) == 0 )
  119. {
  120. std::set<LIB_PIN*> sync_pins;
  121. for( EDA_ITEM* sel_item : selection )
  122. {
  123. lib_item = static_cast<LIB_ITEM*>( sel_item );
  124. if( lib_item->Type() == LIB_PIN_T )
  125. {
  126. LIB_PIN* cur_pin = static_cast<LIB_PIN*>( lib_item );
  127. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  128. std::vector<bool> got_unit( symbol->GetUnitCount() );
  129. got_unit[cur_pin->GetUnit()] = true;
  130. for( LIB_PIN* pin = symbol->GetNextPin(); pin;
  131. pin = symbol->GetNextPin( pin ) )
  132. {
  133. if( !got_unit[pin->GetUnit()]
  134. && pin->GetPosition() == cur_pin->GetPosition()
  135. && pin->GetOrientation() == cur_pin->GetOrientation()
  136. && pin->GetConvert() == cur_pin->GetConvert()
  137. && pin->GetType() == cur_pin->GetType()
  138. && pin->GetName() == cur_pin->GetName() )
  139. {
  140. if( sync_pins.insert( pin ).second )
  141. got_unit[pin->GetUnit()] = true;
  142. }
  143. }
  144. }
  145. }
  146. for( LIB_PIN* pin : sync_pins )
  147. m_selectionTool->AddItemToSel( pin, true /*quiet mode*/ );
  148. }
  149. // Apply any initial offset in case we're coming from a previous command.
  150. //
  151. for( EDA_ITEM* item : selection )
  152. moveItem( item, m_moveOffset );
  153. // Set up the starting position and move/drag offset
  154. //
  155. m_cursor = controls->GetCursorPosition();
  156. if( lib_item->IsNew() )
  157. {
  158. m_anchorPos = selection.GetReferencePoint();
  159. VECTOR2I delta = m_cursor - mapCoords( m_anchorPos );
  160. // Drag items to the current cursor position
  161. for( EDA_ITEM* item : selection )
  162. {
  163. moveItem( item, delta );
  164. updateItem( item, false );
  165. }
  166. m_anchorPos = m_cursor;
  167. }
  168. else if( selection.Size() == 1 && m_frame->GetMoveWarpsCursor() )
  169. {
  170. wxPoint itemPos = lib_item->GetPosition();
  171. m_anchorPos = wxPoint( itemPos.x, -itemPos.y );
  172. getViewControls()->WarpCursor( m_anchorPos, true, true );
  173. m_cursor = m_anchorPos;
  174. }
  175. else
  176. {
  177. m_cursor = getViewControls()->GetCursorPosition( true );
  178. m_anchorPos = m_cursor;
  179. }
  180. controls->SetCursorPosition( m_cursor, false );
  181. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  182. prevPos = m_cursor;
  183. controls->SetAutoPan( true );
  184. m_moveInProgress = true;
  185. }
  186. //------------------------------------------------------------------------
  187. // Follow the mouse
  188. //
  189. m_cursor = controls->GetCursorPosition();
  190. VECTOR2I delta( m_cursor - prevPos );
  191. m_anchorPos = m_cursor;
  192. m_moveOffset += delta;
  193. prevPos = m_cursor;
  194. for( EDA_ITEM* item : selection )
  195. {
  196. moveItem( item, delta );
  197. updateItem( item, false );
  198. }
  199. m_toolMgr->PostEvent( EVENTS::SelectedItemsMoved );
  200. }
  201. //------------------------------------------------------------------------
  202. // Handle cancel
  203. //
  204. else if( evt->IsCancelInteractive() || evt->IsActivate() )
  205. {
  206. if( m_moveInProgress )
  207. {
  208. evt->SetPassEvent( false );
  209. restore_state = true;
  210. }
  211. break;
  212. }
  213. //------------------------------------------------------------------------
  214. // Handle TOOL_ACTION special cases
  215. //
  216. else if( evt->Action() == TA_UNDO_REDO_PRE )
  217. {
  218. unselect = true;
  219. break;
  220. }
  221. else if( evt->Category() == TC_COMMAND )
  222. {
  223. if( evt->IsAction( &ACTIONS::doDelete ) )
  224. {
  225. // Exit on a remove operation; there is no further processing for removed items.
  226. break;
  227. }
  228. else if( evt->IsAction( &ACTIONS::duplicate ) )
  229. {
  230. if( selection.Front()->IsNew() )
  231. {
  232. // This doesn't really make sense; we'll just end up dragging a stack of
  233. // objects so Duplicate() is going to ignore this and we'll just carry on.
  234. continue;
  235. }
  236. // Move original back and exit. The duplicate will run in its own loop.
  237. restore_state = true;
  238. unselect = false;
  239. chain_commands = true;
  240. break;
  241. }
  242. else
  243. {
  244. evt->SetPassEvent();
  245. }
  246. }
  247. //------------------------------------------------------------------------
  248. // Handle context menu
  249. //
  250. else if( evt->IsClick( BUT_RIGHT ) )
  251. {
  252. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  253. }
  254. //------------------------------------------------------------------------
  255. // Handle drop
  256. //
  257. else if( evt->IsMouseUp( BUT_LEFT )
  258. || evt->IsClick( BUT_LEFT )
  259. || evt->IsDblClick( BUT_LEFT ) )
  260. {
  261. if( selection.GetSize() == 1 && selection.Front()->Type() == LIB_PIN_T )
  262. {
  263. SYMBOL_EDITOR_PIN_TOOL* pinTool = m_toolMgr->GetTool<SYMBOL_EDITOR_PIN_TOOL>();
  264. try
  265. {
  266. if( !pinTool->PlacePin( (LIB_PIN*) selection.Front() ) )
  267. restore_state = true;
  268. }
  269. catch( const boost::bad_pointer& e )
  270. {
  271. restore_state = true;
  272. wxLogError( "Boost pointer exception occurred: \"%s\"", e.what() );
  273. }
  274. }
  275. break; // Finish
  276. }
  277. else
  278. {
  279. evt->SetPassEvent();
  280. }
  281. } while( ( evt = Wait() ) ); // Assignment intentional; not equality test
  282. controls->ForceCursorPosition( false );
  283. controls->ShowCursor( false );
  284. controls->SetAutoPan( false );
  285. if( !chain_commands )
  286. m_moveOffset = { 0, 0 };
  287. m_anchorPos = { 0, 0 };
  288. for( EDA_ITEM* item : selection )
  289. item->ClearEditFlags();
  290. if( restore_state )
  291. {
  292. m_frame->RollbackSymbolFromUndo();
  293. if( unselect )
  294. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  295. else
  296. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  297. }
  298. else
  299. {
  300. if( unselect )
  301. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  302. m_frame->OnModify();
  303. }
  304. m_moveInProgress = false;
  305. m_frame->PopTool( tool );
  306. return 0;
  307. }
  308. void SYMBOL_EDITOR_MOVE_TOOL::moveItem( EDA_ITEM* aItem, VECTOR2I aDelta )
  309. {
  310. static_cast<LIB_ITEM*>( aItem )->Offset( mapCoords( aDelta ) );
  311. aItem->SetFlags( IS_MOVING );
  312. }
  313. void SYMBOL_EDITOR_MOVE_TOOL::setTransitions()
  314. {
  315. Go( &SYMBOL_EDITOR_MOVE_TOOL::Main, EE_ACTIONS::move.MakeEvent() );
  316. Go( &SYMBOL_EDITOR_MOVE_TOOL::Main, EE_ACTIONS::symbolMoveActivate.MakeEvent() );
  317. }