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
297 // Mid-edit notifications from text-button editors fire while the user is still typing.
298 // Only validate when the edit is being committed (cell edit control already disabled).
299 if( grid && grid->IsCellEditControlEnabled() )
300 return;
301
302 int row = event.GetRow();
303 int col = event.GetCol();
304 wxString text = event.GetString();
305
306 text.Trim( true ).Trim( false ); // Trim from both sides
307 grid->SetCellValue( row, col, text ); // Update the grid with trimmed value
308
309 if( text.IsEmpty() )
310 {
311 if( grid == m_EnvVars )
312 {
313 if( col == TV_NAME_COL )
314 m_errorMsg = _( "Environment variable name cannot be empty." );
315 else
316 m_errorMsg = _( "Environment variable path cannot be empty." );
317 }
318 else
319 {
320 if( col == SP_ALIAS_COL )
321 m_errorMsg = _( "3D search path alias cannot be empty." );
322 else
323 m_errorMsg = _( "3D search path cannot be empty." );
324 }
325
326 m_errorGrid = dynamic_cast<wxGrid*>( event.GetEventObject() );
327 m_errorRow = row;
328 m_errorCol = col;
329
330 event.Veto();
331 }
332
333 if( grid == m_EnvVars )
334 {
335 if( col == TV_VALUE_COL && m_EnvVars->GetCellValue( row, TV_FLAG_COL ).Length()
336 && !Pgm().GetCommonSettings()->m_DoNotShowAgain.env_var_overwrite_warning )
337 {
338 wxString msg1 = _( "This path was defined externally to the running process and\n"
339 "will only be temporarily overwritten." );
340 wxString msg2 = _( "The next time KiCad is launched, any paths that have already\n"
341 "been defined are honored and any settings defined in the path\n"
342 "configuration dialog are ignored. If you did not intend for\n"
343 "this behavior, either rename any conflicting entries or remove\n"
344 "the external environment variable(s) from your system." );
345 KIDIALOG dlg( this, msg1, KIDIALOG::KD_WARNING );
346 dlg.ShowDetailedText( msg2 );
347 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
348 dlg.ShowModal();
349
350 if( dlg.DoNotShowAgain() )
352 }
353 else if( col == TV_NAME_COL && m_EnvVars->GetCellValue( row, TV_NAME_COL ) != text )
354 {
355 // This env var name is reserved and cannot be added here.
356 if( text == PROJECT_VAR_NAME )
357 {
358 wxMessageBox( wxString::Format( _( "The name %s is reserved, and cannot be used." ),
360 event.Veto();
361 }
362 else // Changing name; clear external flag
363 {
364 m_EnvVars->SetCellValue( row, TV_FLAG_COL, wxEmptyString );
365 }
366 }
367 }
368}
369
370
371void DIALOG_CONFIGURE_PATHS::OnAddEnvVar( wxCommandEvent& event )
372{
373 m_EnvVars->OnAddRow(
374 [&]() -> std::pair<int, int>
375 {
376 AppendEnvVar( wxEmptyString, wxEmptyString, false );
377 return { m_EnvVars->GetNumberRows() - 1, TV_NAME_COL };
378 } );
379}
380
381
382void DIALOG_CONFIGURE_PATHS::OnRemoveEnvVar( wxCommandEvent& event )
383{
384 m_EnvVars->OnDeleteRows(
385 [&]( int row )
386 {
387 if( ENV_VAR::IsEnvVarImmutable( m_EnvVars->GetCellValue( row, TV_NAME_COL ) ) )
388 {
389 wxBell();
390 return false;
391 }
392
393 return true;
394 },
395 [&]( int row )
396 {
397 m_EnvVars->DeleteRows( row, 1 );
398 } );
399}
400
401
402void DIALOG_CONFIGURE_PATHS::OnUpdateUI( wxUpdateUIEvent& event )
403{
404 // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus even when
405 // the original validation was triggered from a killFocus event.
406 if( m_errorGrid )
407 {
408 // We will re-enter this routine when the error dialog is displayed, so make sure we don't
409 // keep putting up more dialogs.
410 wxGrid* grid = m_errorGrid;
411 m_errorGrid = nullptr;
412
414
415 grid->SetFocus();
416 grid->MakeCellVisible( m_errorRow, m_errorCol );
417 grid->SetGridCursor( m_errorRow, m_errorCol );
418
419 grid->EnableCellEditControl( true );
420 grid->ShowCellEditControl();
421 }
422}
423
424
425void DIALOG_CONFIGURE_PATHS::OnHelp( wxCommandEvent& event )
426{
427 wxSizer* sizerMain = GetSizer();
428
429 if( !m_helpBox )
430 {
431 m_helpBox = new HTML_WINDOW( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
432 wxHW_SCROLLBAR_AUTO );
433
434 wxString msg = _( "Enter the name and value for each environment variable. Grey entries "
435 "are names that have been defined externally at the system or user "
436 "level. Environment variables defined at the system or user level "
437 "take precedence over the ones defined in this table. This means the "
438 "values in this table are ignored." );
439 msg << "<br><br><b>";
440 msg << _( "To ensure environment variable names are valid on all platforms, the name field "
441 "will only accept upper case letters, digits, and the underscore characters." );
442 msg << "</b>";
443
444 for( const wxString& var : ENV_VAR::GetPredefinedEnvVars() )
445 {
446 msg << "<br><br><b>" << var << "</b>";
447
448 const auto desc = ENV_VAR::LookUpEnvVarHelp( var );
449
450 if( desc.size() > 0 )
451 msg << ": " << desc;
452
453 }
454
455 m_helpBox->SetPage( msg );
456 m_helpBox->Show( false );
457
458 sizerMain->Insert( sizerMain->GetItemCount() - 1, m_helpBox, 1, wxALL|wxEXPAND, 10 );
459 }
460
461 if( m_helpBox->IsShown() )
462 {
463 m_helpBox->Show( false );
464 SetClientSize( wxSize( GetClientSize().x, m_heightBeforeHelp ) );
465 }
466 else
467 {
468 m_helpBox->Show( true );
469 m_heightBeforeHelp = GetClientSize().y;
470
471 int minHelpBoxHeight = GetTextExtent( wxT( "T" ) ).y * 20;
472
473 if( GetClientSize().y < minHelpBoxHeight * 2 )
474 SetClientSize( wxSize( GetClientSize().x, GetClientSize().y + minHelpBoxHeight ) );
475 }
476
477 Layout();
478}
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:541
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition pgm_base.cpp:787
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:771
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:133
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.