Commit 74ce031e authored by Dick Hollenbeck's avatar Dick Hollenbeck
Browse files

Set PROPERTIES* into FP_LIB_TABLE::ROW, which is a parsed (binary) form of the 'options'.

Write parser and formatter for options.  Write dialog verification used before saving
FP_LIB_TABLEs, triggered from OK button in table editor.
Switch PROPERTY's value column to std::string from wxString.
Add event handler to fp lib table dialog for upcoming options dialog.
parent 85777d65
Loading
Loading
Loading
Loading
+98 −0
Original line number Diff line number Diff line
@@ -142,6 +142,9 @@ void FP_LIB_TABLE::Parse( FP_LIB_TABLE_LEXER* in ) throw( IO_ERROR, PARSE_ERROR
                sawOpts = true;
                in->NeedSYMBOLorNUMBER();
                row.SetOptions( in->FromUTF8() );

                // create PROPERTIES* from options, set into the ROW
                row.properties = ParseOptions( in->CurText() );
                break;

            case T_descr:
@@ -205,6 +208,101 @@ void FP_LIB_TABLE::ROW::Format( OUTPUTFORMATTER* out, int nestLevel ) const
}


#define OPT_SEP     '|'         ///< options separator character

PROPERTIES* FP_LIB_TABLE::ParseOptions( const std::string& aOptionsList )
{
    const char* cp  = &aOptionsList[0];
    const char* end = cp + aOptionsList.size();

    PROPERTIES  props;
    std::string pair;

    // Parse all name=value pairs
    while( cp < end )
    {
        pair.clear();

        // Skip leading white space.
        while( cp < end && isspace( *cp )  )
            ++cp;

        // Find the end of pair/field
        while( cp<end )
        {
            if( *cp == '\\' && cp+1 < end && cp[1]==OPT_SEP  )
            {
                ++cp;           // skip the escape
                pair += *cp++;  // add the separator
            }

            else if( *cp==OPT_SEP )
            {
                ++cp;           // skip the separator
                break;          // process the pair
            }
            else
                pair += *cp++;
        }

        // stash the pair
        if( pair.size() )
        {
            // the first equals size established the end of the name
            size_t  eqNdx = pair.find( '=' );
            if( eqNdx != pair.npos )
            {
                std::string name  = pair.substr( 0, eqNdx );
                std::string value = pair.substr( eqNdx + 1 );
                props[name] = value;
            }
            else
                props[pair] = "";
        }
    }

    if( props.size() )
        return new PROPERTIES( props );
    else
        return NULL;
}


std::string FP_LIB_TABLE::FormatOptions( const PROPERTIES* aProperties )
{
    std::string ret;

    if( aProperties )
    {
        for( PROPERTIES::const_iterator it = aProperties->begin();  it != aProperties->end(); ++it )
        {
            const std::string& name  = it->first;
            const std::string& value = it->second;

            if( ret.size() )
                ret += OPT_SEP;

            ret += name;

            // the separation between name and value is '='
            if( value.size() )
                ret += '=';

            for( std::string::const_iterator si = value.begin();  si != value.end();  ++si )
            {
                // escape any separator in the value.
                if( *si == OPT_SEP )
                    ret += '\\';

                ret += *si;
            }
        }
    }

    return ret;
}


