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