Make FP_TBL_MODEL reusable.

Factor out the FP_LIB_TABLE specific content from FP_TBL_MODEL into a new
LIB_TABLE_GRID class that can be used for any library table grid mix in.

Create new FP_LIB_TABLE_GRID object for the footprint library edit dialog.

Add method GetEnvVariables() to return any environment variables found in
a LIB_TABLE object.
This commit is contained in:
Wayne Stambaugh 2017-03-27 19:21:12 -04:00
parent 3ec28e2acf
commit b4e2757119
5 changed files with 310 additions and 184 deletions

View File

@ -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 <dick@softplc.com>
* Copyright (C) 2012-2016 Wayne Stambaugh <stambaughw@gmail.com>
* Copyright (C) 2012-2016 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2012-2017 Wayne Stambaugh <stambaughw@gmail.com>
* 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() )

View File

@ -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 <dick@softplc.com>
* Copyright (C) 2012-2016 Wayne Stambaugh <stambaughw@verizon.net>
* Copyright (C) 2012-2017 Wayne Stambaugh <stambaughw@verizon.net>
* 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 <io_mgr.h>
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;

View File

@ -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

196
include/lib_table_grid.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef __LIB_TABLE_GRID_H__
#define __LIB_TABLE_GRID_H__
#include <lib_table_base.h>
#include <wx/grid.h>
/// 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:
//-----<wxGridTableBase overloads>-------------------------------------------
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__

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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 <grid_tricks.h>
#include <confirm.h>
#include <wizard_add_fplib.h>
/// 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 <lib_table_grid.h>
/**
* 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;
}
//-----<wxGridTableBase overloads>-------------------------------------------
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;
}
}
//-----</wxGridTableBase overloads>------------------------------------------
};
@ -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<WIZARD_FPLIB_TABLE::LIBRARY>& 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<WIZARD_FPLIB_TABLE::LIBRARY>::const_iterator it = libs.begin();
it != libs.end(); ++it )