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