std::vector<wxString> FP_LIB_TABLE::GetLogicalLibs()
{
    // Only return unique logical library names.  Use std::set::insert() to
+60 −7
Original line number Diff line number Diff line
@@ -103,7 +103,9 @@ public:

        typedef IO_MGR::PCB_FILE_T   LIB_T;

        ROW() : type( IO_MGR::KICAD )
        ROW() :
            type( IO_MGR::KICAD ),
            properties( 0 )
        {
        }

@@ -112,11 +114,39 @@ public:
            nickName( aNick ),
            uri( aURI ),
            options( aOptions ),
            description( aDescr )
            description( aDescr ),
            properties( 0 )
        {
            SetType( aType );
        }

        ROW( const ROW& a ) :
            nickName( a.nickName ),
            uri( a.uri ),
            type( a.type ),
            options( a.options ),
            description( a.description ),
            properties( 0 )
        {
            if( a.properties )
                properties = new PROPERTIES( *a.properties );
        }

        ~ROW()
        {
            delete properties;
        }

        ROW& operator=( const ROW& r )
        {
            nickName = r.nickName;
            uri      = r.uri;
            type     = r.type;
            options  = r.options;
            properties = r.properties ? new PROPERTIES( *r.properties ) : NULL;
            return *this;
        }

        bool operator==( const ROW& r ) const
        {
            return  nickName==r.nickName && uri==r.uri && type==r.type && options==r.options;
@@ -206,6 +236,7 @@ public:
        LIB_T           type;
        wxString        options;
        wxString        description;
        PROPERTIES*     properties;
    };


@@ -261,6 +292,31 @@ public:
     */
    void Parse( FP_LIB_TABLE_LEXER* aParser ) throw( IO_ERROR, PARSE_ERROR );

    /**
     * Function ParseOptions
     * parses @a aOptionsList and places the result into a PROPERTIES object
     * which is returned.  If the options field is empty, then the returned PROPERTIES
     * will be a NULL pointer.
     * <p>
     * Typically aOptionsList comes from the "options" field within a ROW and
     * the format is simply a comma separated list of name value pairs. e.g.:
     * [name1[=value1][|name2[=value2]]] etc.  When using the UI to create or edit
     * a fp lib table, this formatting is handled for you.
     */
    static PROPERTIES* ParseOptions( const std::string& aOptionsList );

    /**
     * Function FormatOptions
     * returns a list of options from the aProperties parameter.  The name=value
     * pairs will be separted with the '|' character.  The =value portion may not
     * be present.  You might expect something like "name1=value1|name2=value2|flag_me".
     * Notice that flag_me does not have a value.  This is ok.
     *
     * @param aProperties is the PROPERTIES to format or NULL.  If NULL the returned
     *  string will be empty.
     */
    static std::string FormatOptions( const PROPERTIES* aProperties );

    /**
     * Function Format
     * serializes this object as utf8 text to an #OUTPUTFORMATTER, and tries to
@@ -272,7 +328,6 @@ public:
     */
    void Format( OUTPUTFORMATTER* out, int nestLevel ) const throw( IO_ERROR );


    /**
     * Function GetLogicalLibs
     * returns the logical library names, all of them that are pertinent to
@@ -280,8 +335,6 @@ public:
     */
    std::vector<wxString> GetLogicalLibs();



    //----<read accessors>----------------------------------------------------
    // the returning of a const wxString* tells if not found, but might be too
    // promiscuous?
+4 −4
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@
#include <unordered_map>

/// Map a C string to a wxString, used in PLUGINs.
typedef std::unordered_map< std::string, wxString >  PROPERTIES;
typedef std::unordered_map< std::string, std::string >  PROPERTIES;

/// Map a C string to an integer.  Used in DSNLEXER.
typedef std::unordered_map< std::string, int >          KEYWORD_MAP;
@@ -57,7 +57,7 @@ typedef std::unordered_map< std::string, EDA_RECT > RECT_MAP;
// see http://www.boost.org/doc/libs/1_49_0/doc/html/boost/unordered_map.html

/// Map a std::string to a wxString, used in PLUGINs.
typedef boost::unordered_map< std::string, wxString >  PROPERTIES;
typedef boost::unordered_map< std::string, std::string >    PROPERTIES;


/// Equality test for "const char*" type used in very specialized KEYWORD_MAP below
+13 −3
Original line number Diff line number Diff line

set( MAKE_LINK_MAPS true )

add_definitions( -DPCBNEW )

if( KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES )
@@ -322,7 +325,6 @@ endif()
# _pcbnew DLL/DSO file creation
###

unset( GITHUB_PLUGIN_LIBRARIES )
if( BUILD_GITHUB_PLUGIN )
    set( GITHUB_PLUGIN_LIBRARIES github_plugin )
endif()
@@ -362,6 +364,11 @@ if( KICAD_SCRIPTING_MODULES )
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/scripting/qa
        )

    if( MAKE_LINK_MAPS )
        # generate a link map with cross reference
        set_target_properties( _pcbnew PROPERTIES LINK_FLAGS "-Wl,-cref -Wl,-Map=_pcbnew.map" )
    endif()

endif()


