CHANGED: Library editors are now usable with no project loaded

Fixes https://gitlab.com/kicad/code/kicad/-/issues/3688
This commit is contained in:
Jon Evans 2020-08-07 15:22:15 -04:00
parent db501c2002
commit dcc484e114
11 changed files with 229 additions and 131 deletions

View File

@ -886,7 +886,9 @@ bool EDA_DRAW_FRAME::LibraryFileBrowser( bool doOpen, wxFileName& aFilename,
} }
else else
{ {
wxFileDialog dlg( this, prompt, Prj().GetProjectPath(), aFilename.GetFullName() , wxString dir = Prj().IsNullProject() ? aFilename.GetFullPath() : Prj().GetProjectPath();
wxFileDialog dlg( this, prompt, dir, aFilename.GetFullName(),
wildcard, doOpen ? wxFD_OPEN | wxFD_FILE_MUST_EXIST wildcard, doOpen ? wxFD_OPEN | wxFD_FILE_MUST_EXIST
: wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT ); : wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT );

View File

@ -132,6 +132,12 @@ const wxString PROJECT::GetProjectName() const
} }
bool PROJECT::IsNullProject() const
{
return m_project_name.GetName().IsEmpty();
}
const wxString PROJECT::SymbolLibTableName() const const wxString PROJECT::SymbolLibTableName() const
{ {
return libTableName( "sym-lib-table" ); return libTableName( "sym-lib-table" );

View File

@ -217,66 +217,82 @@ PANEL_SYM_LIB_TABLE::PANEL_SYM_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent,
m_parent( aParent ), m_parent( aParent ),
m_lastBrowseDir( aProjectBasePath ) m_lastBrowseDir( aProjectBasePath )
{ {
// For user info, shows the table filenames:
m_GblTableFilename->SetLabel( aGlobalTablePath );
m_PrjTableFilename->SetLabel( aProjectTablePath );
// wxGrid only supports user owned tables if they exist past end of ~wxGrid(), // wxGrid only supports user owned tables if they exist past end of ~wxGrid(),
// so make it a grid owned table. // so make it a grid owned table.
m_global_grid->SetTable( new SYMBOL_LIB_TABLE_GRID( *aGlobal ), true ); m_global_grid->SetTable( new SYMBOL_LIB_TABLE_GRID( *aGlobal ), true );
m_project_grid->SetTable( new SYMBOL_LIB_TABLE_GRID( *aProject ), true );
// Give a bit more room for combobox editors // For user info, shows the table filenames:
m_global_grid->SetDefaultRowSize( m_global_grid->GetDefaultRowSize() + 4 ); m_GblTableFilename->SetLabel( aGlobalTablePath );
m_project_grid->SetDefaultRowSize( m_project_grid->GetDefaultRowSize() + 4 );
// add Cut, Copy, and Paste to wxGrids
m_global_grid->PushEventHandler( new SYMBOL_GRID_TRICKS( m_parent, m_global_grid ) );
m_project_grid->PushEventHandler( new SYMBOL_GRID_TRICKS( m_parent, m_project_grid ) );
m_path_subs_grid->PushEventHandler( new GRID_TRICKS( m_path_subs_grid ) );
m_global_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
m_project_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
m_global_grid->AutoSizeColumns( false );
m_project_grid->AutoSizeColumns( false );
wxArrayString pluginChoices; wxArrayString pluginChoices;
pluginChoices.Add( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_KICAD ) ); pluginChoices.Add( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_KICAD ) );
pluginChoices.Add( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_LEGACY ) ); pluginChoices.Add( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_LEGACY ) );
populateEnvironReadOnlyTable(); auto setupGrid =
[&]( WX_GRID* aGrid )
for( wxGrid* g : { m_global_grid, m_project_grid } )
{ {
// Give a bit more room for combobox editors
aGrid->SetDefaultRowSize( aGrid->GetDefaultRowSize() + 4 );
// add Cut, Copy, and Paste to wxGrids
aGrid->PushEventHandler( new SYMBOL_GRID_TRICKS( m_parent, aGrid ) );
aGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
aGrid->AutoSizeColumns( false );
// Set special attributes // Set special attributes
wxGridCellAttr* attr; wxGridCellAttr* attr;
attr = new wxGridCellAttr; attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_SYMLIB_EDITOR( m_parent, &m_lastBrowseDir, attr->SetEditor( new GRID_CELL_SYMLIB_EDITOR( m_parent, &m_lastBrowseDir,
KiCadSymbolLibFileWildcard() ) ); KiCadSymbolLibFileWildcard() ) );
g->SetColAttr( COL_URI, attr ); aGrid->SetColAttr( COL_URI, attr );
attr = new wxGridCellAttr; attr = new wxGridCellAttr;
attr->SetEditor( new wxGridCellChoiceEditor( pluginChoices ) ); attr->SetEditor( new wxGridCellChoiceEditor( pluginChoices ) );
g->SetColAttr( COL_TYPE, attr ); aGrid->SetColAttr( COL_TYPE, attr );
attr = new wxGridCellAttr; attr = new wxGridCellAttr;
attr->SetRenderer( new wxGridCellBoolRenderer() ); attr->SetRenderer( new wxGridCellBoolRenderer() );
attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
g->SetColAttr( COL_ENABLED, attr ); aGrid->SetColAttr( COL_ENABLED, attr );
// all but COL_OPTIONS, which is edited with Option Editor anyways. // all but COL_OPTIONS, which is edited with Option Editor anyways.
g->AutoSizeColumn( COL_NICKNAME, false ); aGrid->AutoSizeColumn( COL_NICKNAME, false );
g->AutoSizeColumn( COL_TYPE, false ); aGrid->AutoSizeColumn( COL_TYPE, false );
g->AutoSizeColumn( COL_URI, false ); aGrid->AutoSizeColumn( COL_URI, false );
g->AutoSizeColumn( COL_DESCR, false ); aGrid->AutoSizeColumn( COL_DESCR, false );
g->AutoSizeColumn( COL_ENABLED, false ); aGrid->AutoSizeColumn( COL_ENABLED, false );
// would set this to width of title, if it was easily known. // would set this to width of title, if it was easily known.
g->SetColSize( COL_OPTIONS, 80 ); aGrid->SetColSize( COL_OPTIONS, 80 );
// Gives a selection to each grid, mainly for delete button. wxGrid's wake up with
// a currentCell which is sometimes not highlighted.
if( aGrid->GetNumberRows() > 0 )
aGrid->SelectRow( 0 );
};
setupGrid( m_global_grid );
if( aProject )
{
m_PrjTableFilename->SetLabel( aProjectTablePath );
m_project_grid->SetTable( new SYMBOL_LIB_TABLE_GRID( *aProject ), true );
setupGrid( m_project_grid );
} }
else
{
m_pageNdx = 0;
m_auinotebook->DeletePage( 1 );
m_project_grid = nullptr;
}
// add Cut, Copy, and Paste to wxGrids
m_path_subs_grid->PushEventHandler( new GRID_TRICKS( m_path_subs_grid ) );
populateEnvironReadOnlyTable();
// select the last selected page // select the last selected page
m_auinotebook->SetSelection( m_pageNdx ); m_auinotebook->SetSelection( m_pageNdx );
@ -294,14 +310,6 @@ PANEL_SYM_LIB_TABLE::PANEL_SYM_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent,
m_move_up_button->SetBitmap( KiBitmap( small_up_xpm ) ); m_move_up_button->SetBitmap( KiBitmap( small_up_xpm ) );
m_move_down_button->SetBitmap( KiBitmap( small_down_xpm ) ); m_move_down_button->SetBitmap( KiBitmap( small_down_xpm ) );
m_browse_button->SetBitmap( KiBitmap( folder_xpm ) ); m_browse_button->SetBitmap( KiBitmap( folder_xpm ) );
// Gives a selection to each grid, mainly for delete button. wxGrid's wake up with
// a currentCell which is sometimes not highlighted.
if( m_global_grid->GetNumberRows() > 0 )
m_global_grid->SelectRow( 0 );
if( m_project_grid->GetNumberRows() > 0 )
m_project_grid->SelectRow( 0 );
} }
@ -315,7 +323,10 @@ PANEL_SYM_LIB_TABLE::~PANEL_SYM_LIB_TABLE()
// Delete the GRID_TRICKS. // Delete the GRID_TRICKS.
// Any additional event handlers should be popped before the window is deleted. // Any additional event handlers should be popped before the window is deleted.
m_global_grid->PopEventHandler( true ); m_global_grid->PopEventHandler( true );
if( m_project_grid )
m_project_grid->PopEventHandler( true ); m_project_grid->PopEventHandler( true );
m_path_subs_grid->PopEventHandler( true ); m_path_subs_grid->PopEventHandler( true );
} }
@ -324,6 +335,9 @@ bool PANEL_SYM_LIB_TABLE::verifyTables()
{ {
for( SYMBOL_LIB_TABLE_GRID* model : { global_model(), project_model() } ) for( SYMBOL_LIB_TABLE_GRID* model : { global_model(), project_model() } )
{ {
if( !model )
continue;
for( int r = 0; r < model->GetNumberRows(); ) for( int r = 0; r < model->GetNumberRows(); )
{ {
wxString nick = model->GetValue( r, COL_NICKNAME ).Trim( false ).Trim(); wxString nick = model->GetValue( r, COL_NICKNAME ).Trim( false ).Trim();
@ -368,6 +382,9 @@ bool PANEL_SYM_LIB_TABLE::verifyTables()
// check for duplicate nickNames, separately in each table. // check for duplicate nickNames, separately in each table.
for( SYMBOL_LIB_TABLE_GRID* model : { global_model(), project_model() } ) for( SYMBOL_LIB_TABLE_GRID* model : { global_model(), project_model() } )
{ {
if( !model )
continue;
for( int r1 = 0; r1 < model->GetNumberRows() - 1; ++r1 ) for( int r1 = 0; r1 < model->GetNumberRows() - 1; ++r1 )
{ {
wxString nick1 = model->GetValue( r1, COL_NICKNAME ); wxString nick1 = model->GetValue( r1, COL_NICKNAME );
@ -399,6 +416,9 @@ bool PANEL_SYM_LIB_TABLE::verifyTables()
for( SYMBOL_LIB_TABLE* table : { global_model(), project_model() } ) for( SYMBOL_LIB_TABLE* table : { global_model(), project_model() } )
{ {
if( !table )
continue;
for( unsigned int r = 0; r < table->GetCount(); ++r ) for( unsigned int r = 0; r < table->GetCount(); ++r )
{ {
SYMBOL_LIB_TABLE_ROW& row = dynamic_cast<SYMBOL_LIB_TABLE_ROW&>( table->At( r ) ); SYMBOL_LIB_TABLE_ROW& row = dynamic_cast<SYMBOL_LIB_TABLE_ROW&>( table->At( r ) );
@ -682,7 +702,7 @@ bool PANEL_SYM_LIB_TABLE::TransferDataFromWindow()
m_globalTable->reindex(); m_globalTable->reindex();
} }
if( *project_model() != *m_projectTable ) if( project_model() && *project_model() != *m_projectTable )
{ {
m_parent->m_ProjectTableChanged = true; m_parent->m_ProjectTableChanged = true;
@ -708,6 +728,9 @@ void PANEL_SYM_LIB_TABLE::populateEnvironReadOnlyTable()
for( SYMBOL_LIB_TABLE_GRID* tbl : { global_model(), project_model() } ) for( SYMBOL_LIB_TABLE_GRID* tbl : { global_model(), project_model() } )
{ {
if( !tbl )
continue;
for( int row = 0; row < tbl->GetNumberRows(); ++row ) for( int row = 0; row < tbl->GetNumberRows(); ++row )
{ {
wxString uri = tbl->GetValue( row, COL_URI ); wxString uri = tbl->GetValue( row, COL_URI );
@ -783,7 +806,7 @@ SYMBOL_LIB_TABLE_GRID* PANEL_SYM_LIB_TABLE::global_model() const
SYMBOL_LIB_TABLE_GRID* PANEL_SYM_LIB_TABLE::project_model() const SYMBOL_LIB_TABLE_GRID* PANEL_SYM_LIB_TABLE::project_model() const
{ {
return (SYMBOL_LIB_TABLE_GRID*) m_project_grid->GetTable(); return m_project_grid ? (SYMBOL_LIB_TABLE_GRID*) m_project_grid->GetTable() : nullptr;
} }
@ -804,12 +827,16 @@ void InvokeSchEditSymbolLibTable( KIWAY* aKiway, wxWindow *aParent )
SYMBOL_LIB_TABLE* globalTable = &SYMBOL_LIB_TABLE::GetGlobalLibTable(); SYMBOL_LIB_TABLE* globalTable = &SYMBOL_LIB_TABLE::GetGlobalLibTable();
wxString globalTablePath = SYMBOL_LIB_TABLE::GetGlobalTableFileName(); wxString globalTablePath = SYMBOL_LIB_TABLE::GetGlobalTableFileName();
SYMBOL_LIB_TABLE* projectTable = aKiway->Prj().SchSymbolLibTable(); SYMBOL_LIB_TABLE* projectTable = nullptr;
wxString projectPath = aKiway->Prj().GetProjectPath(); wxString projectPath = aKiway->Prj().GetProjectPath();
wxFileName projectTableFn( projectPath, SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() ); wxFileName projectTableFn( projectPath, SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
wxString msg; wxString msg;
wxString currentLib; wxString currentLib;
// Don't allow editing project tables if no project is open
if( !aKiway->Prj().IsNullProject() )
projectTable = aKiway->Prj().SchSymbolLibTable();
if( libEditor ) if( libEditor )
{ {
currentLib = libEditor->GetCurLib(); currentLib = libEditor->GetCurLib();
@ -860,7 +887,7 @@ void InvokeSchEditSymbolLibTable( KIWAY* aKiway, wxWindow *aParent )
} }
} }
if( dlg.m_ProjectTableChanged ) if( projectTable && dlg.m_ProjectTableChanged )
{ {
try try
{ {
@ -879,7 +906,7 @@ void InvokeSchEditSymbolLibTable( KIWAY* aKiway, wxWindow *aParent )
if( libEditor ) if( libEditor )
{ {
// Check if the currently selected symbol library been removed or disabled. // Check if the currently selected symbol library been removed or disabled.
if( !currentLib.empty() && !projectTable->HasLibrary( currentLib, true ) ) if( !currentLib.empty() && projectTable && !projectTable->HasLibrary( currentLib, true ) )
{ {
libEditor->SetCurLib( wxEmptyString ); libEditor->SetCurLib( wxEmptyString );
libEditor->emptyScreen(); libEditor->emptyScreen();

View File

@ -656,6 +656,23 @@ void LIB_EDIT_FRAME::RegenerateLibraryTree()
SYMBOL_LIB_TABLE* LIB_EDIT_FRAME::selectSymLibTable( bool aOptional ) SYMBOL_LIB_TABLE* LIB_EDIT_FRAME::selectSymLibTable( bool aOptional )
{ {
// If no project is loaded, always work with the global table
if( Prj().IsNullProject() )
{
SYMBOL_LIB_TABLE* ret = &SYMBOL_LIB_TABLE::GetGlobalLibTable();
if( aOptional )
{
wxMessageDialog dlg( this, _( "Add the library to the global library table?" ),
_( "Add To Global Library Table" ), wxYES_NO );
if( dlg.ShowModal() != wxID_OK )
ret = nullptr;
}
return ret;
}
wxArrayString libTableNames; wxArrayString libTableNames;
libTableNames.Add( _( "Global" ) ); libTableNames.Add( _( "Global" ) );
libTableNames.Add( _( "Project" ) ); libTableNames.Add( _( "Project" ) );

View File

@ -109,6 +109,14 @@ public:
*/ */
VTBL_ENTRY const wxString GetProjectName() const; VTBL_ENTRY const wxString GetProjectName() const;
/**
* Checks if this project is a null project (i.e. the default project object created when
* no real project is open). The null project still presents all the same project interface,
* but is not backed by any files, so saving it makes no sense.
* @return true if this is a bull project
*/
VTBL_ENTRY bool IsNullProject() const;
/** /**
* Return the name of the sheet identified by the given UUID. * Return the name of the sheet identified by the given UUID.
*/ */

View File

@ -262,9 +262,7 @@ void KICAD_MANAGER_FRAME::RecreateLauncher()
void KICAD_MANAGER_FRAME::SyncToolbars() void KICAD_MANAGER_FRAME::SyncToolbars()
{ {
m_launcher->Toggle( KICAD_MANAGER_ACTIONS::editSchematic, m_active_project ); m_launcher->Toggle( KICAD_MANAGER_ACTIONS::editSchematic, m_active_project );
m_launcher->Toggle( KICAD_MANAGER_ACTIONS::editSymbols, m_active_project );
m_launcher->Toggle( KICAD_MANAGER_ACTIONS::editPCB, m_active_project ); m_launcher->Toggle( KICAD_MANAGER_ACTIONS::editPCB, m_active_project );
m_launcher->Toggle( KICAD_MANAGER_ACTIONS::editFootprints, m_active_project );
m_launcher->Refresh(); m_launcher->Refresh();
} }

View File

@ -368,26 +368,12 @@ PANEL_FP_LIB_TABLE::PANEL_FP_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent,
{ {
// For user info, shows the table filenames: // For user info, shows the table filenames:
m_GblTableFilename->SetLabel( aGlobalTblPath ); m_GblTableFilename->SetLabel( aGlobalTblPath );
m_PrjTableFilename->SetLabel( aProjectTblPath );
m_global_grid->SetTable( new FP_LIB_TABLE_GRID( *aGlobal ), true ); m_global_grid->SetTable( new FP_LIB_TABLE_GRID( *aGlobal ), true );
m_project_grid->SetTable( new FP_LIB_TABLE_GRID( *aProject ), true );
// Give a bit more room for wxChoice editors
m_global_grid->SetDefaultRowSize( m_global_grid->GetDefaultRowSize() + 4 );
m_project_grid->SetDefaultRowSize( m_project_grid->GetDefaultRowSize() + 4 );
// add Cut, Copy, and Paste to wxGrids // add Cut, Copy, and Paste to wxGrids
m_global_grid->PushEventHandler( new FP_GRID_TRICKS( m_parent, m_global_grid ) );
m_project_grid->PushEventHandler( new FP_GRID_TRICKS( m_parent, m_project_grid ) );
m_path_subs_grid->PushEventHandler( new GRID_TRICKS( m_path_subs_grid ) ); m_path_subs_grid->PushEventHandler( new GRID_TRICKS( m_path_subs_grid ) );
m_global_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
m_project_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
m_global_grid->AutoSizeColumns( false );
m_project_grid->AutoSizeColumns( false );
wxArrayString choices; wxArrayString choices;
choices.Add( IO_MGR::ShowType( IO_MGR::KICAD_SEXP ) ); choices.Add( IO_MGR::ShowType( IO_MGR::KICAD_SEXP ) );
@ -402,33 +388,64 @@ PANEL_FP_LIB_TABLE::PANEL_FP_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent,
choices.Add( IO_MGR::ShowType( IO_MGR::PCAD ) ); choices.Add( IO_MGR::ShowType( IO_MGR::PCAD ) );
*/ */
populateEnvironReadOnlyTable(); auto setupGrid =
[&]( WX_GRID* aGrid )
for( wxGrid* g : { m_global_grid, m_project_grid } )
{ {
// Give a bit more room for wxChoice editors
aGrid->SetDefaultRowSize( aGrid->GetDefaultRowSize() + 4 );
// add Cut, Copy, and Paste to wxGrids
aGrid->PushEventHandler( new FP_GRID_TRICKS( m_parent, aGrid ) );
aGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
aGrid->AutoSizeColumns( false );
wxGridCellAttr* attr; wxGridCellAttr* attr;
attr = new wxGridCellAttr; attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_PATH_EDITOR( m_parent, &m_lastBrowseDir, wxEmptyString ) ); attr->SetEditor( new GRID_CELL_PATH_EDITOR( m_parent, &m_lastBrowseDir,
g->SetColAttr( COL_URI, attr ); wxEmptyString ) );
aGrid->SetColAttr( COL_URI, attr );
attr = new wxGridCellAttr; attr = new wxGridCellAttr;
attr->SetEditor( new wxGridCellChoiceEditor( choices ) ); attr->SetEditor( new wxGridCellChoiceEditor( choices ) );
g->SetColAttr( COL_TYPE, attr ); aGrid->SetColAttr( COL_TYPE, attr );
attr = new wxGridCellAttr; attr = new wxGridCellAttr;
attr->SetRenderer( new wxGridCellBoolRenderer() ); attr->SetRenderer( new wxGridCellBoolRenderer() );
attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
g->SetColAttr( COL_ENABLED, attr ); aGrid->SetColAttr( COL_ENABLED, attr );
// all but COL_OPTIONS, which is edited with Option Editor anyways. // all but COL_OPTIONS, which is edited with Option Editor anyways.
g->AutoSizeColumn( COL_NICKNAME, false ); aGrid->AutoSizeColumn( COL_NICKNAME, false );
g->AutoSizeColumn( COL_TYPE, false ); aGrid->AutoSizeColumn( COL_TYPE, false );
g->AutoSizeColumn( COL_URI, false ); aGrid->AutoSizeColumn( COL_URI, false );
g->AutoSizeColumn( COL_DESCR, false ); aGrid->AutoSizeColumn( COL_DESCR, false );
// would set this to width of title, if it was easily known. // would set this to width of title, if it was easily known.
g->SetColSize( COL_OPTIONS, 80 ); aGrid->SetColSize( COL_OPTIONS, 80 );
// Gives a selection to each grid, mainly for delete button. wxGrid's wake up with
// a currentCell which is sometimes not highlighted.
if( aGrid->GetNumberRows() > 0 )
aGrid->SelectRow( 0 );
};
setupGrid( m_global_grid );
populateEnvironReadOnlyTable();
if( aProject )
{
m_PrjTableFilename->SetLabel( aProjectTblPath );
m_project_grid->SetTable( new FP_LIB_TABLE_GRID( *aProject ), true );
setupGrid( m_project_grid );
}
else
{
m_pageNdx = 0;
m_auinotebook->DeletePage( 1 );
m_project_grid = nullptr;
} }
m_path_subs_grid->SetColLabelValue( 0, _( "Name" ) ); m_path_subs_grid->SetColLabelValue( 0, _( "Name" ) );
@ -457,14 +474,6 @@ PANEL_FP_LIB_TABLE::PANEL_FP_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent,
m_browseButton->SetWidthPadding( 4 ); m_browseButton->SetWidthPadding( 4 );
m_browseButton->SetMinSize( buttonSize ); m_browseButton->SetMinSize( buttonSize );
// Gives a selection to each grid, mainly for delete button. wxGrid's wake up with
// a currentCell which is sometimes not highlighted.
if( m_global_grid->GetNumberRows() > 0 )
m_global_grid->SelectRow( 0 );
if( m_project_grid->GetNumberRows() > 0 )
m_project_grid->SelectRow( 0 );
// Populate the browse library options // Populate the browse library options
wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu(); wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu();
@ -493,7 +502,10 @@ PANEL_FP_LIB_TABLE::~PANEL_FP_LIB_TABLE()
// Delete the GRID_TRICKS. // Delete the GRID_TRICKS.
// Any additional event handlers should be popped before the window is deleted. // Any additional event handlers should be popped before the window is deleted.
m_global_grid->PopEventHandler( true ); m_global_grid->PopEventHandler( true );
if( m_project_grid )
m_project_grid->PopEventHandler( true ); m_project_grid->PopEventHandler( true );
m_path_subs_grid->PopEventHandler( true ); m_path_subs_grid->PopEventHandler( true );
} }
@ -502,6 +514,9 @@ bool PANEL_FP_LIB_TABLE::verifyTables()
{ {
for( FP_LIB_TABLE_GRID* model : { global_model(), project_model() } ) for( FP_LIB_TABLE_GRID* model : { global_model(), project_model() } )
{ {
if( !model )
continue;
for( int r = 0; r < model->GetNumberRows(); ) for( int r = 0; r < model->GetNumberRows(); )
{ {
wxString nick = model->GetValue( r, COL_NICKNAME ).Trim( false ).Trim(); wxString nick = model->GetValue( r, COL_NICKNAME ).Trim( false ).Trim();
@ -546,6 +561,9 @@ bool PANEL_FP_LIB_TABLE::verifyTables()
// check for duplicate nickNames, separately in each table. // check for duplicate nickNames, separately in each table.
for( FP_LIB_TABLE_GRID* model : { global_model(), project_model() } ) for( FP_LIB_TABLE_GRID* model : { global_model(), project_model() } )
{ {
if( !model )
continue;
for( int r1 = 0; r1 < model->GetNumberRows() - 1; ++r1 ) for( int r1 = 0; r1 < model->GetNumberRows() - 1; ++r1 )
{ {
wxString nick1 = model->GetValue( r1, COL_NICKNAME ); wxString nick1 = model->GetValue( r1, COL_NICKNAME );
@ -909,7 +927,7 @@ bool PANEL_FP_LIB_TABLE::TransferDataFromWindow()
m_global->reindex(); m_global->reindex();
} }
if( *project_model() != *m_project ) if( project_model() && *project_model() != *m_project )
{ {
m_parent->m_ProjectTableChanged = true; m_parent->m_ProjectTableChanged = true;
@ -940,6 +958,9 @@ void PANEL_FP_LIB_TABLE::populateEnvironReadOnlyTable()
for( FP_LIB_TABLE_GRID* tbl : { global_model(), project_model() } ) for( FP_LIB_TABLE_GRID* tbl : { global_model(), project_model() } )
{ {
if( !tbl )
continue;
for( int row = 0; row < tbl->GetNumberRows(); ++row ) for( int row = 0; row < tbl->GetNumberRows(); ++row )
{ {
wxString uri = tbl->GetValue( row, COL_URI ); wxString uri = tbl->GetValue( row, COL_URI );
@ -1010,6 +1031,9 @@ void InvokePcbLibTableEditor( KIWAY* aKiway, wxWindow* aCaller )
DIALOG_EDIT_LIBRARY_TABLES dlg( aCaller, _( "Footprint Libraries" ) ); DIALOG_EDIT_LIBRARY_TABLES dlg( aCaller, _( "Footprint Libraries" ) );
dlg.SetKiway( &dlg, aKiway ); dlg.SetKiway( &dlg, aKiway );
if( aKiway->Prj().IsNullProject() )
projectTable = nullptr;
dlg.InstallPanel( new PANEL_FP_LIB_TABLE( &dlg, globalTable, globalTablePath, dlg.InstallPanel( new PANEL_FP_LIB_TABLE( &dlg, globalTable, globalTablePath,
projectTable, projectTablePath, projectTable, projectTablePath,
aKiway->Prj().GetProjectPath() ) ); aKiway->Prj().GetProjectPath() ) );
@ -1030,7 +1054,7 @@ void InvokePcbLibTableEditor( KIWAY* aKiway, wxWindow* aCaller )
} }
} }
if( dlg.m_ProjectTableChanged ) if( projectTable && dlg.m_ProjectTableChanged )
{ {
try try
{ {

View File

@ -77,7 +77,7 @@ private:
FP_LIB_TABLE_GRID* project_model() const FP_LIB_TABLE_GRID* project_model() const
{ {
return (FP_LIB_TABLE_GRID*) m_project_grid->GetTable(); return m_project_grid ? (FP_LIB_TABLE_GRID*) m_project_grid->GetTable() : nullptr;
} }
FP_LIB_TABLE_GRID* cur_model() const FP_LIB_TABLE_GRID* cur_model() const

View File

@ -375,13 +375,14 @@ void FOOTPRINT_EDIT_FRAME::Export_Module( MODULE* aModule )
} }
wxString PCB_BASE_EDIT_FRAME::CreateNewLibrary(const wxString& aLibName ) wxString PCB_BASE_EDIT_FRAME::CreateNewLibrary( const wxString& aLibName,
const wxString& aProposedName )
{ {
// Kicad cannot write legacy format libraries, only .pretty new format // Kicad cannot write legacy format libraries, only .pretty new format
// because the legacy format cannot handle current features. // because the legacy format cannot handle current features.
// The footprint library is actually a directory // The footprint library is actually a directory
wxString initialPath = wxPathOnly( Prj().GetProjectFullName() ); wxString initialPath = aProposedName.IsEmpty() ? Prj().GetProjectPath() : aProposedName;
wxFileName fn; wxFileName fn;
bool doAdd = false; bool doAdd = false;
@ -490,18 +491,32 @@ bool PCB_BASE_EDIT_FRAME::AddLibrary( const wxString& aFilename )
bool saveInGlobalTable = false; bool saveInGlobalTable = false;
bool saveInProjectTable = false; bool saveInProjectTable = false;
if( Prj().IsNullProject() )
{
saveInGlobalTable = true;
}
else
{
wxArrayString libTableNames; wxArrayString libTableNames;
libTableNames.Add( _( "Global" ) ); libTableNames.Add( _( "Global" ) );
libTableNames.Add( _( "Project" ) ); libTableNames.Add( _( "Project" ) );
switch( SelectSingleOption( this, _( "Select Library Table" ), switch( SelectSingleOption( this, _( "Select Library Table" ),
_( "Choose the Library Table to add the library to:" ), _( "Choose the Library Table to add the library to:" ), libTableNames ) )
libTableNames ) )
{ {
case 0: saveInGlobalTable = true; break; case 0:
case 1: saveInProjectTable = true; break; saveInGlobalTable = true;
default: return false; break;
case 1:
saveInProjectTable = true;
break;
default:
return false;
}
} }
wxString type = IO_MGR::ShowType( IO_MGR::GuessPluginTypeFromLibPath( libPath ) ); wxString type = IO_MGR::ShowType( IO_MGR::GuessPluginTypeFromLibPath( libPath ) );

View File

@ -384,7 +384,7 @@ MODULE* FOOTPRINT_EDIT_FRAME::SelectFootprintFromBoard( BOARD* aPcb )
bool FOOTPRINT_EDIT_FRAME::SaveLibraryAs( const wxString& aLibraryPath ) bool FOOTPRINT_EDIT_FRAME::SaveLibraryAs( const wxString& aLibraryPath )
{ {
const wxString& curLibPath = aLibraryPath; const wxString& curLibPath = aLibraryPath;
wxString dstLibPath = CreateNewLibrary(); wxString dstLibPath = CreateNewLibrary( wxEmptyString, aLibraryPath );
if( !dstLibPath ) if( !dstLibPath )
return false; // user aborted in CreateNewLibrary() return false; // user aborted in CreateNewLibrary()

View File

@ -44,17 +44,18 @@ public:
virtual ~PCB_BASE_EDIT_FRAME(); virtual ~PCB_BASE_EDIT_FRAME();
/** /**
* Function CreateNewLibrary
* If a library name is given, creates a new footprint library in the project folder * If a library name is given, creates a new footprint library in the project folder
* with the given name. If no library name is given it prompts user for a library path, * with the given name. If no library name is given it prompts user for a library path,
* then creates a new footprint library at that location. * then creates a new footprint library at that location.
* If library exists, user is warned about that, and is given a chance * If library exists, user is warned about that, and is given a chance
* to abort the new creation, and in that case existing library is first deleted. * to abort the new creation, and in that case existing library is first deleted.
* @param aProposedName is the inital path and filename shown in the file chooser dialog
* *
* @return wxString - the newly created library path if library was successfully * @return wxString - the newly created library path if library was successfully
* created, else wxEmptyString because user aborted or error. * created, else wxEmptyString because user aborted or error.
*/ */
wxString CreateNewLibrary(const wxString& aLibName = wxEmptyString); wxString CreateNewLibrary( const wxString& aLibName = wxEmptyString,
const wxString& aProposedName = wxEmptyString );
/** /**
* Function AddLibrary * Function AddLibrary