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.

286 lines
9.2 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 CERN
  5. * Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Maciej Suminski <maciej.suminski@cern.ch>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 3
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * https://www.gnu.org/licenses/gpl-3.0.html
  21. * or you may search the http://www.gnu.org website for the version 3 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <fp_tree_synchronizing_adapter.h>
  26. #include <footprint_edit_frame.h>
  27. #include <fp_lib_table.h>
  28. #include <footprint_info_impl.h>
  29. #include <string_utils.h>
  30. #include <board.h>
  31. #include <footprint.h>
  32. #include <tool/tool_manager.h>
  33. #include <tools/footprint_editor_control.h>
  34. #include <wx/settings.h>
  35. wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>
  36. FP_TREE_SYNCHRONIZING_ADAPTER::Create( FOOTPRINT_EDIT_FRAME* aFrame, FP_LIB_TABLE* aLibs )
  37. {
  38. auto* adapter = new FP_TREE_SYNCHRONIZING_ADAPTER( aFrame, aLibs );
  39. return wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>( adapter );
  40. }
  41. FP_TREE_SYNCHRONIZING_ADAPTER::FP_TREE_SYNCHRONIZING_ADAPTER( FOOTPRINT_EDIT_FRAME* aFrame,
  42. FP_LIB_TABLE* aLibs ) :
  43. FP_TREE_MODEL_ADAPTER( aFrame, aLibs ),
  44. m_frame( aFrame )
  45. {
  46. }
  47. TOOL_INTERACTIVE* FP_TREE_SYNCHRONIZING_ADAPTER::GetContextMenuTool()
  48. {
  49. return m_frame->GetToolManager()->GetTool<FOOTPRINT_EDITOR_CONTROL>();
  50. }
  51. bool FP_TREE_SYNCHRONIZING_ADAPTER::IsContainer( const wxDataViewItem& aItem ) const
  52. {
  53. const LIB_TREE_NODE* node = ToNode( aItem );
  54. return node ? node->m_Type == LIB_TREE_NODE::LIB : true;
  55. }
  56. #define PROGRESS_INTERVAL_MILLIS 66
  57. void FP_TREE_SYNCHRONIZING_ADAPTER::Sync()
  58. {
  59. // Process already stored libraries
  60. for( auto it = m_tree.m_Children.begin(); it != m_tree.m_Children.end(); )
  61. {
  62. const wxString& name = it->get()->m_Name;
  63. // Remove the library if it no longer exists or it exists in both the global and the
  64. // project library but the project library entry is disabled.
  65. if( !m_libs->HasLibrary( name, true )
  66. || ( m_libs->FindRow( name, true ) != m_libs->FindRow( name, false ) ) )
  67. {
  68. it = deleteLibrary( it );
  69. continue;
  70. }
  71. updateLibrary( *(LIB_TREE_NODE_LIB*) it->get() );
  72. ++it;
  73. }
  74. // Look for new libraries
  75. size_t count = m_libMap.size();
  76. for( const auto& libName : m_libs->GetLogicalLibs() )
  77. {
  78. if( m_libMap.count( libName ) == 0 )
  79. {
  80. const FP_LIB_TABLE_ROW* library = m_libs->FindRow( libName, true );
  81. DoAddLibrary( libName, library->GetDescr(), getFootprints( libName ), true );
  82. m_libMap.insert( libName );
  83. }
  84. }
  85. if( m_libMap.size() > count )
  86. m_tree.AssignIntrinsicRanks();
  87. }
  88. int FP_TREE_SYNCHRONIZING_ADAPTER::GetLibrariesCount() const
  89. {
  90. return GFootprintTable.GetCount();
  91. }
  92. void FP_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIB& aLibNode )
  93. {
  94. std::vector<LIB_TREE_ITEM*> footprints = getFootprints( aLibNode.m_Name );
  95. // remove the common part from the footprints list
  96. for( auto nodeIt = aLibNode.m_Children.begin(); nodeIt != aLibNode.m_Children.end(); )
  97. {
  98. // Since the list is sorted we can use a binary search to speed up searches within
  99. // libraries with lots of footprints.
  100. FOOTPRINT_INFO_IMPL dummy( wxEmptyString, (*nodeIt)->m_Name );
  101. auto footprintIt = std::lower_bound( footprints.begin(), footprints.end(), &dummy,
  102. []( LIB_TREE_ITEM* a, LIB_TREE_ITEM* b )
  103. {
  104. return StrNumCmp( a->GetName(), b->GetName(), false ) < 0;
  105. } );
  106. if( footprintIt != footprints.end() && dummy.GetName() == (*footprintIt)->GetName() )
  107. {
  108. // footprint exists both in the lib tree and the footprint info list; just
  109. // update the node data
  110. static_cast<LIB_TREE_NODE_LIB_ID*>( nodeIt->get() )->Update( *footprintIt );
  111. footprints.erase( footprintIt );
  112. ++nodeIt;
  113. }
  114. else
  115. {
  116. // node does not exist in the library manager, remove the corresponding node
  117. nodeIt = aLibNode.m_Children.erase( nodeIt );
  118. }
  119. }
  120. // now the footprint list contains only new aliases that need to be added to the tree
  121. for( auto footprint : footprints )
  122. aLibNode.AddItem( footprint );
  123. aLibNode.AssignIntrinsicRanks();
  124. m_libMap.insert( aLibNode.m_Name );
  125. }
  126. LIB_TREE_NODE::PTR_VECTOR::iterator FP_TREE_SYNCHRONIZING_ADAPTER::deleteLibrary(
  127. LIB_TREE_NODE::PTR_VECTOR::iterator& aLibNodeIt )
  128. {
  129. LIB_TREE_NODE* node = aLibNodeIt->get();
  130. m_libMap.erase( node->m_Name );
  131. auto it = m_tree.m_Children.erase( aLibNodeIt );
  132. return it;
  133. }
  134. void FP_TREE_SYNCHRONIZING_ADAPTER::GetValue( wxVariant& aVariant, wxDataViewItem const& aItem,
  135. unsigned int aCol ) const
  136. {
  137. if( IsFrozen() )
  138. {
  139. aVariant = wxEmptyString;
  140. return;
  141. }
  142. auto node = ToNode( aItem );
  143. switch( aCol )
  144. {
  145. case 0:
  146. if( node->m_LibId == m_frame->GetLoadedFPID() && !m_frame->IsCurrentFPFromBoard() )
  147. {
  148. node->m_Name = m_frame->GetLoadedFPID().GetLibItemName();
  149. // mark modified part with an asterisk
  150. if( m_frame->GetScreen()->IsContentModified() )
  151. aVariant = node->m_Name + " *";
  152. else
  153. aVariant = node->m_Name;
  154. }
  155. else if( node->m_Pinned )
  156. aVariant = GetPinningSymbol() + node->m_Name;
  157. else
  158. aVariant = node->m_Name;
  159. break;
  160. case 1:
  161. if( node->m_LibId == m_frame->GetLoadedFPID() && !m_frame->IsCurrentFPFromBoard() )
  162. {
  163. node->m_Desc = m_frame->GetBoard()->GetFirstFootprint()->GetDescription();
  164. }
  165. else if( node->m_Type == LIB_TREE_NODE::LIB )
  166. {
  167. try
  168. {
  169. const FP_LIB_TABLE_ROW* lib =
  170. GFootprintTable.FindRow( node->m_LibId.GetLibNickname() );
  171. if( lib )
  172. node->m_Desc = lib->GetDescr();
  173. }
  174. catch( IO_ERROR& )
  175. {
  176. }
  177. }
  178. aVariant = node->m_Desc;
  179. break;
  180. default: // column == -1 is used for default Compare function
  181. aVariant = node->m_Name;
  182. break;
  183. }
  184. }
  185. bool FP_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, unsigned int aCol,
  186. wxDataViewItemAttr& aAttr ) const
  187. {
  188. if( IsFrozen() )
  189. return false;
  190. // change attributes only for the name field
  191. if( aCol != 0 )
  192. return false;
  193. // don't link to a board footprint, even if the FPIDs match
  194. if( m_frame->IsCurrentFPFromBoard() )
  195. return false;
  196. auto node = ToNode( aItem );
  197. wxCHECK( node, false );
  198. switch( node->m_Type )
  199. {
  200. case LIB_TREE_NODE::LIB:
  201. if( node->m_Name == m_frame->GetLoadedFPID().GetLibNickname() )
  202. {
  203. #ifdef __WXGTK__
  204. // The native wxGTK+ impl ignores background colour, so set the text colour
  205. // instead. Works reasonably well in dark themes, less well in light ones....
  206. aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
  207. #else
  208. aAttr.SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
  209. aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT ) );
  210. #endif
  211. // mark modified libs with bold font
  212. if( m_frame->GetScreen()->IsContentModified() && !m_frame->IsCurrentFPFromBoard() )
  213. aAttr.SetBold( true );
  214. }
  215. break;
  216. case LIB_TREE_NODE::LIBID:
  217. if( node->m_LibId == m_frame->GetLoadedFPID() )
  218. {
  219. #ifdef __WXGTK__
  220. // The native wxGTK+ impl ignores background colour, so set the text colour
  221. // instead. Works reasonably well in dark themes, less well in light ones....
  222. aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
  223. #else
  224. aAttr.SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
  225. aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT ) );
  226. #endif
  227. // mark modified part with bold font
  228. if( m_frame->GetScreen()->IsContentModified() && !m_frame->IsCurrentFPFromBoard() )
  229. aAttr.SetBold( true );
  230. }
  231. break;
  232. default:
  233. return false;
  234. }
  235. return true;
  236. }