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 <bitmaps.h>
28#include <confirm.h>
29#include <validators.h>
31#include <filename_resolver.h>
32#include <env_vars.h>
33#include <grid_tricks.h>
34#include <pgm_base.h>
35#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{
67 m_btnAddEnvVar->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
68 m_btnDeleteEnvVar->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
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.empty() && path.empty() )
183 {
184 // Skip empty rows altogether
185 continue;
186 }
187 else if( name.IsEmpty() )
188 {
190 m_errorRow = row;
192 m_errorMsg = _( "Environment variable name cannot be empty." );
193 return false;
194 }
195 else if( path.IsEmpty() )
196 {
198 m_errorRow = row;
200 m_errorMsg = _( "Environment variable path cannot be empty." );
201 return false;
202 }
203
204 if( envVarMap.count( name ) )
205 envVarMap.at( name ).SetValue( path );
206 else
207 envVarMap[ name ] = ENV_VAR_ITEM( name, path );
208 }
209
210 // Remove deleted env vars
211 for( auto it = envVarMap.begin(); it != envVarMap.end(); )
212 {
213 bool found = false;
214
215 for( int row = 0; row < m_EnvVars->GetNumberRows(); ++row )
216 {
217 wxString name = m_EnvVars->GetCellValue( row, TV_NAME_COL );
218
219 if( it->first == name )
220 {
221 found = true;
222 break;
223 }
224 }
225
226 if( found )
227 ++it;
228 else
229 it = envVarMap.erase( it );
230 }
231
232 Pgm().SetLocalEnvVariables();
233
234 return true;
235}
236
237
239{
240 wxGrid* grid = dynamic_cast<wxGrid*>( event.GetEventObject() );
241 int row = event.GetRow();
242 int col = event.GetCol();
243 wxString text = event.GetString();
244
245 if( text.IsEmpty() )
246 {
247 if( grid == m_EnvVars )
248 {
249 if( col == TV_NAME_COL )
250 m_errorMsg = _( "Environment variable name cannot be empty." );
251 else
252 m_errorMsg = _( "Environment variable path cannot be empty." );
253 }
254 else
255 {
256 if( col == SP_ALIAS_COL )
257 m_errorMsg = _( "3D search path alias cannot be empty." );
258 else
259 m_errorMsg = _( "3D search path cannot be empty." );
260 }
261
262 m_errorGrid = dynamic_cast<wxGrid*>( event.GetEventObject() );
263 m_errorRow = row;
264 m_errorCol = col;
265
266 event.Veto();
267 }
268
269 if( grid == m_EnvVars )
270 {
271 if( col == TV_VALUE_COL && m_EnvVars->GetCellValue( row, TV_FLAG_COL ).Length()
272 && !Pgm().GetCommonSettings()->m_DoNotShowAgain.env_var_overwrite_warning )
273 {
274 wxString msg1 = _( "This path was defined externally to the running process and\n"
275 "will only be temporarily overwritten." );
276 wxString msg2 = _( "The next time KiCad is launched, any paths that have already\n"
277 "been defined are honored and any settings defined in the path\n"
278 "configuration dialog are ignored. If you did not intend for\n"
279 "this behavior, either rename any conflicting entries or remove\n"
280 "the external environment variable(s) from your system." );
281 KIDIALOG dlg( this, msg1, KIDIALOG::KD_WARNING );
282 dlg.ShowDetailedText( msg2 );
283 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
284 dlg.ShowModal();
285
286 if( dlg.DoNotShowAgain() )
287 Pgm().GetCommonSettings()->m_DoNotShowAgain.env_var_overwrite_warning = true;
288 }
289 else if( col == TV_NAME_COL && m_EnvVars->GetCellValue( row, TV_NAME_COL ) != text )
290 {
291 // This env var name is reserved and cannot be added here.
292 if( text == PROJECT_VAR_NAME )
293 {
294 wxMessageBox( wxString::Format( _( "The name %s is reserved, and cannot be used." ),
296 event.Veto();
297 }
298 else // Changing name; clear external flag
299 {
300 m_EnvVars->SetCellValue( row, TV_FLAG_COL, wxEmptyString );
301 }
302 }
303 }
304}
305
306
307void DIALOG_CONFIGURE_PATHS::OnAddEnvVar( wxCommandEvent& event )
308{
310 return;
311
312 AppendEnvVar( wxEmptyString, wxEmptyString, false );
313
314 m_EnvVars->MakeCellVisible( m_EnvVars->GetNumberRows() - 1, TV_NAME_COL );
315 m_EnvVars->SetGridCursor( m_EnvVars->GetNumberRows() - 1, TV_NAME_COL );
316
317 m_EnvVars->EnableCellEditControl( true );
318 m_EnvVars->ShowCellEditControl();
319}
320
321
322void DIALOG_CONFIGURE_PATHS::OnRemoveEnvVar( wxCommandEvent& event )
323{
324 int curRow = m_EnvVars->GetGridCursorRow();
325
326 if( curRow < 0 || m_EnvVars->GetNumberRows() <= curRow )
327 {
328 return;
329 }
330 else if( ENV_VAR::IsEnvVarImmutable( m_EnvVars->GetCellValue( curRow, TV_NAME_COL ) ) )
331 {
332 wxBell();
333 return;
334 }
335
336 m_EnvVars->CommitPendingChanges( true /* silent mode; we don't care if it's valid */ );
337 m_EnvVars->DeleteRows( curRow, 1 );
338
339 m_EnvVars->MakeCellVisible( std::max( 0, curRow-1 ), m_EnvVars->GetGridCursorCol() );
340 m_EnvVars->SetGridCursor( std::max( 0, curRow-1 ), m_EnvVars->GetGridCursorCol() );
341}
342
343
344void DIALOG_CONFIGURE_PATHS::OnUpdateUI( wxUpdateUIEvent& event )
345{
347 {
348 int width = m_EnvVars->GetClientRect().GetWidth();
349
350 m_EnvVars->AutoSizeColumn( TV_NAME_COL );
351 m_EnvVars->SetColSize( TV_NAME_COL, std::max( 72, m_EnvVars->GetColSize( TV_NAME_COL ) ) );
352
353 m_EnvVars->SetColSize( TV_VALUE_COL, std::max( 120, width - m_EnvVars->GetColSize( TV_NAME_COL ) ) );
354
355 m_gridWidth = m_EnvVars->GetSize().GetX();
356 m_gridWidthsDirty = false;
357 }
358
359 // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus even when
360 // the original validation was triggered from a killFocus event.
361 if( m_errorGrid )
362 {
363 // We will re-enter this routine when the error dialog is displayed, so make sure we don't
364 // keep putting up more dialogs.
365 wxGrid* grid = m_errorGrid;
366 m_errorGrid = nullptr;
367
369
370 grid->SetFocus();
371 grid->MakeCellVisible( m_errorRow, m_errorCol );
372 grid->SetGridCursor( m_errorRow, m_errorCol );
373
374 grid->EnableCellEditControl( true );
375 grid->ShowCellEditControl();
376 }
377}
378
379
380void DIALOG_CONFIGURE_PATHS::OnGridSize( wxSizeEvent& event )
381{
382 if( event.GetSize().GetX() != m_gridWidth )
383 m_gridWidthsDirty = true;
384
385 event.Skip();
386}
387
388
389void DIALOG_CONFIGURE_PATHS::OnHelp( wxCommandEvent& event )
390{
391 wxSizer* sizerMain = GetSizer();
392
393 if( !m_helpBox )
394 {
395 m_helpBox = new HTML_WINDOW( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
396 wxHW_SCROLLBAR_AUTO );
397
398 wxString msg = _( "Enter the name and value for each environment variable. Grey entries "
399 "are names that have been defined externally at the system or user "
400 "level. Environment variables defined at the system or user level "
401 "take precedence over the ones defined in this table. This means the "
402 "values in this table are ignored." );
403 msg << "<br><br><b>";
404 msg << _( "To ensure environment variable names are valid on all platforms, the name field "
405 "will only accept upper case letters, digits, and the underscore characters." );
406 msg << "</b>";
407
408 for( const wxString& var : ENV_VAR::GetPredefinedEnvVars() )
409 {
410 msg << "<br><br><b>" << var << "</b>";
411
412 const auto desc = ENV_VAR::LookUpEnvVarHelp( var );
413
414 if( desc.size() > 0 )
415 msg << ": " << desc;
416
417 }
418
419 m_helpBox->SetPage( msg );
420 m_helpBox->Show( false );
421
422 sizerMain->Insert( sizerMain->GetItemCount() - 1, m_helpBox, 1, wxALL|wxEXPAND, 10 );
423 }
424
425 if( m_helpBox->IsShown() )
426 {
427 m_helpBox->Show( false );
428 SetClientSize( wxSize( GetClientSize().x, m_heightBeforeHelp ) );
429 }
430 else
431 {
432 m_helpBox->Show( true );
433 m_heightBeforeHelp = GetClientSize().y;
434
435 int minHelpBoxHeight = GetTextExtent( wxT( "T" ) ).y * 20;
436
437 if( GetClientSize().y < minHelpBoxHeight * 2 )
438 SetClientSize( wxSize( GetClientSize().x, GetClientSize().y + minHelpBoxHeight ) );
439 }
440
441 Layout();
442}
const char * name
Definition: DXF_plotter.cpp:57
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
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:105
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:58
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:47
@ KD_WARNING
Definition: confirm.h:50
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 SetBitmap(const wxBitmapBundle &aBmp)
void ClearRows()
wxWidgets recently added an ASSERT which fires if the position is greater than or equal to the number...
Definition: wx_grid.h:147
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:462
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:305
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
wxString LookUpEnvVarHelp(const wxString &aEnvVar)
Look up long-form help text for a given environment variable.
Definition: env_vars.cpp:145
const ENV_VAR_LIST & GetPredefinedEnvVars()
Get the list of pre-defined environment variables.
Definition: env_vars.cpp:68
bool IsEnvVarImmutable(const wxString &aEnvVar)
Determine if an environment variable is "predefined", i.e.
Definition: env_vars.cpp:53
see class PGM_BASE
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:39
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:119
Custom text control validator definitions.