diff --git a/common/lib_table_base.cpp b/common/lib_table_base.cpp index 422f74cef2..f203fcb9d7 100644 --- a/common/lib_table_base.cpp +++ b/common/lib_table_base.cpp @@ -2,8 +2,8 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2010-2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2012-2016 Wayne Stambaugh - * Copyright (C) 2012-2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2012-2017 Wayne Stambaugh + * Copyright (C) 2012-2017 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -378,6 +378,39 @@ void LIB_TABLE::Save( const wxString& aFileName ) const } +size_t LIB_TABLE::GetEnvVars( wxArrayString& aEnvVars ) const +{ + const LIB_TABLE* cur = this; + + do + { + for( unsigned i = 0; i < cur->rows.size(); i++ ) + { + wxString uri = cur->rows[i].GetFullURI( false ); + + int start = uri.Find( "${" ); + + if( start == wxNOT_FOUND ) + continue; + + int end = uri.Find( '}' ); + + if( end == wxNOT_FOUND || end < start+2 ) + continue; + + wxString envVar = uri.Mid( start+2, end - (start+2) ); + + if( aEnvVars.Index( envVar, false ) == wxNOT_FOUND ) + aEnvVars.Add( envVar ); + } + + // not found, search fall back table(s), if any + } while( ( cur = cur->fallBack ) != 0 ); + + return aEnvVars.GetCount(); +} + + PROPERTIES* LIB_TABLE::ParseOptions( const std::string& aOptionsList ) { if( aOptionsList.size() ) diff --git a/include/fp_lib_table.h b/include/fp_lib_table.h index bb87ed4bc7..c1d4550372 100644 --- a/include/fp_lib_table.h +++ b/include/fp_lib_table.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2010-2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2012-2016 Wayne Stambaugh + * Copyright (C) 2012-2017 Wayne Stambaugh * Copyright (C) 2012-2017 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or @@ -30,7 +30,8 @@ #include class MODULE; -class FP_TBL_MODEL; +class FP_LIB_TABLE_GRID; + /** * Class FP_LIB_TABLE_ROW @@ -101,6 +102,8 @@ private: class FP_LIB_TABLE : public LIB_TABLE { + friend class FP_LIB_TABLE_GRID; + public: virtual void Parse( LIB_TABLE_LEXER* aLexer ) override; diff --git a/include/lib_table_base.h b/include/lib_table_base.h index 381e0f7eef..c0466147eb 100644 --- a/include/lib_table_base.h +++ b/include/lib_table_base.h @@ -43,9 +43,15 @@ class OUTPUTFORMATTER; class LIB_TABLE_LEXER; class LIB_ID; class LIB_TABLE_ROW; +class LIB_TABLE_GRID; class IO_ERROR; +typedef boost::ptr_vector< LIB_TABLE_ROW > LIB_TABLE_ROWS; +typedef LIB_TABLE_ROWS::iterator LIB_TABLE_ROWS_ITER; +typedef LIB_TABLE_ROWS::const_iterator LIB_TABLE_ROWS_CITER; + + /** * Function new_clone * @@ -273,7 +279,7 @@ private: class LIB_TABLE : public PROJECT::_ELEM { friend class DIALOG_FP_LIB_TABLE; - friend class FP_TBL_MODEL; + friend class LIB_TABLE_GRID; public: @@ -421,6 +427,18 @@ public: void Save( const wxString& aFileName ) const throw( IO_ERROR, boost::interprocess::lock_exception ); + /** + * Search the paths all of the #LIB_TABLE_ROWS of the #LIB_TABLE and add all of the + * environment variable substitutions to \a aEnvVars. + * + * This will only add unique environment variables to the list. Duplicates are ignored. + * + * @param aEnvVars is the array to load the environment variables. + * + * @return the number of unique environment variables found in the table. + */ + size_t GetEnvVars( wxArrayString& aEnvVars ) const; + /** * Function ParseOptions * @@ -485,10 +503,6 @@ protected: reindex(); } - typedef boost::ptr_vector< LIB_TABLE_ROW > LIB_TABLE_ROWS; - typedef LIB_TABLE_ROWS::iterator LIB_TABLE_ROWS_ITER; - typedef LIB_TABLE_ROWS::const_iterator LIB_TABLE_ROWS_CITER; - LIB_TABLE_ROWS rows; /// this is a non-owning index into the LIB_TABLE_ROWS table diff --git a/include/lib_table_grid.h b/include/lib_table_grid.h new file mode 100644 index 0000000000..64c2f90aa2 --- /dev/null +++ b/include/lib_table_grid.h @@ -0,0 +1,196 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#ifndef __LIB_TABLE_GRID_H__ +#define __LIB_TABLE_GRID_H__ + +#include + +#include + +/// The library table grid column order is established by this sequence. +enum COL_ORDER +{ + COL_NICKNAME, + COL_URI, + COL_TYPE, + COL_OPTIONS, + COL_DESCR, + COL_COUNT // keep as last +}; + +/** + * This abstract base class mixes any object derived from #LIB_TABLE into wxGridTableBase + * so the result can be used as any type of library table within wxGrid. + */ +class LIB_TABLE_GRID : public wxGridTableBase +{ +public: + + //------------------------------------------------ + + int GetNumberRows() override { return (int) size(); } + + int GetNumberCols() override { return COL_COUNT; } + + wxString GetValue( int aRow, int aCol ) override + { + if( aRow < (int) size() ) + { + const LIB_TABLE_ROW* r = at( (size_t) aRow ); + + switch( aCol ) + { + case COL_NICKNAME: return r->GetNickName(); + case COL_URI: return r->GetFullURI(); + case COL_TYPE: return r->GetType(); + case COL_OPTIONS: return r->GetOptions(); + case COL_DESCR: return r->GetDescr(); + default: + ; // fall thru to wxEmptyString + } + } + + return wxEmptyString; + } + + void SetValue( int aRow, int aCol, const wxString &aValue ) override + { + if( aRow < (int) size() ) + { + LIB_TABLE_ROW* r = at( (size_t) aRow ); + + switch( aCol ) + { + case COL_NICKNAME: r->SetNickName( aValue ); break; + case COL_URI: r->SetFullURI( aValue ); break; + case COL_TYPE: r->SetType( aValue ); break; + case COL_OPTIONS: r->SetOptions( aValue ); break; + case COL_DESCR: r->SetDescr( aValue ); break; + } + } + } + + bool IsEmptyCell( int aRow, int aCol ) override + { + return !GetValue( aRow, aCol ); + } + + bool InsertRows( size_t aPos = 0, size_t aNumRows = 1 ) override + { + if( aPos < size() ) + { + for( size_t i = 0; i < aNumRows; i++ ) + { + insert( begin() + i, makeNewRow() ); + } + + // use the (wxGridStringTable) source Luke. + if( GetView() ) + { + wxGridTableMessage msg( this, + wxGRIDTABLE_NOTIFY_ROWS_INSERTED, + aPos, + aNumRows ); + + GetView()->ProcessTableMessage( msg ); + } + + return true; + } + + return false; + } + + bool AppendRows( size_t aNumRows = 1 ) override + { + // do not modify aNumRows, original value needed for wxGridTableMessage below + for( int i = aNumRows; i; --i ) + push_back( makeNewRow() ); + + if( GetView() ) + { + wxGridTableMessage msg( this, + wxGRIDTABLE_NOTIFY_ROWS_APPENDED, + aNumRows ); + + GetView()->ProcessTableMessage( msg ); + } + + return true; + } + + bool DeleteRows( size_t aPos, size_t aNumRows ) override + { + // aPos may be a large positive, e.g. size_t(-1), and the sum of + // aPos+aNumRows may wrap here, so both ends of the range are tested. + if( aPos < size() && aPos + aNumRows <= size() ) + { + LIB_TABLE_ROWS_ITER start = begin() + aPos; + erase( start, start + aNumRows ); + + if( GetView() ) + { + wxGridTableMessage msg( this, + wxGRIDTABLE_NOTIFY_ROWS_DELETED, + aPos, + aNumRows ); + + GetView()->ProcessTableMessage( msg ); + } + + return true; + } + + return false; + } + + wxString GetColLabelValue( int aCol ) override + { + switch( aCol ) + { + case COL_NICKNAME: return _( "Nickname" ); + case COL_URI: return _( "Library Path" ); + + // keep this "Plugin Type" text fairly long so column is sized wide enough + case COL_TYPE: return _( "Plugin Type" ); + case COL_OPTIONS: return _( "Options" ); + case COL_DESCR: return _( "Description" ); + default: return wxEmptyString; + } + } + +protected: + virtual LIB_TABLE_ROW* at( size_t aIndex ) = 0; + + virtual size_t size() const = 0; + + virtual LIB_TABLE_ROW* makeNewRow() = 0; + + virtual LIB_TABLE_ROWS_ITER begin() = 0; + + virtual LIB_TABLE_ROWS_ITER insert( LIB_TABLE_ROWS_ITER aIterator, LIB_TABLE_ROW* aRow ) = 0; + + virtual void push_back( LIB_TABLE_ROW* aRow ) = 0; + + virtual LIB_TABLE_ROWS_ITER erase( LIB_TABLE_ROWS_ITER aFirst, LIB_TABLE_ROWS_ITER aLast ) = 0; +}; + + +#endif // __LIB_TABLE_GRID_H__ diff --git a/pcbnew/dialogs/dialog_fp_lib_table.cpp b/pcbnew/dialogs/dialog_fp_lib_table.cpp index b41c4fdf7f..d2dc2eaac6 100644 --- a/pcbnew/dialogs/dialog_fp_lib_table.cpp +++ b/pcbnew/dialogs/dialog_fp_lib_table.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2013 CERN - * Copyright (C) 2012-2016 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2012-2017 KiCad Developers, see change_log.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,175 +44,46 @@ #include #include #include - - -/// grid column order is established by this sequence -enum COL_ORDER -{ - COL_NICKNAME, - COL_URI, - COL_TYPE, - COL_OPTIONS, - COL_DESCR, - COL_COUNT // keep as last -}; +#include /** - * Class FP_TBL_MODEL - * mixes in FP_LIB_TABLE into wxGridTableBase so the result can be used - * as a table within wxGrid. + * This class builds a wxGridTableBase by wrapping an #FP_LIB_TABLE object. */ -class FP_TBL_MODEL : public wxGridTableBase, public FP_LIB_TABLE +class FP_LIB_TABLE_GRID : public LIB_TABLE_GRID, public FP_LIB_TABLE { friend class FP_GRID_TRICKS; +protected: + LIB_TABLE_ROW* at( size_t aIndex ) override { return &rows.at( aIndex ); } + + size_t size() const override { return rows.size(); } + + LIB_TABLE_ROW* makeNewRow() override + { + return dynamic_cast< LIB_TABLE_ROW* >( new FP_LIB_TABLE_ROW ); + } + + LIB_TABLE_ROWS_ITER begin() override { return rows.begin(); } + + LIB_TABLE_ROWS_ITER insert( LIB_TABLE_ROWS_ITER aIterator, LIB_TABLE_ROW* aRow ) override + { + return rows.insert( aIterator, aRow ); + } + + void push_back( LIB_TABLE_ROW* aRow ) override { rows.push_back( aRow ); } + + LIB_TABLE_ROWS_ITER erase( LIB_TABLE_ROWS_ITER aFirst, LIB_TABLE_ROWS_ITER aLast ) override + { + return rows.erase( aFirst, aLast ); + } + public: - /** - * Constructor FP_TBL_MODEL - * is a copy constructor that builds a wxGridTableBase (table model) by wrapping - * an FP_LIB_TABLE. - */ - FP_TBL_MODEL( const FP_LIB_TABLE& aTableToEdit ) + FP_LIB_TABLE_GRID( const FP_LIB_TABLE& aTableToEdit ) { rows = aTableToEdit.rows; } - - //------------------------------------------------ - - int GetNumberRows() override { return rows.size(); } - int GetNumberCols() override { return COL_COUNT; } - - wxString GetValue( int aRow, int aCol ) override - { - if( unsigned( aRow ) < rows.size() ) - { - const LIB_TABLE_ROW* r = &rows[aRow]; - - switch( aCol ) - { - case COL_NICKNAME: return r->GetNickName(); - case COL_URI: return r->GetFullURI(); - case COL_TYPE: return r->GetType(); - case COL_OPTIONS: return r->GetOptions(); - case COL_DESCR: return r->GetDescr(); - default: - ; // fall thru to wxEmptyString - } - } - - return wxEmptyString; - } - - void SetValue( int aRow, int aCol, const wxString &aValue ) override - { - if( unsigned( aRow ) < rows.size() ) - { - LIB_TABLE_ROW* r = &rows[aRow]; - - switch( aCol ) - { - case COL_NICKNAME: r->SetNickName( aValue ); break; - case COL_URI: r->SetFullURI( aValue ); break; - case COL_TYPE: r->SetType( aValue ); break; - case COL_OPTIONS: r->SetOptions( aValue ); break; - case COL_DESCR: r->SetDescr( aValue ); break; - } - } - } - - bool IsEmptyCell( int aRow, int aCol ) override - { - return !GetValue( aRow, aCol ); - } - - bool InsertRows( size_t aPos = 0, size_t aNumRows = 1 ) override - { - if( aPos < rows.size() ) - { - for( size_t i = 0; i < aNumRows; i++ ) - { - rows.insert( rows.begin() + i, - dynamic_cast< LIB_TABLE_ROW* >( new FP_LIB_TABLE_ROW ) ); - } - - // use the (wxGridStringTable) source Luke. - if( GetView() ) - { - wxGridTableMessage msg( this, - wxGRIDTABLE_NOTIFY_ROWS_INSERTED, - aPos, - aNumRows ); - - GetView()->ProcessTableMessage( msg ); - } - - return true; - } - - return false; - } - - bool AppendRows( size_t aNumRows = 1 ) override - { - // do not modify aNumRows, original value needed for wxGridTableMessage below - for( int i = aNumRows; i; --i ) - rows.push_back( new FP_LIB_TABLE_ROW ); - - if( GetView() ) - { - wxGridTableMessage msg( this, - wxGRIDTABLE_NOTIFY_ROWS_APPENDED, - aNumRows ); - - GetView()->ProcessTableMessage( msg ); - } - - return true; - } - - bool DeleteRows( size_t aPos, size_t aNumRows ) override - { - // aPos may be a large positive, e.g. size_t(-1), and the sum of - // aPos+aNumRows may wrap here, so both ends of the range are tested. - if( aPos < rows.size() && aPos + aNumRows <= rows.size() ) - { - LIB_TABLE_ROWS_ITER start = rows.begin() + aPos; - rows.erase( start, start + aNumRows ); - - if( GetView() ) - { - wxGridTableMessage msg( this, - wxGRIDTABLE_NOTIFY_ROWS_DELETED, - aPos, - aNumRows ); - - GetView()->ProcessTableMessage( msg ); - } - - return true; - } - - return false; - } - - wxString GetColLabelValue( int aCol ) override - { - switch( aCol ) - { - case COL_NICKNAME: return _( "Nickname" ); - case COL_URI: return _( "Library Path" ); - - // keep this "Plugin Type" text fairly long so column is sized wide enough - case COL_TYPE: return _( "Plugin Type" ); - case COL_OPTIONS: return _( "Options" ); - case COL_DESCR: return _( "Description" ); - default: return wxEmptyString; - } - } - - //----------------------------------------------- }; @@ -230,7 +101,7 @@ protected: /// spreadsheet formatted text. virtual void paste_text( const wxString& cb_text ) override { - FP_TBL_MODEL* tbl = (FP_TBL_MODEL*) m_grid->GetTable(); + FP_LIB_TABLE_GRID* tbl = (FP_LIB_TABLE_GRID*) m_grid->GetTable(); size_t ndx = cb_text.find( "(fp_lib_table" ); @@ -302,8 +173,8 @@ public: // wxGrid only supports user owned tables if they exist past end of ~wxGrid(), // so make it a grid owned table. - m_global_grid->SetTable( new FP_TBL_MODEL( *aGlobal ), true ); - m_project_grid->SetTable( new FP_TBL_MODEL( *aProject ), true ); + m_global_grid->SetTable( new FP_LIB_TABLE_GRID( *aGlobal ), true ); + m_project_grid->SetTable( new FP_LIB_TABLE_GRID( *aProject ), true ); // add Cut, Copy, and Paste to wxGrids m_global_grid->PushEventHandler( new FP_GRID_TRICKS( m_global_grid ) ); @@ -387,8 +258,6 @@ public: private: - typedef FP_LIB_TABLE_ROW ROW; - /// If the cursor is not on a valid cell, because there are no rows at all, return -1, /// else return a 0 based column index. int getCursorCol() const @@ -412,7 +281,7 @@ private: { for( int t=0; t<2; ++t ) { - FP_TBL_MODEL& model = t==0 ? *global_model() : *project_model(); + FP_LIB_TABLE_GRID& model = t==0 ? *global_model() : *project_model(); for( int r = 0; r < model.GetNumberRows(); ) { @@ -427,7 +296,7 @@ private: // button. model.DeleteRows( r, 1 ); } - else if( nick.find(':') != size_t(-1) ) + else if( nick.find( ':' ) != size_t( -1 ) ) { wxString msg = wxString::Format( _( "Illegal character '%s' found in Nickname: '%s' in row %d" ), @@ -461,7 +330,7 @@ private: // check for duplicate nickNames, separately in each table. for( int t=0; t<2; ++t ) { - FP_TBL_MODEL& model = t==0 ? *global_model() : *project_model(); + FP_LIB_TABLE_GRID& model = t==0 ? *global_model() : *project_model(); for( int r1 = 0; r1 < model.GetNumberRows() - 1; ++r1 ) { @@ -588,7 +457,7 @@ private: { int curCol = getCursorCol(); - FP_TBL_MODEL* tbl = cur_model(); + FP_LIB_TABLE_GRID* tbl = cur_model(); boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me = tbl->rows.release( tbl->rows.begin() + curRow ); @@ -620,7 +489,7 @@ private: if( rowsSelected.GetCount() == 0 ) return; - FP_TBL_MODEL* tbl = cur_model(); + FP_LIB_TABLE_GRID* tbl = cur_model(); // @todo: add multiple selection moves. int curRow = rowsSelected[0]; @@ -654,7 +523,7 @@ private: void optionsEditor( wxCommandEvent& event ) override { - FP_TBL_MODEL* tbl = cur_model(); + FP_LIB_TABLE_GRID* tbl = cur_model(); if( tbl->GetNumberRows() ) { @@ -742,8 +611,8 @@ private: // clear the table m_path_subs_grid->DeleteRows( 0, m_path_subs_grid->GetNumberRows() ); - FP_TBL_MODEL* gbl = global_model(); - FP_TBL_MODEL* prj = project_model(); + FP_LIB_TABLE_GRID* gbl = global_model(); + FP_LIB_TABLE_GRID* prj = project_model(); int gblRowCount = gbl->GetNumberRows(); int prjRowCount = prj->GetNumberRows(); @@ -813,9 +682,20 @@ private: FP_LIB_TABLE* m_global; FP_LIB_TABLE* m_project; - FP_TBL_MODEL* global_model() const { return (FP_TBL_MODEL*) m_global_grid->GetTable(); } - FP_TBL_MODEL* project_model() const { return (FP_TBL_MODEL*) m_project_grid->GetTable(); } - FP_TBL_MODEL* cur_model() const { return (FP_TBL_MODEL*) m_cur_grid->GetTable(); } + FP_LIB_TABLE_GRID* global_model() const + { + return (FP_LIB_TABLE_GRID*) m_global_grid->GetTable(); + } + + FP_LIB_TABLE_GRID* project_model() const + { + return (FP_LIB_TABLE_GRID*) m_project_grid->GetTable(); + } + + FP_LIB_TABLE_GRID* cur_model() const + { + return (FP_LIB_TABLE_GRID*) m_cur_grid->GetTable(); + } wxGrid* m_cur_grid; ///< changed based on tab choice static int m_pageNdx; ///< Remember the last notebook page selected during a session @@ -835,7 +715,7 @@ void DIALOG_FP_LIB_TABLE::OnClickLibraryWizard( wxCommandEvent& event ) const std::vector& libs = dlg.GetLibraries(); bool global_scope = dlg.GetLibScope() == WIZARD_FPLIB_TABLE::GLOBAL; wxGrid* libgrid = global_scope ? m_global_grid : m_project_grid; - FP_TBL_MODEL* tbl = (FP_TBL_MODEL*) libgrid->GetTable(); + FP_LIB_TABLE_GRID* tbl = (FP_LIB_TABLE_GRID*) libgrid->GetTable(); for( std::vector::const_iterator it = libs.begin(); it != libs.end(); ++it )