2017-10-28 18:28:14 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2017 Chris Pavlina <pavlina.chris@gmail.com>
|
|
|
|
* Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
|
2020-05-21 20:46:00 +00:00
|
|
|
* Copyright (C) 2014-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
2017-10-28 18:28:14 +00:00
|
|
|
*
|
|
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
#include <lib_tree_model_adapter.h>
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
#include <eda_pattern_match.h>
|
|
|
|
|
|
|
|
#include <wx/progdlg.h>
|
|
|
|
#include <wx/tokenzr.h>
|
2017-11-30 11:31:36 +00:00
|
|
|
#include <wx/wupdlock.h>
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
static const int kDataViewIndent = 20;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert CMP_TREE_NODE -> wxDataViewItem
|
|
|
|
*/
|
2018-07-27 20:47:51 +00:00
|
|
|
wxDataViewItem LIB_TREE_MODEL_ADAPTER::ToItem( LIB_TREE_NODE const* aNode )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
return wxDataViewItem( const_cast<void*>( static_cast<void const*>( aNode ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert wxDataViewItem -> CMP_TREE_NODE
|
|
|
|
*/
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE const* LIB_TREE_MODEL_ADAPTER::ToNode( wxDataViewItem aItem )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
2018-07-27 20:47:51 +00:00
|
|
|
return static_cast<LIB_TREE_NODE const*>( aItem.GetID() );
|
2017-10-28 18:28:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert CMP_TREE_NODE's children to wxDataViewItemArray
|
|
|
|
*/
|
2018-07-27 20:47:51 +00:00
|
|
|
unsigned int LIB_TREE_MODEL_ADAPTER::IntoArray( LIB_TREE_NODE const& aNode,
|
|
|
|
wxDataViewItemArray& aChildren )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
unsigned int n = 0;
|
|
|
|
|
|
|
|
for( auto const& child: aNode.Children )
|
|
|
|
{
|
2017-11-30 11:31:36 +00:00
|
|
|
if( child->Score > 0 )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
aChildren.Add( ToItem( &*child ) );
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_MODEL_ADAPTER::LIB_TREE_MODEL_ADAPTER()
|
2017-10-28 18:28:14 +00:00
|
|
|
:m_filter( CMP_FILTER_NONE ),
|
|
|
|
m_show_units( true ),
|
|
|
|
m_preselect_unit( 0 ),
|
2018-04-28 09:43:41 +00:00
|
|
|
m_freeze( 0 ),
|
2017-10-28 18:28:14 +00:00
|
|
|
m_col_part( nullptr ),
|
|
|
|
m_col_desc( nullptr ),
|
|
|
|
m_widget( nullptr )
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_MODEL_ADAPTER::~LIB_TREE_MODEL_ADAPTER()
|
2017-10-28 18:28:14 +00:00
|
|
|
{}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
void LIB_TREE_MODEL_ADAPTER::SetFilter( CMP_FILTER_TYPE aFilter )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
m_filter = aFilter;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
void LIB_TREE_MODEL_ADAPTER::ShowUnits( bool aShow )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
m_show_units = aShow;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-02-10 00:57:53 +00:00
|
|
|
void LIB_TREE_MODEL_ADAPTER::UpdateWidth( int aCol )
|
|
|
|
{
|
|
|
|
auto col = m_widget->GetColumn( aCol );
|
|
|
|
|
|
|
|
if( col )
|
|
|
|
{
|
|
|
|
col->SetWidth( ColWidth( m_tree, aCol, col->GetTitle() ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
void LIB_TREE_MODEL_ADAPTER::SetPreselectNode( LIB_ID const& aLibId, int aUnit )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
m_preselect_lib_id = aLibId;
|
|
|
|
m_preselect_unit = aUnit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
void LIB_TREE_MODEL_ADAPTER::DoAddLibrary( wxString const& aNodeName, wxString const& aDesc,
|
2018-08-05 11:56:02 +00:00
|
|
|
std::vector<LIB_TREE_ITEM*> const& aItemList,
|
|
|
|
bool presorted )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
2018-02-06 15:14:01 +00:00
|
|
|
auto& lib_node = m_tree.AddLib( aNodeName, aDesc );
|
2017-10-28 18:28:14 +00:00
|
|
|
|
2019-02-10 00:57:53 +00:00
|
|
|
lib_node.VisLen = wxTheApp->GetTopWindow()->GetTextExtent( lib_node.Name ).x;
|
|
|
|
|
2018-07-30 13:18:37 +00:00
|
|
|
for( auto item: aItemList )
|
2019-02-01 14:47:03 +00:00
|
|
|
{
|
|
|
|
if( item )
|
2019-02-10 00:57:53 +00:00
|
|
|
{
|
|
|
|
auto& child_node = lib_node.AddItem( item );
|
|
|
|
child_node.VisLen = wxTheApp->GetTopWindow()->GetTextExtent( child_node.Name ).x;
|
|
|
|
}
|
2019-02-01 14:47:03 +00:00
|
|
|
}
|
2017-10-28 18:28:14 +00:00
|
|
|
|
2018-08-05 11:56:02 +00:00
|
|
|
lib_node.AssignIntrinsicRanks( presorted );
|
2017-10-28 18:28:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-04 17:12:18 +00:00
|
|
|
void LIB_TREE_MODEL_ADAPTER::UpdateSearchString( wxString const& aSearch, bool aState )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
2017-12-19 13:06:26 +00:00
|
|
|
{
|
|
|
|
wxWindowUpdateLocker updateLock( m_widget );
|
|
|
|
|
2021-01-04 17:12:18 +00:00
|
|
|
// Even with the updateLock, wxWidgets sometimes ties its knickers in a knot trying to
|
|
|
|
// run a wxdataview_selection_changed_callback() on a row that has been deleted.
|
|
|
|
// https://bugs.launchpad.net/kicad/+bug/1756255
|
|
|
|
m_widget->UnselectAll();
|
|
|
|
|
2020-12-03 22:03:43 +00:00
|
|
|
// This collapse is required before the call to "Freeze()" below. Once Freeze()
|
|
|
|
// is called, GetParent() will return nullptr. While this works for some calls, it
|
2021-02-04 21:18:36 +00:00
|
|
|
// segfaults when we have any expanded elements b/c the sub units in the tree don't
|
|
|
|
// have explicit references that are maintained over a search
|
2020-12-03 22:03:43 +00:00
|
|
|
// The tree will be expanded again below when we get our matches
|
2021-01-04 17:12:18 +00:00
|
|
|
//
|
|
|
|
// Also note that this cannot happen when we have deleted a symbol as GTK will also
|
|
|
|
// iterate over the tree in this case and find a symbol that has an invalid link
|
|
|
|
// and crash https://gitlab.com/kicad/code/kicad/-/issues/6910
|
2021-04-20 18:44:37 +00:00
|
|
|
if( !aState && m_tree.Children.size() )
|
2021-02-04 21:18:36 +00:00
|
|
|
{
|
|
|
|
for( std::unique_ptr<LIB_TREE_NODE>& child: m_tree.Children )
|
|
|
|
m_widget->Collapse( wxDataViewItem( &*child ) );
|
|
|
|
}
|
2020-12-03 22:03:43 +00:00
|
|
|
|
|
|
|
// DO NOT REMOVE THE FREEZE/THAW. This freeze/thaw is a flag for this model adapter
|
|
|
|
// that tells it when it shouldn't trust any of the data in the model. When set, it will
|
|
|
|
// not return invalid data to the UI, since this invalid data can cause crashes.
|
|
|
|
// This is different than the update locker, which locks the UI aspects only.
|
2020-10-26 18:28:15 +00:00
|
|
|
Freeze();
|
2020-10-29 22:44:32 +00:00
|
|
|
BeforeReset();
|
2018-03-16 19:01:50 +00:00
|
|
|
|
2020-10-26 18:28:15 +00:00
|
|
|
m_tree.ResetScore();
|
|
|
|
|
|
|
|
wxStringTokenizer tokenizer( aSearch );
|
|
|
|
|
|
|
|
while( tokenizer.HasMoreTokens() )
|
|
|
|
{
|
|
|
|
const wxString term = tokenizer.GetNextToken().Lower();
|
|
|
|
EDA_COMBINED_MATCHER matcher( term );
|
|
|
|
|
|
|
|
m_tree.UpdateScore( matcher );
|
|
|
|
}
|
2020-05-21 20:46:00 +00:00
|
|
|
|
2020-10-26 18:28:15 +00:00
|
|
|
m_tree.SortNodes();
|
|
|
|
AfterReset();
|
|
|
|
Thaw();
|
2017-12-19 13:06:26 +00:00
|
|
|
}
|
2017-11-30 11:31:36 +00:00
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE* bestMatch = ShowResults();
|
2018-06-10 22:20:06 +00:00
|
|
|
|
|
|
|
if( !bestMatch )
|
|
|
|
bestMatch = ShowPreselect();
|
|
|
|
|
|
|
|
if( !bestMatch )
|
|
|
|
bestMatch = ShowSingleLibrary();
|
|
|
|
|
|
|
|
if( bestMatch )
|
|
|
|
{
|
|
|
|
auto item = wxDataViewItem( bestMatch );
|
|
|
|
m_widget->Select( item );
|
2019-04-28 16:00:00 +00:00
|
|
|
|
|
|
|
// Make sure the *parent* item is visible. The selected item is the
|
|
|
|
// first (shown) child of the parent. So it's always right below the parent,
|
|
|
|
// and this way the user can also see what library the selected part belongs to,
|
|
|
|
// without having a case where the selection is off the screen (unless the
|
|
|
|
// window is a single row high, which is unlikely)
|
|
|
|
//
|
|
|
|
// This also happens to circumvent https://bugs.launchpad.net/kicad/+bug/1804400
|
|
|
|
// which appears to be a GTK+3 bug.
|
|
|
|
{
|
|
|
|
wxDataViewItem parent = GetParent( item );
|
|
|
|
|
|
|
|
if( parent.IsOk() )
|
|
|
|
item = parent;
|
|
|
|
}
|
|
|
|
|
2018-06-10 22:20:06 +00:00
|
|
|
m_widget->EnsureVisible( item );
|
|
|
|
}
|
2019-02-10 00:57:53 +00:00
|
|
|
|
|
|
|
UpdateWidth( 0 );
|
2017-10-28 18:28:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
void LIB_TREE_MODEL_ADAPTER::AttachTo( wxDataViewCtrl* aDataViewCtrl )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
m_widget = aDataViewCtrl;
|
|
|
|
aDataViewCtrl->SetIndent( kDataViewIndent );
|
|
|
|
aDataViewCtrl->AssociateModel( this );
|
|
|
|
aDataViewCtrl->ClearColumns();
|
|
|
|
|
2018-08-04 11:59:04 +00:00
|
|
|
wxString part_head = _( "Item" );
|
|
|
|
wxString desc_head = _( "Description" );
|
2017-10-28 18:28:14 +00:00
|
|
|
|
2019-02-10 00:57:53 +00:00
|
|
|
m_col_part = aDataViewCtrl->AppendTextColumn( part_head, 0, wxDATAVIEW_CELL_INERT, 360 );
|
|
|
|
m_col_desc = aDataViewCtrl->AppendTextColumn( desc_head, 1, wxDATAVIEW_CELL_INERT, 2000 );
|
2017-10-28 18:28:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_ID LIB_TREE_MODEL_ADAPTER::GetAliasFor( const wxDataViewItem& aSelection ) const
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
auto node = ToNode( aSelection );
|
|
|
|
|
|
|
|
LIB_ID emptyId;
|
|
|
|
|
|
|
|
if( !node )
|
|
|
|
return emptyId;
|
|
|
|
|
|
|
|
return node->LibId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
int LIB_TREE_MODEL_ADAPTER::GetUnitFor( const wxDataViewItem& aSelection ) const
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
auto node = ToNode( aSelection );
|
|
|
|
return node ? node->Unit : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE::TYPE LIB_TREE_MODEL_ADAPTER::GetTypeFor( const wxDataViewItem& aSelection ) const
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
auto node = ToNode( aSelection );
|
2018-07-27 20:47:51 +00:00
|
|
|
return node ? node->Type : LIB_TREE_NODE::INVALID;
|
2017-10-28 18:28:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
int LIB_TREE_MODEL_ADAPTER::GetItemCount() const
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
for( auto& lib: m_tree.Children )
|
2018-07-27 20:47:51 +00:00
|
|
|
n += lib->Children.size();
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
wxDataViewItem LIB_TREE_MODEL_ADAPTER::FindItem( const LIB_ID& aLibId )
|
2017-11-14 11:03:19 +00:00
|
|
|
{
|
|
|
|
for( auto& lib: m_tree.Children )
|
|
|
|
{
|
|
|
|
if( lib->Name != aLibId.GetLibNickname() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// if part name is not specified, return the library node
|
|
|
|
if( aLibId.GetLibItemName() == "" )
|
|
|
|
return ToItem( lib.get() );
|
|
|
|
|
|
|
|
for( auto& alias: lib->Children )
|
|
|
|
{
|
|
|
|
if( alias->Name == aLibId.GetLibItemName() )
|
|
|
|
return ToItem( alias.get() );
|
|
|
|
}
|
|
|
|
|
|
|
|
break; // could not find the part in the requested library
|
|
|
|
}
|
|
|
|
|
|
|
|
return wxDataViewItem();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
unsigned int LIB_TREE_MODEL_ADAPTER::GetChildren( wxDataViewItem const& aItem,
|
|
|
|
wxDataViewItemArray& aChildren ) const
|
2017-11-21 13:54:30 +00:00
|
|
|
{
|
|
|
|
auto node = ( aItem.IsOk() ? ToNode( aItem ) : &m_tree );
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
if( node->Type != LIB_TREE_NODE::TYPE::LIBID
|
|
|
|
|| ( m_show_units && node->Type == LIB_TREE_NODE::TYPE::LIBID ) )
|
2017-11-21 13:54:30 +00:00
|
|
|
return IntoArray( *node, aChildren );
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
bool LIB_TREE_MODEL_ADAPTER::HasContainerColumns( wxDataViewItem const& aItem ) const
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
return IsContainer( aItem );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
bool LIB_TREE_MODEL_ADAPTER::IsContainer( wxDataViewItem const& aItem ) const
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
auto node = ToNode( aItem );
|
|
|
|
return node ? node->Children.size() : true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
wxDataViewItem LIB_TREE_MODEL_ADAPTER::GetParent( wxDataViewItem const& aItem ) const
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
2020-05-21 20:46:00 +00:00
|
|
|
if( m_freeze )
|
|
|
|
return ToItem( nullptr );
|
|
|
|
|
2017-10-28 18:28:14 +00:00
|
|
|
auto node = ToNode( aItem );
|
|
|
|
auto parent = node ? node->Parent : nullptr;
|
|
|
|
|
|
|
|
// wxDataViewModel has no root node, but rather top-level elements have
|
|
|
|
// an invalid (null) parent.
|
2018-07-27 20:47:51 +00:00
|
|
|
if( !node || !parent || parent->Type == LIB_TREE_NODE::TYPE::ROOT )
|
2017-10-28 18:28:14 +00:00
|
|
|
return ToItem( nullptr );
|
|
|
|
else
|
|
|
|
return ToItem( parent );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
void LIB_TREE_MODEL_ADAPTER::GetValue( wxVariant& aVariant,
|
|
|
|
wxDataViewItem const& aItem,
|
|
|
|
unsigned int aCol ) const
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
2018-04-28 09:43:41 +00:00
|
|
|
if( IsFrozen() )
|
|
|
|
{
|
|
|
|
aVariant = wxEmptyString;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-28 18:28:14 +00:00
|
|
|
auto node = ToNode( aItem );
|
|
|
|
wxASSERT( node );
|
|
|
|
|
|
|
|
switch( aCol )
|
|
|
|
{
|
2017-11-12 13:24:55 +00:00
|
|
|
default: // column == -1 is used for default Compare function
|
2017-10-28 18:28:14 +00:00
|
|
|
case 0:
|
|
|
|
aVariant = node->Name;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
aVariant = node->Desc;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
bool LIB_TREE_MODEL_ADAPTER::GetAttr( wxDataViewItem const& aItem,
|
|
|
|
unsigned int aCol,
|
|
|
|
wxDataViewItemAttr& aAttr ) const
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
2018-04-28 09:43:41 +00:00
|
|
|
if( IsFrozen() )
|
|
|
|
return false;
|
|
|
|
|
2017-10-28 18:28:14 +00:00
|
|
|
auto node = ToNode( aItem );
|
|
|
|
wxASSERT( node );
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
if( node->Type != LIB_TREE_NODE::LIBID )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
// Currently only aliases are formatted at all
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !node->IsRoot && aCol == 0 )
|
|
|
|
{
|
|
|
|
// Names of non-root aliases are italicized
|
|
|
|
aAttr.SetItalic( true );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
int LIB_TREE_MODEL_ADAPTER::ColWidth( LIB_TREE_NODE& aTree, int aCol, wxString const& aHeading )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
2018-07-27 20:47:51 +00:00
|
|
|
if( aCol == 0 )
|
2019-02-10 00:57:53 +00:00
|
|
|
{
|
2019-02-15 18:41:05 +00:00
|
|
|
int padding = m_widget->GetTextExtent( "M" ).x;
|
|
|
|
int indent = m_widget->GetIndent();
|
2019-02-10 00:57:53 +00:00
|
|
|
int longest = m_widget->GetTextExtent( aHeading ).x;
|
|
|
|
|
|
|
|
for( auto& node : aTree.Children )
|
|
|
|
{
|
|
|
|
auto item = ToItem( &*node );
|
|
|
|
|
|
|
|
if( !item.IsOk() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if( node->Score > 0 )
|
2019-02-16 09:25:34 +00:00
|
|
|
{
|
|
|
|
// Ensure the text size is up to date:
|
|
|
|
if( node->VisLen == 0 )
|
|
|
|
node->VisLen = m_widget->GetTextExtent( node->Name ).x;
|
|
|
|
|
2019-02-15 18:41:05 +00:00
|
|
|
longest = std::max( longest, node->VisLen + padding + indent );
|
2019-02-16 09:25:34 +00:00
|
|
|
}
|
2019-02-10 00:57:53 +00:00
|
|
|
|
|
|
|
if( !m_widget->IsExpanded( item ) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for( auto& childNode : node->Children )
|
|
|
|
{
|
|
|
|
if( childNode->Score > 0 )
|
2019-02-16 09:25:34 +00:00
|
|
|
{
|
|
|
|
if( childNode->VisLen == 0 )
|
|
|
|
childNode->VisLen = m_widget->GetTextExtent( childNode->Name ).x;
|
|
|
|
|
2019-02-15 18:41:05 +00:00
|
|
|
longest = std::max( longest, childNode->VisLen + padding + 2 * indent );
|
2019-02-16 09:25:34 +00:00
|
|
|
}
|
2019-02-10 00:57:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return longest;
|
|
|
|
}
|
2017-10-28 18:28:14 +00:00
|
|
|
else
|
2018-07-27 20:47:51 +00:00
|
|
|
return 2000;
|
2017-10-28 18:28:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
void LIB_TREE_MODEL_ADAPTER::FindAndExpand( LIB_TREE_NODE& aNode,
|
|
|
|
std::function<bool( LIB_TREE_NODE const* )> aFunc,
|
|
|
|
LIB_TREE_NODE** aHighScore )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
for( auto& node: aNode.Children )
|
|
|
|
{
|
|
|
|
if( aFunc( &*node ) )
|
|
|
|
{
|
2018-06-10 22:20:06 +00:00
|
|
|
auto item = wxDataViewItem( &*node );
|
2017-10-28 18:28:14 +00:00
|
|
|
m_widget->ExpandAncestors( item );
|
2018-06-10 22:20:06 +00:00
|
|
|
|
|
|
|
if( !(*aHighScore) || node->Score > (*aHighScore)->Score )
|
|
|
|
(*aHighScore) = &*node;
|
2017-10-28 18:28:14 +00:00
|
|
|
}
|
2018-08-01 00:21:50 +00:00
|
|
|
|
|
|
|
FindAndExpand( *node, aFunc, aHighScore );
|
2017-10-28 18:28:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE* LIB_TREE_MODEL_ADAPTER::ShowResults()
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE* highScore = nullptr;
|
2018-06-10 22:20:06 +00:00
|
|
|
|
|
|
|
FindAndExpand( m_tree,
|
2018-07-27 20:47:51 +00:00
|
|
|
[]( LIB_TREE_NODE const* n )
|
2018-06-10 22:20:06 +00:00
|
|
|
{
|
2018-08-19 16:57:09 +00:00
|
|
|
// return leaf nodes with some level of matching
|
2019-06-19 20:27:58 +00:00
|
|
|
return n->Type == LIB_TREE_NODE::TYPE::LIBID && n->Score > 1;
|
2018-06-10 22:20:06 +00:00
|
|
|
},
|
|
|
|
&highScore );
|
|
|
|
|
|
|
|
return highScore;
|
2017-10-28 18:28:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE* LIB_TREE_MODEL_ADAPTER::ShowPreselect()
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE* highScore = nullptr;
|
2018-06-10 22:20:06 +00:00
|
|
|
|
2017-10-28 18:28:14 +00:00
|
|
|
if( !m_preselect_lib_id.IsValid() )
|
2018-06-10 22:20:06 +00:00
|
|
|
return highScore;
|
2017-10-28 18:28:14 +00:00
|
|
|
|
2018-06-10 22:20:06 +00:00
|
|
|
FindAndExpand( m_tree,
|
2018-07-27 20:47:51 +00:00
|
|
|
[&]( LIB_TREE_NODE const* n )
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
2018-07-27 20:47:51 +00:00
|
|
|
if( n->Type == LIB_TREE_NODE::LIBID && ( n->Children.empty() || !m_preselect_unit ) )
|
2017-10-28 18:28:14 +00:00
|
|
|
return m_preselect_lib_id == n->LibId;
|
2018-07-27 20:47:51 +00:00
|
|
|
else if( n->Type == LIB_TREE_NODE::UNIT && m_preselect_unit )
|
2017-10-28 18:28:14 +00:00
|
|
|
return m_preselect_lib_id == n->Parent->LibId && m_preselect_unit == n->Unit;
|
|
|
|
else
|
|
|
|
return false;
|
2018-06-10 22:20:06 +00:00
|
|
|
},
|
|
|
|
&highScore );
|
|
|
|
|
|
|
|
return highScore;
|
2017-10-28 18:28:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE* LIB_TREE_MODEL_ADAPTER::ShowSingleLibrary()
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE* highScore = nullptr;
|
2018-06-10 22:20:06 +00:00
|
|
|
|
|
|
|
FindAndExpand( m_tree,
|
2018-07-27 20:47:51 +00:00
|
|
|
[]( LIB_TREE_NODE const* n )
|
2018-06-10 22:20:06 +00:00
|
|
|
{
|
2018-07-27 20:47:51 +00:00
|
|
|
return n->Type == LIB_TREE_NODE::TYPE::LIBID &&
|
2018-06-10 22:20:06 +00:00
|
|
|
n->Parent->Parent->Children.size() == 1;
|
|
|
|
},
|
|
|
|
&highScore );
|
|
|
|
|
|
|
|
return highScore;
|
2017-10-28 18:28:14 +00:00
|
|
|
}
|