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.

229 lines
7.1 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2013 CERN
  5. * @author Maciej Suminski <maciej.suminski@cern.ch>
  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 <class_board.h>
  25. #include <class_module.h>
  26. #include <tool/tool_manager.h>
  27. #include <tool/tool_action.h>
  28. #include <view/view_controls.h>
  29. #include "selection_tool.h"
  30. #include "move_tool.h"
  31. using namespace KiGfx;
  32. using boost::optional;
  33. MOVE_TOOL::MOVE_TOOL() :
  34. TOOL_INTERACTIVE( "pcbnew.InteractiveMove" ), m_selectionTool( NULL ),
  35. m_activate( m_toolName, AS_GLOBAL, 'M', "Move", "Moves the selected item(s)" ),
  36. m_rotate( m_toolName + ".rotate", AS_CONTEXT, ' ', "Rotate", "Rotates selected item(s)" ),
  37. m_flip( m_toolName + ".flip", AS_CONTEXT, 'F', "Flip", "Flips selected item(s)" )
  38. {
  39. }
  40. MOVE_TOOL::~MOVE_TOOL()
  41. {
  42. }
  43. void MOVE_TOOL::Reset()
  44. {
  45. // The tool launches upon reception of action event ("pcbnew.InteractiveMove")
  46. Go( &MOVE_TOOL::Main, m_activate.GetEvent() );
  47. }
  48. bool MOVE_TOOL::Init()
  49. {
  50. // Find the selection tool, so they can cooperate
  51. TOOL_BASE* selectionTool = m_toolMgr->FindTool( "pcbnew.InteractiveSelection" );
  52. if( selectionTool )
  53. {
  54. m_selectionTool = static_cast<SELECTION_TOOL*>( selectionTool );
  55. // Activate hot keys
  56. m_toolMgr->RegisterAction( &m_activate );
  57. m_toolMgr->RegisterAction( &m_rotate );
  58. m_toolMgr->RegisterAction( &m_flip );
  59. // Add context menu entries for the selection tool
  60. m_selectionTool->AddMenuItem( m_activate );
  61. m_selectionTool->AddMenuItem( m_rotate );
  62. m_selectionTool->AddMenuItem( m_flip );
  63. }
  64. else
  65. {
  66. wxLogError( "pcbnew.InteractiveSelection tool is not available" );
  67. return false;
  68. }
  69. return true;
  70. }
  71. int MOVE_TOOL::Main( TOOL_EVENT& aEvent )
  72. {
  73. VECTOR2D dragPosition;
  74. bool dragging = false;
  75. bool restore = false; // Should items' state be restored when finishing the tool?
  76. VIEW* view = getView();
  77. VIEW_CONTROLS* controls = getViewControls();
  78. view->Add( &m_items );
  79. controls->ShowCursor( true );
  80. controls->SetSnapping( true );
  81. controls->SetAutoPan( true );
  82. // Main loop: keep receiving events
  83. while( OPT_TOOL_EVENT evt = Wait() )
  84. {
  85. if( evt->IsCancel() )
  86. {
  87. restore = true;
  88. break; // Finish
  89. }
  90. // Dispatch TOOL_ACTIONs
  91. else if( evt->Category() == TC_Command )
  92. {
  93. VECTOR2D cursorPos = getView()->ToWorld( getViewControls()->GetCursorPosition() );
  94. if( evt->Matches( m_rotate.GetEvent() ) )
  95. {
  96. m_state.Rotate( cursorPos, 900.0 );
  97. m_items.ViewUpdate( VIEW_ITEM::GEOMETRY );
  98. }
  99. else if( evt->Matches( m_flip.GetEvent() ) )
  100. {
  101. m_state.Flip( cursorPos );
  102. m_items.ViewUpdate( VIEW_ITEM::GEOMETRY );
  103. }
  104. }
  105. else if( evt->IsMotion() || evt->IsDrag( MB_Left ) )
  106. {
  107. if( dragging )
  108. {
  109. // Drag items to the current cursor position
  110. VECTOR2D movement = ( evt->Position() - dragPosition );
  111. m_state.Move( movement );
  112. }
  113. else
  114. {
  115. // Prepare to drag
  116. m_selection = m_selectionTool->GetSelection();
  117. if( m_selection.empty() )
  118. break; // there are no items to operate on
  119. std::set<BOARD_ITEM*>::iterator it;
  120. for( it = m_selection.begin(); it != m_selection.end(); ++it )
  121. {
  122. // Save the state of the selected items, in case it has to be restored
  123. m_state.Save( *it );
  124. // Gather all selected items into one VIEW_GROUP
  125. viewGroupAdd( *it, &m_items );
  126. }
  127. // Hide the original items, they are temporarily shown in VIEW_GROUP on overlay
  128. vgSetVisibility( &m_items, false );
  129. vgUpdate( &m_items, VIEW_ITEM::APPEARANCE );
  130. dragging = true;
  131. }
  132. m_items.ViewUpdate( VIEW_ITEM::GEOMETRY );
  133. dragPosition = evt->Position();
  134. }
  135. else if( evt->IsMouseUp( MB_Left ) || evt->IsClick( MB_Left ) )
  136. break; // Finish
  137. }
  138. // Restore visibility of the original items
  139. vgSetVisibility( &m_items, true );
  140. // Movement has to be rollbacked, so restore the previous state of items
  141. if( restore )
  142. {
  143. vgUpdate( &m_items, VIEW_ITEM::APPEARANCE );
  144. m_state.RestoreAll();
  145. }
  146. else
  147. {
  148. vgUpdate( &m_items, m_state.GetUpdateFlag() );
  149. m_state.Apply();
  150. }
  151. m_items.Clear();
  152. view->Remove( &m_items );
  153. controls->ShowCursor( false );
  154. controls->SetSnapping( false );
  155. controls->SetAutoPan( false );
  156. return 0;
  157. }
  158. void MOVE_TOOL::viewGroupAdd( BOARD_ITEM* aItem, VIEW_GROUP* aGroup )
  159. {
  160. // Modules are treated in a special way - when they are moved, we have to
  161. // move all the parts that make the module, not the module itself
  162. if( aItem->Type() == PCB_MODULE_T )
  163. {
  164. MODULE* module = static_cast<MODULE*>( aItem );
  165. // Add everything that belongs to the module (besides the module itself)
  166. for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() )
  167. viewGroupAdd( pad, &m_items );
  168. for( BOARD_ITEM* drawing = module->GraphicalItems().GetFirst(); drawing;
  169. drawing = drawing->Next() )
  170. viewGroupAdd( drawing, &m_items );
  171. viewGroupAdd( &module->Reference(), &m_items );
  172. viewGroupAdd( &module->Value(), &m_items );
  173. }
  174. // Add items to the VIEW_GROUP, so they will be displayed on the overlay
  175. // while dragging
  176. aGroup->Add( aItem );
  177. }
  178. void MOVE_TOOL::vgSetVisibility( VIEW_GROUP* aGroup, bool aVisible ) const
  179. {
  180. std::set<VIEW_ITEM*>::const_iterator it, it_end;
  181. for( it = aGroup->Begin(), it_end = aGroup->End(); it != it_end; ++it )
  182. (*it)->ViewSetVisible( aVisible );
  183. }
  184. void MOVE_TOOL::vgUpdate( VIEW_GROUP* aGroup, VIEW_ITEM::ViewUpdateFlags aFlags ) const
  185. {
  186. std::set<VIEW_ITEM*>::const_iterator it, it_end;
  187. for( it = aGroup->Begin(), it_end = aGroup->End(); it != it_end; ++it )
  188. (*it)->ViewUpdate( aFlags );
  189. }