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, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
26
27#include <algorithm>
28#include <set>
29
30#include <bitmaps.h>
31#include <confirm.h>
32#include <kidialog.h>
33#include <validators.h>
36#include <filename_resolver.h>
37#include <env_vars.h>
38#include <grid_tricks.h>
40#include <pgm_base.h>
44#include <widgets/wx_grid.h>
45
46#include <wx/dirdlg.h>
47#include <wx/regex.h>
48
49
56
63
64
67 m_errorGrid( nullptr ),
68 m_errorRow( -1 ),
69 m_errorCol( -1 ),
70 m_helpBox( nullptr )
71{
72 m_heightBeforeHelp = FromDIP ( 400 );
75
76 m_EnvVars->SetMinSize( FromDIP( m_EnvVars->GetMinSize() ) );
77
78 m_EnvVars->ClearRows();
79 m_EnvVars->AppendCols( 1 ); // for the isExternal flags
80 m_EnvVars->HideCol( TV_FLAG_COL );
81 m_EnvVars->UseNativeColHeader( true );
82
83 wxGridCellAttr* attr = new wxGridCellAttr;
84 attr->SetEditor( new GRID_CELL_PATH_EDITOR( this, m_EnvVars, &m_curdir, wxEmptyString ) );
85 m_EnvVars->SetColAttr( TV_VALUE_COL, attr );
86
87 m_EnvVars->PushEventHandler( new GRID_TRICKS( m_EnvVars,
88 [this]( wxCommandEvent& aEvent )
89 {
90 OnAddEnvVar( aEvent );
91 } ) );
92
93 m_EnvVars->SetSelectionMode( wxGrid::wxGridSelectionModes::wxGridSelectRows );
94
97
98 // wxFormBuilder doesn't include this event...
99 m_EnvVars->Connect( wxEVT_GRID_CELL_CHANGING,
100 wxGridEventHandler( DIALOG_CONFIGURE_PATHS::OnGridCellChanging ),
101 nullptr, this );
102
103 m_EnvVars->SetupColumnAutosizer( TV_VALUE_COL );
104
105 GetSizer()->SetSizeHints( this );
106 Centre();
107}
108
109
111{
112 // Delete the GRID_TRICKS.
113 m_EnvVars->PopEventHandler( true );
114
115 m_EnvVars->Disconnect( wxEVT_GRID_CELL_CHANGING,
116 wxGridEventHandler( DIALOG_CONFIGURE_PATHS::OnGridCellChanging ),
117 nullptr, this );
118}
119
120
122{
123 if( !wxDialog::TransferDataToWindow() )
124 return false;
125
126 const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
127
128 for( auto it = envVars.begin(); it != envVars.end(); ++it )
129 {
130 const wxString& path = it->second.GetValue();
131 AppendEnvVar( it->first, path, it->second.GetDefinedExternally() );
132
133 if( m_curdir.IsEmpty() && !path.StartsWith( "${" ) && !path.StartsWith( "$(" ) )
134 m_curdir = path;
135 }
136
137 // Scan library tables for environment variables that are used but not in envVars.
138 // This allows users to see and manage obsolete versioned variables (e.g., KICAD8_FOOTPRINT_DIR)
139 // that may still be referenced in library tables from older KiCad versions.
140 wxRegEx envVarRegex( wxS( ".*?(\\$\\{(.+?)\\})|(\\$\\((.+?)\\)).*?" ), wxRE_ADVANCED );
141 std::set<wxString> foundEnvVars;
142
144
148 {
151 {
152 std::vector<LIBRARY_TABLE_ROW*> rows = libMgr.Rows( tableType, scope, true );
153
154 for( const LIBRARY_TABLE_ROW* row : rows )
155 {
156 wxString uri = row->URI();
157
158 while( envVarRegex.Matches( uri ) )
159 {
160 wxString envvar = envVarRegex.GetMatch( uri, 2 );
161
162 if( envvar.IsEmpty() )
163 envvar = envVarRegex.GetMatch( uri, 4 );
164
165 if( !envvar.IsEmpty() )
166 foundEnvVars.insert( envvar );
167
168 uri.Replace( envVarRegex.GetMatch( uri, 0 ), wxEmptyString );
169 }
170 }
171 }
172 }
173
174 for( const wxString& envvar : foundEnvVars )
175 {
176 if( envVars.count( envvar ) == 0 && envvar != PROJECT_VAR_NAME )
177 {
178 wxString value;
179 bool isExternal = wxGetEnv( envvar, &value );
180
181 AppendEnvVar( envvar, value, isExternal );
182 }
183 }
184
185 return true;
186}
187
188
189void DIALOG_CONFIGURE_PATHS::AppendEnvVar( const wxString& aName, const wxString& aPath,
190 bool isExternal )
191{
192 int i = m_EnvVars->GetNumberRows();
193
194 m_EnvVars->AppendRows( 1 );
195
196 m_EnvVars->SetCellValue( i, TV_NAME_COL, aName );
197
198 wxGridCellAttr* nameCellAttr = m_EnvVars->GetOrCreateCellAttr( i, TV_NAME_COL );
199 wxGridCellTextEditor* nameTextEditor = new GRID_CELL_TEXT_EDITOR();
200 nameTextEditor->SetValidator( ENV_VAR_NAME_VALIDATOR() );
201 nameCellAttr->SetEditor( nameTextEditor );
202 nameCellAttr->SetReadOnly( ENV_VAR::IsEnvVarImmutable( aName ) );
203 nameCellAttr->DecRef();
204
205 m_EnvVars->SetCellValue( i, TV_VALUE_COL, aPath );
206
207 wxGridCellAttr* pathCellAttr = m_EnvVars->GetOrCreateCellAttr( i, TV_VALUE_COL );
208 wxSystemColour c = isExternal ? wxSYS_COLOUR_MENU : wxSYS_COLOUR_LISTBOX;
209 pathCellAttr->SetBackgroundColour( wxSystemSettings::GetColour( c ) );
210 pathCellAttr->DecRef();
211
212 m_EnvVars->SetCellValue( i, TV_FLAG_COL, isExternal ? wxT( "external" ) : wxEmptyString );
213}
214
215
217{
218 if( !m_EnvVars->CommitPendingChanges() )
219 return false;
220
221 if( !wxDialog::TransferDataFromWindow() )
222 return false;
223
224 // Update environment variables
225 ENV_VAR_MAP& envVarMap = Pgm().GetLocalEnvVariables();
226
227 for( int row = 0; row < m_EnvVars->GetNumberRows(); ++row )
228 {
229 wxString name = m_EnvVars->GetCellValue( row, TV_NAME_COL );
230 wxString path = m_EnvVars->GetCellValue( row, TV_VALUE_COL );
231 bool external = !m_EnvVars->GetCellValue( row, TV_FLAG_COL ).IsEmpty();
232
233 if( external )
234 {
235 // Don't check for consistency on external variables, just use them as-is
236 }
237 else if( name.empty() && path.empty() )
238 {
239 // Skip empty rows altogether
240 continue;
241 }
242 else if( name.IsEmpty() )
243 {
245 m_errorRow = row;
247 m_errorMsg = _( "Environment variable name cannot be empty." );
248 return false;
249 }
250 else if( path.IsEmpty() )
251 {
253 m_errorRow = row;
255 m_errorMsg = _( "Environment variable path cannot be empty." );
256 return false;
257 }
258
259 if( envVarMap.count( name ) )
260 envVarMap.at( name ).SetValue( path );
261 else
262 envVarMap[ name ] = ENV_VAR_ITEM( name, path );
263 }
264
265 // Remove deleted env vars
266 for( auto it = envVarMap.begin(); it != envVarMap.end(); )
267 {
268 bool found = false;
269
270 for( int row = 0; row < m_EnvVars->GetNumberRows(); ++row )
271 {
272 wxString name = m_EnvVars->GetCellValue( row, TV_NAME_COL );
273
274 if( it->first == name )
275 {
276 found = true;
277 break;
278 }
279 }
280
281 if( found )
282 ++it;
283 else
284 it = envVarMap.erase( it );
285 }
286
288
289 return true;
290}
291
292
294{
295 wxGrid* grid = dynamic_cast<wxGrid*>( event.GetEventObject() );
296 int row = event.GetRow();
297 int col = event.GetCol();
298 wxString text = event.GetString();
299
300 text.Trim( true ).Trim( false ); // Trim from both sides
301 grid->SetCellValue( row, col, text ); // Update the grid with trimmed value
302
303 if( text.IsEmpty() )
304 {
305 if( grid == m_EnvVars )
306 {
307 if( col == TV_NAME_COL )
308 m_errorMsg = _( "Environment variable name cannot be empty." );
309 else
310 m_errorMsg = _( "Environment variable path cannot be empty." );
311 }
312 else
313 {
314 if( col == SP_ALIAS_COL )
315 m_errorMsg = _( "3D search path alias cannot be empty." );
316 else
317 m_errorMsg = _( "3D search path cannot be empty." );
318 }
319
320 m_errorGrid = dynamic_cast<wxGrid*>( event.GetEventObject() );
321 m_errorRow = row;
322 m_errorCol = col;
323
324 event.Veto();
325 }
326
327 if( grid == m_EnvVars )
328 {
329 if( col == TV_VALUE_COL && m_EnvVars->GetCellValue( row, TV_FLAG_COL ).Length()
330 && !Pgm().GetCommonSettings()->m_DoNotShowAgain.env_var_overwrite_warning )
331 {
332 wxString msg1 = _( "This path was defined externally to the running process and\n"
333 "will only be temporarily overwritten." );
334 wxString msg2 = _( "The next time KiCad is launched, any paths that have already\n"
335 "been defined are honored and any settings defined in the path\n"
336 "configuration dialog are ignored. If you did not intend for\n"
337 "this behavior, either rename any conflicting entries or remove\n"
338 "the external environment variable(s) from your system." );
339 KIDIALOG dlg( this, msg1, KIDIALOG::KD_WARNING );
340 dlg.ShowDetailedText( msg2 );
341 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
342 dlg.ShowModal();
343
344 if( dlg.DoNotShowAgain() )
346 }
347 else if( col == TV_NAME_COL && m_EnvVars->GetCellValue( row, TV_NAME_COL ) != text )
348 {
349 // This env var name is reserved and cannot be added here.
350 if( text == PROJECT_VAR_NAME )
351 {
352 wxMessageBox( wxString::Format( _( "The name %s is reserved, and cannot be used." ),
354 event.Veto();
355 }
356 else // Changing name; clear external flag
357 {
358 m_EnvVars->SetCellValue( row, TV_FLAG_COL, wxEmptyString );
359 }
360 }
361 }
362}
363
364
365void DIALOG_CONFIGURE_PATHS::OnAddEnvVar( wxCommandEvent& event )
366{
367 m_EnvVars->OnAddRow(
368 [&]() -> std::pair<int, int>
369 {
370 AppendEnvVar( wxEmptyString, wxEmptyString, false );
371 return { m_EnvVars->GetNumberRows() - 1, TV_NAME_COL };
372 } );
373}
374
375
376void DIALOG_CONFIGURE_PATHS::OnRemoveEnvVar( wxCommandEvent& event )
377{
378 m_EnvVars->OnDeleteRows(
379 [&]( int row )
380 {
381 if( ENV_VAR::IsEnvVarImmutable( m_EnvVars->GetCellValue( row, TV_NAME_COL ) ) )
382 {
383 wxBell();
384 return false;
385 }
386
387 return true;
388 },
389 [&]( int row )
390 {
391 m_EnvVars->DeleteRows( row, 1 );
392 } );
393}
394
395
396void DIALOG_CONFIGURE_PATHS::OnUpdateUI( wxUpdateUIEvent& event )
397{
398 // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus even when
399 // the original validation was triggered from a killFocus event.
400 if( m_errorGrid )
401 {
402 // We will re-enter this routine when the error dialog is displayed, so make sure we don't
403 // keep putting up more dialogs.
404 wxGrid* grid = m_errorGrid;
405 m_errorGrid = nullptr;
406
408
409 grid->SetFocus();
410 grid->MakeCellVisible( m_errorRow, m_errorCol );
411 grid->SetGridCursor( m_errorRow, m_errorCol );
412
413 grid->EnableCellEditControl( true );
414 grid->ShowCellEditControl();
415 }
416}
417
418
419void DIALOG_CONFIGURE_PATHS::OnHelp( wxCommandEvent& event )
420{
421 wxSizer* sizerMain = GetSizer();
422
423 if( !m_helpBox )
424 {
425 m_helpBox = new HTML_WINDOW( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
426 wxHW_SCROLLBAR_AUTO );
427
428 wxString msg = _( "Enter the name and value for each environment variable. Grey entries "
429 "are names that have been defined externally at the system or user "
430 "level. Environment variables defined at the system or user level "
431 "take precedence over the ones defined in this table. This means the "
432 "values in this table are ignored." );
433 msg << "<br><br><b>";
434 msg << _( "To ensure environment variable names are valid on all platforms, the name field "
435 "will only accept upper case letters, digits, and the underscore characters." );
436 msg << "</b>";
437
438 for( const wxString& var : ENV_VAR::GetPredefinedEnvVars() )
439 {
440 msg << "<br><br><b>" << var << "</b>";
441
442 const auto desc = ENV_VAR::LookUpEnvVarHelp( var );
443
444 if( desc.size() > 0 )
445 msg << ": " << desc;
446
447 }
448
449 m_helpBox->SetPage( msg );
450 m_helpBox->Show( false );
451
452 sizerMain->Insert( sizerMain->GetItemCount() - 1, m_helpBox, 1, wxALL|wxEXPAND, 10 );
453 }
454
455 if( m_helpBox->IsShown() )
456 {
457 m_helpBox->Show( false );
458 SetClientSize( wxSize( GetClientSize().x, m_heightBeforeHelp ) );
459 }
460 else
461 {
462 m_helpBox->Show( true );
463 m_heightBeforeHelp = GetClientSize().y;
464
465 int minHelpBoxHeight = GetTextExtent( wxT( "T" ) ).y * 20;
466
467 if( GetClientSize().y < minHelpBoxHeight * 2 )
468 SetClientSize( wxSize( GetClientSize().x, GetClientSize().y + minHelpBoxHeight ) );
469 }
470
471 Layout();
472}
const char * name
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
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:82
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:69
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:61
Add dark theme support to wxHtmlWindow.
Definition html_window.h:35
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition kidialog.h:42
@ KD_WARNING
Definition kidialog.h:47
bool DoNotShowAgain() const
Checks the 'do not show again' setting for the dialog.
Definition kidialog.cpp:63
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
Definition kidialog.cpp:55
int ShowModal() override
Definition kidialog.cpp:93
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:535
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition pgm_base.cpp:781
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:765
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:134
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:202
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:152
KICOMMON_API const std::vector< wxString > & GetPredefinedEnvVars()
Get the list of pre-defined environment variables.
Definition env_vars.cpp:61
KICOMMON_API bool IsEnvVarImmutable(const wxString &aEnvVar)
Determine if an environment variable is "predefined", i.e.
Definition env_vars.cpp:49
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:41
std::string path
Custom text control validator definitions.