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.

350 lines
11 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  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-2023 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 <pgm_base.h>
  26. #include <project/project_file.h>
  27. #include <fp_tree_synchronizing_adapter.h>
  28. #include <footprint_edit_frame.h>
  29. #include <footprint_preview_panel.h>
  30. #include <fp_lib_table.h>
  31. #include <footprint_info_impl.h>
  32. #include <gal/graphics_abstraction_layer.h>
  33. #include <string_utils.h>
  34. #include <board.h>
  35. #include <footprint.h>
  36. #include <tool/tool_manager.h>
  37. #include <tools/footprint_editor_control.h>
  38. #include <wx/settings.h>
  39. wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>
  40. FP_TREE_SYNCHRONIZING_ADAPTER::Create( FOOTPRINT_EDIT_FRAME* aFrame, FP_LIB_TABLE* aLibs )
  41. {
  42. auto* adapter = new FP_TREE_SYNCHRONIZING_ADAPTER( aFrame, aLibs );
  43. return wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>( adapter );
  44. }
  45. FP_TREE_SYNCHRONIZING_ADAPTER::FP_TREE_SYNCHRONIZING_ADAPTER( FOOTPRINT_EDIT_FRAME* aFrame,
  46. FP_LIB_TABLE* aLibs ) :
  47. FP_TREE_MODEL_ADAPTER( aFrame, aLibs ),
  48. m_frame( aFrame )
  49. {
  50. }
  51. TOOL_INTERACTIVE* FP_TREE_SYNCHRONIZING_ADAPTER::GetContextMenuTool()
  52. {
  53. return m_frame->GetToolManager()->GetTool<FOOTPRINT_EDITOR_CONTROL>();
  54. }
  55. bool FP_TREE_SYNCHRONIZING_ADAPTER::IsContainer( const wxDataViewItem& aItem ) const
  56. {
  57. const LIB_TREE_NODE* node = ToNode( aItem );
  58. return node ? node->m_Type == LIB_TREE_NODE::LIBRARY : true;
  59. }
  60. #define PROGRESS_INTERVAL_MILLIS 33 // 30 FPS refresh rate
  61. void FP_TREE_SYNCHRONIZING_ADAPTER::Sync( FP_LIB_TABLE* aLibs )
  62. {
  63. m_libs = aLibs;
  64. // Process already stored libraries
  65. for( auto it = m_tree.m_Children.begin(); it != m_tree.m_Children.end(); )
  66. {
  67. const wxString& name = it->get()->m_Name;
  68. // Remove the library if it no longer exists or it exists in both the global and the
  69. // project library but the project library entry is disabled.
  70. if( !m_libs->HasLibrary( name, true )
  71. || m_libs->FindRow( name, true ) != m_libs->FindRow( name, false ) )
  72. {
  73. it = deleteLibrary( it );
  74. continue;
  75. }
  76. updateLibrary( *(LIB_TREE_NODE_LIBRARY*) it->get() );
  77. ++it;
  78. }
  79. // Look for new libraries
  80. COMMON_SETTINGS* cfg = Pgm().GetCommonSettings();
  81. PROJECT_FILE& project = m_frame->Prj().GetProjectFile();
  82. size_t count = m_libMap.size();
  83. for( const wxString& libName : m_libs->GetLogicalLibs() )
  84. {
  85. if( m_libMap.count( libName ) == 0 )
  86. {
  87. try
  88. {
  89. const FP_LIB_TABLE_ROW* library = m_libs->FindRow( libName, true );
  90. bool pinned = alg::contains( cfg->m_Session.pinned_fp_libs, libName )
  91. || alg::contains( project.m_PinnedFootprintLibs, libName );
  92. DoAddLibrary( libName, library->GetDescr(), getFootprints( libName ), pinned, true );
  93. m_libMap.insert( libName );
  94. }
  95. catch( ... )
  96. {
  97. // do nothing if libname is not found. Just skip it
  98. }
  99. }
  100. }
  101. if( m_libMap.size() > count )
  102. m_tree.AssignIntrinsicRanks();
  103. }
  104. int FP_TREE_SYNCHRONIZING_ADAPTER::GetLibrariesCount() const
  105. {
  106. return GFootprintTable.GetCount();
  107. }
  108. void FP_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIBRARY& aLibNode )
  109. {
  110. std::vector<LIB_TREE_ITEM*> footprints = getFootprints( aLibNode.m_Name );
  111. // remove the common part from the footprints list
  112. for( auto nodeIt = aLibNode.m_Children.begin(); nodeIt != aLibNode.m_Children.end(); )
  113. {
  114. // Since the list is sorted we can use a binary search to speed up searches within
  115. // libraries with lots of footprints.
  116. FOOTPRINT_INFO_IMPL dummy( wxEmptyString, (*nodeIt)->m_Name );
  117. auto footprintIt = std::lower_bound( footprints.begin(), footprints.end(), &dummy,
  118. []( LIB_TREE_ITEM* a, LIB_TREE_ITEM* b )
  119. {
  120. return StrNumCmp( a->GetName(), b->GetName(), false ) < 0;
  121. } );
  122. if( footprintIt != footprints.end() && dummy.GetName() == (*footprintIt)->GetName() )
  123. {
  124. // footprint exists both in the lib tree and the footprint info list; just
  125. // update the node data
  126. static_cast<LIB_TREE_NODE_ITEM*>( nodeIt->get() )->Update( *footprintIt );
  127. footprints.erase( footprintIt );
  128. ++nodeIt;
  129. }
  130. else
  131. {
  132. // node does not exist in the library manager, remove the corresponding node
  133. nodeIt = aLibNode.m_Children.erase( nodeIt );
  134. }
  135. }
  136. // now the footprint list contains only new aliases that need to be added to the tree
  137. for( LIB_TREE_ITEM* footprint : footprints )
  138. aLibNode.AddItem( footprint );
  139. aLibNode.AssignIntrinsicRanks();
  140. m_libMap.insert( aLibNode.m_Name );
  141. }
  142. LIB_TREE_NODE::PTR_VECTOR::iterator
  143. FP_TREE_SYNCHRONIZING_ADAPTER::deleteLibrary( LIB_TREE_NODE::PTR_VECTOR::iterator& aLibNodeIt )
  144. {
  145. LIB_TREE_NODE* node = aLibNodeIt->get();
  146. m_libMap.erase( node->m_Name );
  147. auto it = m_tree.m_Children.erase( aLibNodeIt );
  148. return it;
  149. }
  150. wxDataViewItem FP_TREE_SYNCHRONIZING_ADAPTER::GetCurrentDataViewItem()
  151. {
  152. return FindItem( m_frame->GetLoadedFPID() );
  153. }
  154. void FP_TREE_SYNCHRONIZING_ADAPTER::GetValue( wxVariant& aVariant, wxDataViewItem const& aItem,
  155. unsigned int aCol ) const
  156. {
  157. if( IsFrozen() )
  158. {
  159. aVariant = wxEmptyString;
  160. return;
  161. }
  162. LIB_TREE_NODE* node = ToNode( aItem );
  163. switch( aCol )
  164. {
  165. case NAME_COL:
  166. {
  167. if( node->m_LibId == m_frame->GetLoadedFPID() && !m_frame->IsCurrentFPFromBoard() )
  168. {
  169. // Do not use GetLoadedFPID(); it returns m_footprintNameWhenLoaded.
  170. node->m_Name =
  171. m_frame->GetBoard()->GetFirstFootprint()->GetFPID().GetUniStringLibItemName();
  172. // mark modified part with an asterisk
  173. if( m_frame->GetScreen()->IsContentModified() )
  174. aVariant = node->m_Name + wxT( " *" );
  175. else
  176. aVariant = node->m_Name;
  177. }
  178. else if( node->m_Pinned )
  179. {
  180. aVariant = GetPinningSymbol() + node->m_Name;
  181. }
  182. else
  183. {
  184. aVariant = node->m_Name;
  185. }
  186. break;
  187. }
  188. case DESC_COL:
  189. {
  190. if( node->m_LibId == m_frame->GetLoadedFPID() && !m_frame->IsCurrentFPFromBoard() )
  191. {
  192. node->m_Desc = m_frame->GetBoard()->GetFirstFootprint()->GetLibDescription();
  193. }
  194. else if( node->m_Type == LIB_TREE_NODE::LIBRARY )
  195. {
  196. try
  197. {
  198. const FP_LIB_TABLE_ROW* lib =
  199. GFootprintTable.FindRow( node->m_LibId.GetLibNickname() );
  200. if( lib )
  201. node->m_Desc = lib->GetDescr();
  202. }
  203. catch( IO_ERROR& )
  204. {
  205. }
  206. }
  207. wxString descStr = UnescapeString( node->m_Desc );
  208. descStr.Replace( wxS( "\n" ), wxS( " " ) ); // Clear line breaks
  209. aVariant = descStr;
  210. break;
  211. }
  212. default: // column == -1 is used for default Compare function
  213. aVariant = node->m_Name;
  214. break;
  215. }
  216. }
  217. bool FP_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, unsigned int aCol,
  218. wxDataViewItemAttr& aAttr ) const
  219. {
  220. if( IsFrozen() )
  221. return false;
  222. // change attributes only for the name field
  223. if( aCol != 0 )
  224. return false;
  225. // don't link to a board footprint, even if the FPIDs match
  226. if( m_frame->IsCurrentFPFromBoard() )
  227. return false;
  228. LIB_TREE_NODE* node = ToNode( aItem );
  229. wxCHECK( node, false );
  230. switch( node->m_Type )
  231. {
  232. case LIB_TREE_NODE::LIBRARY:
  233. if( node->m_Name == m_frame->GetLoadedFPID().GetLibNickname() )
  234. {
  235. // mark the current library if it's collapsed
  236. if( !m_widget->IsExpanded( ToItem( node ) ) )
  237. {
  238. aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
  239. // proxy for "is canvas item"
  240. }
  241. // mark modified libs with bold font
  242. if( m_frame->GetScreen()->IsContentModified() && !m_frame->IsCurrentFPFromBoard() )
  243. aAttr.SetBold( true );
  244. }
  245. break;
  246. case LIB_TREE_NODE::ITEM:
  247. if( node->m_LibId == m_frame->GetLoadedFPID() )
  248. {
  249. // mark the current (on-canvas) part
  250. aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
  251. // proxy for "is canvas item"
  252. // mark modified part with bold font
  253. if( m_frame->GetScreen()->IsContentModified() && !m_frame->IsCurrentFPFromBoard() )
  254. aAttr.SetBold( true );
  255. }
  256. break;
  257. default:
  258. return false;
  259. }
  260. return true;
  261. }
  262. bool FP_TREE_SYNCHRONIZING_ADAPTER::HasPreview( const wxDataViewItem& aItem )
  263. {
  264. LIB_TREE_NODE* node = ToNode( aItem );
  265. wxCHECK( node, false );
  266. return node->m_Type == LIB_TREE_NODE::ITEM && node->m_LibId != m_frame->GetLoadedFPID();
  267. }
  268. void FP_TREE_SYNCHRONIZING_ADAPTER::ShowPreview( wxWindow* aParent, const wxDataViewItem& aItem )
  269. {
  270. static const wxString c_previewName = wxS( "fpHoverPreview" );
  271. LIB_TREE_NODE* node = ToNode( aItem );
  272. wxCHECK( node, /* void */ );
  273. FOOTPRINT_PREVIEW_PANEL* preview = dynamic_cast<FOOTPRINT_PREVIEW_PANEL*>(
  274. wxWindow::FindWindowByName( c_previewName, aParent ) );
  275. if( !preview )
  276. {
  277. wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
  278. aParent->SetSizer( mainSizer );
  279. preview = FOOTPRINT_PREVIEW_PANEL::New( &m_frame->Kiway(), aParent, m_frame );
  280. preview->SetName( c_previewName );
  281. preview->GetGAL()->SetAxesEnabled( false );
  282. mainSizer->Add( preview, 1, wxEXPAND | wxALL, 1 );
  283. aParent->Layout();
  284. }
  285. preview->DisplayFootprint( node->m_LibId );
  286. }