2012-10-15 22:30:01 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
2021-02-18 11:17:20 +00:00
|
|
|
* Copyright (C) 2013-2021 CERN
|
2024-02-02 14:20:36 +00:00
|
|
|
* Copyright (C) 2012-2024 KiCad Developers, see AUTHORS.txt for contributors.
|
2012-10-15 22:30:01 +00:00
|
|
|
*
|
|
|
|
* 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 2
|
|
|
|
* 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, you may find one here:
|
|
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2013-01-21 03:12:16 +00:00
|
|
|
/* TODO:
|
|
|
|
|
2013-10-13 21:33:58 +00:00
|
|
|
*) After any change to uri, reparse the environment variables.
|
2013-01-21 03:27:01 +00:00
|
|
|
|
2013-01-21 03:12:16 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <set>
|
2021-03-20 15:35:37 +00:00
|
|
|
#include <wx/dir.h>
|
2013-10-23 18:56:03 +00:00
|
|
|
#include <wx/regex.h>
|
2018-03-14 04:51:59 +00:00
|
|
|
#include <wx/grid.h>
|
2021-05-01 07:50:29 +00:00
|
|
|
#include <wx/dirdlg.h>
|
|
|
|
#include <wx/filedlg.h>
|
2024-04-27 19:57:24 +00:00
|
|
|
#include <wx/msgdlg.h>
|
2013-01-21 03:12:16 +00:00
|
|
|
|
* KIWAY Milestone A): Make major modules into DLL/DSOs.
! The initial testing of this commit should be done using a Debug build so that
all the wxASSERT()s are enabled. Also, be sure and keep enabled the
USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it
off is senseless anyways. If you want stable code, go back to a prior version,
the one tagged with "stable".
* Relocate all functionality out of the wxApp derivative into more finely
targeted purposes:
a) DLL/DSO specific
b) PROJECT specific
c) EXE or process specific
d) configuration file specific data
e) configuration file manipulations functions.
All of this functionality was blended into an extremely large wxApp derivative
and that was incompatible with the desire to support multiple concurrently
loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects.
An amazing amount of organization come from simply sorting each bit of
functionality into the proper box.
* Switch to wxConfigBase from wxConfig everywhere except instantiation.
* Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD,
PGM_SINGLE_TOP,
* Remove "Return" prefix on many function names.
* Remove obvious comments from CMakeLists.txt files, and from else() and endif()s.
* Fix building boost for use in a DSO on linux.
* Remove some of the assumptions in the CMakeLists.txt files that windows had
to be the host platform when building windows binaries.
* Reduce the number of wxStrings being constructed at program load time via
static construction.
* Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that
these functions are useful even when the wxConfigBase comes from another
source, as is the case in the KICAD_MANAGER_FRAME.
* Move the setting of the KIPRJMOD environment variable into class PROJECT,
so that it can be moved into a project variable soon, and out of FP_LIB_TABLE.
* Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all
its child wxFrames and wxDialogs now have a Kiway() member function which
returns a KIWAY& that that window tree branch is in support of. This is like
wxWindows DNA in that child windows get this member with proper value at time
of construction.
* Anticipate some of the needs for milestones B) and C) and make code
adjustments now in an effort to reduce work in those milestones.
* No testing has been done for python scripting, since milestone C) has that
being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
|
|
|
#include <project.h>
|
2024-01-16 22:20:45 +00:00
|
|
|
#include <env_vars.h>
|
|
|
|
#include <3d_viewer/eda_3d_viewer_frame.h>
|
2018-07-20 17:46:56 +00:00
|
|
|
#include <panel_fp_lib_table.h>
|
2018-04-13 09:26:28 +00:00
|
|
|
#include <lib_id.h>
|
2012-10-15 22:30:01 +00:00
|
|
|
#include <fp_lib_table.h>
|
2016-11-20 23:35:08 +00:00
|
|
|
#include <lib_table_lexer.h>
|
2013-10-13 21:33:58 +00:00
|
|
|
#include <invoke_pcb_dialog.h>
|
2018-03-14 04:51:59 +00:00
|
|
|
#include <bitmaps.h>
|
2022-06-08 21:05:47 +00:00
|
|
|
#include <lib_table_grid_tricks.h>
|
2018-08-19 16:10:14 +00:00
|
|
|
#include <widgets/wx_grid.h>
|
2022-12-13 20:47:28 +00:00
|
|
|
#include <widgets/std_bitmap_button.h>
|
2013-11-20 16:35:03 +00:00
|
|
|
#include <confirm.h>
|
2017-03-27 23:21:12 +00:00
|
|
|
#include <lib_table_grid.h>
|
2018-05-23 09:49:32 +00:00
|
|
|
#include <wildcards_and_files_ext.h>
|
|
|
|
#include <pgm_base.h>
|
2018-07-19 19:15:40 +00:00
|
|
|
#include <pcb_edit_frame.h>
|
2018-05-23 09:49:32 +00:00
|
|
|
#include <env_paths.h>
|
2020-10-25 13:36:19 +00:00
|
|
|
#include <dialogs/dialog_edit_library_tables.h>
|
2024-05-01 21:52:02 +00:00
|
|
|
#include <dialogs/dialog_global_fp_lib_table_config.h>
|
2023-05-28 19:10:07 +00:00
|
|
|
#include <dialogs/dialog_plugin_options.h>
|
2018-08-06 18:33:28 +00:00
|
|
|
#include <footprint_viewer_frame.h>
|
|
|
|
#include <footprint_edit_frame.h>
|
|
|
|
#include <kiway.h>
|
2022-09-27 22:58:03 +00:00
|
|
|
#include <kiway_express.h>
|
2018-09-12 12:59:33 +00:00
|
|
|
#include <widgets/grid_readonly_text_helpers.h>
|
2018-11-24 14:40:02 +00:00
|
|
|
#include <widgets/grid_text_button_helpers.h>
|
2020-01-03 16:10:53 +00:00
|
|
|
#include <pcbnew_id.h> // For ID_PCBNEW_END_LIST
|
2020-09-01 02:00:38 +00:00
|
|
|
#include <settings/settings_manager.h>
|
2021-01-31 23:53:44 +00:00
|
|
|
#include <paths.h>
|
2021-03-20 15:35:37 +00:00
|
|
|
#include <macros.h>
|
2023-09-28 03:15:54 +00:00
|
|
|
#include <project_pcb.h>
|
2024-04-27 19:57:24 +00:00
|
|
|
#include <common.h>
|
2023-09-05 04:18:42 +00:00
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
// clang-format off
|
2020-01-21 10:39:07 +00:00
|
|
|
|
|
|
|
/**
|
2021-03-15 16:00:51 +00:00
|
|
|
* Container that describes file type info for the add a library options
|
|
|
|
*/
|
2021-02-18 11:17:20 +00:00
|
|
|
struct SUPPORTED_FILE_TYPE
|
2018-05-23 09:49:32 +00:00
|
|
|
{
|
2020-01-21 10:39:07 +00:00
|
|
|
wxString m_Description; ///< Description shown in the file picker dialog
|
|
|
|
wxString m_FileFilter; ///< Filter used for file pickers if m_IsFile is true
|
|
|
|
wxString m_FolderSearchExtension; ///< In case of folders it stands for extensions of files stored inside
|
|
|
|
bool m_IsFile; ///< Whether the library is a folder or a file
|
2023-12-19 17:39:26 +00:00
|
|
|
PCB_IO_MGR::PCB_FILE_T m_Plugin;
|
2018-05-23 09:49:32 +00:00
|
|
|
};
|
|
|
|
|
2023-09-05 04:18:42 +00:00
|
|
|
// clang-format on
|
2020-01-21 10:39:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Traverser implementation that looks to find any and all "folder" libraries by looking for files
|
|
|
|
* with a specific extension inside folders
|
2020-01-03 15:09:26 +00:00
|
|
|
*/
|
2020-01-21 10:39:07 +00:00
|
|
|
class LIBRARY_TRAVERSER : public wxDirTraverser
|
2018-05-23 09:49:32 +00:00
|
|
|
{
|
2020-01-21 10:39:07 +00:00
|
|
|
public:
|
2023-08-13 02:26:06 +00:00
|
|
|
LIBRARY_TRAVERSER( std::vector<std::string> aSearchExtensions, wxString aInitialDir ) :
|
|
|
|
m_searchExtensions( aSearchExtensions ), m_currentDir( aInitialDir )
|
2020-01-21 10:39:07 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-01-22 11:51:44 +00:00
|
|
|
virtual wxDirTraverseResult OnFile( const wxString& aFileName ) override
|
2020-01-21 10:39:07 +00:00
|
|
|
{
|
|
|
|
wxFileName file( aFileName );
|
2021-02-18 11:17:20 +00:00
|
|
|
|
2023-08-13 02:26:06 +00:00
|
|
|
for( const std::string& ext : m_searchExtensions )
|
|
|
|
{
|
|
|
|
if( file.GetExt().IsSameAs( ext, false ) )
|
|
|
|
m_foundDirs.insert( { m_currentDir, 1 } );
|
|
|
|
}
|
2020-01-21 10:39:07 +00:00
|
|
|
|
|
|
|
return wxDIR_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2020-01-22 11:51:44 +00:00
|
|
|
virtual wxDirTraverseResult OnOpenError( const wxString& aOpenErrorName ) override
|
2020-01-21 10:39:07 +00:00
|
|
|
{
|
|
|
|
m_failedDirs.insert( { aOpenErrorName, 1 } );
|
|
|
|
return wxDIR_IGNORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HasDirectoryOpenFailures()
|
|
|
|
{
|
|
|
|
return m_failedDirs.size() > 0;
|
|
|
|
}
|
|
|
|
|
2020-01-22 11:51:44 +00:00
|
|
|
virtual wxDirTraverseResult OnDir( const wxString& aDirName ) override
|
2020-01-21 10:39:07 +00:00
|
|
|
{
|
|
|
|
m_currentDir = aDirName;
|
|
|
|
return wxDIR_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GetPaths( wxArrayString& aPathArray )
|
|
|
|
{
|
2021-02-18 11:17:20 +00:00
|
|
|
for( std::pair<const wxString, int>& foundDirsPair : m_foundDirs )
|
2020-01-21 10:39:07 +00:00
|
|
|
aPathArray.Add( foundDirsPair.first );
|
|
|
|
}
|
|
|
|
|
|
|
|
void GetFailedPaths( wxArrayString& aPathArray )
|
|
|
|
{
|
2021-02-18 11:17:20 +00:00
|
|
|
for( std::pair<const wxString, int>& failedDirsPair : m_failedDirs )
|
2020-01-21 10:39:07 +00:00
|
|
|
aPathArray.Add( failedDirsPair.first );
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2023-08-13 02:26:06 +00:00
|
|
|
std::vector<std::string> m_searchExtensions;
|
2020-01-21 10:39:07 +00:00
|
|
|
wxString m_currentDir;
|
|
|
|
std::unordered_map<wxString, int> m_foundDirs;
|
|
|
|
std::unordered_map<wxString, int> m_failedDirs;
|
2020-01-03 15:09:26 +00:00
|
|
|
};
|
2020-01-21 10:39:07 +00:00
|
|
|
|
2012-10-17 01:00:25 +00:00
|
|
|
|
2012-10-17 15:12:17 +00:00
|
|
|
/**
|
2017-03-27 23:21:12 +00:00
|
|
|
* This class builds a wxGridTableBase by wrapping an #FP_LIB_TABLE object.
|
2012-10-17 15:12:17 +00:00
|
|
|
*/
|
2017-03-27 23:21:12 +00:00
|
|
|
class FP_LIB_TABLE_GRID : public LIB_TABLE_GRID, public FP_LIB_TABLE
|
2012-10-17 01:00:25 +00:00
|
|
|
{
|
2018-07-19 19:15:40 +00:00
|
|
|
friend class PANEL_FP_LIB_TABLE;
|
2013-10-23 18:56:03 +00:00
|
|
|
friend class FP_GRID_TRICKS;
|
2012-10-17 01:00:25 +00:00
|
|
|
|
2017-03-27 23:21:12 +00:00
|
|
|
protected:
|
2023-01-22 23:30:17 +00:00
|
|
|
LIB_TABLE_ROW* at( size_t aIndex ) override { return &m_rows.at( aIndex ); }
|
2012-10-17 01:00:25 +00:00
|
|
|
|
2023-01-22 23:30:17 +00:00
|
|
|
size_t size() const override { return m_rows.size(); }
|
2012-10-17 01:00:25 +00:00
|
|
|
|
2017-03-27 23:21:12 +00:00
|
|
|
LIB_TABLE_ROW* makeNewRow() override
|
2012-10-17 01:00:25 +00:00
|
|
|
{
|
2017-03-27 23:21:12 +00:00
|
|
|
return dynamic_cast< LIB_TABLE_ROW* >( new FP_LIB_TABLE_ROW );
|
2012-10-17 01:00:25 +00:00
|
|
|
}
|
|
|
|
|
2023-01-22 23:30:17 +00:00
|
|
|
LIB_TABLE_ROWS_ITER begin() override { return m_rows.begin(); }
|
2012-10-17 01:00:25 +00:00
|
|
|
|
2017-03-27 23:21:12 +00:00
|
|
|
LIB_TABLE_ROWS_ITER insert( LIB_TABLE_ROWS_ITER aIterator, LIB_TABLE_ROW* aRow ) override
|
2012-10-17 01:00:25 +00:00
|
|
|
{
|
2023-01-22 23:30:17 +00:00
|
|
|
return m_rows.insert( aIterator, aRow );
|
2012-10-17 01:00:25 +00:00
|
|
|
}
|
|
|
|
|
2023-01-22 23:30:17 +00:00
|
|
|
void push_back( LIB_TABLE_ROW* aRow ) override { m_rows.push_back( aRow ); }
|
2012-10-18 15:28:50 +00:00
|
|
|
|
2017-03-27 23:21:12 +00:00
|
|
|
LIB_TABLE_ROWS_ITER erase( LIB_TABLE_ROWS_ITER aFirst, LIB_TABLE_ROWS_ITER aLast ) override
|
2012-10-17 01:00:25 +00:00
|
|
|
{
|
2023-01-22 23:30:17 +00:00
|
|
|
return m_rows.erase( aFirst, aLast );
|
2012-10-17 01:00:25 +00:00
|
|
|
}
|
|
|
|
|
2017-03-27 23:21:12 +00:00
|
|
|
public:
|
2012-10-17 01:00:25 +00:00
|
|
|
|
2017-03-27 23:21:12 +00:00
|
|
|
FP_LIB_TABLE_GRID( const FP_LIB_TABLE& aTableToEdit )
|
2012-10-17 01:00:25 +00:00
|
|
|
{
|
2023-01-22 23:30:17 +00:00
|
|
|
m_rows = aTableToEdit.m_rows;
|
2012-10-17 01:00:25 +00:00
|
|
|
}
|
2023-12-07 11:14:10 +00:00
|
|
|
|
|
|
|
void SetValue( int aRow, int aCol, const wxString &aValue ) override
|
|
|
|
{
|
2024-01-15 06:53:08 +00:00
|
|
|
wxCHECK( aRow < (int) size(), /* void */ );
|
|
|
|
|
2023-12-07 11:14:10 +00:00
|
|
|
LIB_TABLE_GRID::SetValue( aRow, aCol, aValue );
|
|
|
|
|
|
|
|
// If setting a filepath, attempt to auto-detect the format
|
|
|
|
if( aCol == COL_URI )
|
|
|
|
{
|
2024-01-15 06:53:08 +00:00
|
|
|
LIB_TABLE_ROW* row = at( (size_t) aRow );
|
|
|
|
wxString fullURI = row->GetFullURI( true );
|
|
|
|
|
|
|
|
PCB_IO_MGR::PCB_FILE_T pluginType = PCB_IO_MGR::GuessPluginTypeFromLibPath( fullURI );
|
2023-12-07 11:14:10 +00:00
|
|
|
|
2024-01-16 16:35:10 +00:00
|
|
|
if( pluginType == PCB_IO_MGR::FILE_TYPE_NONE )
|
2023-12-19 17:39:26 +00:00
|
|
|
pluginType = PCB_IO_MGR::KICAD_SEXP;
|
2023-12-07 11:14:10 +00:00
|
|
|
|
2023-12-19 17:39:26 +00:00
|
|
|
SetValue( aRow, COL_TYPE, PCB_IO_MGR::ShowType( pluginType ) );
|
2023-12-07 11:14:10 +00:00
|
|
|
}
|
|
|
|
}
|
2012-10-17 01:00:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
|
2022-06-08 21:05:47 +00:00
|
|
|
class FP_GRID_TRICKS : public LIB_TABLE_GRID_TRICKS
|
2012-10-15 22:30:01 +00:00
|
|
|
{
|
2013-10-23 18:56:03 +00:00
|
|
|
public:
|
2019-03-04 11:02:12 +00:00
|
|
|
FP_GRID_TRICKS( DIALOG_EDIT_LIBRARY_TABLES* aParent, WX_GRID* aGrid ) :
|
2024-06-12 15:58:20 +00:00
|
|
|
LIB_TABLE_GRID_TRICKS( aGrid ),
|
|
|
|
m_dialog( aParent )
|
2018-07-20 17:46:56 +00:00
|
|
|
{ }
|
2012-10-18 15:28:50 +00:00
|
|
|
|
2013-10-23 18:56:03 +00:00
|
|
|
protected:
|
2018-07-20 17:46:56 +00:00
|
|
|
DIALOG_EDIT_LIBRARY_TABLES* m_dialog;
|
|
|
|
|
2023-05-28 19:10:07 +00:00
|
|
|
void optionsEditor( int aRow ) override
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
|
|
|
FP_LIB_TABLE_GRID* tbl = (FP_LIB_TABLE_GRID*) m_grid->GetTable();
|
2018-07-20 17:46:56 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
if( tbl->GetNumberRows() > aRow )
|
|
|
|
{
|
|
|
|
LIB_TABLE_ROW* row = tbl->at( (size_t) aRow );
|
|
|
|
const wxString& options = row->GetOptions();
|
|
|
|
wxString result = options;
|
2023-05-28 19:10:07 +00:00
|
|
|
STRING_UTF8_MAP choices;
|
|
|
|
|
2023-12-19 17:39:26 +00:00
|
|
|
PCB_IO_MGR::PCB_FILE_T pi_type = PCB_IO_MGR::EnumFromStr( row->GetType() );
|
2023-12-27 20:39:29 +00:00
|
|
|
IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::PluginFind( pi_type ) );
|
2023-12-27 00:25:41 +00:00
|
|
|
pi->GetLibraryOptions( &choices );
|
2018-03-14 04:51:59 +00:00
|
|
|
|
2023-05-28 19:10:07 +00:00
|
|
|
DIALOG_PLUGIN_OPTIONS dlg( m_dialog, row->GetNickName(), choices, options, &result );
|
|
|
|
dlg.ShowModal();
|
2018-03-14 04:51:59 +00:00
|
|
|
|
|
|
|
if( options != result )
|
|
|
|
{
|
|
|
|
row->SetOptions( result );
|
|
|
|
m_grid->Refresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-23 18:56:03 +00:00
|
|
|
/// handle specialized clipboard text, with leading "(fp_lib_table", OR
|
|
|
|
/// spreadsheet formatted text.
|
2018-03-14 04:51:59 +00:00
|
|
|
void paste_text( const wxString& cb_text ) override
|
2013-10-23 18:56:03 +00:00
|
|
|
{
|
2018-03-14 04:51:59 +00:00
|
|
|
FP_LIB_TABLE_GRID* tbl = (FP_LIB_TABLE_GRID*) m_grid->GetTable();
|
|
|
|
size_t ndx = cb_text.find( "(fp_lib_table" );
|
2012-10-18 15:28:50 +00:00
|
|
|
|
2013-10-23 18:56:03 +00:00
|
|
|
if( ndx != std::string::npos )
|
2012-12-09 20:51:42 +00:00
|
|
|
{
|
2016-10-28 13:11:23 +00:00
|
|
|
// paste the FP_LIB_TABLE_ROWs of s-expression (fp_lib_table), starting
|
2013-10-23 18:56:03 +00:00
|
|
|
// at column 0 regardless of current cursor column.
|
2012-10-18 15:28:50 +00:00
|
|
|
|
2022-02-04 22:44:59 +00:00
|
|
|
STRING_LINE_READER slr( TO_UTF8( cb_text ), wxT( "Clipboard" ) );
|
2016-11-20 23:35:08 +00:00
|
|
|
LIB_TABLE_LEXER lexer( &slr );
|
2013-10-23 18:56:03 +00:00
|
|
|
FP_LIB_TABLE tmp_tbl;
|
|
|
|
bool parsed = true;
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2013-10-23 18:56:03 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
tmp_tbl.Parse( &lexer );
|
|
|
|
}
|
|
|
|
catch( PARSE_ERROR& pe )
|
|
|
|
{
|
2018-07-20 17:46:56 +00:00
|
|
|
DisplayError( m_dialog, pe.What() );
|
2013-10-23 18:56:03 +00:00
|
|
|
parsed = false;
|
|
|
|
}
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2013-10-23 18:56:03 +00:00
|
|
|
if( parsed )
|
|
|
|
{
|
2018-03-14 04:51:59 +00:00
|
|
|
// make sure the table is big enough...
|
2019-02-02 21:50:36 +00:00
|
|
|
if( tmp_tbl.GetCount() > (unsigned) tbl->GetNumberRows() )
|
2018-03-14 04:51:59 +00:00
|
|
|
tbl->AppendRows( tmp_tbl.GetCount() - tbl->GetNumberRows() );
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2019-02-02 21:50:36 +00:00
|
|
|
for( unsigned i = 0; i < tmp_tbl.GetCount(); ++i )
|
2023-01-22 23:30:17 +00:00
|
|
|
tbl->m_rows.replace( i, tmp_tbl.At( i ).clone() );
|
2013-10-23 18:56:03 +00:00
|
|
|
}
|
2016-11-19 22:15:34 +00:00
|
|
|
|
2013-10-23 18:56:03 +00:00
|
|
|
m_grid->AutoSizeColumns( false );
|
|
|
|
}
|
|
|
|
else
|
2012-12-09 20:51:42 +00:00
|
|
|
{
|
2013-10-23 18:56:03 +00:00
|
|
|
// paste spreadsheet formatted text.
|
|
|
|
GRID_TRICKS::paste_text( cb_text );
|
2018-03-14 04:51:59 +00:00
|
|
|
|
|
|
|
m_grid->AutoSizeColumns( false );
|
2012-12-09 20:51:42 +00:00
|
|
|
}
|
2013-10-23 18:56:03 +00:00
|
|
|
}
|
2024-06-12 15:58:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
bool toggleCell( int aRow, int aCol, bool aPreserveSelection ) override
|
|
|
|
{
|
|
|
|
if( aCol == COL_VISIBLE )
|
|
|
|
{
|
|
|
|
m_dialog->ShowInfoBarError( _( "Hidden footprint libraries are not yet supported." ) );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return LIB_TABLE_GRID_TRICKS::toggleCell( aRow, aCol, aPreserveSelection );
|
|
|
|
}
|
2013-10-23 18:56:03 +00:00
|
|
|
};
|
2012-12-09 20:51:42 +00:00
|
|
|
|
|
|
|
|
2024-05-01 21:52:02 +00:00
|
|
|
void PANEL_FP_LIB_TABLE::setupGrid( WX_GRID* aGrid )
|
|
|
|
{
|
|
|
|
PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
|
|
|
|
|
2024-05-10 16:54:32 +00:00
|
|
|
auto autoSizeCol = [&]( WX_GRID* aLocGrid, int aCol )
|
2024-05-01 21:52:02 +00:00
|
|
|
{
|
2024-05-10 16:54:32 +00:00
|
|
|
int prevWidth = aLocGrid->GetColSize( aCol );
|
2024-05-01 21:52:02 +00:00
|
|
|
|
2024-05-10 16:54:32 +00:00
|
|
|
aLocGrid->AutoSizeColumn( aCol, false );
|
|
|
|
aLocGrid->SetColSize( aCol, std::max( prevWidth, aLocGrid->GetColSize( aCol ) ) );
|
2024-05-01 21:52:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Give a bit more room for wxChoice editors
|
|
|
|
for( int ii = 0; ii < aGrid->GetNumberRows(); ++ii )
|
|
|
|
aGrid->SetRowSize( ii, aGrid->GetDefaultRowSize() + 4 );
|
|
|
|
|
|
|
|
// add Cut, Copy, and Paste to wxGrids
|
|
|
|
aGrid->PushEventHandler( new FP_GRID_TRICKS( m_parent, aGrid ) );
|
|
|
|
|
|
|
|
aGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
|
|
|
|
|
|
|
|
wxGridCellAttr* attr;
|
|
|
|
|
|
|
|
attr = new wxGridCellAttr;
|
|
|
|
attr->SetEditor( new GRID_CELL_PATH_EDITOR(
|
|
|
|
m_parent, aGrid, &cfg->m_lastFootprintLibDir, true, m_projectBasePath,
|
|
|
|
[this]( WX_GRID* grid, int row ) -> wxString
|
|
|
|
{
|
|
|
|
auto* libTable = static_cast<FP_LIB_TABLE_GRID*>( grid->GetTable() );
|
|
|
|
auto* tableRow = static_cast<FP_LIB_TABLE_ROW*>( libTable->at( row ) );
|
|
|
|
PCB_IO_MGR::PCB_FILE_T fileType = tableRow->GetFileType();
|
|
|
|
const IO_BASE::IO_FILE_DESC& pluginDesc = m_supportedFpFiles.at( fileType );
|
|
|
|
|
|
|
|
if( pluginDesc.m_IsFile )
|
|
|
|
return pluginDesc.FileFilter();
|
|
|
|
else
|
|
|
|
return wxEmptyString;
|
|
|
|
} ) );
|
|
|
|
aGrid->SetColAttr( COL_URI, attr );
|
|
|
|
|
|
|
|
attr = new wxGridCellAttr;
|
|
|
|
attr->SetEditor( new wxGridCellChoiceEditor( m_pluginChoices ) );
|
|
|
|
aGrid->SetColAttr( COL_TYPE, attr );
|
|
|
|
|
|
|
|
attr = new wxGridCellAttr;
|
|
|
|
attr->SetRenderer( new wxGridCellBoolRenderer() );
|
|
|
|
attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
|
|
|
|
aGrid->SetColAttr( COL_ENABLED, attr );
|
|
|
|
|
2024-06-12 15:58:20 +00:00
|
|
|
attr = new wxGridCellAttr;
|
|
|
|
attr->SetRenderer( new wxGridCellBoolRenderer() );
|
|
|
|
attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
|
|
|
|
aGrid->SetColAttr( COL_VISIBLE, attr );
|
2024-05-01 21:52:02 +00:00
|
|
|
// No visibility control for footprint libraries yet; this feature is primarily
|
|
|
|
// useful for database libraries and it's only implemented for schematic symbols
|
|
|
|
// at the moment.
|
|
|
|
aGrid->HideCol( COL_VISIBLE );
|
|
|
|
|
|
|
|
// all but COL_OPTIONS, which is edited with Option Editor anyways.
|
|
|
|
autoSizeCol( aGrid, COL_NICKNAME );
|
|
|
|
autoSizeCol( aGrid, COL_TYPE );
|
|
|
|
autoSizeCol( aGrid, COL_URI );
|
|
|
|
autoSizeCol( aGrid, COL_DESCR );
|
|
|
|
|
|
|
|
// 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 );
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2023-08-28 20:44:10 +00:00
|
|
|
PANEL_FP_LIB_TABLE::PANEL_FP_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent, PROJECT* aProject,
|
|
|
|
FP_LIB_TABLE* aGlobalTable, const wxString& aGlobalTblPath,
|
|
|
|
FP_LIB_TABLE* aProjectTable, const wxString& aProjectTblPath,
|
2018-08-06 18:33:28 +00:00
|
|
|
const wxString& aProjectBasePath ) :
|
2018-07-19 19:15:40 +00:00
|
|
|
PANEL_FP_LIB_TABLE_BASE( aParent ),
|
2023-08-28 20:44:10 +00:00
|
|
|
m_globalTable( aGlobalTable ),
|
|
|
|
m_projectTable( aProjectTable ),
|
2018-07-19 19:15:40 +00:00
|
|
|
m_project( aProject ),
|
2018-08-06 18:33:28 +00:00
|
|
|
m_projectBasePath( aProjectBasePath ),
|
2018-07-19 19:15:40 +00:00
|
|
|
m_parent( aParent )
|
2012-10-15 22:30:01 +00:00
|
|
|
{
|
2023-08-28 20:44:10 +00:00
|
|
|
m_global_grid->SetTable( new FP_LIB_TABLE_GRID( *aGlobalTable ), true );
|
2013-10-30 15:33:51 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// add Cut, Copy, and Paste to wxGrids
|
2018-09-12 12:59:33 +00:00
|
|
|
m_path_subs_grid->PushEventHandler( new GRID_TRICKS( m_path_subs_grid ) );
|
2013-10-30 15:33:51 +00:00
|
|
|
|
2023-08-13 02:26:06 +00:00
|
|
|
populatePluginList();
|
|
|
|
|
|
|
|
for( auto& [fileType, desc] : m_supportedFpFiles )
|
2024-05-01 21:52:02 +00:00
|
|
|
m_pluginChoices.Add( PCB_IO_MGR::ShowType( fileType ) );
|
2023-08-13 02:26:06 +00:00
|
|
|
|
2013-10-30 15:33:51 +00:00
|
|
|
|
2020-09-01 02:00:38 +00:00
|
|
|
PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
|
|
|
|
|
2021-12-07 00:14:36 +00:00
|
|
|
if( cfg->m_lastFootprintLibDir.IsEmpty() )
|
|
|
|
cfg->m_lastFootprintLibDir = PATHS::GetDefaultUserFootprintsPath();
|
|
|
|
|
2021-01-31 23:53:44 +00:00
|
|
|
m_lastProjectLibDir = m_projectBasePath;
|
2013-10-30 15:33:51 +00:00
|
|
|
|
2020-08-07 19:22:15 +00:00
|
|
|
setupGrid( m_global_grid );
|
|
|
|
|
|
|
|
populateEnvironReadOnlyTable();
|
2013-10-30 15:33:51 +00:00
|
|
|
|
2023-08-28 20:44:10 +00:00
|
|
|
if( aProjectTable )
|
2020-08-07 19:22:15 +00:00
|
|
|
{
|
2023-08-28 20:44:10 +00:00
|
|
|
m_project_grid->SetTable( new FP_LIB_TABLE_GRID( *aProjectTable ), true );
|
2020-08-07 19:22:15 +00:00
|
|
|
setupGrid( m_project_grid );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_pageNdx = 0;
|
2020-11-01 13:30:27 +00:00
|
|
|
m_notebook->DeletePage( 1 );
|
2020-08-07 19:22:15 +00:00
|
|
|
m_project_grid = nullptr;
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2013-11-12 20:49:17 +00:00
|
|
|
|
2019-05-14 11:57:27 +00:00
|
|
|
m_path_subs_grid->SetColLabelValue( 0, _( "Name" ) );
|
|
|
|
m_path_subs_grid->SetColLabelValue( 1, _( "Value" ) );
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// select the last selected page
|
2020-11-01 13:30:27 +00:00
|
|
|
m_notebook->SetSelection( m_pageNdx );
|
2018-07-23 22:06:26 +00:00
|
|
|
m_cur_grid = ( m_pageNdx == 0 ) ? m_global_grid : m_project_grid;
|
|
|
|
|
|
|
|
// for ALT+A handling, we want the initial focus to be on the first selected grid.
|
|
|
|
m_parent->SetInitialFocus( m_cur_grid );
|
2013-10-30 15:33:51 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// Configure button logos
|
2023-10-21 18:56:19 +00:00
|
|
|
m_append_button->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
|
|
|
|
m_delete_button->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
|
|
|
|
m_move_up_button->SetBitmap( KiBitmapBundle( BITMAPS::small_up ) );
|
|
|
|
m_move_down_button->SetBitmap( KiBitmapBundle( BITMAPS::small_down ) );
|
|
|
|
m_browseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
|
2014-09-22 07:51:06 +00:00
|
|
|
|
2021-02-18 11:38:05 +00:00
|
|
|
// For aesthetic reasons, we must set the size of m_browseButton to match the other bitmaps
|
|
|
|
// manually (for instance m_append_button)
|
|
|
|
Layout(); // Needed at least on MSW to compute the actual buttons sizes, after initializing
|
|
|
|
// their bitmaps
|
2020-06-03 17:46:50 +00:00
|
|
|
wxSize buttonSize = m_append_button->GetSize();
|
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
m_browseButton->SetWidthPadding( 4 );
|
2020-06-03 17:46:50 +00:00
|
|
|
m_browseButton->SetMinSize( buttonSize );
|
2020-01-03 15:09:26 +00:00
|
|
|
|
|
|
|
// Populate the browse library options
|
|
|
|
wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu();
|
2020-06-03 17:46:50 +00:00
|
|
|
|
2023-08-13 02:26:06 +00:00
|
|
|
auto joinExts = []( const std::vector<std::string>& aExts )
|
|
|
|
{
|
|
|
|
wxString joined;
|
|
|
|
for( const std::string& ext : aExts )
|
|
|
|
{
|
|
|
|
if( !joined.empty() )
|
|
|
|
joined << wxS( ", " );
|
|
|
|
|
|
|
|
joined << wxS( "*." ) << ext;
|
|
|
|
}
|
|
|
|
|
|
|
|
return joined;
|
|
|
|
};
|
|
|
|
|
|
|
|
for( auto& [type, desc] : m_supportedFpFiles )
|
2020-01-03 15:09:26 +00:00
|
|
|
{
|
2023-12-19 17:39:26 +00:00
|
|
|
wxString entryStr = PCB_IO_MGR::ShowType( type );
|
2023-08-13 02:26:06 +00:00
|
|
|
|
|
|
|
if( desc.m_IsFile && !desc.m_FileExtensions.empty() )
|
|
|
|
{
|
2023-10-27 22:24:24 +00:00
|
|
|
entryStr << wxString::Format( wxS( " (%s)" ),
|
|
|
|
joinExts( desc.m_FileExtensions ) );
|
2023-08-13 02:26:06 +00:00
|
|
|
}
|
|
|
|
else if( !desc.m_IsFile && !desc.m_ExtensionsInDir.empty() )
|
|
|
|
{
|
2023-10-27 22:24:24 +00:00
|
|
|
wxString midPart = wxString::Format( _( "folder with %s files" ),
|
|
|
|
joinExts( desc.m_ExtensionsInDir ) );
|
2023-08-13 02:26:06 +00:00
|
|
|
|
|
|
|
entryStr << wxString::Format( wxS( " (%s)" ), midPart );
|
|
|
|
}
|
|
|
|
|
|
|
|
browseMenu->Append( type, entryStr );
|
2020-01-03 15:09:26 +00:00
|
|
|
|
|
|
|
browseMenu->Bind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_FP_LIB_TABLE::browseLibrariesHandler,
|
2023-08-13 02:26:06 +00:00
|
|
|
this, type );
|
2020-01-03 15:09:26 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 08:19:31 +00:00
|
|
|
Layout();
|
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
// This is the button only press for the browse button instead of the menu
|
|
|
|
m_browseButton->Bind( wxEVT_BUTTON, &PANEL_FP_LIB_TABLE::browseLibrariesHandler, this );
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-19 19:15:40 +00:00
|
|
|
PANEL_FP_LIB_TABLE::~PANEL_FP_LIB_TABLE()
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
2023-10-02 11:22:00 +00:00
|
|
|
wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu();
|
|
|
|
for( auto& [type, desc] : m_supportedFpFiles )
|
|
|
|
{
|
|
|
|
browseMenu->Unbind( wxEVT_COMMAND_MENU_SELECTED,
|
|
|
|
&PANEL_FP_LIB_TABLE::browseLibrariesHandler, this, type );
|
|
|
|
}
|
|
|
|
m_browseButton->Unbind( wxEVT_BUTTON, &PANEL_FP_LIB_TABLE::browseLibrariesHandler, this );
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// Delete the GRID_TRICKS.
|
|
|
|
// Any additional event handlers should be popped before the window is deleted.
|
|
|
|
m_global_grid->PopEventHandler( true );
|
2020-08-07 19:22:15 +00:00
|
|
|
|
|
|
|
if( m_project_grid )
|
|
|
|
m_project_grid->PopEventHandler( true );
|
|
|
|
|
2018-09-12 12:59:33 +00:00
|
|
|
m_path_subs_grid->PopEventHandler( true );
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2013-10-30 15:33:51 +00:00
|
|
|
|
|
|
|
|
2023-08-13 02:26:06 +00:00
|
|
|
void PANEL_FP_LIB_TABLE::populatePluginList()
|
|
|
|
{
|
2023-12-19 17:39:26 +00:00
|
|
|
for( const auto& plugin : PCB_IO_MGR::PLUGIN_REGISTRY::Instance()->AllPlugins() )
|
2023-08-13 02:26:06 +00:00
|
|
|
{
|
2023-12-27 20:39:29 +00:00
|
|
|
IO_RELEASER<PCB_IO> pi( plugin.m_createFunc() );
|
2023-08-13 02:26:06 +00:00
|
|
|
|
|
|
|
if( !pi )
|
|
|
|
continue;
|
|
|
|
|
2023-12-27 16:34:59 +00:00
|
|
|
if( const IO_BASE::IO_FILE_DESC& desc = pi->GetLibraryDesc() )
|
2023-08-13 02:26:06 +00:00
|
|
|
m_supportedFpFiles.emplace( plugin.m_type, desc );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-19 19:15:40 +00:00
|
|
|
bool PANEL_FP_LIB_TABLE::verifyTables()
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
2021-05-08 12:58:54 +00:00
|
|
|
wxString msg;
|
|
|
|
|
2018-09-12 12:59:33 +00:00
|
|
|
for( FP_LIB_TABLE_GRID* model : { global_model(), project_model() } )
|
2012-12-09 20:51:42 +00:00
|
|
|
{
|
2020-08-07 19:22:15 +00:00
|
|
|
if( !model )
|
|
|
|
continue;
|
|
|
|
|
2018-09-12 12:59:33 +00:00
|
|
|
for( int r = 0; r < model->GetNumberRows(); )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
2018-09-12 12:59:33 +00:00
|
|
|
wxString nick = model->GetValue( r, COL_NICKNAME ).Trim( false ).Trim();
|
|
|
|
wxString uri = model->GetValue( r, COL_URI ).Trim( false ).Trim();
|
2018-03-14 04:51:59 +00:00
|
|
|
unsigned illegalCh = 0;
|
|
|
|
|
|
|
|
if( !nick || !uri )
|
|
|
|
{
|
2021-05-08 12:58:54 +00:00
|
|
|
if( !nick && !uri )
|
|
|
|
msg = _( "A library table row nickname and path cells are empty." );
|
|
|
|
else if( !nick )
|
|
|
|
msg = _( "A library table row nickname cell is empty." );
|
|
|
|
else
|
|
|
|
msg = _( "A library table row path cell is empty." );
|
|
|
|
|
2024-02-02 17:47:34 +00:00
|
|
|
wxWindow* topLevelParent = wxGetTopLevelParent( this );
|
|
|
|
|
|
|
|
wxMessageDialog badCellDlg( topLevelParent, msg, _( "Invalid Row Definition" ),
|
2021-05-08 12:58:54 +00:00
|
|
|
wxYES_NO | wxCENTER | wxICON_QUESTION | wxYES_DEFAULT );
|
|
|
|
badCellDlg.SetExtendedMessage( _( "Empty cells will result in all rows that are "
|
|
|
|
"invalid to be removed from the table." ) );
|
2022-02-04 22:44:59 +00:00
|
|
|
badCellDlg.SetYesNoLabels( wxMessageDialog::ButtonLabel( _( "Remove Invalid Cells" ) ),
|
|
|
|
wxMessageDialog::ButtonLabel( _( "Cancel Table Update" ) ) );
|
2021-05-08 12:58:54 +00:00
|
|
|
|
|
|
|
if( badCellDlg.ShowModal() == wxID_NO )
|
|
|
|
return false;
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// Delete the "empty" row, where empty means missing nick or uri.
|
|
|
|
// 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.
|
2018-09-12 12:59:33 +00:00
|
|
|
model->DeleteRows( r, 1 );
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2020-12-17 23:32:23 +00:00
|
|
|
else if( ( illegalCh = LIB_ID::FindIllegalLibraryNameChar( nick ) ) )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
2021-05-08 12:58:54 +00:00
|
|
|
msg = wxString::Format( _( "Illegal character '%c' in nickname '%s'." ),
|
|
|
|
illegalCh,
|
|
|
|
nick );
|
2018-03-14 04:51:59 +00:00
|
|
|
|
|
|
|
// show the tabbed panel holding the grid we have flunked:
|
2018-09-12 12:59:33 +00:00
|
|
|
if( model != cur_model() )
|
2020-11-01 13:30:27 +00:00
|
|
|
m_notebook->SetSelection( model == global_model() ? 0 : 1 );
|
2018-03-14 04:51:59 +00:00
|
|
|
|
|
|
|
m_cur_grid->MakeCellVisible( r, 0 );
|
|
|
|
m_cur_grid->SetGridCursor( r, 1 );
|
|
|
|
|
2024-02-02 17:47:34 +00:00
|
|
|
wxWindow* topLevelParent = wxGetTopLevelParent( this );
|
|
|
|
|
|
|
|
wxMessageDialog errdlg( topLevelParent, msg, _( "Library Nickname Error" ) );
|
2018-03-14 04:51:59 +00:00
|
|
|
errdlg.ShowModal();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// set the trimmed values back into the table so they get saved to disk.
|
2018-09-12 12:59:33 +00:00
|
|
|
model->SetValue( r, COL_NICKNAME, nick );
|
|
|
|
model->SetValue( r, COL_URI, uri );
|
2023-01-28 14:23:00 +00:00
|
|
|
|
|
|
|
// Make sure to not save a hidden flag
|
|
|
|
model->SetValue( r, COL_VISIBLE, wxS( "1" ) );
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
++r; // this row was OK.
|
|
|
|
}
|
|
|
|
}
|
2013-10-05 10:34:55 +00:00
|
|
|
}
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// check for duplicate nickNames, separately in each table.
|
2018-09-12 12:59:33 +00:00
|
|
|
for( FP_LIB_TABLE_GRID* model : { global_model(), project_model() } )
|
2013-09-24 21:23:13 +00:00
|
|
|
{
|
2020-08-07 19:22:15 +00:00
|
|
|
if( !model )
|
|
|
|
continue;
|
|
|
|
|
2018-09-12 12:59:33 +00:00
|
|
|
for( int r1 = 0; r1 < model->GetNumberRows() - 1; ++r1 )
|
2012-12-09 20:51:42 +00:00
|
|
|
{
|
2018-09-12 12:59:33 +00:00
|
|
|
wxString nick1 = model->GetValue( r1, COL_NICKNAME );
|
2013-09-24 21:23:13 +00:00
|
|
|
|
2018-09-12 12:59:33 +00:00
|
|
|
for( int r2 = r1 + 1; r2 < model->GetNumberRows(); ++r2 )
|
2012-12-09 20:51:42 +00:00
|
|
|
{
|
2018-09-12 12:59:33 +00:00
|
|
|
wxString nick2 = model->GetValue( r2, COL_NICKNAME );
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
if( nick1 == nick2 )
|
2013-09-24 21:23:13 +00:00
|
|
|
{
|
2021-05-08 12:58:54 +00:00
|
|
|
msg = wxString::Format( _( "Multiple libraries cannot share the same "
|
|
|
|
"nickname ('%s')." ),
|
|
|
|
nick1 );
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2013-09-24 21:23:13 +00:00
|
|
|
// show the tabbed panel holding the grid we have flunked:
|
2018-09-12 12:59:33 +00:00
|
|
|
if( model != cur_model() )
|
2020-11-01 13:30:27 +00:00
|
|
|
m_notebook->SetSelection( model == global_model() ? 0 : 1 );
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// go to the lower of the two rows, it is technically the duplicate:
|
|
|
|
m_cur_grid->MakeCellVisible( r2, 0 );
|
|
|
|
m_cur_grid->SetGridCursor( r2, 1 );
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2024-02-02 17:47:34 +00:00
|
|
|
wxWindow* topLevelParent = wxGetTopLevelParent( this );
|
|
|
|
|
|
|
|
wxMessageDialog errdlg( topLevelParent, msg, _( "Library Nickname Error" ) );
|
2013-09-24 21:23:13 +00:00
|
|
|
errdlg.ShowModal();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
return true;
|
|
|
|
}
|
2012-12-09 20:51:42 +00:00
|
|
|
|
|
|
|
|
2020-11-14 20:12:49 +00:00
|
|
|
void PANEL_FP_LIB_TABLE::OnUpdateUI( wxUpdateUIEvent& event )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
|
|
|
}
|
2012-10-17 15:12:17 +00:00
|
|
|
|
2012-10-16 06:56:57 +00:00
|
|
|
|
2018-07-19 19:15:40 +00:00
|
|
|
void PANEL_FP_LIB_TABLE::appendRowHandler( wxCommandEvent& event )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
2018-08-19 16:10:14 +00:00
|
|
|
if( !m_cur_grid->CommitPendingChanges() )
|
|
|
|
return;
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
if( m_cur_grid->AppendRows( 1 ) )
|
2013-10-04 22:23:53 +00:00
|
|
|
{
|
2018-03-14 04:51:59 +00:00
|
|
|
int last_row = m_cur_grid->GetNumberRows() - 1;
|
2013-10-04 22:23:53 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// wx documentation is wrong, SetGridCursor does not make visible.
|
2023-04-02 14:09:29 +00:00
|
|
|
m_cur_grid->MakeCellVisible( last_row, COL_ENABLED );
|
|
|
|
m_cur_grid->SetGridCursor( last_row, COL_NICKNAME );
|
2018-03-14 04:51:59 +00:00
|
|
|
m_cur_grid->EnableCellEditControl( true );
|
|
|
|
m_cur_grid->ShowCellEditControl();
|
2012-10-16 06:56:57 +00:00
|
|
|
}
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2012-10-16 06:56:57 +00:00
|
|
|
|
2013-09-24 21:23:13 +00:00
|
|
|
|
2018-07-19 19:15:40 +00:00
|
|
|
void PANEL_FP_LIB_TABLE::deleteRowHandler( wxCommandEvent& event )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
2018-08-19 16:10:14 +00:00
|
|
|
if( !m_cur_grid->CommitPendingChanges() )
|
|
|
|
return;
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
int curRow = m_cur_grid->GetGridCursorRow();
|
|
|
|
int curCol = m_cur_grid->GetGridCursorCol();
|
|
|
|
|
|
|
|
// In a wxGrid, collect rows that have a selected cell, or are selected
|
2019-08-20 17:22:30 +00:00
|
|
|
// It is not so easy: it depends on the way the selection was made.
|
|
|
|
// Here, we collect rows selected by clicking on a row label, and rows that contain any
|
|
|
|
// previously-selected cells.
|
2018-03-14 04:51:59 +00:00
|
|
|
// If no candidate, just delete the row with the grid cursor.
|
|
|
|
wxArrayInt selectedRows = m_cur_grid->GetSelectedRows();
|
|
|
|
wxGridCellCoordsArray cells = m_cur_grid->GetSelectedCells();
|
|
|
|
wxGridCellCoordsArray blockTopLeft = m_cur_grid->GetSelectionBlockTopLeft();
|
|
|
|
wxGridCellCoordsArray blockBotRight = m_cur_grid->GetSelectionBlockBottomRight();
|
|
|
|
|
|
|
|
// Add all row having cell selected to list:
|
|
|
|
for( unsigned ii = 0; ii < cells.GetCount(); ii++ )
|
|
|
|
selectedRows.Add( cells[ii].GetRow() );
|
|
|
|
|
|
|
|
// Handle block selection
|
|
|
|
if( !blockTopLeft.IsEmpty() && !blockBotRight.IsEmpty() )
|
|
|
|
{
|
|
|
|
for( int i = blockTopLeft[0].GetRow(); i <= blockBotRight[0].GetRow(); ++i )
|
|
|
|
selectedRows.Add( i );
|
2012-10-16 06:56:57 +00:00
|
|
|
}
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// Use the row having the grid cursor only if we have no candidate:
|
|
|
|
if( selectedRows.size() == 0 && m_cur_grid->GetGridCursorRow() >= 0 )
|
|
|
|
selectedRows.Add( m_cur_grid->GetGridCursorRow() );
|
2017-08-15 09:56:05 +00:00
|
|
|
|
2018-08-19 16:10:42 +00:00
|
|
|
if( selectedRows.size() == 0 )
|
|
|
|
{
|
|
|
|
wxBell();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
std::sort( selectedRows.begin(), selectedRows.end() );
|
2014-12-21 14:13:14 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// Remove selected rows (note: a row can be stored more than once in list)
|
|
|
|
int last_row = -1;
|
2020-06-03 17:46:50 +00:00
|
|
|
|
2023-06-09 06:01:38 +00:00
|
|
|
// Needed to avoid a wxWidgets alert if the row to delete is the last row
|
|
|
|
// at least on wxMSW 3.2
|
|
|
|
m_cur_grid->ClearSelection();
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
for( int ii = selectedRows.GetCount()-1; ii >= 0; ii-- )
|
|
|
|
{
|
|
|
|
int row = selectedRows[ii];
|
2014-12-21 14:13:14 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
if( row != last_row )
|
2014-12-21 14:13:14 +00:00
|
|
|
{
|
2018-03-14 04:51:59 +00:00
|
|
|
last_row = row;
|
|
|
|
m_cur_grid->DeleteRows( row, 1 );
|
2014-12-21 14:13:14 +00:00
|
|
|
}
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2014-12-21 14:13:14 +00:00
|
|
|
|
2020-06-14 15:39:19 +00:00
|
|
|
if( m_cur_grid->GetNumberRows() > 0 && curRow >= 0 )
|
|
|
|
m_cur_grid->SetGridCursor( std::min( curRow, m_cur_grid->GetNumberRows() - 1 ), curCol );
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2014-12-21 14:13:14 +00:00
|
|
|
|
2012-10-16 06:56:57 +00:00
|
|
|
|
2018-07-19 19:15:40 +00:00
|
|
|
void PANEL_FP_LIB_TABLE::moveUpHandler( wxCommandEvent& event )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
2018-08-19 16:10:14 +00:00
|
|
|
if( !m_cur_grid->CommitPendingChanges() )
|
|
|
|
return;
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
FP_LIB_TABLE_GRID* tbl = cur_model();
|
|
|
|
int curRow = m_cur_grid->GetGridCursorRow();
|
2016-11-19 22:15:34 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// @todo: add multiple selection moves.
|
|
|
|
if( curRow >= 1 )
|
|
|
|
{
|
|
|
|
boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me =
|
2023-01-22 23:30:17 +00:00
|
|
|
tbl->m_rows.release( tbl->m_rows.begin() + curRow );
|
2016-11-19 22:15:34 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
--curRow;
|
2023-01-22 23:30:17 +00:00
|
|
|
tbl->m_rows.insert( tbl->m_rows.begin() + curRow, move_me.release() );
|
2016-11-19 22:15:34 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
if( tbl->GetView() )
|
2012-10-18 15:28:50 +00:00
|
|
|
{
|
2018-03-14 04:51:59 +00:00
|
|
|
// Update the wxGrid
|
|
|
|
wxGridTableMessage msg( tbl, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, curRow, 0 );
|
|
|
|
tbl->GetView()->ProcessTableMessage( msg );
|
|
|
|
}
|
2012-10-18 15:28:50 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
m_cur_grid->MakeCellVisible( curRow, m_cur_grid->GetGridCursorCol() );
|
|
|
|
m_cur_grid->SetGridCursor( curRow, m_cur_grid->GetGridCursorCol() );
|
|
|
|
}
|
|
|
|
}
|
2012-10-18 15:28:50 +00:00
|
|
|
|
|
|
|
|
2018-07-19 19:15:40 +00:00
|
|
|
void PANEL_FP_LIB_TABLE::moveDownHandler( wxCommandEvent& event )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
2018-08-19 16:10:14 +00:00
|
|
|
if( !m_cur_grid->CommitPendingChanges() )
|
|
|
|
return;
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
FP_LIB_TABLE_GRID* tbl = cur_model();
|
|
|
|
int curRow = m_cur_grid->GetGridCursorRow();
|
2012-10-18 15:28:50 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// @todo: add multiple selection moves.
|
2023-01-22 23:30:17 +00:00
|
|
|
if( unsigned( curRow + 1 ) < tbl->m_rows.size() )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
|
|
|
boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me =
|
2023-01-22 23:30:17 +00:00
|
|
|
tbl->m_rows.release( tbl->m_rows.begin() + curRow );
|
2012-10-18 15:28:50 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
++curRow;
|
2023-01-22 23:30:17 +00:00
|
|
|
tbl->m_rows.insert( tbl->m_rows.begin() + curRow, move_me.release() );
|
2012-10-31 14:41:47 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
if( tbl->GetView() )
|
|
|
|
{
|
|
|
|
// Update the wxGrid
|
|
|
|
wxGridTableMessage msg( tbl, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, curRow - 1, 0 );
|
|
|
|
tbl->GetView()->ProcessTableMessage( msg );
|
2012-10-18 15:28:50 +00:00
|
|
|
}
|
2016-11-19 22:15:34 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
m_cur_grid->MakeCellVisible( curRow, m_cur_grid->GetGridCursorCol() );
|
|
|
|
m_cur_grid->SetGridCursor( curRow, m_cur_grid->GetGridCursorCol() );
|
|
|
|
}
|
|
|
|
}
|
2016-11-19 22:15:34 +00:00
|
|
|
|
2012-10-31 14:41:47 +00:00
|
|
|
|
2023-08-28 20:44:10 +00:00
|
|
|
// @todo refactor this function into single location shared with PANEL_SYM_LIB_TABLE
|
|
|
|
void PANEL_FP_LIB_TABLE::onMigrateLibraries( wxCommandEvent& event )
|
|
|
|
{
|
|
|
|
if( !m_cur_grid->CommitPendingChanges() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
wxArrayInt selectedRows = m_cur_grid->GetSelectedRows();
|
|
|
|
|
|
|
|
if( selectedRows.empty() && m_cur_grid->GetGridCursorRow() >= 0 )
|
|
|
|
selectedRows.push_back( m_cur_grid->GetGridCursorRow() );
|
|
|
|
|
|
|
|
wxArrayInt rowsToMigrate;
|
2023-12-19 17:39:26 +00:00
|
|
|
wxString kicadType = PCB_IO_MGR::ShowType( PCB_IO_MGR::KICAD_SEXP );
|
2023-08-28 20:44:10 +00:00
|
|
|
wxString msg;
|
|
|
|
|
|
|
|
for( int row : selectedRows )
|
|
|
|
{
|
|
|
|
if( m_cur_grid->GetCellValue( row, COL_TYPE ) != kicadType )
|
|
|
|
rowsToMigrate.push_back( row );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( rowsToMigrate.size() <= 0 )
|
|
|
|
{
|
|
|
|
wxMessageBox( wxString::Format( _( "Select one or more rows containing libraries "
|
|
|
|
"to save as current KiCad format." ) ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( rowsToMigrate.size() == 1 )
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Save '%s' as current KiCad format "
|
|
|
|
"and replace entry in table?" ),
|
|
|
|
m_cur_grid->GetCellValue( rowsToMigrate[0], COL_NICKNAME ) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Save %d libraries as current KiCad format "
|
|
|
|
"and replace entries in table?" ),
|
|
|
|
(int) rowsToMigrate.size() );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !IsOK( m_parent, msg ) )
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for( int row : rowsToMigrate )
|
|
|
|
{
|
|
|
|
wxString libName = m_cur_grid->GetCellValue( row, COL_NICKNAME );
|
|
|
|
wxString relPath = m_cur_grid->GetCellValue( row, COL_URI );
|
|
|
|
wxString resolvedPath = ExpandEnvVarSubstitutions( relPath, m_project );
|
|
|
|
wxFileName legacyLib( resolvedPath );
|
|
|
|
|
|
|
|
if( !legacyLib.Exists() )
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Library '%s' not found." ), relPath );
|
2024-02-02 17:47:34 +00:00
|
|
|
DisplayErrorMessage( wxGetTopLevelParent( this ), msg );
|
2023-08-28 20:44:10 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxFileName newLib( resolvedPath );
|
2023-12-28 02:10:01 +00:00
|
|
|
newLib.AppendDir( newLib.GetName() + "." + FILEEXT::KiCadFootprintLibPathExtension );
|
2023-08-28 20:44:10 +00:00
|
|
|
newLib.SetName( "" );
|
|
|
|
newLib.ClearExt();
|
|
|
|
|
|
|
|
if( newLib.DirExists() )
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Folder '%s' already exists. Do you want overwrite any existing footprints?" ),
|
|
|
|
newLib.GetFullPath() );
|
|
|
|
|
|
|
|
switch( wxMessageBox( msg, _( "Migrate Library" ),
|
|
|
|
wxYES_NO | wxCANCEL | wxICON_QUESTION, m_parent ) )
|
|
|
|
{
|
|
|
|
case wxYES: break;
|
|
|
|
case wxNO: continue;
|
|
|
|
case wxCANCEL: return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wxString options = m_cur_grid->GetCellValue( row, COL_OPTIONS );
|
|
|
|
std::unique_ptr<STRING_UTF8_MAP> props( LIB_TABLE::ParseOptions( options.ToStdString() ) );
|
|
|
|
|
2024-02-13 21:19:24 +00:00
|
|
|
if( PCB_IO_MGR::ConvertLibrary( props.get(), legacyLib.GetFullPath(), newLib.GetFullPath() ) )
|
2023-08-28 20:44:10 +00:00
|
|
|
{
|
|
|
|
relPath = NormalizePath( newLib.GetFullPath(), &Pgm().GetLocalEnvVariables(),
|
|
|
|
m_project );
|
|
|
|
|
|
|
|
// Do not use the project path in the global library table. This will almost
|
|
|
|
// assuredly be wrong for a different project.
|
|
|
|
if( m_cur_grid == m_global_grid && relPath.Contains( "${KIPRJMOD}" ) )
|
|
|
|
relPath = newLib.GetFullPath();
|
|
|
|
|
|
|
|
m_cur_grid->SetCellValue( row, COL_URI, relPath );
|
|
|
|
m_cur_grid->SetCellValue( row, COL_TYPE, kicadType );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Failed to save footprint library file '%s'." ), newLib.GetFullPath() );
|
2024-02-02 17:47:34 +00:00
|
|
|
DisplayErrorMessage( wxGetTopLevelParent( this ), msg );
|
2023-08-28 20:44:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-19 19:15:40 +00:00
|
|
|
void PANEL_FP_LIB_TABLE::browseLibrariesHandler( wxCommandEvent& event )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
2018-08-19 16:10:14 +00:00
|
|
|
if( !m_cur_grid->CommitPendingChanges() )
|
|
|
|
return;
|
|
|
|
|
2023-12-19 17:39:26 +00:00
|
|
|
PCB_IO_MGR::PCB_FILE_T fileType = PCB_IO_MGR::FILE_TYPE_NONE;
|
2020-01-03 15:09:26 +00:00
|
|
|
|
|
|
|
// We are bound both to the menu and button with this one handler
|
|
|
|
// So we must set the file type based on it
|
|
|
|
if( event.GetEventType() == wxEVT_BUTTON )
|
|
|
|
{
|
2020-11-14 22:00:12 +00:00
|
|
|
// Let's default to adding a kicad footprint file for just the footprint
|
2023-12-19 17:39:26 +00:00
|
|
|
fileType = PCB_IO_MGR::KICAD_SEXP;
|
2020-01-03 15:09:26 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-12-19 17:39:26 +00:00
|
|
|
fileType = static_cast<PCB_IO_MGR::PCB_FILE_T>( event.GetId() );
|
2020-01-03 15:09:26 +00:00
|
|
|
}
|
|
|
|
|
2023-12-19 17:39:26 +00:00
|
|
|
if( fileType == PCB_IO_MGR::FILE_TYPE_NONE )
|
2020-01-03 15:09:26 +00:00
|
|
|
{
|
2022-02-04 22:44:59 +00:00
|
|
|
wxLogWarning( wxT( "File type selection event received but could not find the file type "
|
|
|
|
"in the table" ) );
|
2020-01-03 15:09:26 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-12-27 16:34:59 +00:00
|
|
|
const IO_BASE::IO_FILE_DESC& fileDesc = m_supportedFpFiles.at( fileType );
|
2023-08-13 02:26:06 +00:00
|
|
|
PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
|
2012-10-31 14:41:47 +00:00
|
|
|
|
2023-12-19 17:39:26 +00:00
|
|
|
wxString title = wxString::Format( _( "Select %s Library" ), PCB_IO_MGR::ShowType( fileType ) );
|
2021-01-31 23:53:44 +00:00
|
|
|
wxString openDir = cfg->m_lastFootprintLibDir;
|
2021-05-22 19:38:36 +00:00
|
|
|
|
2021-01-31 23:53:44 +00:00
|
|
|
if( m_cur_grid == m_project_grid )
|
|
|
|
openDir = m_lastProjectLibDir;
|
|
|
|
|
2023-08-13 02:26:06 +00:00
|
|
|
wxArrayString files;
|
|
|
|
|
2024-02-02 14:20:36 +00:00
|
|
|
wxWindow* topLevelParent = wxGetTopLevelParent( this );
|
|
|
|
|
2023-08-13 02:26:06 +00:00
|
|
|
if( fileDesc.m_IsFile )
|
2020-01-03 15:09:26 +00:00
|
|
|
{
|
2024-02-02 14:20:36 +00:00
|
|
|
wxFileDialog dlg( topLevelParent, title, openDir, wxEmptyString, fileDesc.FileFilter(),
|
2020-12-12 21:54:47 +00:00
|
|
|
wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE );
|
2012-10-31 14:41:47 +00:00
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
int result = dlg.ShowModal();
|
|
|
|
|
|
|
|
if( result == wxID_CANCEL )
|
|
|
|
return;
|
|
|
|
|
|
|
|
dlg.GetPaths( files );
|
|
|
|
|
2021-05-22 19:38:36 +00:00
|
|
|
if( m_cur_grid == m_global_grid )
|
|
|
|
cfg->m_lastFootprintLibDir = dlg.GetDirectory();
|
|
|
|
else
|
|
|
|
m_lastProjectLibDir = dlg.GetDirectory();
|
2020-01-03 15:09:26 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-02-02 14:20:36 +00:00
|
|
|
wxDirDialog dlg( topLevelParent, title, openDir,
|
2021-05-22 19:38:36 +00:00
|
|
|
wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST | wxDD_MULTIPLE );
|
2020-01-03 15:09:26 +00:00
|
|
|
|
|
|
|
int result = dlg.ShowModal();
|
|
|
|
|
|
|
|
if( result == wxID_CANCEL )
|
|
|
|
return;
|
|
|
|
|
2021-05-22 19:38:36 +00:00
|
|
|
dlg.GetPaths( files );
|
2020-01-03 15:09:26 +00:00
|
|
|
|
2021-05-22 19:38:36 +00:00
|
|
|
if( !files.IsEmpty() )
|
2021-01-31 23:53:44 +00:00
|
|
|
{
|
2021-05-22 19:38:36 +00:00
|
|
|
wxFileName first( files.front() );
|
|
|
|
|
|
|
|
if( m_cur_grid == m_global_grid )
|
|
|
|
cfg->m_lastFootprintLibDir = first.GetPath();
|
|
|
|
else
|
|
|
|
m_lastProjectLibDir = first.GetPath();
|
2021-01-31 23:53:44 +00:00
|
|
|
}
|
2020-01-03 15:09:26 +00:00
|
|
|
}
|
2012-10-31 14:41:47 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// Drop the last directory if the path is a .pretty folder
|
2023-12-28 02:10:01 +00:00
|
|
|
if( cfg->m_lastFootprintLibDir.EndsWith( FILEEXT::KiCadFootprintLibPathExtension ) )
|
2020-09-01 02:00:38 +00:00
|
|
|
cfg->m_lastFootprintLibDir = cfg->m_lastFootprintLibDir.BeforeLast( wxFileName::GetPathSeparator() );
|
2012-10-31 14:41:47 +00:00
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
|
|
|
|
bool addDuplicates = false;
|
|
|
|
bool applyToAll = false;
|
2020-12-18 00:25:51 +00:00
|
|
|
wxString warning = _( "Warning: Duplicate Nicknames" );
|
|
|
|
wxString msg = _( "A library nicknamed '%s' already exists." );
|
|
|
|
wxString detailedMsg = _( "One of the nicknames will need to be changed after "
|
|
|
|
"adding this library." );
|
2012-10-16 06:56:57 +00:00
|
|
|
|
2021-02-18 11:17:20 +00:00
|
|
|
for( const wxString& filePath : files )
|
2012-10-16 06:56:57 +00:00
|
|
|
{
|
2018-03-14 04:51:59 +00:00
|
|
|
wxFileName fn( filePath );
|
2021-06-30 10:53:04 +00:00
|
|
|
wxString nickname = LIB_ID::FixIllegalChars( fn.GetName(), true );
|
2020-01-03 15:09:26 +00:00
|
|
|
bool doAdd = true;
|
2012-10-16 06:56:57 +00:00
|
|
|
|
2023-12-28 02:10:01 +00:00
|
|
|
if( fileType == PCB_IO_MGR::KICAD_SEXP
|
|
|
|
&& fn.GetExt() != FILEEXT::KiCadFootprintLibPathExtension )
|
2023-09-14 15:35:19 +00:00
|
|
|
nickname = LIB_ID::FixIllegalChars( fn.GetFullName(), true ).wx_str();
|
2023-01-06 18:07:23 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
if( cur_model()->ContainsNickname( nickname ) )
|
2013-12-31 16:59:33 +00:00
|
|
|
{
|
2018-08-01 23:06:12 +00:00
|
|
|
if( !applyToAll )
|
2013-12-31 16:59:33 +00:00
|
|
|
{
|
2020-02-14 19:07:27 +00:00
|
|
|
// The cancel button adds the library to the table anyway
|
2024-02-02 17:47:34 +00:00
|
|
|
addDuplicates = OKOrCancelDialog( wxGetTopLevelParent( this ), warning,
|
|
|
|
wxString::Format( msg, nickname ),
|
2020-12-18 00:25:51 +00:00
|
|
|
detailedMsg, _( "Skip" ), _( "Add Anyway" ),
|
|
|
|
&applyToAll ) == wxID_CANCEL;
|
2013-12-31 16:59:33 +00:00
|
|
|
}
|
2018-08-01 23:06:12 +00:00
|
|
|
|
|
|
|
doAdd = addDuplicates;
|
2012-10-18 15:28:50 +00:00
|
|
|
}
|
2018-05-23 09:49:32 +00:00
|
|
|
|
2018-08-01 23:06:12 +00:00
|
|
|
if( doAdd && m_cur_grid->AppendRows( 1 ) )
|
2018-05-23 09:49:32 +00:00
|
|
|
{
|
2018-03-14 04:51:59 +00:00
|
|
|
int last_row = m_cur_grid->GetNumberRows() - 1;
|
2018-05-23 09:49:32 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
m_cur_grid->SetCellValue( last_row, COL_NICKNAME, nickname );
|
2018-05-23 09:49:32 +00:00
|
|
|
|
2023-12-19 17:39:26 +00:00
|
|
|
m_cur_grid->SetCellValue( last_row, COL_TYPE, PCB_IO_MGR::ShowType( fileType ) );
|
2018-05-23 09:49:32 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// try to use path normalized to an environmental variable or project path
|
2018-08-06 18:33:28 +00:00
|
|
|
wxString path = NormalizePath( filePath, &envVars, m_projectBasePath );
|
2018-07-19 19:15:40 +00:00
|
|
|
|
2019-06-25 12:36:32 +00:00
|
|
|
// Do not use the project path in the global library table. This will almost
|
|
|
|
// assuredly be wrong for a different project.
|
2022-02-04 22:44:59 +00:00
|
|
|
if( m_pageNdx == 0 && path.Contains( wxT( "${KIPRJMOD}" ) ) )
|
2018-07-19 19:15:40 +00:00
|
|
|
path = fn.GetFullPath();
|
|
|
|
|
|
|
|
m_cur_grid->SetCellValue( last_row, COL_URI, path );
|
2018-05-23 09:49:32 +00:00
|
|
|
}
|
|
|
|
}
|
2014-12-21 14:13:14 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
if( !files.IsEmpty() )
|
2012-12-09 20:51:42 +00:00
|
|
|
{
|
2018-03-14 04:51:59 +00:00
|
|
|
int new_row = m_cur_grid->GetNumberRows() - 1;
|
|
|
|
m_cur_grid->MakeCellVisible( new_row, m_cur_grid->GetGridCursorCol() );
|
|
|
|
m_cur_grid->SetGridCursor( new_row, m_cur_grid->GetGridCursorCol() );
|
2012-12-09 20:51:42 +00:00
|
|
|
}
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
|
2018-07-19 19:15:40 +00:00
|
|
|
void PANEL_FP_LIB_TABLE::adjustPathSubsGridColumns( int aWidth )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
|
|
|
// Account for scroll bars
|
|
|
|
aWidth -= ( m_path_subs_grid->GetSize().x - m_path_subs_grid->GetClientSize().x );
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
m_path_subs_grid->AutoSizeColumn( 0 );
|
2022-11-16 12:35:21 +00:00
|
|
|
m_path_subs_grid->SetColSize( 0, std::max( 72, m_path_subs_grid->GetColSize( 0 ) ) );
|
|
|
|
m_path_subs_grid->SetColSize( 1, std::max( 120, aWidth - m_path_subs_grid->GetColSize( 0 ) ) );
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2012-10-16 06:56:57 +00:00
|
|
|
|
2013-10-22 19:29:37 +00:00
|
|
|
|
2018-07-19 19:15:40 +00:00
|
|
|
void PANEL_FP_LIB_TABLE::onSizeGrid( wxSizeEvent& event )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
|
|
|
adjustPathSubsGridColumns( event.GetSize().GetX() );
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
event.Skip();
|
|
|
|
}
|
2012-10-16 06:56:57 +00:00
|
|
|
|
2012-10-18 15:28:50 +00:00
|
|
|
|
2024-05-01 21:52:02 +00:00
|
|
|
void PANEL_FP_LIB_TABLE::onReset( wxCommandEvent& event )
|
|
|
|
{
|
|
|
|
if( !m_cur_grid->CommitPendingChanges() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// No need to prompt to preserve an empty table
|
|
|
|
if( m_global_grid->GetNumberRows() > 0 &&
|
|
|
|
!IsOK( this, wxString::Format( _( "This action will reset your global library table on "
|
|
|
|
"disk and cannot be undone." ) ) ) )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
DIALOG_GLOBAL_FP_LIB_TABLE_CONFIG dlg( m_parent );
|
|
|
|
|
|
|
|
if( dlg.ShowModal() == wxID_OK )
|
|
|
|
{
|
|
|
|
m_global_grid->Freeze();
|
|
|
|
|
|
|
|
wxGridTableBase* table = m_global_grid->GetTable();
|
|
|
|
m_global_grid->DestroyTable( table );
|
|
|
|
|
|
|
|
m_global_grid->SetTable( new FP_LIB_TABLE_GRID( *m_globalTable ), true );
|
|
|
|
m_global_grid->PopEventHandler( true );
|
|
|
|
setupGrid( m_global_grid );
|
|
|
|
m_parent->m_GlobalTableChanged = true;
|
|
|
|
|
|
|
|
m_global_grid->Thaw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PANEL_FP_LIB_TABLE::onPageChange( wxBookCtrlEvent& event )
|
|
|
|
{
|
|
|
|
m_pageNdx = (unsigned) std::max( 0, m_notebook->GetSelection() );
|
|
|
|
|
|
|
|
if( m_pageNdx == 0 )
|
|
|
|
{
|
|
|
|
m_cur_grid = m_global_grid;
|
|
|
|
m_resetGlobal->Enable();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_cur_grid = m_project_grid;
|
|
|
|
m_resetGlobal->Disable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-19 19:15:40 +00:00
|
|
|
bool PANEL_FP_LIB_TABLE::TransferDataFromWindow()
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
2018-08-19 16:10:14 +00:00
|
|
|
if( !m_cur_grid->CommitPendingChanges() )
|
|
|
|
return false;
|
2012-12-09 20:51:42 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
if( verifyTables() )
|
2013-01-21 03:12:16 +00:00
|
|
|
{
|
2023-08-28 20:44:10 +00:00
|
|
|
if( *global_model() != *m_globalTable )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
2018-07-19 19:15:40 +00:00
|
|
|
m_parent->m_GlobalTableChanged = true;
|
2013-01-21 03:12:16 +00:00
|
|
|
|
2023-08-28 20:44:10 +00:00
|
|
|
m_globalTable->Clear();
|
|
|
|
m_globalTable->TransferRows( global_model()->m_rows );
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2013-01-21 03:12:16 +00:00
|
|
|
|
2023-08-28 20:44:10 +00:00
|
|
|
if( project_model() && *project_model() != *m_projectTable )
|
2013-01-21 03:12:16 +00:00
|
|
|
{
|
2018-07-19 19:15:40 +00:00
|
|
|
m_parent->m_ProjectTableChanged = true;
|
2012-10-16 06:56:57 +00:00
|
|
|
|
2023-08-28 20:44:10 +00:00
|
|
|
m_projectTable->Clear();
|
|
|
|
m_projectTable->TransferRows( project_model()->m_rows );
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2018-08-06 18:33:28 +00:00
|
|
|
|
|
|
|
return true;
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2018-07-19 19:15:40 +00:00
|
|
|
|
2018-08-06 18:33:28 +00:00
|
|
|
return false;
|
2018-03-14 04:51:59 +00:00
|
|
|
}
|
2013-01-21 03:12:16 +00:00
|
|
|
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
/// Populate the readonly environment variable table with names and values
|
|
|
|
/// by examining all the full_uri columns.
|
2018-07-19 19:15:40 +00:00
|
|
|
void PANEL_FP_LIB_TABLE::populateEnvironReadOnlyTable()
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
|
|
|
wxRegEx re( ".*?(\\$\\{(.+?)\\})|(\\$\\((.+?)\\)).*?", wxRE_ADVANCED );
|
|
|
|
wxASSERT( re.IsValid() ); // wxRE_ADVANCED is required.
|
2013-01-21 03:12:16 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
std::set< wxString > unique;
|
2013-11-15 14:11:46 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// clear the table
|
2021-08-10 11:59:53 +00:00
|
|
|
m_path_subs_grid->ClearRows();
|
2013-01-21 03:12:16 +00:00
|
|
|
|
2018-09-12 12:59:33 +00:00
|
|
|
for( FP_LIB_TABLE_GRID* tbl : { global_model(), project_model() } )
|
2018-03-14 04:51:59 +00:00
|
|
|
{
|
2020-08-07 19:22:15 +00:00
|
|
|
if( !tbl )
|
|
|
|
continue;
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
for( int row = 0; row < tbl->GetNumberRows(); ++row )
|
2013-01-21 03:12:16 +00:00
|
|
|
{
|
2018-03-14 04:51:59 +00:00
|
|
|
wxString uri = tbl->GetValue( row, COL_URI );
|
2013-01-21 03:12:16 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
while( re.Matches( uri ) )
|
|
|
|
{
|
|
|
|
wxString envvar = re.GetMatch( uri, 2 );
|
2013-01-21 03:12:16 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// if not ${...} form then must be $(...)
|
|
|
|
if( envvar.IsEmpty() )
|
|
|
|
envvar = re.GetMatch( uri, 4 );
|
2013-01-21 03:12:16 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// ignore duplicates
|
|
|
|
unique.insert( envvar );
|
2013-01-21 03:12:16 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// delete the last match and search again
|
|
|
|
uri.Replace( re.GetMatch( uri, 0 ), wxEmptyString );
|
|
|
|
}
|
|
|
|
}
|
2018-05-23 09:49:32 +00:00
|
|
|
}
|
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// Make sure this special environment variable shows up even if it was
|
|
|
|
// not used yet. It is automatically set by KiCad to the directory holding
|
|
|
|
// the current project.
|
|
|
|
unique.insert( PROJECT_VAR_NAME );
|
|
|
|
unique.insert( FP_LIB_TABLE::GlobalPathEnvVariableName() );
|
2024-02-02 17:47:34 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// This special environment variable is used to locate 3d shapes
|
2024-01-16 22:20:45 +00:00
|
|
|
unique.insert( ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ) );
|
2012-10-16 06:56:57 +00:00
|
|
|
|
2019-12-05 15:20:59 +00:00
|
|
|
for( const wxString& evName : unique )
|
2017-03-27 23:21:12 +00:00
|
|
|
{
|
2018-03-14 04:51:59 +00:00
|
|
|
int row = m_path_subs_grid->GetNumberRows();
|
|
|
|
m_path_subs_grid->AppendRows( 1 );
|
2017-03-27 23:21:12 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
m_path_subs_grid->SetCellValue( row, 0, wxT( "${" ) + evName + wxT( "}" ) );
|
2018-09-12 12:59:33 +00:00
|
|
|
m_path_subs_grid->SetCellEditor( row, 0, new GRID_CELL_READONLY_TEXT_EDITOR() );
|
2017-03-27 23:21:12 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
wxString evValue;
|
|
|
|
wxGetEnv( evName, &evValue );
|
|
|
|
m_path_subs_grid->SetCellValue( row, 1, evValue );
|
2018-09-12 12:59:33 +00:00
|
|
|
m_path_subs_grid->SetCellEditor( row, 1, new GRID_CELL_READONLY_TEXT_EDITOR() );
|
2017-03-27 23:21:12 +00:00
|
|
|
}
|
2012-10-16 06:56:57 +00:00
|
|
|
|
2018-03-14 04:51:59 +00:00
|
|
|
// No combobox editors here, but it looks better if its consistent with the other
|
|
|
|
// grids in the dialog.
|
2018-09-12 12:59:33 +00:00
|
|
|
m_path_subs_grid->SetDefaultRowSize( m_path_subs_grid->GetDefaultRowSize() + 2 );
|
2018-03-14 04:51:59 +00:00
|
|
|
|
|
|
|
adjustPathSubsGridColumns( m_path_subs_grid->GetRect().GetWidth() );
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----</event handlers>---------------------------------
|
|
|
|
|
2018-05-23 09:49:32 +00:00
|
|
|
|
2012-10-15 22:30:01 +00:00
|
|
|
|
2018-07-19 19:15:40 +00:00
|
|
|
size_t PANEL_FP_LIB_TABLE::m_pageNdx = 0;
|
2016-11-19 22:15:34 +00:00
|
|
|
|
2012-10-15 22:30:01 +00:00
|
|
|
|
2018-08-06 18:33:28 +00:00
|
|
|
void InvokePcbLibTableEditor( KIWAY* aKiway, wxWindow* aCaller )
|
2012-10-15 22:30:01 +00:00
|
|
|
{
|
2018-08-06 18:33:28 +00:00
|
|
|
FP_LIB_TABLE* globalTable = &GFootprintTable;
|
|
|
|
wxString globalTablePath = FP_LIB_TABLE::GetGlobalTableFileName();
|
2023-09-28 03:15:54 +00:00
|
|
|
FP_LIB_TABLE* projectTable = PROJECT_PCB::PcbFootprintLibs( &aKiway->Prj() );
|
2018-08-06 18:33:28 +00:00
|
|
|
wxString projectTablePath = aKiway->Prj().FootprintLibTblName();
|
|
|
|
wxString msg;
|
|
|
|
|
2018-07-19 19:15:40 +00:00
|
|
|
DIALOG_EDIT_LIBRARY_TABLES dlg( aCaller, _( "Footprint Libraries" ) );
|
2018-08-06 18:33:28 +00:00
|
|
|
dlg.SetKiway( &dlg, aKiway );
|
2012-10-15 22:30:01 +00:00
|
|
|
|
2020-08-07 19:22:15 +00:00
|
|
|
if( aKiway->Prj().IsNullProject() )
|
|
|
|
projectTable = nullptr;
|
|
|
|
|
2023-08-28 20:44:10 +00:00
|
|
|
dlg.InstallPanel( new PANEL_FP_LIB_TABLE( &dlg, &aKiway->Prj(), globalTable, globalTablePath,
|
2018-08-06 18:33:28 +00:00
|
|
|
projectTable, projectTablePath,
|
|
|
|
aKiway->Prj().GetProjectPath() ) );
|
2018-07-19 19:15:40 +00:00
|
|
|
|
2018-08-06 18:33:28 +00:00
|
|
|
if( dlg.ShowModal() == wxID_CANCEL )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( dlg.m_GlobalTableChanged )
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
globalTable->Save( globalTablePath );
|
|
|
|
}
|
|
|
|
catch( const IO_ERROR& ioe )
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Error saving global library table:\n\n%s" ), ioe.What() );
|
|
|
|
wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
|
|
|
|
}
|
|
|
|
}
|
2018-07-29 15:33:58 +00:00
|
|
|
|
2020-08-07 19:22:15 +00:00
|
|
|
if( projectTable && dlg.m_ProjectTableChanged )
|
2018-07-29 15:33:58 +00:00
|
|
|
{
|
2018-08-06 18:33:28 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
projectTable->Save( projectTablePath );
|
|
|
|
}
|
|
|
|
catch( const IO_ERROR& ioe )
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Error saving project-specific library table:\n\n%s" ), ioe.What() );
|
|
|
|
wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
|
|
|
|
}
|
2018-07-29 15:33:58 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 22:58:03 +00:00
|
|
|
std::string payload = "";
|
2023-01-19 00:14:31 +00:00
|
|
|
aKiway->ExpressMail( FRAME_FOOTPRINT_EDITOR, MAIL_RELOAD_LIB, payload );
|
|
|
|
aKiway->ExpressMail( FRAME_FOOTPRINT_VIEWER, MAIL_RELOAD_LIB, payload );
|
2022-09-27 22:58:03 +00:00
|
|
|
aKiway->ExpressMail( FRAME_CVPCB, MAIL_RELOAD_LIB, payload );
|
2012-10-15 22:30:01 +00:00
|
|
|
}
|