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.

418 lines
14 KiB

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 The 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 <symbol_tree_synchronizing_adapter.h>
  28. #include <lib_symbol_library_manager.h>
  29. #include <symbol_lib_table.h>
  30. #include <tools/symbol_editor_control.h>
  31. #include <project_sch.h>
  32. #include <string_utils.h>
  33. #include <symbol_preview_widget.h>
  34. #include <widgets/wx_panel.h>
  35. wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>
  36. SYMBOL_TREE_SYNCHRONIZING_ADAPTER::Create( SYMBOL_EDIT_FRAME* aParent,
  37. SYMBOL_LIBRARY_MANAGER* aLibMgr )
  38. {
  39. auto* adapter = new SYMBOL_TREE_SYNCHRONIZING_ADAPTER( aParent, aLibMgr );
  40. return wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>( adapter );
  41. }
  42. SYMBOL_TREE_SYNCHRONIZING_ADAPTER::SYMBOL_TREE_SYNCHRONIZING_ADAPTER( SYMBOL_EDIT_FRAME* aParent,
  43. SYMBOL_LIBRARY_MANAGER* aLibMgr ) :
  44. LIB_TREE_MODEL_ADAPTER( aParent, "pinned_symbol_libs",
  45. aParent->GetViewerSettingsBase()->m_LibTree ),
  46. m_frame( aParent ),
  47. m_libMgr( aLibMgr ),
  48. m_lastSyncHash( -1 )
  49. {
  50. }
  51. TOOL_INTERACTIVE* SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetContextMenuTool()
  52. {
  53. return m_frame->GetToolManager()->GetTool<SYMBOL_EDITOR_CONTROL>();
  54. }
  55. bool SYMBOL_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::TYPE::LIBRARY : true;
  59. }
  60. #define PROGRESS_INTERVAL_MILLIS 120
  61. void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::Sync( const wxString& aForceRefresh,
  62. std::function<void( int, int, const wxString& )> aProgressCallback )
  63. {
  64. wxLongLong nextUpdate = wxGetUTCTimeMillis() + (PROGRESS_INTERVAL_MILLIS / 2);
  65. m_lastSyncHash = m_libMgr->GetHash();
  66. int i = 0, max = GetLibrariesCount();
  67. // Process already stored libraries
  68. for( auto it = m_tree.m_Children.begin(); it != m_tree.m_Children.end(); )
  69. {
  70. const wxString& name = it->get()->m_Name;
  71. if( wxGetUTCTimeMillis() > nextUpdate )
  72. {
  73. aProgressCallback( i, max, name );
  74. nextUpdate = wxGetUTCTimeMillis() + PROGRESS_INTERVAL_MILLIS;
  75. }
  76. // There is a bug in SYMBOL_LIBRARY_MANAGER::LibraryExists() that uses the buffered
  77. // modified libraries before the symbol library table which prevents the library from
  78. // being removed from the tree control.
  79. if( !m_libMgr->LibraryExists( name, true )
  80. || !PROJECT_SCH::SchSymbolLibTable( &m_frame->Prj() )->HasLibrary( name, true )
  81. || PROJECT_SCH::SchSymbolLibTable( &m_frame->Prj() )->FindRow( name, true )
  82. != PROJECT_SCH::SchSymbolLibTable( &m_frame->Prj() )->FindRow( name, false )
  83. || name == aForceRefresh )
  84. {
  85. it = deleteLibrary( it );
  86. continue;
  87. }
  88. else
  89. {
  90. updateLibrary( *(LIB_TREE_NODE_LIBRARY*) it->get() );
  91. }
  92. ++it;
  93. ++i;
  94. }
  95. // Look for new libraries
  96. //
  97. COMMON_SETTINGS* cfg = Pgm().GetCommonSettings();
  98. PROJECT_FILE& project = m_frame->Prj().GetProjectFile();
  99. for( const wxString& libName : m_libMgr->GetLibraryNames() )
  100. {
  101. if( m_libHashes.count( libName ) == 0 )
  102. {
  103. if( wxGetUTCTimeMillis() > nextUpdate )
  104. {
  105. aProgressCallback( i++, max, libName );
  106. nextUpdate = wxGetUTCTimeMillis() + PROGRESS_INTERVAL_MILLIS;
  107. }
  108. SYMBOL_LIB_TABLE_ROW* library = m_libMgr->GetLibrary( libName );
  109. bool pinned = alg::contains( cfg->m_Session.pinned_symbol_libs, libName )
  110. || alg::contains( project.m_PinnedSymbolLibs, libName );
  111. LIB_TREE_NODE_LIBRARY& lib_node = DoAddLibraryNode( libName, library->GetDescr(),
  112. pinned );
  113. updateLibrary( lib_node );
  114. }
  115. }
  116. m_tree.AssignIntrinsicRanks();
  117. }
  118. int SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetLibrariesCount() const
  119. {
  120. int count = LIB_TREE_MODEL_ADAPTER::GetLibrariesCount();
  121. for( const wxString& libName : m_libMgr->GetLibraryNames() )
  122. {
  123. if( m_libHashes.count( libName ) == 0 )
  124. ++count;
  125. }
  126. return count;
  127. }
  128. void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIBRARY& aLibNode )
  129. {
  130. auto hashIt = m_libHashes.find( aLibNode.m_Name );
  131. if( hashIt == m_libHashes.end() )
  132. {
  133. // add a new library
  134. for( LIB_SYMBOL* symbol : m_libMgr->EnumerateSymbols( aLibNode.m_Name ) )
  135. aLibNode.AddItem( symbol );
  136. }
  137. else if( hashIt->second != m_libMgr->GetLibraryHash( aLibNode.m_Name ) )
  138. {
  139. // update an existing library
  140. std::list<LIB_SYMBOL*> symbols = m_libMgr->EnumerateSymbols( aLibNode.m_Name );
  141. // remove the common part from the aliases list
  142. for( auto nodeIt = aLibNode.m_Children.begin(); nodeIt != aLibNode.m_Children.end(); /**/ )
  143. {
  144. auto symbolIt = std::find_if( symbols.begin(), symbols.end(),
  145. [&] ( const LIB_SYMBOL* a )
  146. {
  147. return a->GetName() == (*nodeIt)->m_LibId.GetLibItemName().wx_str();
  148. } );
  149. if( symbolIt != symbols.end() )
  150. {
  151. // alias exists both in the symbol tree and the library manager,
  152. // update only the node data.
  153. static_cast<LIB_TREE_NODE_ITEM*>( nodeIt->get() )->Update( *symbolIt );
  154. symbols.erase( symbolIt );
  155. ++nodeIt;
  156. }
  157. else
  158. {
  159. // node does not exist in the library manager, remove the corresponding node
  160. nodeIt = aLibNode.m_Children.erase( nodeIt );
  161. }
  162. }
  163. // now the aliases list contains only new aliases that need to be added to the tree
  164. for( LIB_SYMBOL* symbol : symbols )
  165. aLibNode.AddItem( symbol );
  166. }
  167. aLibNode.AssignIntrinsicRanks();
  168. m_libHashes[aLibNode.m_Name] = m_libMgr->GetLibraryHash( aLibNode.m_Name );
  169. }
  170. LIB_TREE_NODE::PTR_VECTOR::iterator
  171. SYMBOL_TREE_SYNCHRONIZING_ADAPTER::deleteLibrary( LIB_TREE_NODE::PTR_VECTOR::iterator& aLibNodeIt )
  172. {
  173. LIB_TREE_NODE* node = aLibNodeIt->get();
  174. m_libHashes.erase( node->m_Name );
  175. return m_tree.m_Children.erase( aLibNodeIt );
  176. }
  177. wxDataViewItem SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetCurrentDataViewItem()
  178. {
  179. if( m_frame->GetCurSymbol() )
  180. return FindItem( m_frame->GetCurSymbol()->GetLibId() );
  181. return wxDataViewItem();
  182. }
  183. void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetValue( wxVariant& aVariant, wxDataViewItem const& aItem,
  184. unsigned int aCol ) const
  185. {
  186. if( IsFrozen() )
  187. {
  188. aVariant = wxEmptyString;
  189. return;
  190. }
  191. LIB_TREE_NODE* node = ToNode( aItem );
  192. wxASSERT( node );
  193. switch( aCol )
  194. {
  195. case NAME_COL:
  196. if( m_frame->GetCurSymbol() && m_frame->GetCurSymbol()->GetLibId() == node->m_LibId )
  197. node->m_Name = m_frame->GetCurSymbol()->GetLibId().GetUniStringLibItemName();
  198. if( node->m_Pinned )
  199. aVariant = GetPinningSymbol() + UnescapeString( node->m_Name );
  200. else
  201. aVariant = UnescapeString( node->m_Name );
  202. // mark modified items with an asterisk
  203. if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY )
  204. {
  205. if( m_libMgr->IsLibraryModified( node->m_Name ) )
  206. aVariant = aVariant.GetString() + " *";
  207. }
  208. else if( node->m_Type == LIB_TREE_NODE::TYPE::ITEM )
  209. {
  210. if( m_libMgr->IsSymbolModified( node->m_Name, node->m_Parent->m_Name ) )
  211. aVariant = aVariant.GetString() + " *";
  212. }
  213. break;
  214. default:
  215. if( m_colIdxMap.count( aCol ) )
  216. {
  217. if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY )
  218. {
  219. LIB_SYMBOL_LIBRARY_MANAGER& libMgr = m_frame->GetLibManager();
  220. SYMBOL_LIB_TABLE_ROW* lib = libMgr.GetLibrary( node->m_LibId.GetLibNickname() );
  221. if( lib )
  222. node->m_Desc = lib->GetDescr();
  223. if( !m_libMgr->IsLibraryLoaded( node->m_Name ) )
  224. aVariant = _( "(failed to load)" ) + wxS( " " ) + aVariant.GetString();
  225. else if( m_libMgr->IsLibraryReadOnly( node->m_Name ) )
  226. aVariant = _( "(read-only)" ) + wxS( " " ) + aVariant.GetString();
  227. }
  228. const wxString& key = m_colIdxMap.at( aCol );
  229. if( m_frame->GetCurSymbol() && m_frame->GetCurSymbol()->GetLibId() == node->m_LibId )
  230. {
  231. node->m_Desc = m_frame->GetCurSymbol()->GetDescription();
  232. }
  233. wxString valueStr;
  234. if( key == wxT( "Description" ) )
  235. valueStr = node->m_Desc;
  236. else if( node->m_Fields.count( key ) )
  237. valueStr = node->m_Fields.at( key );
  238. else
  239. valueStr = wxEmptyString;
  240. valueStr.Replace( wxS( "\n" ), wxS( " " ) ); // Clear line breaks
  241. if( !aVariant.GetString().IsEmpty() )
  242. {
  243. if( !valueStr.IsEmpty() )
  244. aVariant = valueStr + wxS( " - " ) + aVariant.GetString();
  245. }
  246. else
  247. {
  248. aVariant = valueStr;
  249. }
  250. }
  251. break;
  252. }
  253. }
  254. bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, unsigned int aCol,
  255. wxDataViewItemAttr& aAttr ) const
  256. {
  257. if( IsFrozen() )
  258. return false;
  259. LIB_TREE_NODE* node = ToNode( aItem );
  260. wxCHECK( node, false );
  261. // Mark both columns of unloaded libraries using grey text color (to look disabled)
  262. if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY && !m_libMgr->IsLibraryLoaded( node->m_Name ) )
  263. {
  264. aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
  265. return true;
  266. }
  267. // The remaining attributes are only for the name column
  268. if( aCol != NAME_COL )
  269. return false;
  270. LIB_SYMBOL* curSymbol = m_frame->GetCurSymbol();
  271. switch( node->m_Type )
  272. {
  273. case LIB_TREE_NODE::TYPE::LIBRARY:
  274. // mark modified libs with bold font
  275. aAttr.SetBold( m_libMgr->IsLibraryModified( node->m_Name ) );
  276. // mark the current library if it's collapsed
  277. if( curSymbol && curSymbol->GetLibId().GetLibNickname() == node->m_LibId.GetLibNickname() )
  278. {
  279. if( !m_widget->IsExpanded( ToItem( node ) ) )
  280. {
  281. aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
  282. // proxy for "is canvas item"
  283. }
  284. }
  285. break;
  286. case LIB_TREE_NODE::TYPE::ITEM:
  287. // mark modified part with bold font
  288. aAttr.SetBold( m_libMgr->IsSymbolModified( node->m_Name, node->m_Parent->m_Name ) );
  289. // mark aliases with italic font
  290. aAttr.SetItalic( !node->m_IsRoot );
  291. // mark the current (on-canvas) part
  292. if( curSymbol && curSymbol->GetLibId() == node->m_LibId )
  293. {
  294. aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
  295. // proxy for "is canvas item"
  296. }
  297. break;
  298. default:
  299. return false;
  300. }
  301. return true;
  302. }
  303. bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::HasPreview( const wxDataViewItem& aItem )
  304. {
  305. LIB_TREE_NODE* node = ToNode( aItem );
  306. wxCHECK( node, false );
  307. return node->m_Type == LIB_TREE_NODE::TYPE::ITEM && node->m_LibId != m_frame->GetTargetLibId();
  308. }
  309. void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::ShowPreview( wxWindow* aParent,
  310. const wxDataViewItem& aItem )
  311. {
  312. static const wxString c_previewName = wxS( "symHoverPreview" );
  313. LIB_TREE_NODE* node = ToNode( aItem );
  314. wxCHECK( node, /* void */ );
  315. SYMBOL_PREVIEW_WIDGET* preview = dynamic_cast<SYMBOL_PREVIEW_WIDGET*>(
  316. wxWindow::FindWindowByName( c_previewName, aParent ) );
  317. if( !preview )
  318. {
  319. wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
  320. aParent->SetSizer( mainSizer );
  321. WX_PANEL* panel = new WX_PANEL( aParent );
  322. panel->SetBorders( true, true, true, true );
  323. panel->SetBorderColor( KIGFX::COLOR4D::BLACK );
  324. wxBoxSizer* panelSizer = new wxBoxSizer( wxVERTICAL );
  325. panel->SetSizer( panelSizer );
  326. EDA_DRAW_PANEL_GAL::GAL_TYPE backend = m_frame->GetCanvas()->GetBackend();
  327. preview = new SYMBOL_PREVIEW_WIDGET( panel, &m_frame->Kiway(), false, backend );
  328. preview->SetName( c_previewName );
  329. preview->SetLayoutDirection( wxLayout_LeftToRight );
  330. panelSizer->Add( preview, 1, wxEXPAND | wxALL, 1 );
  331. mainSizer->Add( panel, 1, wxEXPAND, 0 );
  332. aParent->Layout();
  333. }
  334. preview->DisplaySymbol( node->m_LibId, node->m_Unit );
  335. }