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 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 m_gridAutosizer = std::make_unique<WX_GRID_AUTOSIZER>( *m_EnvVars,
103 { TV_NAME_COL, 72 },
104 { TV_VALUE_COL, 120 },
105 },
106 TV_VALUE_COL );
107
108 GetSizer()->SetSizeHints( this );
109 Centre();
110}
111
112
114{
115 // Delete the GRID_TRICKS.
116 m_EnvVars->PopEventHandler( true );
117
118 m_EnvVars->Disconnect( wxEVT_GRID_CELL_CHANGING,
119 wxGridEventHandler( DIALOG_CONFIGURE_PATHS::OnGridCellChanging ),
120 nullptr, this );
121}
122
123
125{
126 if( !wxDialog::TransferDataToWindow() )
127 return false;
128
129 const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
130
131 for( auto it = envVars.begin(); it != envVars.end(); ++it )
132 {
133 const wxString& path = it->second.GetValue();
134 AppendEnvVar( it->first, path, it->second.GetDefinedExternally() );
135
136 if( m_curdir.IsEmpty() && !path.StartsWith( "${" ) && !path.StartsWith( "$(" ) )
137 m_curdir = path;
138 }
139
140 return true;
141}
142
143
144void DIALOG_CONFIGURE_PATHS::AppendEnvVar( const wxString& aName, const wxString& aPath,
145 bool isExternal )
146{
147 int i = m_EnvVars->GetNumberRows();
148
149 m_EnvVars->AppendRows( 1 );
150
151 m_EnvVars->SetCellValue( i, TV_NAME_COL, aName );
152
153 wxGridCellAttr* nameCellAttr = m_EnvVars->GetOrCreateCellAttr( i, TV_NAME_COL );
154 wxGridCellTextEditor* nameTextEditor = new GRID_CELL_TEXT_EDITOR();
155 nameTextEditor->SetValidator( ENV_VAR_NAME_VALIDATOR() );
156 nameCellAttr->SetEditor( nameTextEditor );
157 nameCellAttr->SetReadOnly( ENV_VAR::IsEnvVarImmutable( aName ) );
158 nameCellAttr->DecRef();
159
160 m_EnvVars->SetCellValue( i, TV_VALUE_COL, aPath );
161
162 wxGridCellAttr* pathCellAttr = m_EnvVars->GetOrCreateCellAttr( i, TV_VALUE_COL );
163 wxSystemColour c = isExternal ? wxSYS_COLOUR_MENU : wxSYS_COLOUR_LISTBOX;
164 pathCellAttr->SetBackgroundColour( wxSystemSettings::GetColour( c ) );
165 pathCellAttr->DecRef();
166
167 m_EnvVars->SetCellValue( i, TV_FLAG_COL, isExternal ? wxT( "external" ) : wxEmptyString );
168}
169
170
172{
174 return false;
175
176 if( !wxDialog::TransferDataFromWindow() )
177 return false;
178
179 // Update environment variables
180 ENV_VAR_MAP& envVarMap = Pgm().GetLocalEnvVariables();
181
182 for( int row = 0; row < m_EnvVars->GetNumberRows(); ++row )
183 {
184 wxString name = m_EnvVars->GetCellValue( row, TV_NAME_COL );
185 wxString path = m_EnvVars->GetCellValue( row, TV_VALUE_COL );
186 bool external = !m_EnvVars->GetCellValue( row, TV_FLAG_COL ).IsEmpty();
187
188 if( external )
189 {
190 // Don't check for consistency on external variables, just use them as-is
191 }
192 else if( name.empty() && path.empty() )
193 {
194 // Skip empty rows altogether
195 continue;
196 }
197 else if( name.IsEmpty() )
198 {
200 m_errorRow = row;
202 m_errorMsg = _( "Environment variable name cannot be empty." );
203 return false;
204 }
205 else if( path.IsEmpty() )
206 {
208 m_errorRow = row;
210 m_errorMsg = _( "Environment variable path cannot be empty." );
211 return false;
212 }
213
214 if( envVarMap.count( name ) )
215 envVarMap.at( name ).SetValue( path );
216 else
217 envVarMap[ name ] = ENV_VAR_ITEM( name, path );
218 }
219
220 // Remove deleted env vars
221 for( auto it = envVarMap.begin(); it != envVarMap.end(); )
222 {
223 bool found = false;
224
225 for( int row = 0; row < m_EnvVars->GetNumberRows(); ++row )
226 {
227 wxString name = m_EnvVars->GetCellValue( row, TV_NAME_COL );
228
229 if( it->first == name )
230 {
231 found = true;
232 break;
233 }
234 }
235
236 if( found )
237 ++it;
238 else
239 it = envVarMap.erase( it );
240 }
241
243
244 return true;
245}
246
247
249{
250 wxGrid* grid = dynamic_cast<wxGrid*>( event.GetEventObject() );
251 int row = event.GetRow();
252 int col = event.GetCol();
253 wxString text = event.GetString();
254
255 text.Trim( true ).Trim( false ); // Trim from both sides
256 grid->SetCellValue( row, col, text ); // Update the grid with trimmed value
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 [&]() -> std::pair<int, int>
324 {
325 AppendEnvVar( wxEmptyString, wxEmptyString, false );
326 return { m_EnvVars->GetNumberRows() - 1, TV_NAME_COL };
327 } );
328}
329
330
331void DIALOG_CONFIGURE_PATHS::OnRemoveEnvVar( wxCommandEvent& event )
332{
334 [&]( int row )
335 {
336 if( ENV_VAR::IsEnvVarImmutable( m_EnvVars->GetCellValue( row, TV_NAME_COL ) ) )
337 {
338 wxBell();
339 return false;
340 }
341
342 return true;
343 },
344 [&]( int row )
345 {
346 m_EnvVars->DeleteRows( row, 1 );
347 } );
348}
349
350
351void DIALOG_CONFIGURE_PATHS::OnUpdateUI( wxUpdateUIEvent& event )
352{
353 // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus even when
354 // the original validation was triggered from a killFocus event.
355 if( m_errorGrid )
356 {
357 // We will re-enter this routine when the error dialog is displayed, so make sure we don't
358 // keep putting up more dialogs.
359 wxGrid* grid = m_errorGrid;
360 m_errorGrid = nullptr;
361
363
364 grid->SetFocus();
365 grid->MakeCellVisible( m_errorRow, m_errorCol );
366 grid->SetGridCursor( m_errorRow, m_errorCol );
367
368 grid->EnableCellEditControl( true );
369 grid->ShowCellEditControl();
370 }
371}
372
373
374void DIALOG_CONFIGURE_PATHS::OnHelp( wxCommandEvent& event )
375{
376 wxSizer* sizerMain = GetSizer();
377
378 if( !m_helpBox )
379 {
380 m_helpBox = new HTML_WINDOW( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
381 wxHW_SCROLLBAR_AUTO );
382
383 wxString msg = _( "Enter the name and value for each environment variable. Grey entries "
384 "are names that have been defined externally at the system or user "
385 "level. Environment variables defined at the system or user level "
386 "take precedence over the ones defined in this table. This means the "
387 "values in this table are ignored." );
388 msg << "<br><br><b>";
389 msg << _( "To ensure environment variable names are valid on all platforms, the name field "
390 "will only accept upper case letters, digits, and the underscore characters." );
391 msg << "</b>";
392
393 for( const wxString& var : ENV_VAR::GetPredefinedEnvVars() )
394 {
395 msg << "<br><br><b>" << var << "</b>";
396
397 const auto desc = ENV_VAR::LookUpEnvVarHelp( var );
398
399 if( desc.size() > 0 )
400 msg << ": " << desc;
401
402 }
403
404 m_helpBox->SetPage( msg );
405 m_helpBox->Show( false );
406
407 sizerMain->Insert( sizerMain->GetItemCount() - 1, m_helpBox, 1, wxALL|wxEXPAND, 10 );
408 }
409
410 if( m_helpBox->IsShown() )
411 {
412 m_helpBox->Show( false );
413 SetClientSize( wxSize( GetClientSize().x, m_heightBeforeHelp ) );
414 }
415 else
416 {
417 m_helpBox->Show( true );
418 m_heightBeforeHelp = GetClientSize().y;
419
420 int minHelpBoxHeight = GetTextExtent( wxT( "T" ) ).y * 20;
421
422 if( GetClientSize().y < minHelpBoxHeight * 2 )
423 SetClientSize( wxSize( GetClientSize().x, GetClientSize().y + minHelpBoxHeight ) );
424 }
425
426 Layout();
427}
const char * name
Definition: DXF_plotter.cpp:62
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:75
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:69
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:50
@ KD_WARNING
Definition: kidialog.h:53
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:565
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition: pgm_base.cpp:811
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:795
void SetBitmap(const wxBitmapBundle &aBmp)
std::map< int, int > COL_MIN_WIDTHS
Map of column indices to minimum widths.
void OnDeleteRows(const std::function< void(int row)> &aDeleter)
Handles a row deletion event.
Definition: wx_grid.cpp:704
void OnAddRow(const std::function< std::pair< int, int >()> &aAdder)
Definition: wx_grid.cpp:684
void ClearRows()
wxWidgets recently added an ASSERT which fires if the position is greater than or equal to the number...
Definition: wx_grid.h:220
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:632
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:194
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:152
KICOMMON_API const std::vector< wxString > & GetPredefinedEnvVars()
Get the list of pre-defined environment variables.
Definition: env_vars.cpp:61
KICOMMON_API bool IsEnvVarImmutable(const wxString &aEnvVar)
Determine if an environment variable is "predefined", i.e.
Definition: env_vars.cpp:49
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:902
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.