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