kicad-source/eeschema/symbol_tree_synchronizing_adapter.cpp
Seth Hillbrand 0b2d4d4879 Revise Copyright statement to align with TLF
Recommendation is to avoid using the year nomenclature as this
information is already encoded in the git repo.  Avoids needing to
repeatly update.

Also updates AUTHORS.txt from current repo with contributor names
2025-01-01 14:12:04 -08:00

416 lines
14 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* https://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <pgm_base.h>
#include <project/project_file.h>
#include <symbol_tree_synchronizing_adapter.h>
#include <lib_symbol_library_manager.h>
#include <symbol_lib_table.h>
#include <tools/symbol_editor_control.h>
#include <project_sch.h>
#include <string_utils.h>
#include <symbol_preview_widget.h>
#include <widgets/wx_panel.h>
wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>
SYMBOL_TREE_SYNCHRONIZING_ADAPTER::Create( SYMBOL_EDIT_FRAME* aParent,
SYMBOL_LIBRARY_MANAGER* aLibMgr )
{
auto* adapter = new SYMBOL_TREE_SYNCHRONIZING_ADAPTER( aParent, aLibMgr );
return wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>( adapter );
}
SYMBOL_TREE_SYNCHRONIZING_ADAPTER::SYMBOL_TREE_SYNCHRONIZING_ADAPTER( SYMBOL_EDIT_FRAME* aParent,
SYMBOL_LIBRARY_MANAGER* aLibMgr ) :
LIB_TREE_MODEL_ADAPTER( aParent, "pinned_symbol_libs", aParent->GetViewerSettingsBase() ),
m_frame( aParent ),
m_libMgr( aLibMgr ),
m_lastSyncHash( -1 )
{
}
TOOL_INTERACTIVE* SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetContextMenuTool()
{
return m_frame->GetToolManager()->GetTool<SYMBOL_EDITOR_CONTROL>();
}
bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::IsContainer( const wxDataViewItem& aItem ) const
{
const LIB_TREE_NODE* node = ToNode( aItem );
return node ? node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY : true;
}
#define PROGRESS_INTERVAL_MILLIS 120
void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::Sync( const wxString& aForceRefresh,
std::function<void( int, int, const wxString& )> aProgressCallback )
{
wxLongLong nextUpdate = wxGetUTCTimeMillis() + (PROGRESS_INTERVAL_MILLIS / 2);
m_lastSyncHash = m_libMgr->GetHash();
int i = 0, max = GetLibrariesCount();
// Process already stored libraries
for( auto it = m_tree.m_Children.begin(); it != m_tree.m_Children.end(); /* iteration inside */ )
{
const wxString& name = it->get()->m_Name;
if( wxGetUTCTimeMillis() > nextUpdate )
{
aProgressCallback( i, max, name );
nextUpdate = wxGetUTCTimeMillis() + PROGRESS_INTERVAL_MILLIS;
}
// There is a bug in SYMBOL_LIBRARY_MANAGER::LibraryExists() that uses the buffered
// modified libraries before the symbol library table which prevents the library from
// being removed from the tree control.
if( !m_libMgr->LibraryExists( name, true )
|| !PROJECT_SCH::SchSymbolLibTable( &m_frame->Prj() )->HasLibrary( name, true )
|| PROJECT_SCH::SchSymbolLibTable( &m_frame->Prj() )->FindRow( name, true ) !=
PROJECT_SCH::SchSymbolLibTable( &m_frame->Prj() )->FindRow( name, false )
|| name == aForceRefresh )
{
it = deleteLibrary( it );
continue;
}
else
{
updateLibrary( *(LIB_TREE_NODE_LIBRARY*) it->get() );
}
++it;
++i;
}
// Look for new libraries
//
COMMON_SETTINGS* cfg = Pgm().GetCommonSettings();
PROJECT_FILE& project = m_frame->Prj().GetProjectFile();
for( const wxString& libName : m_libMgr->GetLibraryNames() )
{
if( m_libHashes.count( libName ) == 0 )
{
if( wxGetUTCTimeMillis() > nextUpdate )
{
aProgressCallback( i++, max, libName );
nextUpdate = wxGetUTCTimeMillis() + PROGRESS_INTERVAL_MILLIS;
}
SYMBOL_LIB_TABLE_ROW* library = m_libMgr->GetLibrary( libName );
bool pinned = alg::contains( cfg->m_Session.pinned_symbol_libs, libName )
|| alg::contains( project.m_PinnedSymbolLibs, libName );
LIB_TREE_NODE_LIBRARY& lib_node = DoAddLibraryNode( libName, library->GetDescr(),
pinned );
updateLibrary( lib_node );
}
}
m_tree.AssignIntrinsicRanks();
}
int SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetLibrariesCount() const
{
int count = LIB_TREE_MODEL_ADAPTER::GetLibrariesCount();
for( const wxString& libName : m_libMgr->GetLibraryNames() )
{
if( m_libHashes.count( libName ) == 0 )
++count;
}
return count;
}
void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIBRARY& aLibNode )
{
auto hashIt = m_libHashes.find( aLibNode.m_Name );
if( hashIt == m_libHashes.end() )
{
// add a new library
for( LIB_SYMBOL* alias : m_libMgr->GetAliases( aLibNode.m_Name ) )
aLibNode.AddItem( alias );
}
else if( hashIt->second != m_libMgr->GetLibraryHash( aLibNode.m_Name ) )
{
// update an existing library
std::list<LIB_SYMBOL*> aliases = m_libMgr->GetAliases( aLibNode.m_Name );
// remove the common part from the aliases list
for( auto nodeIt = aLibNode.m_Children.begin(); nodeIt != aLibNode.m_Children.end(); /**/ )
{
auto aliasIt = std::find_if( aliases.begin(), aliases.end(),
[&] ( const LIB_SYMBOL* a )
{
return a->GetName() == (*nodeIt)->m_LibId.GetLibItemName().wx_str();
} );
if( aliasIt != aliases.end() )
{
// alias exists both in the symbol tree and the library manager,
// update only the node data.
static_cast<LIB_TREE_NODE_ITEM*>( nodeIt->get() )->Update( *aliasIt );
aliases.erase( aliasIt );
++nodeIt;
}
else
{
// node does not exist in the library manager, remove the corresponding node
nodeIt = aLibNode.m_Children.erase( nodeIt );
}
}
// now the aliases list contains only new aliases that need to be added to the tree
for( LIB_SYMBOL* alias : aliases )
aLibNode.AddItem( alias );
}
aLibNode.AssignIntrinsicRanks();
m_libHashes[aLibNode.m_Name] = m_libMgr->GetLibraryHash( aLibNode.m_Name );
}
LIB_TREE_NODE::PTR_VECTOR::iterator
SYMBOL_TREE_SYNCHRONIZING_ADAPTER::deleteLibrary( LIB_TREE_NODE::PTR_VECTOR::iterator& aLibNodeIt )
{
LIB_TREE_NODE* node = aLibNodeIt->get();
m_libHashes.erase( node->m_Name );
return m_tree.m_Children.erase( aLibNodeIt );
}
wxDataViewItem SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetCurrentDataViewItem()
{
if( m_frame->GetCurSymbol() )
return FindItem( m_frame->GetCurSymbol()->GetLibId() );
return wxDataViewItem();
}
void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetValue( wxVariant& aVariant, wxDataViewItem const& aItem,
unsigned int aCol ) const
{
if( IsFrozen() )
{
aVariant = wxEmptyString;
return;
}
LIB_TREE_NODE* node = ToNode( aItem );
wxASSERT( node );
switch( aCol )
{
case NAME_COL:
if( m_frame->GetCurSymbol() && m_frame->GetCurSymbol()->GetLibId() == node->m_LibId )
node->m_Name = m_frame->GetCurSymbol()->GetLibId().GetUniStringLibItemName();
if( node->m_Pinned )
aVariant = GetPinningSymbol() + UnescapeString( node->m_Name );
else
aVariant = UnescapeString( node->m_Name );
// mark modified items with an asterisk
if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY )
{
if( m_libMgr->IsLibraryModified( node->m_Name ) )
aVariant = aVariant.GetString() + " *";
}
else if( node->m_Type == LIB_TREE_NODE::TYPE::ITEM )
{
if( m_libMgr->IsSymbolModified( node->m_Name, node->m_Parent->m_Name ) )
aVariant = aVariant.GetString() + " *";
}
break;
default:
if( m_colIdxMap.count( aCol ) )
{
if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY )
{
LIB_SYMBOL_LIBRARY_MANAGER& libMgr = m_frame->GetLibManager();
SYMBOL_LIB_TABLE_ROW* lib = libMgr.GetLibrary( node->m_LibId.GetLibNickname() );
if( lib )
node->m_Desc = lib->GetDescr();
if( !m_libMgr->IsLibraryLoaded( node->m_Name ) )
aVariant = _( "(failed to load)" ) + wxS( " " ) + aVariant.GetString();
else if( m_libMgr->IsLibraryReadOnly( node->m_Name ) )
aVariant = _( "(read-only)" ) + wxS( " " ) + aVariant.GetString();
}
const wxString& key = m_colIdxMap.at( aCol );
if( m_frame->GetCurSymbol() && m_frame->GetCurSymbol()->GetLibId() == node->m_LibId )
{
node->m_Desc = m_frame->GetCurSymbol()->GetDescription();
}
wxString valueStr;
if( key == wxT( "Description" ) )
valueStr = node->m_Desc;
else if( node->m_Fields.count( key ) )
valueStr = node->m_Fields.at( key );
else
valueStr = wxEmptyString;
valueStr.Replace( wxS( "\n" ), wxS( " " ) ); // Clear line breaks
if( !aVariant.GetString().IsEmpty() )
{
if( !valueStr.IsEmpty() )
aVariant = valueStr + wxS( " - " ) + aVariant.GetString();
}
else
{
aVariant = valueStr;
}
}
break;
}
}
bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, unsigned int aCol,
wxDataViewItemAttr& aAttr ) const
{
if( IsFrozen() )
return false;
LIB_TREE_NODE* node = ToNode( aItem );
wxCHECK( node, false );
// Mark both columns of unloaded libraries using grey text color (to look disabled)
if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY && !m_libMgr->IsLibraryLoaded( node->m_Name ) )
{
aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
return true;
}
// The remaining attributes are only for the name column
if( aCol != NAME_COL )
return false;
LIB_SYMBOL* curSymbol = m_frame->GetCurSymbol();
switch( node->m_Type )
{
case LIB_TREE_NODE::TYPE::LIBRARY:
// mark modified libs with bold font
aAttr.SetBold( m_libMgr->IsLibraryModified( node->m_Name ) );
// mark the current library if it's collapsed
if( curSymbol && curSymbol->GetLibId().GetLibNickname() == node->m_LibId.GetLibNickname() )
{
if( !m_widget->IsExpanded( ToItem( node ) ) )
{
aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
// proxy for "is canvas item"
}
}
break;
case LIB_TREE_NODE::TYPE::ITEM:
// mark modified part with bold font
aAttr.SetBold( m_libMgr->IsSymbolModified( node->m_Name, node->m_Parent->m_Name ) );
// mark aliases with italic font
aAttr.SetItalic( !node->m_IsRoot );
// mark the current (on-canvas) part
if( curSymbol && curSymbol->GetLibId() == node->m_LibId )
{
aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
// proxy for "is canvas item"
}
break;
default:
return false;
}
return true;
}
bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::HasPreview( const wxDataViewItem& aItem )
{
LIB_TREE_NODE* node = ToNode( aItem );
wxCHECK( node, false );
return node->m_Type == LIB_TREE_NODE::TYPE::ITEM && node->m_LibId != m_frame->GetTargetLibId();
}
void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::ShowPreview( wxWindow* aParent,
const wxDataViewItem& aItem )
{
static const wxString c_previewName = wxS( "symHoverPreview" );
LIB_TREE_NODE* node = ToNode( aItem );
wxCHECK( node, /* void */ );
SYMBOL_PREVIEW_WIDGET* preview = dynamic_cast<SYMBOL_PREVIEW_WIDGET*>(
wxWindow::FindWindowByName( c_previewName, aParent ) );
if( !preview )
{
wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
aParent->SetSizer( mainSizer );
WX_PANEL* panel = new WX_PANEL( aParent );
panel->SetBorders( true, true, true, true );
panel->SetBorderColor( KIGFX::COLOR4D::BLACK );
wxBoxSizer* panelSizer = new wxBoxSizer( wxVERTICAL );
panel->SetSizer( panelSizer );
EDA_DRAW_PANEL_GAL::GAL_TYPE backend = m_frame->GetCanvas()->GetBackend();
preview = new SYMBOL_PREVIEW_WIDGET( panel, &m_frame->Kiway(), false, backend );
preview->SetName( c_previewName );
preview->SetLayoutDirection( wxLayout_LeftToRight );
panelSizer->Add( preview, 1, wxEXPAND | wxALL, 1 );
mainSizer->Add( panel, 1, wxEXPAND, 0 );
aParent->Layout();
}
preview->DisplaySymbol( node->m_LibId, node->m_Unit );
}