Commit 392e3a0a authored by Henner Zeller's avatar Henner Zeller Committed by Wayne Stambaugh
Browse files

Eeschema component selection dialog improvements.

* Allow to select units in components that have more than one right in the
  component chooser dialog.
* Keep chosen unit in history.
* Show preview of current component unit as thumbnail image next to the
  description box.
* Fixes lp:1280567
parent 7da13137
Loading
Loading
Loading
Loading
+138 −101
Original line number Original line Diff line number Diff line
@@ -44,14 +44,19 @@ static const unsigned kLowestDefaultScore = 1;


struct COMPONENT_TREE_SEARCH_CONTAINER::TREE_NODE
struct COMPONENT_TREE_SEARCH_CONTAINER::TREE_NODE
{
{
    TREE_NODE(TREE_NODE* aParent, CMP_LIBRARY* aOwningLib,
    // Levels of nodes.
    enum NODE_TYPE {
        TYPE_LIB,
        TYPE_ALIAS,
        TYPE_UNIT
    };

    TREE_NODE(NODE_TYPE aType, TREE_NODE* aParent, LIB_ALIAS* aAlias,
              const wxString& aName, const wxString& aDisplayInfo,
              const wxString& aName, const wxString& aDisplayInfo,
              const wxString& aSearchText,
              const wxString& aSearchText )
              bool aNormallyExpanded = false)
        : Type( aType ),
        : Parent( aParent ),
          Parent( aParent ), Alias( aAlias ), Unit( 0 ),
          Lib( aOwningLib ),
          DisplayName( aName ),
          NormallyExpanded( aNormallyExpanded ),
          Name( aName ),
          DisplayInfo( aDisplayInfo ),
          DisplayInfo( aDisplayInfo ),
          MatchName( aName.Lower() ),
          MatchName( aName.Lower() ),
          SearchText( aSearchText.Lower() ),
          SearchText( aSearchText.Lower() ),
@@ -59,10 +64,11 @@ struct COMPONENT_TREE_SEARCH_CONTAINER::TREE_NODE
    {
    {
    }
    }


    TREE_NODE* const Parent;      ///< NULL if library, pointer to lib-node when component.
    const NODE_TYPE Type;         ///< Type of node in the hierarchy.
    CMP_LIBRARY* const Lib;       ///< Owning library of this component.
    TREE_NODE* const Parent;      ///< NULL if library, pointer to parent when component/alias.
    const bool NormallyExpanded;  ///< If this is a parent node, should it be unfolded ?
    LIB_ALIAS* const Alias;       ///< Component alias associated with this entry.
    const wxString Name;          ///< Exact name as displayed to the user.
    int Unit;                     ///< Part number; Assigned: >= 1; default = 0
    const wxString DisplayName;   ///< Exact name as displayed to the user.
    const wxString DisplayInfo;   ///< Additional info displayed in the tree (description..)
    const wxString DisplayInfo;   ///< Additional info displayed in the tree (description..)


    const wxString MatchName;     ///< Preprocessed: lowercased display name.
    const wxString MatchName;     ///< Preprocessed: lowercased display name.
@@ -75,27 +81,25 @@ struct COMPONENT_TREE_SEARCH_CONTAINER::TREE_NODE




// Sort tree nodes by reverse match-score (bigger is first), then alphabetically.
// Sort tree nodes by reverse match-score (bigger is first), then alphabetically.
// Library nodes (i.e. the ones that don't have a parent) are always sorted before any
// Library (i.e. the ones that don't have a parent) are always sorted before any
// leaf nodes.
// leaf nodes. Component
bool COMPONENT_TREE_SEARCH_CONTAINER::scoreComparator( const TREE_NODE* a1, const TREE_NODE* a2 )
bool COMPONENT_TREE_SEARCH_CONTAINER::scoreComparator( const TREE_NODE* a1, const TREE_NODE* a2 )
{
{
    if ( a1->Parent == NULL && a2->Parent != NULL )
    if( a1->Type != a2->Type )
        return true;
        return a1->Type < a2->Type;

    if ( a1->Parent != NULL && a2->Parent == NULL )
        return false;


    if( a1->MatchScore != a2->MatchScore )
    if( a1->MatchScore != a2->MatchScore )
        return a1->MatchScore > a2->MatchScore;  // biggest first.
        return a1->MatchScore > a2->MatchScore;  // biggest first.


    if( a1->Parent != a2->Parent )
    if( a1->Parent != a2->Parent )
        return a1->Parent->MatchName.Cmp(a2->Parent->MatchName) < 0;
        return scoreComparator( a1->Parent, a2->Parent );


    return a1->MatchName.Cmp( a2->MatchName ) < 0;
    return a1->MatchName.Cmp( a2->MatchName ) < 0;
}
}



COMPONENT_TREE_SEARCH_CONTAINER::COMPONENT_TREE_SEARCH_CONTAINER()
COMPONENT_TREE_SEARCH_CONTAINER::COMPONENT_TREE_SEARCH_CONTAINER()
    : tree( NULL )
    : tree( NULL ), libraries_added( 0 ), preselect_unit_number( -1 )
{
{
}
}


@@ -108,9 +112,11 @@ COMPONENT_TREE_SEARCH_CONTAINER::~COMPONENT_TREE_SEARCH_CONTAINER()
}
}




