KiCad PCB EDA Suite
Loading...
Searching...
No Matches
startwizard_provider_libraries.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 * @author Jon Evans <[email protected]>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <pgm_base.h>
26#include <paths.h>
29
30#include <magic_enum.hpp>
32#include <trace_helpers.h>
33#include <regex>
34#include <set>
35#include <wx/log.h>
36
37
39{
40public:
41 PANEL_STARTWIZARD_LIBRARIES( const std::shared_ptr<STARTWIZARD_PROVIDER_LIBRARIES_MODEL>& aModel,
42 wxWindow* aParent, STARTWIZARD* aWizard ) :
44 m_model( aModel ),
45 m_wizard( aWizard )
46 {
47 wxWindowBase::SetMaxSize( { FromDIP( 640 ), -1 } );
49 m_sizerWarning->Layout();
50
51 Bind( wxEVT_RADIOBUTTON, &PANEL_STARTWIZARD_LIBRARIES::OnModeChanged, this,
52 m_rbDefaultTables->GetId() );
53 Bind( wxEVT_RADIOBUTTON, &PANEL_STARTWIZARD_LIBRARIES::OnModeChanged, this,
54 m_rbImport->GetId() );
55 Bind( wxEVT_RADIOBUTTON, &PANEL_STARTWIZARD_LIBRARIES::OnModeChanged, this,
56 m_rbBlankTables->GetId() );
57
58 m_introText = m_stIntro->GetLabel();
59 m_warningText = m_stWarning->GetLabel();
60
64
65 m_containingSizer->Fit( this );
66 SetSize( GetBestSize() );
67 }
68
69 bool TransferDataFromWindow() override
70 {
71 if( m_rbDefaultTables->GetValue() )
73 else if( m_rbImport->GetValue() )
75 else
77
78 m_model->mode_initialized = true;
79
80 m_model->migrate_built_in_libraries = m_cbMigrateBuiltInLibraries->GetValue();
81
82 return true;
83 }
84
85 bool TransferDataToWindow() override
86 {
87 if( !m_model->mode_initialized )
88 {
89 // If the user is importing settings from a previous version, let's default to taking their
90 // library settings as well, since we can clean up old stock paths now.
91 if( auto settings = dynamic_cast<STARTWIZARD_PROVIDER_SETTINGS*>( m_wizard->GetProvider( "settings" ) ) )
92 {
93 if( settings->NeedsUserInput() )
95 else
97 }
98
99 m_model->mode_initialized = true;
100 }
101
102 switch( m_model->mode )
103 {
104 case STARTWIZARD_LIBRARIES_MODE::USE_DEFAULTS: m_rbDefaultTables->SetValue( true ); break;
105 case STARTWIZARD_LIBRARIES_MODE::IMPORT: m_rbImport->SetValue( true ); break;
106 case STARTWIZARD_LIBRARIES_MODE::CREATE_BLANK: m_rbBlankTables->SetValue( true ); break;
107 }
108
109 m_cbMigrateBuiltInLibraries->SetValue( m_model->migrate_built_in_libraries );
110
111 if( auto settings = dynamic_cast<STARTWIZARD_PROVIDER_SETTINGS*>( m_wizard->GetProvider( "settings" ) ) )
112 {
113 if( !settings->NeedsUserInput() )
114 {
115 // If the user didn't see the settings screen at all, just hide this option; we
116 // only offer import when we have the context of a previous version settings
117 // path selected by the user
118 m_rbImport->Show( false );
119 m_cbMigrateBuiltInLibraries->Show( false );
120 }
121 else if( settings->GetModel().mode == STARTWIZARD_SETTINGS_MODE::USE_DEFAULTS )
122 {
123 // But if they did see the screen and chose not to import, show the option but
124 // disable it as a breadcrumb that they can go back and choose to import settings
125 m_rbImport->Disable();
126
127 if( m_rbImport->GetValue() )
128 m_rbDefaultTables->SetValue( true );
129 }
130 else
131 {
132 m_rbImport->Enable();
133 }
134 }
135
137
139 m_stWarning->Show( m_showWarning );
140
141 return true;
142 }
143
145 {
146 wxString missingTablesText;
147 m_showWarning = false;
148
149 for( const LIBRARY_TABLE_TYPE& type : m_model->missing_tables )
150 {
153 {
154 wxLogTrace( traceLibraries, wxT( "Stock table path for %s is not valid; will show warning" ),
155 magic_enum::enum_name( type ) );
156 m_showWarning = true;
157 }
158
159 switch( type )
160 {
162 missingTablesText.Append( _( "Symbol library table" ) + "\n" );
163 break;
164
166 missingTablesText.Append( _( "Footprint library table" ) + "\n" );
167 break;
168
170 missingTablesText.Append( _( "Design Block library table" ) + "\n" );
171 break;
172
174 break;
175 }
176 }
177
178 m_stRequiredTables->SetLabel( missingTablesText.BeforeLast( '\n' ) );
179 }
180
181 void OnSize( wxSizeEvent& aEvt ) override
182 {
183 aEvt.Skip();
184
185 // Wrapping is destructive and the size can change a few times during construction, at least
186 // on macOS. Resetting the label makes wrapping work properly.
187 m_stIntro->SetLabel( m_introText );
188 m_stWarning->SetLabel( m_warningText );
189
190#if defined( __WXMAC__ )
191 m_stIntro->Wrap( GetClientSize().x - FromDIP( 20 ) );
192 m_stWarning->Wrap( GetClientSize().x - m_bmpWarning->GetSize().x - FromDIP( 28 ) );
193#elif defined( __WXMSW__ )
194 m_stIntro->Wrap( GetClientSize().x );
195 m_stWarning->Wrap( GetClientSize().x - m_bmpWarning->GetSize().x - FromDIP( 10 ) );
196#else
197 m_stIntro->Wrap( GetClientSize().x - FromDIP( 10 ) );
198 m_stWarning->Wrap( GetClientSize().x - m_bmpWarning->GetSize().x - FromDIP( 18 ) );
199#endif
200 }
201
202private:
203 void OnModeChanged( wxCommandEvent& aEvt )
204 {
205 aEvt.Skip();
207 }
208
210 {
211 m_cbMigrateBuiltInLibraries->Enable( m_rbImport->IsShown()
212 && m_rbImport->IsEnabled()
213 && m_rbImport->GetValue() );
214 }
215
216 std::shared_ptr<STARTWIZARD_PROVIDER_LIBRARIES_MODEL> m_model;
219 wxString m_introText;
221};
222
223
228
229
234
235
236wxPanel* STARTWIZARD_PROVIDER_LIBRARIES::GetWizardPanel( wxWindow* aParent, STARTWIZARD* aWizard )
237{
238 m_model = std::make_shared<STARTWIZARD_PROVIDER_LIBRARIES_MODEL>();
240 return new PANEL_STARTWIZARD_LIBRARIES( m_model, aParent, aWizard );
241}
242
243
245 const wxString& aStockPath, bool aStockPathValid )
246{
247 const std::regex builtInPattern( aType == LIBRARY_TABLE_TYPE::SYMBOL
248 ? R"(^\$\{KICAD\d+_SYMBOL_DIR\})"
249 : R"(^\$\{KICAD\d+_FOOTPRINT_DIR\})" );
250
251 // Only re-introduce the stock libraries if the imported table actually referenced them.
252 // A user who removed all built-in libraries in the previous version should not have them
253 // silently added back during migration.
254 bool insertStock = false;
255 bool haveChained = false;
256 bool chainedRepointed = false;
257 std::set<wxString> toRemove;
258
259 for( LIBRARY_TABLE_ROW& row : aTable.Rows() )
260 {
261 if( std::regex_search( row.URI().ToStdString(), builtInPattern ) )
262 {
263 toRemove.insert( row.URI() );
264 insertStock = true;
265 wxLogTrace( traceLibraries, wxT( "Libraries migration: removing old stock row '%s'" ), row.URI() );
266 }
267 else if( row.Type() == LIBRARY_TABLE_ROW::TABLE_TYPE_NAME && row.Nickname() == wxT( "KiCad" ) )
268 {
269 haveChained = true;
270
271 if( row.URI().Matches( aStockPath ) )
272 {
273 wxLogTrace( traceLibraries,
274 wxT( "Libraries migration: migrated table already has latest stock setup" ) );
275 }
276 else if( LIBRARY_MANAGER::IsTableValid( row.URI() ) && !aStockPathValid )
277 {
278 wxLogTrace( traceLibraries,
279 wxT( "Libraries migration: migrated table has working stock table at "
280 "'%s' but calculated stock path '%s' is missing or invalid. Leaving alone." ),
281 row.URI(), aStockPath );
282 }
283 else
284 {
285 wxLogTrace( traceLibraries,
286 wxT( "Libraries migration: migrated chained table URI from '%s' to '%s'" ),
287 row.URI(), aStockPath );
288 row.SetDescription( _( "KiCad Default Libraries" ) );
289 row.SetURI( aStockPath );
290 chainedRepointed = true;
291 }
292 }
293 }
294
295 auto toErase = std::ranges::remove_if( aTable.Rows(),
296 [&]( const LIBRARY_TABLE_ROW& aRow )
297 {
298 return toRemove.contains( aRow.URI() );
299 } );
300
301 aTable.Rows().erase( toErase.begin(), toErase.end() );
302
303 // An existing chained reference was already migrated in place, so don't add a second one.
304 if( haveChained )
305 insertStock = false;
306
307 if( insertStock )
308 {
309 wxLogTrace( traceLibraries, wxT( "Libraries migration: inserting chained default table" ) );
310 LIBRARY_TABLE_ROW chained = aTable.MakeRow();
312 chained.SetNickname( wxT( "KiCad" ) );
313 chained.SetDescription( _( "KiCad Default Libraries" ) );
314 chained.SetURI( aStockPath );
315 aTable.Rows().insert( aTable.Rows().begin(), chained );
316 }
317
318 return !toRemove.empty() || insertStock || chainedRepointed;
319}
320
321
323{
324 bool populateTables = m_model->mode == STARTWIZARD_LIBRARIES_MODE::USE_DEFAULTS;
325
326 if( m_model->mode == STARTWIZARD_LIBRARIES_MODE::IMPORT && m_model->migrate_built_in_libraries )
327 {
329 {
330 wxString tablePath = LIBRARY_MANAGER::DefaultGlobalTablePath( type );
331 wxString stockPath = LIBRARY_MANAGER::StockTablePath( type );
332
333 if( !LIBRARY_MANAGER::IsTableValid( tablePath ) )
334 continue;
335
336 wxFileName tableFile( tablePath );
338
339 if( !table.IsOk() )
340 continue;
341
342 if( MigrateBuiltInLibraries( table, type, stockPath, LIBRARY_MANAGER::IsTableValid( stockPath ) ) )
343 table.Save();
344 else
345 wxLogTrace( traceLibraries, wxT( "Libraries migration: no actions needed" ) );
346 }
347 }
348
350 {
351 LIBRARY_MANAGER::CreateGlobalTable( type, populateTables );
352 }
353}
354
355
363
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
static wxString DefaultGlobalTablePath(LIBRARY_TABLE_TYPE aType)
static std::vector< LIBRARY_TABLE_TYPE > InvalidGlobalTables()
static bool GlobalTablesValid()
static bool CreateGlobalTable(LIBRARY_TABLE_TYPE aType, bool aPopulateDefaultLibraries)
static wxString StockTablePath(LIBRARY_TABLE_TYPE aType)
static bool IsTableValid(const wxString &aPath)
void SetNickname(const wxString &aNickname)
void SetType(const wxString &aType)
void SetDescription(const wxString &aDescription)
static const wxString TABLE_TYPE_NAME
void SetURI(const wxString &aUri)
LIBRARY_TABLE_ROW MakeRow() const
Builds a new row that is suitable for this table (does not insert it)
const std::deque< LIBRARY_TABLE_ROW > & Rows() const
PANEL_STARTWIZARD_LIBRARIES_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
PANEL_STARTWIZARD_LIBRARIES(const std::shared_ptr< STARTWIZARD_PROVIDER_LIBRARIES_MODEL > &aModel, wxWindow *aParent, STARTWIZARD *aWizard)
std::shared_ptr< STARTWIZARD_PROVIDER_LIBRARIES_MODEL > m_model
void OnSize(wxSizeEvent &aEvt) override
std::shared_ptr< STARTWIZARD_PROVIDER_LIBRARIES_MODEL > m_model
wxPanel * GetWizardPanel(wxWindow *aParent, STARTWIZARD *aWizard) override
void ApplyDefaults() override
Apply whatever actions and settings should happen if the user cancels the startup wizard.
static KICOMMON_API bool MigrateBuiltInLibraries(LIBRARY_TABLE &aTable, LIBRARY_TABLE_TYPE aType, const wxString &aStockPath, bool aStockPathValid)
Migrates built-in (stock) library references in an imported global library table to the current versi...
STARTWIZARD_PROVIDER(const wxString &aPageName)
#define _(s)
const wxChar *const traceLibraries
Flag to enable library table and library manager tracing.
LIBRARY_TABLE_TYPE
see class PGM_BASE
std::vector< std::vector< std::string > > table
wxLogTrace helper definitions.