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 The 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{
70 m_heightBeforeHelp = FromDIP ( 400 );
71 m_btnAddEnvVar->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
72 m_btnDeleteEnvVar->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
73
74 m_EnvVars->SetMinSize( FromDIP( m_EnvVars->GetMinSize() ) );
75
77 m_EnvVars->AppendCols( 1 ); // for the isExternal flags
78 m_EnvVars->HideCol( TV_FLAG_COL );
79 m_EnvVars->UseNativeColHeader( true );
80
81 wxGridCellAttr* attr = new wxGridCellAttr;
82 attr->SetEditor( new GRID_CELL_PATH_EDITOR( this, m_EnvVars, &m_curdir, wxEmptyString ) );
83 m_EnvVars->SetColAttr( TV_VALUE_COL, attr );
84
85 // Give a bit more room for combobox editors
86 m_EnvVars->SetDefaultRowSize( m_EnvVars->GetDefaultRowSize() + FromDIP( 4 ) );
87
88 m_EnvVars->PushEventHandler( new GRID_TRICKS( m_EnvVars,
89 [this]( wxCommandEvent& aEvent )
90 {
91 OnAddEnvVar( aEvent );
92 } ) );
93
94 m_EnvVars->SetSelectionMode( wxGrid::wxGridSelectionModes::wxGridSelectRows );
95
98
99 // wxFormBuilder doesn't include this event...
100 m_EnvVars->Connect( wxEVT_GRID_CELL_CHANGING,
101 wxGridEventHandler( DIALOG_CONFIGURE_PATHS::OnGridCellChanging ),
102 nullptr, this );
103
104 m_gridAutosizer = std::make_unique<WX_GRID_AUTOSIZER>( *m_EnvVars,
106 { TV_NAME_COL, 72 },
107 { TV_VALUE_COL, 120 },
108 },
109 TV_VALUE_COL );
110
111 GetSizer()->SetSizeHints( this );
112 Centre();
113}
114
115
117{
118 // Delete the GRID_TRICKS.
119 m_EnvVars->PopEventHandler( true );
120
121 m_EnvVars->Disconnect( wxEVT_GRID_CELL_CHANGING,
122 wxGridEventHandler( DIALOG_CONFIGURE_PATHS::OnGridCellChanging ),
123 nullptr, this );
124}
125
126
128{
129 if( !wxDialog::TransferDataToWindow() )
130 return false;
131
132 const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
133
134 for( auto it = envVars.begin(); it != envVars.end(); ++it )
135 {
136 const wxString& path = it->second.GetValue();
137 AppendEnvVar( it->first, path, it->second.GetDefinedExternally() );
138
139 if( m_curdir.IsEmpty() && !path.StartsWith( "${" ) && !path.StartsWith( "$(" ) )
140 m_curdir = path;
141 }
142
143 return true;
144}
145
146
147void DIALOG_CONFIGURE_PATHS::AppendEnvVar( const wxString& aName, const wxString& aPath,
148 bool isExternal )
149{
150 int i = m_EnvVars->GetNumberRows();
151
152 m_EnvVars->AppendRows( 1 );
153
154 m_EnvVars->SetCellValue( i, TV_NAME_COL, aName );
155
156 wxGridCellAttr* nameCellAttr = m_EnvVars->GetOrCreateCellAttr( i, TV_NAME_COL );
157 wxGridCellTextEditor* nameTextEditor = new GRID_CELL_TEXT_EDITOR();
158 nameTextEditor->SetValidator( ENV_VAR_NAME_VALIDATOR() );
159 nameCellAttr->SetEditor( nameTextEditor );
160 nameCellAttr->SetReadOnly( ENV_VAR::IsEnvVarImmutable( aName ) );
161 nameCellAttr->DecRef();
162
163 m_EnvVars->SetCellValue( i, TV_VALUE_COL, aPath );
164
165 wxGridCellAttr* pathCellAttr = m_EnvVars->GetOrCreateCellAttr( i, TV_VALUE_COL );
166 wxSystemColour c = isExternal ? wxSYS_COLOUR_MENU : wxSYS_COLOUR_LISTBOX;
167 pathCellAttr->SetBackgroundColour( wxSystemSettings::GetColour( c ) );
168 pathCellAttr->DecRef();
169
170 m_EnvVars->SetCellValue( i, TV_FLAG_COL, isExternal ? wxT( "external" ) : wxEmptyString );
171}
172
173
175{
177 return false;
178
179 if( !wxDialog::TransferDataFromWindow() )
180 return false;
181
182 // Update environment variables
183 ENV_VAR_MAP& envVarMap = Pgm().GetLocalEnvVariables();
184
185 for( int row = 0; row < m_EnvVars->GetNumberRows(); ++row )
186 {
187 wxString name = m_EnvVars->GetCellValue( row, TV_NAME_COL );
188 wxString path = m_EnvVars->GetCellValue( row, TV_VALUE_COL );
189 bool external = !m_EnvVars->GetCellValue( row, TV_FLAG_COL ).IsEmpty();
190
191 if( external )
192 {
193 // Don't check for consistency on external variables, just use them as-is
194 }
195 else if( name.empty() && path.empty() )
196 {
197 // Skip empty rows altogether
198 continue;
199 }
200 else if( name.IsEmpty() )
201 {
203 m_errorRow = row;
205 m_errorMsg = _( "Environment variable name cannot be empty." );
206 return false;
207 }
208 else if( path.IsEmpty() )
209 {
211 m_errorRow = row;
213 m_errorMsg = _( "Environment variable path cannot be empty." );
214 return false;
215 }
216
217 if( envVarMap.count( name ) )
218 envVarMap.at( name ).SetValue( path );
219 else
220 envVarMap[ name ] = ENV_VAR_ITEM( name, path );
221 }
222
223 // Remove deleted env vars
224 for( auto it = envVarMap.begin(); it != envVarMap.end(); )
225 {
226 bool found = false;
227
228 for( int row = 0; row < m_EnvVars->GetNumberRows(); ++row )
229 {
230 wxString name = m_EnvVars->GetCellValue( row, TV_NAME_COL );
231
232 if( it->first == name )
233 {
234 found = true;
235 break;
236 }
237 }
238
239 if( found )
240 ++it;
241 else
242 it = envVarMap.erase( it );
243 }
244
246
247 return true;
248}
249
250
252{
253 wxGrid* grid = dynamic_cast<wxGrid*>( event.GetEventObject() );
254 int row = event.GetRow();
255 int col = event.GetCol();
256 wxString text = event.GetString();
257
258 if( text.IsEmpty() )
259 {
260 if( grid == m_EnvVars )
261 {
262 if( col == TV_NAME_COL )
263 m_errorMsg = _( "Environment variable name cannot be empty." );
264 else
265 m_errorMsg = _( "Environment variable path cannot be empty." );
266 }
267 else
268 {
269 if( col == SP_ALIAS_COL )
270 m_errorMsg = _( "3D search path alias cannot be empty." );
271 else
272 m_errorMsg = _( "3D search path cannot be empty." );
273 }
274
275 m_errorGrid = dynamic_cast<wxGrid*>( event.GetEventObject() );
276 m_errorRow = row;
277 m_errorCol = col;
278
279 event.Veto();
280 }
281
282 if( grid == m_EnvVars )
283 {
284 if( col == TV_VALUE_COL && m_EnvVars->GetCellValue( row, TV_FLAG_COL ).Length()
286 {
287 wxString msg1 = _( "This path was defined externally to the running process and\n"
288 "will only be temporarily overwritten." );
289 wxString msg2 = _( "The next time KiCad is launched, any paths that have already\n"
290 "been defined are honored and any settings defined in the path\n"
291 "configuration dialog are ignored. If you did not intend for\n"
292 "this behavior, either rename any conflicting entries or remove\n"
293 "the external environment variable(s) from your system." );
294 KIDIALOG dlg( this, msg1, KIDIALOG::KD_WARNING );
295 dlg.ShowDetailedText( msg2 );
296 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
297 dlg.ShowModal();
298
299 if( dlg.DoNotShowAgain() )
301 }
302 else if( col == TV_NAME_COL && m_EnvVars->GetCellValue( row, TV_NAME_COL ) != text )
303 {
304 // This env var name is reserved and cannot be added here.
305 if( text == PROJECT_VAR_NAME )
306 {
307 wxMessageBox( wxString::Format( _( "The name %s is reserved, and cannot be used." ),
309 event.Veto();
310 }
311 else // Changing name; clear external flag
312 {
313 m_EnvVars->SetCellValue( row, TV_FLAG_COL, wxEmptyString );
314 }
315 }
316 }
317}
318
319
320void DIALOG_CONFIGURE_PATHS::OnAddEnvVar( wxCommandEvent& event )
321{
323 return;
324
325 AppendEnvVar( wxEmptyString, wxEmptyString, false );
326
327 m_EnvVars->MakeCellVisible( m_EnvVars->GetNumberRows() - 1, TV_NAME_COL );
328 m_EnvVars->SetGridCursor( m_EnvVars->GetNumberRows() - 1, TV_NAME_COL );
329
330 m_EnvVars->EnableCellEditControl( true );
331 m_EnvVars->ShowCellEditControl();
332}
333
334
335void DIALOG_CONFIGURE_PATHS::OnRemoveEnvVar( wxCommandEvent& event )
336{
337 int curRow = m_EnvVars->GetGridCursorRow();
338
339 if( curRow < 0 || m_EnvVars->GetNumberRows() <= curRow )
340 {
341 return;
342 }
343 else if( ENV_VAR::IsEnvVarImmutable( m_EnvVars->GetCellValue( curRow, TV_NAME_COL ) ) )
344 {
345 wxBell();
346 return;
347 }
348
349 m_EnvVars->CommitPendingChanges( true /* silent mode; we don't care if it's valid */ );
350 m_EnvVars->DeleteRows( curRow, 1 );
351
352 m_EnvVars->MakeCellVisible( std::max( 0, curRow-1 ), m_EnvVars->GetGridCursorCol() );
353 m_EnvVars->SetGridCursor( std::max( 0, curRow-1 ), m_EnvVars->GetGridCursorCol() );
354}
355
356
357void DIALOG_CONFIGURE_PATHS::OnUpdateUI( wxUpdateUIEvent& event )
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::OnHelp( wxCommandEvent& event )
381{
382 wxSizer* sizerMain = GetSizer();
383
384 if( !m_helpBox )
385 {
386 m_helpBox = new HTML_WINDOW( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
387 wxHW_SCROLLBAR_AUTO );
388
389 wxString msg = _( "Enter the name and value for each environment variable. Grey entries "
390 "are names that have been defined externally at the system or user "
391 "level. Environment variables defined at the system or user level "
392 "take precedence over the ones defined in this table. This means the "
393 "values in this table are ignored." );
394 msg << "<br><br><b>";
395 msg << _( "To ensure environment variable names are valid on all platforms, the name field "
396 "will only accept upper case letters, digits, and the underscore characters." );
397 msg << "</b>";
398
399 for( const wxString& var : ENV_VAR::GetPredefinedEnvVars() )
400 {
401 msg << "<br><br><b>" << var << "</b>";
402
403 const auto desc = ENV_VAR::LookUpEnvVarHelp( var );
404
405 if( desc.size() > 0 )
406 msg << ": " << desc;
407
408 }
409
410 m_helpBox->SetPage( msg );
411 m_helpBox->Show( false );
412
413 sizerMain->Insert( sizerMain->GetItemCount() - 1, m_helpBox, 1, wxALL|wxEXPAND, 10 );
414 }
415
416 if( m_helpBox->IsShown() )
417 {
418 m_helpBox->Show( false );
419 SetClientSize( wxSize( GetClientSize().x, m_heightBeforeHelp ) );
420 }
421 else
422 {
423 m_helpBox->Show( true );
424 m_heightBeforeHelp = GetClientSize().y;
425
426 int minHelpBoxHeight = GetTextExtent( wxT( "T" ) ).y * 20;
427
428 if( GetClientSize().y < minHelpBoxHeight * 2 )
429 SetClientSize( wxSize( GetClientSize().x, GetClientSize().y + minHelpBoxHeight ) );
430 }
431
432 Layout();
433}
const char * name
Definition: DXF_plotter.cpp:59
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
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,...
Provide a custom wxValidator object for limiting the allowable characters when defining an environmen...
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...
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
Checks the 'do not show again' setting for the dialog.
Definition: kidialog.cpp:59
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
Definition: kidialog.cpp:51
int ShowModal() override
Definition: kidialog.cpp:95
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition: pgm_base.cpp:689
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition: pgm_base.cpp:935
virtual void SetLocalEnvVariables()
Update the local environment with the contents of the current ENV_VAR_MAP stored in the COMMON_SETTIN...
Definition: pgm_base.cpp:919
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:193
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:644
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:146
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:1073
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.