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.

353 lines
11 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2014 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 "placement_tool.h"
  25. #include "common_actions.h"
  26. #include "selection_tool.h"
  27. #include <tool/tool_manager.h>
  28. #include <wxPcbStruct.h>
  29. #include <class_board.h>
  30. #include <ratsnest_data.h>
  31. #include <confirm.h>
  32. #include <boost/foreach.hpp>
  33. PLACEMENT_TOOL::PLACEMENT_TOOL() :
  34. TOOL_INTERACTIVE( "pcbnew.Placement" ), m_selectionTool( NULL )
  35. {
  36. }
  37. PLACEMENT_TOOL::~PLACEMENT_TOOL()
  38. {
  39. }
  40. bool PLACEMENT_TOOL::Init()
  41. {
  42. // Find the selection tool, so they can cooperate
  43. m_selectionTool = static_cast<SELECTION_TOOL*>( m_toolMgr->FindTool( "pcbnew.InteractiveSelection" ) );
  44. if( !m_selectionTool )
  45. {
  46. DisplayError( NULL, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
  47. return false;
  48. }
  49. // Create a context menu and make it available through selection tool
  50. CONTEXT_MENU* menu = new CONTEXT_MENU;
  51. menu->Add( COMMON_ACTIONS::alignTop );
  52. menu->Add( COMMON_ACTIONS::alignBottom );
  53. menu->Add( COMMON_ACTIONS::alignLeft );
  54. menu->Add( COMMON_ACTIONS::alignRight );
  55. menu->AppendSeparator();
  56. menu->Add( COMMON_ACTIONS::distributeHorizontally );
  57. menu->Add( COMMON_ACTIONS::distributeVertically );
  58. m_selectionTool->GetMenu().AddMenu( menu, _( "Align/distribute" ), false,
  59. SELECTION_CONDITIONS::MoreThan( 1 ) );
  60. return true;
  61. }
  62. int PLACEMENT_TOOL::AlignTop( const TOOL_EVENT& aEvent )
  63. {
  64. const SELECTION& selection = m_selectionTool->GetSelection();
  65. if( selection.Size() > 1 )
  66. {
  67. PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
  68. RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
  69. editFrame->OnModify();
  70. editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
  71. // Compute the highest point of selection - it will be the edge of alignment
  72. int top = selection.Item<BOARD_ITEM>( 0 )->GetBoundingBox().GetY();
  73. for( int i = 1; i < selection.Size(); ++i )
  74. {
  75. int currentTop = selection.Item<BOARD_ITEM>( i )->GetBoundingBox().GetY();
  76. if( top > currentTop ) // Y decreases when going up
  77. top = currentTop;
  78. }
  79. // Move the selected items
  80. for( int i = 0; i < selection.Size(); ++i )
  81. {
  82. BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
  83. int difference = top - item->GetBoundingBox().GetY();
  84. item->Move( wxPoint( 0, difference ) );
  85. item->ViewUpdate();
  86. ratsnest->Update( item );
  87. }
  88. getModel<BOARD>()->GetRatsnest()->Recalculate();
  89. }
  90. return 0;
  91. }
  92. int PLACEMENT_TOOL::AlignBottom( const TOOL_EVENT& aEvent )
  93. {
  94. const SELECTION& selection = m_selectionTool->GetSelection();
  95. if( selection.Size() > 1 )
  96. {
  97. PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
  98. RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
  99. editFrame->OnModify();
  100. editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
  101. // Compute the lowest point of selection - it will be the edge of alignment
  102. int bottom = selection.Item<BOARD_ITEM>( 0 )->GetBoundingBox().GetBottom();
  103. for( int i = 1; i < selection.Size(); ++i )
  104. {
  105. int currentBottom = selection.Item<BOARD_ITEM>( i )->GetBoundingBox().GetBottom();
  106. if( bottom < currentBottom ) // Y increases when going down
  107. bottom = currentBottom;
  108. }
  109. // Move the selected items
  110. for( int i = 0; i < selection.Size(); ++i )
  111. {
  112. BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
  113. int difference = bottom - item->GetBoundingBox().GetBottom();
  114. item->Move( wxPoint( 0, difference ) );
  115. item->ViewUpdate();
  116. ratsnest->Update( item );
  117. }
  118. getModel<BOARD>()->GetRatsnest()->Recalculate();
  119. }
  120. return 0;
  121. }
  122. int PLACEMENT_TOOL::AlignLeft( const TOOL_EVENT& aEvent )
  123. {
  124. const SELECTION& selection = m_selectionTool->GetSelection();
  125. if( selection.Size() > 1 )
  126. {
  127. PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
  128. RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
  129. editFrame->OnModify();
  130. editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
  131. // Compute the leftmost point of selection - it will be the edge of alignment
  132. int left = selection.Item<BOARD_ITEM>( 0 )->GetBoundingBox().GetX();
  133. for( int i = 1; i < selection.Size(); ++i )
  134. {
  135. int currentLeft = selection.Item<BOARD_ITEM>( i )->GetBoundingBox().GetX();
  136. if( left > currentLeft ) // X decreases when going left
  137. left = currentLeft;
  138. }
  139. // Move the selected items
  140. for( int i = 0; i < selection.Size(); ++i )
  141. {
  142. BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
  143. int difference = left - item->GetBoundingBox().GetX();
  144. item->Move( wxPoint( difference, 0 ) );
  145. item->ViewUpdate();
  146. ratsnest->Update( item );
  147. }
  148. getModel<BOARD>()->GetRatsnest()->Recalculate();
  149. }
  150. return 0;
  151. }
  152. int PLACEMENT_TOOL::AlignRight( const TOOL_EVENT& aEvent )
  153. {
  154. const SELECTION& selection = m_selectionTool->GetSelection();
  155. if( selection.Size() > 1 )
  156. {
  157. PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
  158. RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
  159. editFrame->OnModify();
  160. editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
  161. // Compute the rightmost point of selection - it will be the edge of alignment
  162. int right = selection.Item<BOARD_ITEM>( 0 )->GetBoundingBox().GetRight();
  163. for( int i = 1; i < selection.Size(); ++i )
  164. {
  165. int currentRight = selection.Item<BOARD_ITEM>( i )->GetBoundingBox().GetRight();
  166. if( right < currentRight ) // X increases when going right
  167. right = currentRight;
  168. }
  169. // Move the selected items
  170. for( int i = 0; i < selection.Size(); ++i )
  171. {
  172. BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
  173. int difference = right - item->GetBoundingBox().GetRight();
  174. item->Move( wxPoint( difference, 0 ) );
  175. item->ViewUpdate();
  176. ratsnest->Update( item );
  177. }
  178. getModel<BOARD>()->GetRatsnest()->Recalculate();
  179. }
  180. return 0;
  181. }
  182. static bool compareX( const BOARD_ITEM* aA, const BOARD_ITEM* aB )
  183. {
  184. return aA->GetBoundingBox().Centre().x < aB->GetBoundingBox().Centre().x;
  185. }
  186. static bool compareY( const BOARD_ITEM* aA, const BOARD_ITEM* aB )
  187. {
  188. return aA->GetBoundingBox().Centre().y < aB->GetBoundingBox().Centre().y;
  189. }
  190. int PLACEMENT_TOOL::DistributeHorizontally( const TOOL_EVENT& aEvent )
  191. {
  192. const SELECTION& selection = m_selectionTool->GetSelection();
  193. if( selection.Size() > 1 )
  194. {
  195. PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
  196. RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
  197. editFrame->OnModify();
  198. editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
  199. // Prepare a list, so the items can be sorted by their X coordinate
  200. std::list<BOARD_ITEM*> itemsList;
  201. for( int i = 0; i < selection.Size(); ++i )
  202. itemsList.push_back( selection.Item<BOARD_ITEM>( i ) );
  203. // Sort items by X coordinate
  204. itemsList.sort( compareX );
  205. // Expected X coordinate for the next item (=minX)
  206. int position = (*itemsList.begin())->GetBoundingBox().Centre().x;
  207. // X coordinate for the last item
  208. const int maxX = (*itemsList.rbegin())->GetBoundingBox().Centre().x;
  209. // Distance between items
  210. const int distance = ( maxX - position ) / ( itemsList.size() - 1 );
  211. BOOST_FOREACH( BOARD_ITEM* item, itemsList )
  212. {
  213. int difference = position - item->GetBoundingBox().Centre().x;
  214. item->Move( wxPoint( difference, 0 ) );
  215. item->ViewUpdate();
  216. ratsnest->Update( item );
  217. position += distance;
  218. }
  219. getModel<BOARD>()->GetRatsnest()->Recalculate();
  220. }
  221. return 0;
  222. }
  223. int PLACEMENT_TOOL::DistributeVertically( const TOOL_EVENT& aEvent )
  224. {
  225. const SELECTION& selection = m_selectionTool->GetSelection();
  226. if( selection.Size() > 1 )
  227. {
  228. PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
  229. RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
  230. editFrame->OnModify();
  231. editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
  232. // Prepare a list, so the items can be sorted by their Y coordinate
  233. std::list<BOARD_ITEM*> itemsList;
  234. for( int i = 0; i < selection.Size(); ++i )
  235. itemsList.push_back( selection.Item<BOARD_ITEM>( i ) );
  236. // Sort items by Y coordinate
  237. itemsList.sort( compareY );
  238. // Expected Y coordinate for the next item (=minY)
  239. int position = (*itemsList.begin())->GetBoundingBox().Centre().y;
  240. // Y coordinate for the last item
  241. const int maxY = (*itemsList.rbegin())->GetBoundingBox().Centre().y;
  242. // Distance between items
  243. const int distance = ( maxY - position ) / ( itemsList.size() - 1 );
  244. BOOST_FOREACH( BOARD_ITEM* item, itemsList )
  245. {
  246. int difference = position - item->GetBoundingBox().Centre().y;
  247. item->Move( wxPoint( 0, difference ) );
  248. item->ViewUpdate();
  249. ratsnest->Update( item );
  250. position += distance;
  251. }
  252. getModel<BOARD>()->GetRatsnest()->Recalculate();
  253. }
  254. return 0;
  255. }
  256. void PLACEMENT_TOOL::SetTransitions()
  257. {
  258. Go( &PLACEMENT_TOOL::AlignTop, COMMON_ACTIONS::alignTop.MakeEvent() );
  259. Go( &PLACEMENT_TOOL::AlignBottom, COMMON_ACTIONS::alignBottom.MakeEvent() );
  260. Go( &PLACEMENT_TOOL::AlignLeft, COMMON_ACTIONS::alignLeft.MakeEvent() );
  261. Go( &PLACEMENT_TOOL::AlignRight, COMMON_ACTIONS::alignRight.MakeEvent() );
  262. Go( &PLACEMENT_TOOL::DistributeHorizontally, COMMON_ACTIONS::distributeHorizontally.MakeEvent() );
  263. Go( &PLACEMENT_TOOL::DistributeVertically, COMMON_ACTIONS::distributeVertically.MakeEvent() );
  264. }