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