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.

372 lines
8.8 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020 Joshua Redstone redstone at gmail.com
  5. * Copyright (C) 1992-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 <bitmaps.h>
  25. #include <board.h>
  26. #include <board_item.h>
  27. #include <pcb_group.h>
  28. #include <confirm.h>
  29. #include <widgets/msgpanel.h>
  30. #include <view/view.h>
  31. PCB_GROUP::PCB_GROUP( BOARD_ITEM* aParent ) :
  32. BOARD_ITEM( aParent, PCB_GROUP_T )
  33. {
  34. }
  35. bool PCB_GROUP::AddItem( BOARD_ITEM* aItem )
  36. {
  37. // Items can only be in one group at a time
  38. if( aItem->GetParentGroup() )
  39. aItem->GetParentGroup()->RemoveItem( aItem );
  40. m_items.insert( aItem );
  41. aItem->SetParentGroup( this );
  42. return true;
  43. }
  44. bool PCB_GROUP::RemoveItem( BOARD_ITEM* aItem )
  45. {
  46. // Only clear the item's group field if it was inside this group
  47. if( m_items.erase( aItem ) == 1 )
  48. {
  49. aItem->SetParentGroup( nullptr );
  50. return true;
  51. }
  52. return false;
  53. }
  54. void PCB_GROUP::RemoveAll()
  55. {
  56. for( BOARD_ITEM* item : m_items )
  57. item->SetParentGroup( nullptr );
  58. m_items.clear();
  59. }
  60. PCB_GROUP* getTopLevelGroup( BOARD_ITEM* aItem, PCB_GROUP* aScope, bool isFootprintEditor )
  61. {
  62. PCB_GROUP* group = nullptr;
  63. if( isFootprintEditor )
  64. {
  65. group = aItem->GetParentGroup();
  66. }
  67. else
  68. {
  69. if( aItem->GetParent() && aItem->GetParent()->Type() == PCB_FOOTPRINT_T )
  70. group = aItem->GetParent()->GetParentGroup();
  71. else
  72. group = aItem->GetParentGroup();
  73. }
  74. while( group && group->GetParentGroup() && group->GetParentGroup() != aScope )
  75. {
  76. if( group->GetParent()->Type() == PCB_FOOTPRINT_T && isFootprintEditor )
  77. break;
  78. group = group->GetParentGroup();
  79. }
  80. return group;
  81. }
  82. PCB_GROUP* PCB_GROUP::TopLevelGroup( BOARD_ITEM* aItem, PCB_GROUP* aScope, bool isFootprintEditor )
  83. {
  84. PCB_GROUP* candidate = getTopLevelGroup( aItem, aScope, isFootprintEditor );
  85. return candidate == aScope ? nullptr : candidate;
  86. }
  87. bool PCB_GROUP::WithinScope( BOARD_ITEM* aItem, PCB_GROUP* aScope, bool isFootprintEditor )
  88. {
  89. PCB_GROUP* candidate = getTopLevelGroup( aItem, aScope, isFootprintEditor );
  90. return candidate == aScope;
  91. }
  92. wxPoint PCB_GROUP::GetPosition() const
  93. {
  94. return GetBoundingBox().Centre();
  95. }
  96. void PCB_GROUP::SetPosition( const wxPoint& aNewpos )
  97. {
  98. wxPoint delta = aNewpos - GetPosition();
  99. Move( delta );
  100. }
  101. void PCB_GROUP::SetLayerRecursive( PCB_LAYER_ID aLayer, int aDepth )
  102. {
  103. for( auto item : m_items )
  104. {
  105. if( ( item->Type() == PCB_GROUP_T ) && ( aDepth > 0 ) )
  106. {
  107. static_cast<PCB_GROUP*>( item )->SetLayerRecursive( aLayer, aDepth - 1 );
  108. }
  109. else
  110. {
  111. item->SetLayer( aLayer );
  112. }
  113. }
  114. }
  115. void PCB_GROUP::SetLocked( bool aLockState )
  116. {
  117. BOARD_ITEM::SetLocked( aLockState );
  118. RunOnChildren(
  119. [&]( BOARD_ITEM* child )
  120. {
  121. child->SetLocked( aLockState );
  122. } );
  123. }
  124. EDA_ITEM* PCB_GROUP::Clone() const
  125. {
  126. // Use copy constructor to get the same uuid and other fields
  127. PCB_GROUP* newGroup = new PCB_GROUP( *this );
  128. return newGroup;
  129. }
  130. PCB_GROUP* PCB_GROUP::DeepClone() const
  131. {
  132. // Use copy constructor to get the same uuid and other fields
  133. PCB_GROUP* newGroup = new PCB_GROUP( *this );
  134. newGroup->m_items.clear();
  135. for( BOARD_ITEM* member : m_items )
  136. {
  137. if( member->Type() == PCB_GROUP_T )
  138. newGroup->AddItem( static_cast<PCB_GROUP*>( member )->DeepClone() );
  139. else
  140. newGroup->AddItem( static_cast<BOARD_ITEM*>( member->Clone() ) );
  141. }
  142. return newGroup;
  143. }
  144. PCB_GROUP* PCB_GROUP::DeepDuplicate() const
  145. {
  146. PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( Duplicate() );
  147. newGroup->m_items.clear();
  148. for( BOARD_ITEM* member : m_items )
  149. {
  150. if( member->Type() == PCB_GROUP_T )
  151. newGroup->AddItem( static_cast<PCB_GROUP*>( member )->DeepDuplicate() );
  152. else
  153. newGroup->AddItem( static_cast<BOARD_ITEM*>( member->Duplicate() ) );
  154. }
  155. return newGroup;
  156. }
  157. void PCB_GROUP::SwapData( BOARD_ITEM* aImage )
  158. {
  159. assert( aImage->Type() == PCB_GROUP_T );
  160. std::swap( *( (PCB_GROUP*) this ), *( (PCB_GROUP*) aImage ) );
  161. }
  162. bool PCB_GROUP::HitTest( const wxPoint& aPosition, int aAccuracy ) const
  163. {
  164. // Groups are selected by promoting a selection of one of their children
  165. return false;
  166. }
  167. bool PCB_GROUP::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
  168. {
  169. // Groups are selected by promoting a selection of one of their children
  170. return false;
  171. }
  172. const EDA_RECT PCB_GROUP::GetBoundingBox() const
  173. {
  174. EDA_RECT area;
  175. for( BOARD_ITEM* item : m_items )
  176. area.Merge( item->GetBoundingBox() );
  177. area.Inflate( Millimeter2iu( 0.25 ) ); // Give a min size to the area
  178. return area;
  179. }
  180. SEARCH_RESULT PCB_GROUP::Visit( INSPECTOR aInspector, void* aTestData, const KICAD_T aScanTypes[] )
  181. {
  182. for( const KICAD_T* stype = aScanTypes; *stype != EOT; ++stype )
  183. {
  184. // If caller wants to inspect my type
  185. if( *stype == Type() )
  186. {
  187. if( SEARCH_RESULT::QUIT == aInspector( this, aTestData ) )
  188. return SEARCH_RESULT::QUIT;
  189. }
  190. }
  191. return SEARCH_RESULT::CONTINUE;
  192. }
  193. LSET PCB_GROUP::GetLayerSet() const
  194. {
  195. LSET aSet;
  196. for( BOARD_ITEM* item : m_items )
  197. aSet |= item->GetLayerSet();
  198. return aSet;
  199. }
  200. bool PCB_GROUP::IsOnLayer( PCB_LAYER_ID aLayer ) const
  201. {
  202. // A group is on a layer if any item is on the layer
  203. for( BOARD_ITEM* item : m_items )
  204. {
  205. if( item->IsOnLayer( aLayer ) )
  206. return true;
  207. }
  208. return false;
  209. }
  210. void PCB_GROUP::ViewGetLayers( int aLayers[], int& aCount ) const
  211. {
  212. aCount = 1;
  213. aLayers[0] = LAYER_ANCHOR;
  214. }
  215. double PCB_GROUP::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
  216. {
  217. if( aView->IsLayerVisible( LAYER_ANCHOR ) )
  218. return 0.0;
  219. return std::numeric_limits<double>::max();
  220. }
  221. void PCB_GROUP::Move( const wxPoint& aMoveVector )
  222. {
  223. for( BOARD_ITEM* member : m_items )
  224. member->Move( aMoveVector );
  225. }
  226. void PCB_GROUP::Rotate( const wxPoint& aRotCentre, double aAngle )
  227. {
  228. for( BOARD_ITEM* item : m_items )
  229. item->Rotate( aRotCentre, aAngle );
  230. }
  231. void PCB_GROUP::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
  232. {
  233. for( BOARD_ITEM* item : m_items )
  234. item->Flip( aCentre, aFlipLeftRight );
  235. }
  236. wxString PCB_GROUP::GetSelectMenuText( EDA_UNITS aUnits ) const
  237. {
  238. if( m_name.empty() )
  239. {
  240. return wxString::Format( _( "Unnamed Group, %zu members" ),
  241. m_items.size() );
  242. }
  243. return wxString::Format( _( "Group '%s', %zu members" ),
  244. m_name,
  245. m_items.size() );
  246. }
  247. BITMAPS PCB_GROUP::GetMenuImage() const
  248. {
  249. return BITMAPS::module;
  250. }
  251. void PCB_GROUP::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  252. {
  253. aList.emplace_back( _( "Group" ), m_name.empty() ? _( "<unnamed>" ) : m_name );
  254. aList.emplace_back( _( "Members" ), wxString::Format( "%zu", m_items.size() ) );
  255. if( IsLocked() )
  256. aList.emplace_back( _( "Status" ), _( "Locked" ) );
  257. }
  258. void PCB_GROUP::RunOnChildren( const std::function<void( BOARD_ITEM* )>& aFunction ) const
  259. {
  260. try
  261. {
  262. for( BOARD_ITEM* item : m_items )
  263. aFunction( item );
  264. }
  265. catch( std::bad_function_call& )
  266. {
  267. wxFAIL_MSG( wxT( "Error calling function in PCB_GROUP::RunOnChildren" ) );
  268. }
  269. }
  270. void PCB_GROUP::RunOnDescendants( const std::function<void( BOARD_ITEM* )>& aFunction ) const
  271. {
  272. try
  273. {
  274. for( BOARD_ITEM* item : m_items )
  275. {
  276. aFunction( item );
  277. if( item->Type() == PCB_GROUP_T )
  278. static_cast<PCB_GROUP*>( item )->RunOnDescendants( aFunction );
  279. }
  280. }
  281. catch( std::bad_function_call& )
  282. {
  283. wxFAIL_MSG( wxT( "Error calling function in PCB_GROUP::RunOnDescendants" ) );
  284. }
  285. }