KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_configure_paths.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 (C) 2015 Wayne Stambaugh <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU 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
22
23#include <algorithm>
24#include <set>
25
26#include <bitmaps.h>
27#include <common.h>
28#include <confirm.h>
29#include <kidialog.h>
30#include <validators.h>
33#include <filename_resolver.h>
34#include <env_vars.h>
35#include <grid_tricks.h>
37#include <pgm_base.h>
41#include <widgets/wx_grid.h>
42
43#include <wx/dirdlg.h>
44#include <wx/regex.h>
45
46
53
60
61
64 m_errorGrid( nullptr ),
65 m_errorRow( -1 ),
66 m_errorCol( -1 ),
67 m_helpBox( nullptr )
68{
69 m_heightBeforeHelp = FromDIP ( 400 );
72
73 m_EnvVars->SetMinSize( FromDIP( m_EnvVars->GetMinSize() ) );
74
75 m_EnvVars->ClearRows();
76 m_EnvVars->AppendCols( 1 ); // for the isExternal flags
77 m_EnvVars->HideCol( TV_FLAG_COL );
78 m_EnvVars->UseNativeColHeader( true );
79
80 wxGridCellAttr* attr = new wxGridCellAttr;
81 attr->SetEditor( new GRID_CELL_PATH_EDITOR( this, m_EnvVars, &m_curdir, wxEmptyString ) );
82 m_EnvVars->SetColAttr( TV_VALUE_COL, attr );
83
84 m_EnvVars->PushEventHandler( new GRID_TRICKS( m_EnvVars,
85 [this]( wxCommandEvent& aEvent )
86 {
87 OnAddEnvVar( aEvent );
88 } ) );
89
90 m_EnvVars->SetSelectionMode( wxGrid::wxGridSelectionModes::wxGridSelectRows );
91
94
95 // wxFormBuilder doesn't include this event...
96 m_EnvVars->Connect( wxEVT_GRID_CELL_CHANGING,
98 nullptr, this );
99
100 m_EnvVars->SetupColumnAutosizer( TV_VALUE_COL );
101
102 GetSizer()->SetSizeHints( this );
103 Centre();
104}
105
106
108{
109 // Delete the GRID_TRICKS.
110 m_EnvVars->PopEventHandler( true );
111
112 m_EnvVars->Disconnect( wxEVT_GRID_CELL_CHANGING,
113 wxGridEventHandler( DIALOG_CONFIGURE_PATHS::OnGridCellChanging ),
114 nullptr, this );
115}
116
117
119{
120 if( !wxDialog::TransferDataToWindow() )
121 return false;
122
123 const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
124
125 for( auto it = envVars.begin(); it != envVars.end(); ++it )
126 {
127 const wxString& path = it->second.GetValue();
128 AppendEnvVar( it->first, path, it->second.GetDefinedExternally() );
129
130 if( m_curdir.IsEmpty() && !path.StartsWith( "${" ) && !path.StartsWith( "$(" ) )
131 m_curdir = path;
132 }
133
134 // Scan library tables for environment variables that are used but not in envVars.
135 // This allows users to see and manage obsolete versioned variables (e.g., KICAD8_FOOTPRINT_DIR)
136 // that may still be referenced in library tables from older KiCad versions.
137 wxRegEx envVarRegex( wxS( ".*?(\\$\\{(.+?)\\})|(\\$\\((.+?)\\)).*?" ), wxRE_ADVANCED );
138 std::set<wxString> foundEnvVars;
139
141
145 {
148 {
149 std::vector<LIBRARY_TABLE_ROW*> rows = libMgr.Rows( tableType, scope, true );
150
151 for( const LIBRARY_TABLE_ROW* row : rows )
152 {
153 wxString uri = row->URI();
154
155 while( envVarRegex.Matches( uri ) )
156 {
157 wxString envvar = envVarRegex.GetMatch( uri, 2 );
158
159 if( envvar.IsEmpty() )
160 envvar = envVarRegex.GetMatch( uri, 4 );
161
162 if( !envvar.IsEmpty() )
163 foundEnvVars.insert( envvar );
164
165 uri.Replace( envVarRegex.GetMatch( uri, 0 ), wxEmptyString );
166 }
167 }
168 }
169 }
170
171 for( const wxString& envvar : foundEnvVars )
172 {
173 if( envVars.count( envvar ) == 0 && envvar != PROJECT_VAR_NAME )
174 {
175 wxString value;
176 bool isExternal = wxGetEnv( envvar, &value );
177
178 // Obsolete versioned vars (e.g. KICAD9_FOOTPRINT_DIR) resolve at runtime but show blank here
179 // Resolve the same way so the row matches what's actually used
180 if( value.IsEmpty() && !isExternal )
181 {
182 wxString token = wxString::Format( wxS( "${%s}" ), envvar );
183 wxString resolved = ExpandEnvVarSubstitutions( token, nullptr );
184
185 if( resolved != token )
186 value = resolved;
187 }
188
189 AppendEnvVar( envvar, value, isExternal );
190 }
191 }
192
193 return true;
194}
195
196
197void DIALOG_CONFIGURE_PATHS::AppendEnvVar( const wxString& aName, const wxString& aPath,
198 bool isExternal )
199{
200 int i = m_EnvVars->GetNumberRows();
201
202 m_EnvVars->AppendRows( 1 );
203
204 m_EnvVars->SetCellValue( i, TV_NAME_COL, aName );
205
206 wxGridCellAttr* nameCellAttr = m_EnvVars->GetOrCreateCellAttr( i, TV_NAME_COL );
207 wxGridCellTextEditor* nameTextEditor = new GRID_CELL_TEXT_EDITOR();
208 nameTextEditor->SetValidator( ENV_VAR_NAME_VALIDATOR() );
209 nameCellAttr->SetEditor( nameTextEditor );
210 nameCellAttr->SetReadOnly( ENV_VAR::IsEnvVarImmutable( aName ) );
211 nameCellAttr->DecRef();
212
213 m_EnvVars->SetCellValue( i, TV_VALUE_COL, aPath );
214
215 wxGridCellAttr* pathCellAttr = m_EnvVars->GetOrCreateCellAttr( i, TV_VALUE_COL );
216 wxSystemColour c = isExternal ? wxSYS_COLOUR_MENU : wxSYS_COLOUR_LISTBOX;
217 pathCellAttr->SetBackgroundColour( wxSystemSettings::GetColour( c ) );
218 pathCellAttr->DecRef();
219
220 m_EnvVars->SetCellValue( i, TV_FLAG_COL, isExternal ? wxT( "external" ) : wxEmptyString );
221}
222
223
225{
226 if( !m_EnvVars->CommitPendingChanges() )
227 return false;
228
229 if( !wxDialog::TransferDataFromWindow() )
230 return false;
231
232 // Update environment variables
233 ENV_VAR_MAP& envVarMap = Pgm().GetLocalEnvVariables();
234
235 for( int row = 0; row < m_EnvVars->GetNumberRows(); ++row )
236 {
237 wxString name = m_EnvVars->GetCellValue( row, TV_NAME_COL );
238 wxString path = m_EnvVars->GetCellValue( row, TV_VALUE_COL );
239 bool external = !m_EnvVars->GetCellValue( row, TV_FLAG_COL ).IsEmpty();
240
241 if( external )
242 {
243 // Don't check for consistency on external variables, just use them as-is
244 }
245 else if( name.empty() && path.empty() )
246 {
247 // Skip empty rows altogether
248 continue;
249 }
250 else if( name.IsEmpty() )
251 {
253 m_errorRow = row;
255 m_errorMsg = _( "Environment variable name cannot be empty." );
256 return false;
257 }
258 else if( path.IsEmpty() )
259 {
261 m_errorRow = row;
263 m_errorMsg = _( "Environment variable path cannot be empty." );
264 return false;
265 }
266
267 if( envVarMap.count( name ) )
268 envVarMap.at( name ).SetValue( path );
269 else
270 envVarMap[ name ] = ENV_VAR_ITEM( name, path );
271 }
272
273 // Remove deleted env vars
274 for( auto it = envVarMap.begin(); it != envVarMap.end(); )
275 {
276 bool found = false;
277
278 for( int row = 0; row < m_EnvVars->GetNumberRows(); ++row )
279 {
280 wxString name = m_EnvVars->GetCellValue( row, TV_NAME_COL );
281
282 if( it->first == name )
283 {
284 found = true;
285 break;
286 }
287 }
288
289 if( found )
290 ++it;
291 else
292 it = envVarMap.erase( it );
293 }
294
296
297 return true;
298}
299
300
302{
303 wxGrid* grid = dynamic_cast<wxGrid*>( event.GetEventObject() );
304
305 // Mid-edit notifications from text-button editors fire while the user is still typing.
306 // Only validate when the edit is being committed (cell edit control already disabled).
307 if( grid && grid->IsCellEditControlEnabled() )
308 return;
309
310 int row = event.GetRow();
311 int col = event.GetCol();
312 wxString text = event.GetString();
313
314 text.Trim( true ).Trim( false ); // Trim from both sides
315 grid->SetCellValue( row, col, text ); // Update the grid with trimmed value
316
317 if( text.IsEmpty() )
318 {
319 if( grid == m_EnvVars )
320 {
321 if( col == TV_NAME_COL )
322 m_errorMsg = _( "Environment variable name cannot be empty." );
323 else
324 m_errorMsg = _( "Environment variable path cannot be empty." );
325 }
326 else
327 {
328 if( col == SP_ALIAS_COL )
329 m_errorMsg = _( "3D search path alias cannot be empty." );
330 else
331 m_errorMsg = _( "3D search path cannot be empty." );
332 }
333
334 m_errorGrid = dynamic_cast<wxGrid*>( event.GetEventObject() );
335 m_errorRow = row;
336 m_errorCol = col;
337
338 event.Veto();
339 }
340
341 if( grid == m_EnvVars )
342 {
343 if( col == TV_VALUE_COL && m_EnvVars->GetCellValue( row, TV_FLAG_COL ).Length()
344 && !Pgm().GetCommonSettings()->m_DoNotShowAgain.env_var_overwrite_warning )
345 {
346 wxString msg1 = _( "This path was defined externally to the running process and\n"
347 "will only be temporarily overwritten." );
348 wxString msg2 = _( "The next time KiCad is launched, any paths that have already\n"
349 "been defined are honored and any settings defined in the path\n"
350 "configuration dialog are ignored. If you did not intend for\n"
351 "this behavior, either rename any conflicting entries or remove\n"
352 "the external environment variable(s) from your system." );
353 KIDIALOG dlg( this, msg1, KIDIALOG::KD_WARNING );
354 dlg.ShowDetailedText( msg2 );
355 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
356 dlg.ShowModal();
357
358 if( dlg.DoNotShowAgain() )
360 }
361 else if( col == TV_NAME_COL && m_EnvVars->GetCellValue( row, TV_NAME_COL ) != text )
362 {
363 // This env var name is reserved and cannot be added here.
364 if( text == PROJECT_VAR_NAME )
365 {
366 wxMessageBox( wxString::Format( _( "The name %s is reserved, and cannot be used." ),
368 event.Veto();
369 }
370 else // Changing name; clear external flag
371 {
372 m_EnvVars->SetCellValue( row, TV_FLAG_COL, wxEmptyString );
373 }
374 }
375 }
376}
377
378
379void DIALOG_CONFIGURE_PATHS::OnAddEnvVar( wxCommandEvent& event )
380{
381 m_EnvVars->OnAddRow(
382 [&]() -> std::pair<int, int>
383 {
384 AppendEnvVar( wxEmptyString, wxEmptyString, false );
385 return { m_EnvVars->GetNumberRows() - 1, TV_NAME_COL };
386 } );
387}
388
389
390void DIALOG_CONFIGURE_PATHS::OnRemoveEnvVar( wxCommandEvent& event )
391{
392 m_EnvVars->OnDeleteRows(
393 [&]( int row )
394 {
395 if( ENV_VAR::IsEnvVarImmutable( m_EnvVars->GetCellValue( row, TV_NAME_COL ) ) )
396 {
397 wxBell();
398 return false;
399 }
400
401 return true;
402 },
403 [&]( int row )
404 {
405 m_EnvVars->DeleteRows( row, 1 );
406 } );
407}
408
409
410void DIALOG_CONFIGURE_PATHS::OnUpdateUI( wxUpdateUIEvent& event )
411{
412 // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus even when
413 // the original validation was triggered from a killFocus event.
414 if( m_errorGrid )
415 {
416 // We will re-enter this routine when the error dialog is displayed, so make sure we don't
417 // keep putting up more dialogs.
418 wxGrid* grid = m_errorGrid;
419 m_errorGrid = nullptr;
420
422
423 grid->SetFocus();
424 grid->MakeCellVisible( m_errorRow, m_errorCol );
425 grid->SetGridCursor( m_errorRow, m_errorCol );
426
427 grid->EnableCellEditControl( true );
428 grid->ShowCellEditControl();
429 }
430}
431
432
433void DIALOG_CONFIGURE_PATHS::OnHelp( wxCommandEvent& event )
434{
435 wxSizer* sizerMain = GetSizer();
436
437 if( !m_helpBox )
438 {
439 m_helpBox = new HTML_WINDOW( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
440 wxHW_SCROLLBAR_AUTO );
441
442 wxString msg = _( "Enter the name and value for each environment variable. Grey entries "
443 "are names that have been defined externally at the system or user "
444 "level. Environment variables defined at the system or user level "
445 "take precedence over the ones defined in this table. This means the "
446 "values in this table are ignored." );
447 msg << "<br><br><b>";
448 msg << _( "To ensure environment variable names are valid on all platforms, the name field "
449 "will only accept upper case letters, digits, and the underscore characters." );
450 msg << "</b>";
451
452 for( const wxString& var : ENV_VAR::GetPredefinedEnvVars() )
453 {
454 msg << "<br><br><b>" << var << "</b>";
455
456 const auto desc = ENV_VAR::LookUpEnvVarHelp( var );
457
458 if( desc.size() > 0 )
459 msg << ": " << desc;
460
461 }
462
463 m_helpBox->SetPage( msg );
464 m_helpBox->Show( false );
465
466 sizerMain->Insert( sizerMain->GetItemCount() - 1, m_helpBox, 1, wxALL|wxEXPAND, 10 );
467 }
468
469 if( m_helpBox->IsShown() )
470 {
471 m_helpBox->Show( false );
472 SetClientSize( wxSize( GetClientSize().x, m_heightBeforeHelp ) );
473 }
474 else
475 {
476 m_helpBox->Show( true );
477 m_heightBeforeHelp = GetClientSize().y;
478
479 int minHelpBoxHeight = GetTextExtent( wxT( "T" ) ).y * 20;
480
481 if( GetClientSize().y < minHelpBoxHeight * 2 )
482 SetClientSize( wxSize( GetClientSize().x, GetClientSize().y + minHelpBoxHeight ) );
483 }
484
485 Layout();
486}
const char * name
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
DO_NOT_SHOW_AGAIN m_DoNotShowAgain
DIALOG_CONFIGURE_PATHS_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Configure Paths"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void AppendEnvVar(const wxString &aName, const wxString &aPath, bool isExternal)
void OnGridCellChanging(wxGridEvent &event)
void OnUpdateUI(wxUpdateUIEvent &event) override
void OnHelp(wxCommandEvent &event) override
void OnRemoveEnvVar(wxCommandEvent &event) override
DIALOG_CONFIGURE_PATHS(wxWindow *aParent)
void OnAddEnvVar(wxCommandEvent &event) override
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition dialog_shim.h:94
void SetupStandardButtons(std::map< int, wxString > aLabels={})
KiCad uses environment variables internally for determining the base paths for libraries,...
Provide a custom wxValidator object for limiting the allowable characters when defining an environmen...
Definition validators.h:65
Editor for wxGrid cells that adds a file/folder browser to the grid input field.
This class works around a bug in wxGrid where the first keystroke doesn't get sent through the valida...
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:57
Add dark theme support to wxHtmlWindow.
Definition html_window.h:31
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition kidialog.h:38
@ KD_WARNING
Definition kidialog.h:43
bool DoNotShowAgain() const
Checks the 'do not show again' setting for the dialog.
Definition kidialog.cpp:59
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
Definition kidialog.cpp:51
int ShowModal() override
Definition kidialog.cpp:89
std::vector< LIBRARY_TABLE_ROW * > Rows(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH, bool aIncludeInvalid=false) const
Returns a flattened list of libraries of the given type.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:546
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition pgm_base.cpp:792
virtual void SetLocalEnvVariables()
Update the local environment with the contents of the current ENV_VAR_MAP stored in the COMMON_SETTIN...
Definition pgm_base.cpp:776
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:125
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:776
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
This file is part of the common library.
#define _(s)
Functions related to environment variables, including help functions.
std::map< wxString, ENV_VAR_ITEM > ENV_VAR_MAP
LIBRARY_TABLE_TYPE
LIBRARY_TABLE_SCOPE
KICOMMON_API wxString LookUpEnvVarHelp(const wxString &aEnvVar)
Look up long-form help text for a given environment variable.
Definition env_vars.cpp:167
KICOMMON_API const std::vector< wxString > & GetPredefinedEnvVars()
Get the list of pre-defined environment variables.
Definition env_vars.cpp:62
KICOMMON_API bool IsEnvVarImmutable(const wxString &aEnvVar)
Determine if an environment variable is "predefined", i.e.
Definition env_vars.cpp:50
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition project.h:38
std::string path
Custom text control validator definitions.