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 (C) 2015-2022 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
29#include <bitmaps.h>
30#include <confirm.h>
31#include <kidialog.h>
32#include <validators.h>
35#include <filename_resolver.h>
36#include <env_vars.h>
37#include <grid_tricks.h>
38#include <pgm_base.h>
42#include <widgets/wx_grid.h>
44
45#include <wx/dirdlg.h>
46
47
49{
53};
54
56{
60};
61
62
65 m_errorGrid( nullptr ),
66 m_errorRow( -1 ),
67 m_errorCol( -1 ),
68 m_helpBox( nullptr ),
69 m_heightBeforeHelp( 400 )
70{
71 m_btnAddEnvVar->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
72 m_btnDeleteEnvVar->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
73
75 m_EnvVars->AppendCols( 1 ); // for the isExternal flags
76 m_EnvVars->HideCol( TV_FLAG_COL );
77 m_EnvVars->UseNativeColHeader( true );
78
79 wxGridCellAttr* attr = new wxGridCellAttr;
80 attr->SetEditor( new GRID_CELL_PATH_EDITOR( this, m_EnvVars, &m_curdir, wxEmptyString ) );
81 m_EnvVars->SetColAttr( TV_VALUE_COL, attr );
82
83 // Give a bit more room for combobox editors
84 m_EnvVars->SetDefaultRowSize( m_EnvVars->GetDefaultRowSize() + 4 );
85
86 m_EnvVars->PushEventHandler( new GRID_TRICKS( m_EnvVars,
87 [this]( wxCommandEvent& aEvent )
88 {
89 OnAddEnvVar( aEvent );
90 } ) );
91
92 m_EnvVars->SetSelectionMode( wxGrid::wxGridSelectionModes::wxGridSelectRows );
93
96
97 // wxFormBuilder doesn't include this event...
98 m_EnvVars->Connect( wxEVT_GRID_CELL_CHANGING,
100 nullptr, this );
101
102 m_gridAutosizer = std::make_unique<WX_GRID_AUTOSIZER>( *m_EnvVars,
104 { TV_NAME_COL, 72 },
105 { TV_VALUE_COL, 120 },
106 },
107 TV_VALUE_COL );
108
109 GetSizer()->SetSizeHints( this );
110 Centre();
111}
112
113
115{
116 // Delete the GRID_TRICKS.
117 m_EnvVars->PopEventHandler( true );
118
119 m_EnvVars->Disconnect( wxEVT_GRID_CELL_CHANGING,
120 wxGridEventHandler( DIALOG_CONFIGURE_PATHS::OnGridCellChanging ),
121 nullptr, this );
122}
123
124
126{
127 if( !wxDialog::TransferDataToWindow() )
128 return false;
129
130 const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
131
132 for( auto it = envVars.begin(); it != envVars.end(); ++it )
133 {
134 const wxString& path = it->second.GetValue();
135 AppendEnvVar( it->first, path, it->second.GetDefinedExternally() );
136
137 if( m_curdir.IsEmpty() && !path.StartsWith( "${" ) && !path.StartsWith( "$(" ) )
138 m_curdir = path;
139 }
140
141 return true;
142}
143
144
145void DIALOG_CONFIGURE_PATHS::AppendEnvVar( const wxString& aName, const wxString& aPath,
146 bool isExternal )
147{
148 int i = m_EnvVars->GetNumberRows();
149
150 m_EnvVars->AppendRows( 1 );
151
152 m_EnvVars->SetCellValue( i, TV_NAME_COL, aName );
153
154 wxGridCellAttr* nameCellAttr = m_EnvVars->GetOrCreateCellAttr( i, TV_NAME_COL );
155 wxGridCellTextEditor* nameTextEditor = new GRID_CELL_TEXT_EDITOR();
156 nameTextEditor->SetValidator( ENV_VAR_NAME_VALIDATOR() );
157 nameCellAttr->SetEditor( nameTextEditor );
158 nameCellAttr->SetReadOnly( ENV_VAR::IsEnvVarImmutable( aName ) );
159 nameCellAttr->DecRef();
160
161 m_EnvVars->SetCellValue( i, TV_VALUE_COL, aPath );
162
163 wxGridCellAttr* pathCellAttr = m_EnvVars->GetOrCreateCellAttr( i, TV_VALUE_COL );
164 wxSystemColour c = isExternal ? wxSYS_COLOUR_MENU : wxSYS_COLOUR_LISTBOX;
165 pathCellAttr->SetBackgroundColour( wxSystemSettings::GetColour( c ) );
166 pathCellAttr->DecRef();
167
168 m_EnvVars->SetCellValue( i, TV_FLAG_COL, isExternal ? wxT( "external" ) : wxEmptyString );
169}
170
171
173{
175 return false;
176
177 if( !wxDialog::TransferDataFromWindow() )
178 return false;
179
180 // Update environment variables
181 ENV_VAR_MAP& envVarMap = Pgm().GetLocalEnvVariables();
182
183 for( int row = 0; row < m_EnvVars->GetNumberRows(); ++row )
184 {
185 wxString name = m_EnvVars->GetCellValue( row, TV_NAME_COL );
186 wxString path = m_EnvVars->GetCellValue( row, TV_VALUE_COL );
187 bool external = !m_EnvVars->GetCellValue( row, TV_FLAG_COL ).IsEmpty();
188
189 if( external )
190 {
191 // Don't check for consistency on external variables, just use them as-is
192 }
193 else if( name.empty() && path.empty() )
194 {
195 // Skip empty rows altogether
196 continue;
197 }
198 else if( name.IsEmpty() )
199 {
201 m_errorRow = row;
203 m_errorMsg = _( "Environment variable name cannot be empty." );
204 return false;
205 }
206 else if( path.IsEmpty() )
207 {
209 m_errorRow = row;
211 m_errorMsg = _( "Environment variable path cannot be empty." );
212 return false;
213 }
214
215 if( envVarMap.count( name ) )
216 envVarMap.at( name ).SetValue( path );
217 else
218 envVarMap[ name ] = ENV_VAR_ITEM( name, path );
219 }
220
221 // Remove deleted env vars
222 for( auto it = envVarMap.begin(); it != envVarMap.end(); )
223 {
224 bool found = false;
225
226 for( int row = 0; row < m_EnvVars->GetNumberRows(); ++row )
227 {
228 wxString name = m_EnvVars->GetCellValue( row, TV_NAME_COL );
229
230 if( it->first == name )
231 {
232 found = true;
233 break;
234 }
235 }
236
237 if( found )
238 ++it;
239 else
240 it = envVarMap.erase( it );
241 }
242
244
245 return true;
246}
247
248
250{
251 wxGrid* grid = dynamic_cast<wxGrid*>( event.GetEventObject() );
252 int row = event.GetRow();
253 int col = event.GetCol();
254 wxString text = event.GetString();
255
256 if( text.IsEmpty() )
257 {
258 if( grid == m_EnvVars )
259 {
260 if( col == TV_NAME_COL )
261 m_errorMsg = _( "Environment variable name cannot be empty." );
262 else
263 m_errorMsg = _( "Environment variable path cannot be empty." );
264 }
265 else
266 {
267 if( col == SP_ALIAS_COL )
268 m_errorMsg = _( "3D search path alias cannot be empty." );
269 else
270 m_errorMsg = _( "3D search path cannot be empty." );
271 }
272
273 m_errorGrid = dynamic_cast<wxGrid*>( event.GetEventObject() );
274 m_errorRow = row;
275 m_errorCol = col;
276
277 event.Veto();
278 }
279
280 if( grid == m_EnvVars )
281 {
282 if( col == TV_VALUE_COL && m_EnvVars->GetCellValue( row, TV_FLAG_COL ).Length()
284 {
285 wxString msg1 = _( "This path was defined externally to the running process and\n"
286 "will only be temporarily overwritten." );
287 wxString msg2 = _( "The next time KiCad is launched, any paths that have already\n"
288 "been defined are honored and any settings defined in the path\n"
289 "configuration dialog are ignored. If you did not intend for\n"
290 "this behavior, either rename any conflicting entries or remove\n"
291 "the external environment variable(s) from your system." );
292 KIDIALOG dlg( this, msg1, KIDIALOG::KD_WARNING );
293 dlg.ShowDetailedText( msg2 );
294 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
295 dlg.ShowModal();
296
297 if( dlg.DoNotShowAgain() )
299 }
300 else if( col == TV_NAME_COL && m_EnvVars->GetCellValue( row, TV_NAME_COL ) != text )
301 {
302 // This env var name is reserved and cannot be added here.
303 if( text == PROJECT_VAR_NAME )
304 {
305 wxMessageBox( wxString::Format( _( "The name %s is reserved, and cannot be used." ),
307 event.Veto();
308 }
309 else // Changing name; clear external flag
310 {
311 m_EnvVars->SetCellValue( row, TV_FLAG_COL, wxEmptyString );
312 }
313 }
314 }
315}
316
317
318void DIALOG_CONFIGURE_PATHS::OnAddEnvVar( wxCommandEvent& event )
319{
321 return;
322
323 AppendEnvVar( wxEmptyString, wxEmptyString, false );
324
325 m_EnvVars->MakeCellVisible( m_EnvVars->GetNumberRows() - 1, TV_NAME_COL );
326 m_EnvVars->SetGridCursor( m_EnvVars->GetNumberRows() - 1, TV_NAME_COL );
327
328 m_EnvVars->EnableCellEditControl( true );
329 m_EnvVars->ShowCellEditControl();
330}
331
332
333void DIALOG_CONFIGURE_PATHS::OnRemoveEnvVar( wxCommandEvent& event )
334{
335 int curRow = m_EnvVars->GetGridCursorRow();
336
337 if( curRow < 0 || m_EnvVars->GetNumberRows() <= curRow )
338 {
339 return;
340 }
341 else if( ENV_VAR::IsEnvVarImmutable( m_EnvVars->GetCellValue( curRow, TV_NAME_COL ) ) )
342 {
343 wxBell();
344 return;
345 }
346
347 m_EnvVars->CommitPendingChanges( true /* silent mode; we don't care if it's valid */ );
348 m_EnvVars->DeleteRows( curRow, 1 );
349
350 m_EnvVars->MakeCellVisible( std::max( 0, curRow-1 ), m_EnvVars->GetGridCursorCol() );
351 m_EnvVars->SetGridCursor( std::max( 0, curRow-1 ), m_EnvVars->GetGridCursorCol() );
352}
353
354
355void DIALOG_CONFIGURE_PATHS::OnUpdateUI( wxUpdateUIEvent& event )
356{
357 // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus even when
358 // the original validation was triggered from a killFocus event.
359 if( m_errorGrid )
360 {
361 // We will re-enter this routine when the error dialog is displayed, so make sure we don't
362 // keep putting up more dialogs.
363 wxGrid* grid = m_errorGrid;
364 m_errorGrid = nullptr;
365
367
368 grid->SetFocus();
369 grid->MakeCellVisible( m_errorRow, m_errorCol );
370 grid->SetGridCursor( m_errorRow, m_errorCol );
371
372 grid->EnableCellEditControl( true );
373 grid->ShowCellEditControl();
374 }
375}
376
377
378void DIALOG_CONFIGURE_PATHS::OnHelp( wxCommandEvent& event )
379{
380 wxSizer* sizerMain = GetSizer();
381
382 if( !m_helpBox )
383 {
384 m_helpBox = new HTML_WINDOW( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
385 wxHW_SCROLLBAR_AUTO );
386
387 wxString msg = _( "Enter the name and value for each environment variable. Grey entries "
388 "are names that have been defined externally at the system or user "
389 "level. Environment variables defined at the system or user level "
390 "take precedence over the ones defined in this table. This means the "
391 "values in this table are ignored." );
392 msg << "<br><br><b>";
393 msg << _( "To ensure environment variable names are valid on all platforms, the name field "
394 "will only accept upper case letters, digits, and the underscore characters." );
395 msg << "</b>";
396
397 for( const wxString& var : ENV_VAR::GetPredefinedEnvVars() )
398 {
399 msg << "<br><br><b>" << var << "</b>";
400
401 const auto desc = ENV_VAR::LookUpEnvVarHelp( var );
402
403 if( desc.size() > 0 )
404 msg << ": " << desc;
405
406 }
407
408 m_helpBox->SetPage( msg );
409 m_helpBox->Show( false );
410
411 sizerMain->Insert( sizerMain->GetItemCount() - 1, m_helpBox, 1, wxALL|wxEXPAND, 10 );
412 }
413
414 if( m_helpBox->IsShown() )
415 {
416 m_helpBox->Show( false );
417 SetClientSize( wxSize( GetClientSize().x, m_heightBeforeHelp ) );
418 }
419 else
420 {
421 m_helpBox->Show( true );
422 m_heightBeforeHelp = GetClientSize().y;
423
424 int minHelpBoxHeight = GetTextExtent( wxT( "T" ) ).y * 20;
425
426 if( GetClientSize().y < minHelpBoxHeight * 2 )
427 SetClientSize( wxSize( GetClientSize().x, GetClientSize().y + minHelpBoxHeight ) );
428 }
429
430 Layout();
431}
const char * name
Definition: DXF_plotter.cpp:57
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
DO_NOT_SHOW_AGAIN m_DoNotShowAgain
Class DIALOG_CONFIGURE_PATHS_BASE.
std::unique_ptr< WX_GRID_AUTOSIZER > m_gridAutosizer
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:102
void SetupStandardButtons(std::map< int, wxString > aLabels={})
KiCad uses environment variables internally for determining the base paths for libraries,...
This class provides a custom wxValidator object for limiting the allowable characters when defining a...
Definition: validators.h:88
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
bool SetPage(const wxString &aSource) override
Definition: html_window.cpp:50
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: kidialog.h:43
@ KD_WARNING
Definition: kidialog.h:46
bool DoNotShowAgain() const
Definition: kidialog.cpp:59
void DoNotShowCheckbox(wxString file, int line)
Checks the 'do not show again' setting for the dialog.
Definition: kidialog.cpp:51
int ShowModal() override
Definition: kidialog.cpp:95
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition: pgm_base.cpp:679
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition: pgm_base.cpp:924
virtual void SetLocalEnvVariables()
Updates the local environment with the contents of the current ENV_VAR_MAP stored in the COMMON_SETTI...
Definition: pgm_base.cpp:908
void SetBitmap(const wxBitmapBundle &aBmp)
std::map< int, int > COL_MIN_WIDTHS
Map of column indices to minimum widths.
void ClearRows()
wxWidgets recently added an ASSERT which fires if the position is greater than or equal to the number...
Definition: wx_grid.h:184
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:637
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:195
This file is part of the common library.
SEARCH_PATH_GRID_COLUMNS
#define _(s)
Functions related to environment variables, including help functions.
std::map< wxString, ENV_VAR_ITEM > ENV_VAR_MAP
This file is part of the common library.
KICOMMON_API wxString LookUpEnvVarHelp(const wxString &aEnvVar)
Look up long-form help text for a given environment variable.
Definition: env_vars.cpp:145
KICOMMON_API const ENV_VAR_LIST & GetPredefinedEnvVars()
Get the list of pre-defined environment variables.
Definition: env_vars.cpp:68
KICOMMON_API bool IsEnvVarImmutable(const wxString &aEnvVar)
Determine if an environment variable is "predefined", i.e.
Definition: env_vars.cpp:53
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1060
see class PGM_BASE
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:40
Custom text control validator definitions.