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.

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