@@ -507,6 +514,7 @@ target_link_libraries( pcbnew
    pcad2kicadpcb
    polygon
    bitmaps
    ${GITHUB_PLUGIN_LIBRARIES}
    ${wxWidgets_LIBRARIES}
    ${OPENGL_LIBRARIES}
    ${GDI_PLUS_LIBRARIES}
@@ -514,8 +522,10 @@ target_link_libraries( pcbnew
    ${PCBNEW_EXTRA_LIBS}
    )

if( BUILD_GITHUB_PLUGIN )
    target_link_libraries( pcbnew github_plugin )

if( MAKE_LINK_MAPS )
    # generate a link map with cross reference
    set_target_properties( pcbnew PROPERTIES LINK_FLAGS "-Wl,-cref -Wl,-Map=pcbnew.map" )
endif()


+143 −30
Original line number Diff line number Diff line
@@ -48,16 +48,9 @@
#include <wx/regex.h>
#include <set>

/**
 * Class FP_TBL_MODEL
 * mixes in wxGridTableBase into FP_LIB_TABLE so that the latter can be used
 * as a table within wxGrid.
 */
class FP_TBL_MODEL : public wxGridTableBase, public FP_LIB_TABLE
{
public:

    enum COL_ORDER      ///<  grid column order, established by this sequence
/// grid column order is established by this sequence
enum COL_ORDER
{
    COL_NICKNAME,
    COL_URI,
@@ -67,6 +60,16 @@ public:
    COL_COUNT       // keep as last
};


/**
 * Class FP_TBL_MODEL
 * mixes in wxGridTableBase into FP_LIB_TABLE so that the latter can be used
 * as a table within wxGrid.
 */
class FP_TBL_MODEL : public wxGridTableBase, public FP_LIB_TABLE
{
public:

    /**
     * Constructor FP_TBL_MODEL
     * is a copy constructor that builds a wxGridTableBase (table model) by wrapping
@@ -394,6 +397,102 @@ class DIALOG_FP_LIB_TABLE : public DIALOG_FP_LIB_TABLE_BASE
        }
    }

    /**
     * Function verifyTables
     * trims important fields, removes blank row entries, and checks for duplicates.
     * @return bool - true if tables are OK, else false.
     */
    bool verifyTables()
    {
        for( int t=0; t<2; ++t )
        {
            FP_TBL_MODEL& model = t==0 ? m_global_model : m_project_model;

            for( int r = 0; r < model.GetNumberRows(); )
            {
                wxString nick = model.GetValue( r, COL_NICKNAME ).Trim( false ).Trim();
                wxString uri  = model.GetValue( r, COL_URI ).Trim( false ).Trim();
                wxString type = model.GetValue( r, COL_TYPE ).Trim( false ).Trim();

                if( !nick || !uri || !type )
                {
                    // Delete the "empty" row, where empty means missing nick, uri, or type.
                    // This also updates the UI which could be slow, but there should only be a few
                    // rows to delete, unless the user fell asleep on the Add Row
                    // button.
                    model.DeleteRows( r, 1 );
                }
                else if( nick.find(':') != size_t(-1) )
                {
                    wxString msg = wxString::Format(
                        _( "Illegal character '%s' found in Nickname: '%s' in row %d" ),
                        wxT( ":" ), GetChars( nick ), r );

                    // show the tabbed panel holding the grid we have flunked:
                    if( &model != (FP_TBL_MODEL*) m_cur_grid->GetTable() )
                    {
                        m_auinotebook->SetSelection( &model == &m_global_model ? 0 : 1 );
                    }

                    // go to the bottom of the two rows, it is technically the duplicate:
                    m_cur_grid->SelectBlock( r, 0, r, 0 );
                    m_cur_grid->MakeCellVisible( r, 0 );

                    wxMessageDialog errdlg( this, msg, _( "No Colon in Nicknames" ) );
                    errdlg.ShowModal();
                    return false;
                }
                else
                {
                    // set the trimmed values back into the table so they get saved to disk.
                    model.SetValue( r, COL_NICKNAME, nick );
                    model.SetValue( r, COL_URI, uri );
                    model.SetValue( r, COL_TYPE, type );
                    ++r;        // this row was OK.
                }
            }
        }

        // check for duplicate nickNames, separately in each table.
        for( int t=0; t<2; ++t )
        {
            FP_TBL_MODEL& model = t==0 ? m_global_model : m_project_model;

            for( int r1 = 0; r1 < model.GetNumberRows() - 1;  ++r1 )
            {
                for( int r2=r1+1; r2 < model.GetNumberRows();  ++r2 )
                {
                    wxString    nick1 = model.GetValue( r1, COL_NICKNAME );
                    wxString    nick2 = model.GetValue( r2, COL_NICKNAME );

                    if( nick1 == nick2 )
                    {
                        wxString msg = wxString::Format(
                            _( "Duplicate Nickname: '%s' in rows %d and %d" ),
                            GetChars( nick1 ), r1+1, r2+1
                            );

                        // show the tabbed panel holding the grid we have flunked:
                        if( &model != (FP_TBL_MODEL*) m_cur_grid->GetTable() )
                        {
                            m_auinotebook->SetSelection( &model == &m_global_model ? 0 : 1 );
                        }

                        // go to the bottom of the two rows, it is technically the duplicate:
                        m_cur_grid->SelectBlock( r2, 0, r2, 0 );
                        m_cur_grid->MakeCellVisible( r2, 0 );

                        wxMessageDialog errdlg( this, msg, _( "Please Delete or Modify One" ) );
                        errdlg.ShowModal();
                        return false;
                    }
                }
            }
        }

        return true;
    }

    //-----<event handlers>----------------------------------

    void pageChangedHandler( wxAuiNotebookEvent& event )
@@ -404,7 +503,13 @@ class DIALOG_FP_LIB_TABLE : public DIALOG_FP_LIB_TABLE_BASE

    void appendRowHandler( wxMouseEvent& event )
    {
        m_cur_grid->AppendRows( 1 );
        if( m_cur_grid->AppendRows( 1 ) )
        {
            int last_row = m_cur_grid->GetNumberRows() - 1;

            m_cur_grid->SelectBlock( last_row, 0, last_row, 0 );
            m_cur_grid->MakeCellVisible( last_row, 0 );
        }
    }

    void deleteRowHandler( wxMouseEvent& event )
@@ -474,6 +579,12 @@ class DIALOG_FP_LIB_TABLE : public DIALOG_FP_LIB_TABLE_BASE
        D(printf("%s\n", __func__);)
    }

    void optionsEditor( wxMouseEvent& event )
    {
        // @todo: write the options editor, and pass the options to the Footprint*() calls.
        D(printf("%s:%d\n", __func__, (int) m_cur_grid->GetRowCount() );)
    }

    void onCancelButtonClick( wxCommandEvent& event )
    {
        EndModal( 0 );
@@ -483,6 +594,8 @@ class DIALOG_FP_LIB_TABLE : public DIALOG_FP_LIB_TABLE_BASE
    {
        int dialogRet = 0;

        if( verifyTables() )
        {
            if( m_global_model != *m_global )
            {
                dialogRet |= 1;
@@ -501,6 +614,7 @@ class DIALOG_FP_LIB_TABLE : public DIALOG_FP_LIB_TABLE_BASE

            EndModal( dialogRet );
        }
    }

    void onGridCellLeftClick( wxGridEvent& event )
    {
@@ -546,7 +660,7 @@ class DIALOG_FP_LIB_TABLE : public DIALOG_FP_LIB_TABLE_BASE

        for( row = 0;  row < gblRowCount;  ++row )
        {
            wxString uri = m_global_model.GetValue( row, FP_TBL_MODEL::COL_URI );
            wxString uri = m_global_model.GetValue( row, COL_URI );

            while( re.Matches( uri ) )
            {
@@ -562,7 +676,7 @@ class DIALOG_FP_LIB_TABLE : public DIALOG_FP_LIB_TABLE_BASE

        for( row = 0;  row < prjRowCount;  ++row )
        {
            wxString uri = m_project_model.GetValue( row, FP_TBL_MODEL::COL_URI );
            wxString uri = m_project_model.GetValue( row, COL_URI );

            while( re.Matches( uri ) )
            {
@@ -643,11 +757,11 @@ public:

        attr = new wxGridCellAttr;
        attr->SetEditor( new wxGridCellChoiceEditor( choices ) );
        m_project_grid->SetColAttr( FP_TBL_MODEL::COL_TYPE, attr );
        m_project_grid->SetColAttr( COL_TYPE, attr );

        attr = new wxGridCellAttr;
        attr->SetEditor( new wxGridCellChoiceEditor( choices ) );
        m_global_grid->SetColAttr(  FP_TBL_MODEL::COL_TYPE, attr );
        m_global_grid->SetColAttr( COL_TYPE, attr );

        m_global_grid->AutoSizeColumns();
        m_project_grid->AutoSizeColumns();
@@ -688,4 +802,3 @@ int InvokePcbLibTableEditor( wxFrame* aParent, FP_LIB_TABLE* aGlobal, FP_LIB_TAB

    return dialogRet;
}
Loading