void COMPONENT_TREE_SEARCH_CONTAINER::SetPreselectNode( const wxString& aComponentName )
void COMPONENT_TREE_SEARCH_CONTAINER::SetPreselectNode( const wxString& aComponentName,
                                                        int aUnit )
{
{
    preselect_node_name = aComponentName.Lower();
    preselect_node_name = aComponentName.Lower();
    preselect_unit_number = aUnit;
}
}




@@ -123,71 +129,79 @@ void COMPONENT_TREE_SEARCH_CONTAINER::SetTree( wxTreeCtrl* aTree )


void COMPONENT_TREE_SEARCH_CONTAINER::AddLibrary( CMP_LIBRARY& aLib )
void COMPONENT_TREE_SEARCH_CONTAINER::AddLibrary( CMP_LIBRARY& aLib )
{
{
    wxArrayString all_comp;
    wxArrayString all_aliases;


    aLib.GetEntryNames( all_comp );
    aLib.GetEntryNames( all_aliases );
    AddComponentList( aLib.GetName(), all_comp, &aLib, false );
    AddAliasList( aLib.GetName(), all_aliases, &aLib );
    ++libraries_added;
}
}




void COMPONENT_TREE_SEARCH_CONTAINER::AddComponentList( const wxString& aNodeName,
void COMPONENT_TREE_SEARCH_CONTAINER::AddAliasList( const wxString& aNodeName,
                                                        const wxArrayString& aComponentNameList,
                                                    const wxArrayString& aAliasNameList,
                                                        CMP_LIBRARY* aOptionalLib,
                                                    CMP_LIBRARY* aOptionalLib )
                                                        bool aNormallyExpanded )
{
{
    TREE_NODE* parent_node = new TREE_NODE( NULL, NULL, aNodeName, wxEmptyString, wxEmptyString,
    static const wxChar unitLetter[] = wxT( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" );
                                            aNormallyExpanded );


    nodes.push_back( parent_node );
    TREE_NODE* const lib_node = new TREE_NODE( TREE_NODE::TYPE_LIB,  NULL, NULL,
                                               aNodeName, wxEmptyString, wxEmptyString );
    nodes.push_back( lib_node );


    BOOST_FOREACH( const wxString& cName, aComponentNameList )
    BOOST_FOREACH( const wxString& aName, aAliasNameList )
    {
    {
        LIB_COMPONENT *c;
        LIB_ALIAS* a;


        if( aOptionalLib )
        if( aOptionalLib )
            c = aOptionalLib->FindComponent( cName );
            a = aOptionalLib->FindAlias( aName );
        else
        else
            c = CMP_LIBRARY::FindLibraryComponent( cName, wxEmptyString );
            a = CMP_LIBRARY::FindLibraryEntry( aName, wxEmptyString );


        if (c == NULL)
        if( a == NULL )
            continue;
            continue;


        wxString keywords, descriptions;
        wxString search_text;
        wxString display_info;
        search_text = ( a->GetKeyWords().empty() ) ? wxT("        ") : a->GetKeyWords();
        search_text += a->GetDescription();


        for ( size_t i = 0; i < c->GetAliasCount(); ++i )
        wxString display_info;
        {
            LIB_ALIAS *a = c->GetAlias( i );
            keywords += a->GetKeyWords();
            descriptions += a->GetDescription();


            if ( display_info.empty() && !a->GetDescription().empty() )
        if( !a->GetDescription().empty() )
        {
        {
            // Preformatting. Unfortunately, the tree widget doesn't have columns
            // Preformatting. Unfortunately, the tree widget doesn't have columns
            display_info.Printf( wxT(" %s[ %s ]"),
            display_info.Printf( wxT(" %s[ %s ]"),
                                     ( cName.length() <= 8 ) ? wxT("\t\t") : wxT("\t"),
                                 ( a->GetName().length() <= 8 ) ? wxT("\t\t") : wxT("\t"),
                                 GetChars( a->GetDescription() ) );
                                 GetChars( a->GetDescription() ) );
        }
        }
        }


        // If there are no keywords, we give a couple of characters whitespace penalty. We want
        TREE_NODE* alias_node = new TREE_NODE( TREE_NODE::TYPE_ALIAS, lib_node,
        // a component with a search-term found in the keywords score slightly higher than another
                                               a, a->GetName(), display_info, search_text );
        // component without keywords, but that term in the descriptions.
        nodes.push_back( alias_node );
        wxString search_text = ( !keywords.empty() ) ? keywords : wxT("        ");

        search_text += descriptions;
        if( a->GetComponent()->IsMulti() )    // Add all units as sub-nodes.
        nodes.push_back( new TREE_NODE( parent_node, c->GetLibrary(),
            for ( int u = 0; u < a->GetComponent()->GetPartCount(); ++u )
                                        cName, display_info, search_text ) );
            {
                const wxString unitName = unitLetter[u];
                TREE_NODE* unit_node = new TREE_NODE(TREE_NODE::TYPE_UNIT, alias_node, a,
                                                     _("Unit ") + unitName,
                                                     wxEmptyString, wxEmptyString );
                unit_node->Unit = u + 1;
                nodes.push_back( unit_node );
            }
    }
    }
}
}




LIB_COMPONENT* COMPONENT_TREE_SEARCH_CONTAINER::GetSelectedComponent()
LIB_ALIAS* COMPONENT_TREE_SEARCH_CONTAINER::GetSelectedAlias( int* aUnit )
{
{
    const wxTreeItemId& select_id = tree->GetSelection();
    const wxTreeItemId& select_id = tree->GetSelection();

    BOOST_FOREACH( TREE_NODE* node, nodes )
    BOOST_FOREACH( TREE_NODE* node, nodes )
    {
    {
        if ( node->MatchScore > 0 && node->TreeId == select_id && node->Lib )
        if( node->MatchScore > 0 && node->TreeId == select_id ) {
            return node->Lib->FindComponent( node->Name );
            if( aUnit && node->Unit > 0 )
                *aUnit = node->Unit;
            return node->Alias;
        }
    }
    }
    return NULL;
    return NULL;
}
}
@@ -220,7 +234,7 @@ void COMPONENT_TREE_SEARCH_CONTAINER::UpdateSearchTerm( const wxString& aSearch
    BOOST_FOREACH( TREE_NODE* node, nodes )
    BOOST_FOREACH( TREE_NODE* node, nodes )
    {
    {
        node->PreviousScore = node->MatchScore;
        node->PreviousScore = node->MatchScore;
        node->MatchScore = node->Parent ? kLowestDefaultScore : 0;  // start-match for leafs.
        node->MatchScore = ( node->Type == TREE_NODE::TYPE_LIB ) ? 0 : kLowestDefaultScore;
    }
    }


    // Create match scores for each node for all the terms, that come space-separated.
    // Create match scores for each node for all the terms, that come space-separated.
@@ -239,10 +253,11 @@ void COMPONENT_TREE_SEARCH_CONTAINER::UpdateSearchTerm( const wxString& aSearch
    while ( tokenizer.HasMoreTokens() )
    while ( tokenizer.HasMoreTokens() )
    {
    {
        const wxString term = tokenizer.GetNextToken().Lower();
        const wxString term = tokenizer.GetNextToken().Lower();

        BOOST_FOREACH( TREE_NODE* node, nodes )
        BOOST_FOREACH( TREE_NODE* node, nodes )
        {
        {
            if ( node->Parent == NULL)
            if( node->Type != TREE_NODE::TYPE_ALIAS )
                continue;      // Library nodes are not scored here.
                continue;      // Only aliases are actually scored here.


            if( node->MatchScore == 0)
            if( node->MatchScore == 0)
                continue;   // Leaf node without score are out of the game.
                continue;   // Leaf node without score are out of the game.
@@ -277,19 +292,32 @@ void COMPONENT_TREE_SEARCH_CONTAINER::UpdateSearchTerm( const wxString& aSearch
        }
        }
    }
    }


    // Parent nodes have the maximum score seen in any of their children.
    // Library nodes have the maximum score seen in any of their children.
    // Alias nodes have the score of their parents.
    unsigned highest_score_seen = 0;
    unsigned highest_score_seen = 0;
    bool any_change = false;
    bool any_change = false;

    BOOST_FOREACH( TREE_NODE* node, nodes )
    BOOST_FOREACH( TREE_NODE* node, nodes )
    {
    {
        if ( node->Parent == NULL )
        switch( node->Type )
            continue;
        {

        case TREE_NODE::TYPE_ALIAS:
            {
                any_change |= (node->PreviousScore != node->MatchScore);
                any_change |= (node->PreviousScore != node->MatchScore);
                // Update library score.
                node->Parent->MatchScore = std::max( node->Parent->MatchScore, node->MatchScore );
                node->Parent->MatchScore = std::max( node->Parent->MatchScore, node->MatchScore );
                highest_score_seen = std::max( highest_score_seen, node->MatchScore );
                highest_score_seen = std::max( highest_score_seen, node->MatchScore );
            }
            }
            break;


        case TREE_NODE::TYPE_UNIT:
            node->MatchScore = node->Parent->MatchScore;
            break;

        default:
            break;
        }
    }


    // The tree update might be slow, so we want to bail out if there is no change.
    // The tree update might be slow, so we want to bail out if there is no change.
    if( !any_change )
    if( !any_change )
@@ -305,6 +333,7 @@ void COMPONENT_TREE_SEARCH_CONTAINER::UpdateSearchTerm( const wxString& aSearch
    const wxTreeItemId root_id = tree->AddRoot( wxEmptyString );
    const wxTreeItemId root_id = tree->AddRoot( wxEmptyString );
    const TREE_NODE* first_match = NULL;
    const TREE_NODE* first_match = NULL;
    const TREE_NODE* preselected_node = NULL;
    const TREE_NODE* preselected_node = NULL;

    BOOST_FOREACH( TREE_NODE* node, nodes )
    BOOST_FOREACH( TREE_NODE* node, nodes )
    {
    {
        if( node->MatchScore == 0 )
        if( node->MatchScore == 0 )
@@ -318,34 +347,42 @@ void COMPONENT_TREE_SEARCH_CONTAINER::UpdateSearchTerm( const wxString& aSearch
        if( highest_score_seen > kLowestDefaultScore && node->MatchScore == kLowestDefaultScore )
        if( highest_score_seen > kLowestDefaultScore && node->MatchScore == kLowestDefaultScore )
            continue;
            continue;


        const bool isLeaf = ( node->Parent != NULL );
        wxString node_text;
        wxString node_text;
#if 0
#if 0
        // Node text with scoring information for debugging
        // Node text with scoring information for debugging
        node_text.Printf( wxT("%s (s=%u)%s"), GetChars(node->Name),
        node_text.Printf( wxT("%s (s=%u)%s"), GetChars(node->DisplayName),
                          node->MatchScore, GetChars( node->DisplayInfo ));
                          node->MatchScore, GetChars( node->DisplayInfo ));
#else
#else
        node_text = node->Name + node->DisplayInfo;
        node_text = node->DisplayName + node->DisplayInfo;
#endif
#endif
        node->TreeId = tree->AppendItem( !isLeaf ? root_id : node->Parent->TreeId, node_text );
        node->TreeId = tree->AppendItem( node->Parent ? node->Parent->TreeId : root_id,

                                         node_text );
        // If we are a leaf node, we might need to expand.

        if ( isLeaf )
        // If we are a nicely scored alias, we want to have it visible. Also, if there
        {
        // is only a single library in this container, we want to have it unfolded
            if ( node->MatchScore > kLowestDefaultScore )
        // (example: power library).
        if( node->Type == TREE_NODE::TYPE_ALIAS
             && ( node->MatchScore > kLowestDefaultScore || libraries_added == 1 ) )
        {
        {
            tree->EnsureVisible( node->TreeId );
            tree->EnsureVisible( node->TreeId );


            if( first_match == NULL )
            if( first_match == NULL )
                    first_match = node;   // The "I am feeling lucky" element.
                first_match = node;   // First, highest scoring: the "I am feeling lucky" element.
        }
        }


            if ( preselected_node == NULL && node->MatchName == preselect_node_name )
        // The first node that matches our pre-select criteria is choosen. 'First node'
        // means, it shows up in the history, as the history node is displayed very first
        // (by virtue of alphabetical ordering)
        if( preselected_node == NULL
             && node->Type == TREE_NODE::TYPE_ALIAS
             && node->MatchName == preselect_node_name )
            preselected_node = node;
            preselected_node = node;
        }


        if ( !isLeaf && node->NormallyExpanded )
        // Refinement in case we come accross a matching unit node.
            tree->Expand( node->TreeId );
        if( preselected_node != NULL && preselected_node->Type == TREE_NODE::TYPE_ALIAS
             && node->Parent == preselected_node
             && preselect_unit_number >= 1 && node->Unit == preselect_unit_number )
            preselected_node = node;
    }
    }


    if( first_match )                      // Highest score search match pre-selected.
    if( first_match )                      // Highest score search match pre-selected.
+19 −11
Original line number Original line Diff line number Diff line
@@ -21,11 +21,13 @@
 * or you may write to the Free Software Foundation, Inc.,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */
 */
#ifndef COMPONENT_TREE_SEARCH_CONTAINER_H
#define COMPONENT_TREE_SEARCH_CONTAINER_H


#include <vector>
#include <vector>
#include <wx/string.h>
#include <wx/string.h>


class LIB_COMPONENT;
class LIB_ALIAS;
class CMP_LIBRARY;
class CMP_LIBRARY;
class wxTreeCtrl;
class wxTreeCtrl;
class wxArrayString;
class wxArrayString;
@@ -44,7 +46,7 @@ public:
    ~COMPONENT_TREE_SEARCH_CONTAINER();
    ~COMPONENT_TREE_SEARCH_CONTAINER();


    /** Function AddLibrary
    /** Function AddLibrary
     * Add the components of this library to be searched.
     * Add all the components and their aliases of this library to be searched.
     * To be called in the setup phase to fill this container.
     * To be called in the setup phase to fill this container.
     *
     *
     * @param aLib containting all the components to be added.
     * @param aLib containting all the components to be added.
@@ -56,19 +58,19 @@ public:
     * To be called in the setup phase to fill this container.
     * To be called in the setup phase to fill this container.
     *
     *
     * @param aNodeName          The parent node name the components will show up as leaf.
     * @param aNodeName          The parent node name the components will show up as leaf.
     * @param aComponentNameList List of component names.
     * @param aAliasNameList List of alias names.
     * @param aOptionalLib       Library to look up the component names (if NULL: global lookup)
     * @param aOptionalLib       Library to look up the component names (if NULL: global lookup)
     * @param aNormallyExpanded  Should the node in the tree be expanded by default.
     */
     */
    void AddComponentList( const wxString& aNodeName, const wxArrayString& aComponentNameList,
    void AddAliasList( const wxString& aNodeName, const wxArrayString& aAliasNameList,
                           CMP_LIBRARY* aOptionalLib, bool aNormallyExpanded );
                       CMP_LIBRARY* aOptionalLib );


    /** Function SetPreselectNode
    /** Function SetPreselectNode
     * Set the component name to be selected in absence of any search-result.
     * Set the component name to be selected in absence of any search-result.
     *
     *
     * @param aComponentName the component name to be selected.
     * @param aComponentName the component name to be selected.
     * @param aUnit          the component unit to be selected (if > 0).
     */
     */
    void SetPreselectNode( const wxString& aComponentName );
    void SetPreselectNode( const wxString& aComponentName, int aUnit );


    /** Function SetTree
    /** Function SetTree
     * Set the tree to be manipulated.
     * Set the tree to be manipulated.
@@ -92,17 +94,23 @@ public:
     */
     */
    void UpdateSearchTerm( const wxString& aSearch );
    void UpdateSearchTerm( const wxString& aSearch );


    /** Function GetSelectedComponent
    /** Function GetSelectedAlias
     *
     *
     * @return the selected component or NULL if there is none.
     * @param if not-NULL, the selected sub-unit is set here.
     * @return the selected alias or NULL if there is none.
     */
     */
    LIB_COMPONENT* GetSelectedComponent();
    LIB_ALIAS* GetSelectedAlias( int* aUnit );


private:
private:
    struct TREE_NODE;
    struct TREE_NODE;
    static bool scoreComparator( const TREE_NODE* a1, const TREE_NODE* a2 );
    static bool scoreComparator( const TREE_NODE* a1, const TREE_NODE* a2 );


    std::vector<TREE_NODE*> nodes;
    std::vector<TREE_NODE*> nodes;
    wxString preselect_node_name;
    wxTreeCtrl* tree;
    wxTreeCtrl* tree;
    int libraries_added;

    wxString preselect_node_name;
    int preselect_unit_number;
};
};

#endif /* COMPONENT_TREE_SEARCH_CONTAINER_H */
Loading