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.

249 lines
6.5 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2013-2017 CERN
  5. * Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  7. * @author Maciej Suminski <maciej.suminski@cern.ch>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include <algorithm>
  27. #include <eda_item.h>
  28. #include <tool/selection.h>
  29. bool SELECTION::operator==( const SELECTION& aOther ) const
  30. {
  31. return ( m_items == aOther.m_items
  32. && m_itemsOrders == aOther.m_itemsOrders
  33. && m_isHover == aOther.m_isHover
  34. && m_lastAddedItem == aOther.m_lastAddedItem
  35. && m_orderCounter == aOther.m_orderCounter );
  36. }
  37. void SELECTION::Add( EDA_ITEM* aItem )
  38. {
  39. // We're not sorting here; this is just a time-optimized way to do an
  40. // inclusion check. std::lower_bound will return the first i >= aItem
  41. // and the second i > aItem check rules out i == aItem.
  42. ITER i = std::lower_bound( m_items.begin(), m_items.end(), aItem );
  43. if( i == m_items.end() || *i > aItem )
  44. {
  45. m_itemsOrders.insert( m_itemsOrders.begin() + std::distance( m_items.begin(), i ),
  46. m_orderCounter );
  47. m_items.insert( i, aItem );
  48. m_orderCounter++;
  49. m_lastAddedItem = aItem;
  50. }
  51. }
  52. void SELECTION::Remove( EDA_ITEM* aItem )
  53. {
  54. ITER i = std::lower_bound( m_items.begin(), m_items.end(), aItem );
  55. if( !( i == m_items.end() || *i > aItem ) )
  56. {
  57. m_itemsOrders.erase( m_itemsOrders.begin() + std::distance( m_items.begin(), i ) );
  58. m_items.erase( i );
  59. if( aItem == m_lastAddedItem )
  60. m_lastAddedItem = nullptr;
  61. }
  62. }
  63. KIGFX::VIEW_ITEM* SELECTION::GetItem( unsigned int aIdx ) const
  64. {
  65. if( aIdx < m_items.size() )
  66. return m_items[aIdx];
  67. return nullptr;
  68. }
  69. bool SELECTION::Contains( EDA_ITEM* aItem ) const
  70. {
  71. CITER i = std::lower_bound( m_items.begin(), m_items.end(), aItem );
  72. return !( i == m_items.end() || *i > aItem );
  73. }
  74. /// Returns the center point of the selection area bounding box.
  75. VECTOR2I SELECTION::GetCenter() const
  76. {
  77. bool hasOnlyText = true;
  78. // If the selection contains only texts calculate the center as the mean of all positions
  79. // instead of using the center of the total bounding box. Otherwise rotating the selection will
  80. // also translate it.
  81. for( EDA_ITEM* item : m_items )
  82. {
  83. if( !item->IsType( { SCH_TEXT_T, SCH_LABEL_LOCATE_ANY_T } ) )
  84. {
  85. hasOnlyText = false;
  86. break;
  87. }
  88. }
  89. BOX2I bbox;
  90. if( hasOnlyText )
  91. {
  92. VECTOR2I center( 0, 0 );
  93. for( EDA_ITEM* item : m_items )
  94. center += item->GetPosition();
  95. center = center / static_cast<int>( m_items.size() );
  96. return static_cast<VECTOR2I>( center );
  97. }
  98. for( EDA_ITEM* item : m_items )
  99. {
  100. if( !item->IsType( { SCH_TEXT_T, SCH_LABEL_LOCATE_ANY_T } ) )
  101. bbox.Merge( item->GetBoundingBox() );
  102. }
  103. return static_cast<VECTOR2I>( bbox.GetCenter() );
  104. }
  105. BOX2I SELECTION::GetBoundingBox() const
  106. {
  107. BOX2I bbox;
  108. for( EDA_ITEM* item : m_items )
  109. bbox.Merge( item->GetBoundingBox() );
  110. return bbox;
  111. }
  112. bool SELECTION::HasType( KICAD_T aType ) const
  113. {
  114. for( const EDA_ITEM* item : m_items )
  115. {
  116. if( item->IsType( { aType } ) )
  117. return true;
  118. }
  119. return false;
  120. }
  121. size_t SELECTION::CountType( KICAD_T aType ) const
  122. {
  123. size_t count = 0;
  124. for( const EDA_ITEM* item : m_items )
  125. {
  126. if( item->IsType( { aType } ) )
  127. count++;
  128. }
  129. return count;
  130. }
  131. VECTOR2I SELECTION::GetReferencePoint() const
  132. {
  133. if( m_referencePoint )
  134. return *m_referencePoint;
  135. else
  136. return GetBoundingBox().Centre();
  137. }
  138. void SELECTION::SetReferencePoint( const VECTOR2I& aP )
  139. {
  140. m_referencePoint = aP;
  141. }
  142. void SELECTION::ClearReferencePoint()
  143. {
  144. m_referencePoint = std::nullopt;
  145. }
  146. const std::vector<KIGFX::VIEW_ITEM*> SELECTION::updateDrawList() const
  147. {
  148. std::vector<VIEW_ITEM*> items;
  149. for( EDA_ITEM* item : m_items )
  150. items.push_back( item );
  151. return items;
  152. }
  153. bool SELECTION::AreAllItemsIdentical() const
  154. {
  155. return ( std::all_of( m_items.begin() + 1, m_items.end(),
  156. [&]( const EDA_ITEM* r )
  157. {
  158. return r->Type() == m_items.front()->Type();
  159. } ) );
  160. }
  161. bool SELECTION::OnlyContains( std::vector<KICAD_T> aList ) const
  162. {
  163. return ( std::all_of( m_items.begin(), m_items.end(),
  164. [&]( const EDA_ITEM* r )
  165. {
  166. return r->IsType( aList );
  167. } ) );
  168. }
  169. const std::vector<EDA_ITEM*> SELECTION::GetItemsSortedBySelectionOrder() const
  170. {
  171. using pairedIterators =
  172. std::pair<decltype( m_items.begin() ), decltype( m_itemsOrders.begin() )>;
  173. // Create a vector of all {selection item, selection order} iterator pairs
  174. std::vector<pairedIterators> pairs;
  175. auto item = m_items.begin();
  176. auto order = m_itemsOrders.begin();
  177. for( ; item != m_items.end(); ++item, ++order )
  178. pairs.emplace_back( make_pair( item, order ) );
  179. // Sort the pairs by the selection order
  180. std::sort( pairs.begin(), pairs.end(),
  181. []( pairedIterators const& a, pairedIterators const& b )
  182. {
  183. return *a.second < *b.second;
  184. } );
  185. // Make a vector of just the sortedItems
  186. std::vector<EDA_ITEM*> sortedItems;
  187. for( pairedIterators sortedItem : pairs )
  188. sortedItems.emplace_back( *sortedItem.first );
  189. return sortedItems;
  190. }