KiCad PCB EDA Suite
Loading...
Searching...
No Matches
grid_text_button_helpers.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) 2021 CERN
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, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <wx/checkbox.h>
22#include <wx/combo.h>
23#include <wx/filedlg.h>
24#include <wx/dirdlg.h>
25#include <wx/textctrl.h>
26
27#include <bitmaps.h>
28#include <embedded_files.h>
29#include <kiplatform/ui.h>
30#include <kiway.h>
31#include <kiway_player.h>
32#include <kiway_mail.h>
33#include <string_utils.h>
34#include <dialog_shim.h>
35#include <common.h>
36#include <env_paths.h>
37#include <pgm_base.h>
38#include <widgets/wx_grid.h>
41#include <eda_doc.h>
42
43
44//-------- Renderer ---------------------------------------------------------------------
45// None required; just render as normal text.
46
47
48
49class TEXT_BUTTON_SYMBOL_CHOOSER : public wxComboCtrl
50{
51public:
52 TEXT_BUTTON_SYMBOL_CHOOSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg,
53 const wxString& aPreselect ) :
54 wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 0, 0 ) ),
55 m_dlg( aParentDlg ),
56 m_preselect( aPreselect )
57 {
58 SetButtonBitmaps( KiBitmapBundle( BITMAPS::small_library ) );
59
60 // win32 fix, avoids drawing the "native dropdown caret"
61 Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
62 }
63
64protected:
65 void DoSetPopupControl( wxComboPopup* popup ) override
66 {
67 m_popup = nullptr;
68 }
69
70 wxString escapeLibId( const wxString& aRawValue )
71 {
72 wxString itemName;
73 wxString libName = aRawValue.BeforeFirst( ':', &itemName );
74 return EscapeString( libName, CTX_LIBID ) + ':' + EscapeString( itemName, CTX_LIBID );
75 }
76
77 void OnButtonClick() override
78 {
79 // pick a symbol using the symbol picker.
80 wxString rawValue = GetValue();
81
82 if( rawValue.IsEmpty() )
83 rawValue = m_preselect;
84
85 wxString symbolId = escapeLibId( rawValue );
86
87 if( KIWAY_PLAYER* frame = m_dlg->Kiway().Player( FRAME_SYMBOL_CHOOSER, true, m_dlg ) )
88 {
89 if( frame->ShowModal( &symbolId, m_dlg ) )
90 SetValue( UnescapeString( symbolId ) );
91
92 frame->Destroy();
93 }
94 }
95
97 wxString m_preselect;
98};
99
100
101void GRID_CELL_SYMBOL_ID_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
102 wxEvtHandler* aEventHandler )
103{
104 m_control = new TEXT_BUTTON_SYMBOL_CHOOSER( aParent, m_dlg, m_preselect );
106
107 wxGridCellEditor::Create( aParent, aId, aEventHandler );
108}
109
110
111class TEXT_BUTTON_FP_CHOOSER : public wxComboCtrl
112{
113public:
114 TEXT_BUTTON_FP_CHOOSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg,
115 const wxString& aSymbolNetlist, const wxString& aPreselect ) :
116 wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 0, 0 ),
117 wxTE_PROCESS_ENTER | wxBORDER_NONE ),
118 m_dlg( aParentDlg ),
119 m_preselect( aPreselect ),
120 m_symbolNetlist( aSymbolNetlist.ToStdString() )
121 {
122 m_buttonFpChooserLock = false;
123 SetButtonBitmaps( KiBitmapBundle( BITMAPS::small_library ) );
124
125 // win32 fix, avoids drawing the "native dropdown caret"
126 Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
127 }
128
129protected:
130 void DoSetPopupControl( wxComboPopup* popup ) override
131 {
132 m_popup = nullptr;
133 }
134
135 void OnButtonClick() override
136 {
137 if( m_buttonFpChooserLock ) // The button to show the FP chooser is clicked, but
138 // a previous click is currently in progress.
139 return;
140
141 // Disable the button until we have finished processing it. Normally this is not an issue
142 // but if the footprint chooser is loading for the first time, it can be slow enough that
143 // multiple clicks will cause multiple instances of the footprint loader process to start
145
146 // pick a footprint using the footprint picker.
147 wxString fpid = GetValue();
148
149 if( fpid.IsEmpty() )
150 fpid = m_preselect;
151
152 if( KIWAY_PLAYER* frame = m_dlg->Kiway().Player( FRAME_FOOTPRINT_CHOOSER, true, m_dlg ) )
153 {
154 if( !m_symbolNetlist.empty() )
155 {
158 frame->KiwayMailIn( event );
159 }
160
161 if( frame->ShowModal( &fpid, m_dlg ) )
162 SetValue( fpid );
163
164 frame->Destroy();
165 }
166
167 m_buttonFpChooserLock = false;
168 }
169
170protected:
172 wxString m_preselect;
173
174 // Lock flag to lock the button to show the FP chooser
175 // true when the button is busy, waiting all footprints loaded to
176 // avoid running more than once the FP chooser
178
179 /*
180 * Symbol netlist format:
181 * pinNumber pinName <tab> pinNumber pinName...
182 * fpFilter fpFilter...
183 */
184 std::string m_symbolNetlist;
185};
186
187
188void GRID_CELL_FPID_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
189 wxEvtHandler* aEventHandler )
190{
191 m_control = new TEXT_BUTTON_FP_CHOOSER( aParent, m_dlg, m_symbolNetlist, m_preselect );
193
194#if wxUSE_VALIDATORS
195 // validate text in textctrl, if validator is set
196 if ( m_validator )
197 {
198 Combo()->SetValidator( *m_validator );
199 }
200#endif
201
202 wxGridCellEditor::Create( aParent, aId, aEventHandler );
203}
204
205
206class TEXT_BUTTON_URL : public wxComboCtrl
207{
208public:
209 TEXT_BUTTON_URL( wxWindow* aParent, DIALOG_SHIM* aParentDlg, SEARCH_STACK* aSearchStack,
210 std::vector<EMBEDDED_FILES*> aFilesStack ) :
211 wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 0, 0 ),
212 wxTE_PROCESS_ENTER | wxBORDER_NONE ),
213 m_dlg( aParentDlg ),
214 m_searchStack( aSearchStack ),
215 m_filesStack( aFilesStack )
216 {
218
219 // win32 fix, avoids drawing the "native dropdown caret"
220 Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
221
222 // Bind event to handle text changes
223 Bind( wxEVT_TEXT, &TEXT_BUTTON_URL::OnTextChange, this );
224 }
225
227 {
228 Unbind( wxEVT_TEXT, &TEXT_BUTTON_URL::OnTextChange, this );
229
230 m_filesStack.clear(); // we don't own pointers
231 }
232
233 // We don't own any of our raw pointers, so compiler's copy c'tor an operator= are OK.
234
235protected:
236 void DoSetPopupControl( wxComboPopup* popup ) override
237 {
238 m_popup = nullptr;
239 }
240
241 void OnButtonClick() override
242 {
243 m_dlg->PrepareForModalSubDialog();
244
245 wxString filename = GetValue();
246
247 if( filename.IsEmpty() || filename == wxT( "~" ) )
248 {
249 FILEDLG_HOOK_EMBED_FILE customize;
250
251 wxFileDialog openFileDialog( this, _( "Open file" ), "", "", _( "All Files" ) + wxT( " (*.*)|*.*" ),
252 wxFD_OPEN | wxFD_FILE_MUST_EXIST );
253
254 openFileDialog.SetCustomizeHook( customize );
255
257
258 if( openFileDialog.ShowModal() == wxID_OK )
259 {
260 filename = openFileDialog.GetPath();
261 wxFileName fn( filename );
262
263 if( customize.GetEmbed() )
264 {
265 EMBEDDED_FILES::EMBEDDED_FILE* result = m_filesStack[0]->AddFile( fn, false );
266 SetValue( result->GetLink() );
267 }
268 else
269 {
270 SetValue( "file://" + filename );
271 }
272 }
273 }
274 else
275 {
277 }
278
279 m_dlg->CleanupAfterModalSubDialog();
280 }
281
282 void OnTextChange(wxCommandEvent& event)
283 {
285 event.Skip(); // Ensure that other handlers can process this event too
286 }
287
289 {
290 if( GetValue().IsEmpty() )
291 SetButtonBitmaps( KiBitmapBundle( BITMAPS::small_folder ) );
292 else
293 SetButtonBitmaps( KiBitmapBundle( BITMAPS::www ) );
294 }
295
296protected:
298 SEARCH_STACK* m_searchStack; // No ownership of pointer
299 std::vector<EMBEDDED_FILES*> m_filesStack; // No ownership of pointers
300};
301
302
303void GRID_CELL_URL_EDITOR::Create( wxWindow* aParent, wxWindowID aId, wxEvtHandler* aEventHandler )
304{
305 m_control = new TEXT_BUTTON_URL( aParent, m_dlg, m_searchStack, m_filesStack );
307
308#if wxUSE_VALIDATORS
309 // validate text in textctrl, if validator is set
310 if ( m_validator )
311 Combo()->SetValidator( *m_validator );
312#endif
313
314 wxGridCellEditor::Create( aParent, aId, aEventHandler );
315}
316
317
318class TEXT_BUTTON_FILE_BROWSER : public wxComboCtrl
319{
320public:
321 TEXT_BUTTON_FILE_BROWSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg, WX_GRID* aGrid,
322 wxString* aCurrentDir, const wxString& aFileFilter = wxEmptyString,
323 bool aNormalize = false,
324 const wxString& aNormalizeBasePath = wxEmptyString,
325 std::function<wxString( const wxString& )> aEmbedCallback = nullptr ) :
326 wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 0, 0 ),
327 wxTE_PROCESS_ENTER | wxBORDER_NONE ),
328 m_dlg( aParentDlg ),
329 m_grid( aGrid ),
330 m_currentDir( aCurrentDir ),
331 m_normalize( aNormalize ),
332 m_normalizeBasePath( aNormalizeBasePath ),
333 m_fileFilter( aFileFilter ),
334 m_embedCallback( std::move( aEmbedCallback ) )
335 {
336 SetButtonBitmaps( KiBitmapBundle( BITMAPS::small_folder ) );
337
338 // win32 fix, avoids drawing the "native dropdown caret"
339 Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
340 }
341
342 TEXT_BUTTON_FILE_BROWSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg, WX_GRID* aGrid,
343 wxString* aCurrentDir,
344 std::function<wxString( WX_GRID* grid, int row )> aFileFilterFn,
345 bool aNormalize = false,
346 const wxString& aNormalizeBasePath = wxEmptyString,
347 std::function<wxString( const wxString& )> aEmbedCallback = nullptr ) :
348 wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 0, 0 ),
349 wxTE_PROCESS_ENTER | wxBORDER_NONE ),
350 m_dlg( aParentDlg ),
351 m_grid( aGrid ),
352 m_currentDir( aCurrentDir ),
353 m_normalize( aNormalize ),
354 m_normalizeBasePath( aNormalizeBasePath ),
355 m_fileFilterFn( std::move( aFileFilterFn ) ),
356 m_embedCallback( std::move( aEmbedCallback ) )
357 {
358 SetButtonBitmaps( KiBitmapBundle( BITMAPS::small_folder ) );
359
360 // win32 fix, avoids drawing the "native dropdown caret"
361 Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
362 }
363
364
365protected:
366 void DoSetPopupControl( wxComboPopup* popup ) override
367 {
368 m_popup = nullptr;
369 }
370
371 void OnButtonClick() override
372 {
373 m_dlg->PrepareForModalSubDialog();
374
375 if( m_fileFilterFn )
376 m_fileFilter = m_fileFilterFn( m_grid, m_grid->GetGridCursorRow() );
377
378 wxFileName fn = GetValue();
379
380 if( fn.GetPath().IsEmpty() && m_currentDir )
381 fn.SetPath( *m_currentDir );
382 else
383 fn.SetPath( ExpandEnvVarSubstitutions( fn.GetPath(), &m_dlg->Prj() ) );
384
385 if( !m_fileFilter.IsEmpty() )
386 {
387 FILEDLG_HOOK_EMBED_FILE customize( false );
388 wxFileDialog dlg( m_dlg, _( "Select a File" ), fn.GetPath(), fn.GetFullName(),
389 m_fileFilter, wxFD_FILE_MUST_EXIST | wxFD_OPEN );
390
391 if( m_embedCallback )
392 dlg.SetCustomizeHook( customize );
393
395
396 if( dlg.ShowModal() == wxID_OK )
397 {
398 wxString filePath = dlg.GetPath();
399 wxString lastPath = dlg.GetDirectory();
400 wxString relPath = wxEmptyString;
401
402 if( m_embedCallback && customize.GetEmbed() )
403 {
404 relPath = m_embedCallback( filePath );
405
406 if( relPath.IsEmpty() )
407 {
408 m_dlg->CleanupAfterModalSubDialog();
409 return;
410 }
411 }
412 else if( m_normalize )
413 {
414 relPath = NormalizePath( filePath, &Pgm().GetLocalEnvVariables(),
416 lastPath = NormalizePath( dlg.GetDirectory(), &Pgm().GetLocalEnvVariables(),
418 }
419 else
420 {
421 relPath = filePath;
422 }
423
424 SetValue( relPath );
425
426 if( !m_grid->CommitPendingChanges() )
427 {;} // shouldn't happen, but Coverity doesn't know that
428
429 if( m_currentDir )
430 *m_currentDir = lastPath;
431 }
432 }
433 else
434 {
435 wxDirDialog dlg( m_dlg, _( "Select Path" ), fn.GetPath(),
436 wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST );
437
438 if( dlg.ShowModal() == wxID_OK )
439 {
440 wxString filePath = dlg.GetPath();
441 wxString relPath = wxEmptyString;
442
443 if ( m_normalize )
444 {
445 relPath = NormalizePath( filePath, &Pgm().GetLocalEnvVariables(),
447 }
448 else
449 {
450 relPath = filePath;
451 }
452
453 SetValue( relPath );
454
455 if( !m_grid->CommitPendingChanges() )
456 {;} // shouldn't happen, but Coverity doesn't know that
457
458 if( m_currentDir )
459 *m_currentDir = relPath;
460 }
461 }
462
463 m_dlg->CleanupAfterModalSubDialog();
464 }
465
466protected:
469 wxString* m_currentDir;
472
473 wxString m_fileFilter;
474 std::function<wxString( WX_GRID* aGrid, int aRow )> m_fileFilterFn;
475 std::function<wxString( const wxString& )> m_embedCallback;
476};
477
478
479void GRID_CELL_PATH_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
480 wxEvtHandler* aEventHandler )
481{
482 if( m_fileFilterFn )
483 {
484 m_control = new TEXT_BUTTON_FILE_BROWSER( aParent, m_dlg, m_grid, m_currentDir,
487 }
488 else
489 {
490 m_control = new TEXT_BUTTON_FILE_BROWSER( aParent, m_dlg, m_grid, m_currentDir,
493 }
494
496
497#if wxUSE_VALIDATORS
498 // validate text in textctrl, if validator is set
499 if ( m_validator )
500 Combo()->SetValidator( *m_validator );
501#endif
502
503 wxGridCellEditor::Create( aParent, aId, aEventHandler );
504}
505
506class TEXT_BUTTON_RUN_FUNCTION final : public wxComboCtrl
507{
508public:
509 TEXT_BUTTON_RUN_FUNCTION( wxWindow* aParent, DIALOG_SHIM* aParentDlg, std::function<void( int, int )>& aFunction,
510 int& aRow, int& aCol ) :
511 wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 0, 0 ),
512 wxTE_PROCESS_ENTER | wxBORDER_NONE ),
513 m_dlg( aParentDlg ),
514 m_function( aFunction ),
515 m_row( aRow ),
516 m_col( aCol )
517 {
518 SetButtonBitmaps( KiBitmapBundle( BITMAPS::small_refresh ) );
519
520 // win32 fix, avoids drawing the "native dropdown caret"
521 Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
522 }
523
524protected:
525 void DoSetPopupControl( wxComboPopup* popup ) override { m_popup = nullptr; }
526
527 void OnButtonClick() override { m_function( m_row, m_col ); }
528
530 std::function<void( int, int )>& m_function;
531 int& m_row;
532 int& m_col;
533};
534
535
536void GRID_CELL_RUN_FUNCTION_EDITOR::Create( wxWindow* aParent, wxWindowID aId, wxEvtHandler* aEventHandler )
537{
538 m_control = new TEXT_BUTTON_RUN_FUNCTION( aParent, m_dlg, m_function, m_row, m_col );
540
541#if wxUSE_VALIDATORS
542 // validate text in textctrl, if validator is set
543 if( m_validator )
544 {
545 Combo()->SetValidator( *m_validator );
546 }
547#endif
548
549 wxGridCellEditor::Create( aParent, aId, aEventHandler );
550}
551
552
553void GRID_CELL_RUN_FUNCTION_EDITOR::BeginEdit( int aRow, int aCol, wxGrid* aGrid )
554{
555 m_row = aRow;
556 m_col = aCol;
557
558 GRID_CELL_TEXT_BUTTON::BeginEdit( aRow, aCol, aGrid );
559}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition dialog_shim.h:65
void Create(wxWindow *aParent, wxWindowID aId, wxEvtHandler *aEventHandler) override
void Create(wxWindow *aParent, wxWindowID aId, wxEvtHandler *aEventHandler) override
std::function< wxString(WX_GRID *aGrid, int aRow)> m_fileFilterFn
std::function< wxString(const wxString &)> m_embedCallback
void BeginEdit(int aRow, int aCol, wxGrid *aGrid) override
void Create(wxWindow *aParent, wxWindowID aId, wxEvtHandler *aEventHandler) override
std::function< void(int, int)> m_function
void Create(wxWindow *aParent, wxWindowID aId, wxEvtHandler *aEventHandler) override
wxComboCtrl * Combo() const
void BeginEdit(int aRow, int aCol, wxGrid *aGrid) override
void Create(wxWindow *aParent, wxWindowID aId, wxEvtHandler *aEventHandler) override
std::vector< EMBEDDED_FILES * > m_filesStack
Carry a payload from one KIWAY_PLAYER to another within a PROJECT.
Definition kiway_mail.h:34
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Look for files in a number of paths.
void DoSetPopupControl(wxComboPopup *popup) override
std::function< wxString(const wxString &)> m_embedCallback
TEXT_BUTTON_FILE_BROWSER(wxWindow *aParent, DIALOG_SHIM *aParentDlg, WX_GRID *aGrid, wxString *aCurrentDir, const wxString &aFileFilter=wxEmptyString, bool aNormalize=false, const wxString &aNormalizeBasePath=wxEmptyString, std::function< wxString(const wxString &)> aEmbedCallback=nullptr)
TEXT_BUTTON_FILE_BROWSER(wxWindow *aParent, DIALOG_SHIM *aParentDlg, WX_GRID *aGrid, wxString *aCurrentDir, std::function< wxString(WX_GRID *grid, int row)> aFileFilterFn, bool aNormalize=false, const wxString &aNormalizeBasePath=wxEmptyString, std::function< wxString(const wxString &)> aEmbedCallback=nullptr)
std::function< wxString(WX_GRID *aGrid, int aRow)> m_fileFilterFn
void DoSetPopupControl(wxComboPopup *popup) override
TEXT_BUTTON_FP_CHOOSER(wxWindow *aParent, DIALOG_SHIM *aParentDlg, const wxString &aSymbolNetlist, const wxString &aPreselect)
std::function< void(int, int)> & m_function
void DoSetPopupControl(wxComboPopup *popup) override
TEXT_BUTTON_RUN_FUNCTION(wxWindow *aParent, DIALOG_SHIM *aParentDlg, std::function< void(int, int)> &aFunction, int &aRow, int &aCol)
void DoSetPopupControl(wxComboPopup *popup) override
wxString escapeLibId(const wxString &aRawValue)
TEXT_BUTTON_SYMBOL_CHOOSER(wxWindow *aParent, DIALOG_SHIM *aParentDlg, const wxString &aPreselect)
std::vector< EMBEDDED_FILES * > m_filesStack
TEXT_BUTTON_URL(wxWindow *aParent, DIALOG_SHIM *aParentDlg, SEARCH_STACK *aSearchStack, std::vector< EMBEDDED_FILES * > aFilesStack)
void DoSetPopupControl(wxComboPopup *popup) override
void OnTextChange(wxCommandEvent &event)
static void CellEditorSetMargins(wxTextEntryBase *aEntry)
A helper function to set OS-specific margins for text-based cell editors.
Definition wx_grid.cpp:75
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:704
The common library.
#define _(s)
bool GetAssociatedDocument(wxWindow *aParent, const wxString &aDocName, PROJECT *aProject, SEARCH_STACK *aPaths, std::vector< EMBEDDED_FILES * > aFilesStack)
Open a document (file) with the suitable browser.
Definition eda_doc.cpp:59
This file is part of the common library.
wxString NormalizePath(const wxFileName &aFilePath, const ENV_VAR_MAP *aEnvVars, const wxString &aProjectPath)
Normalize a file path to an environmental variable, if possible.
Definition env_paths.cpp:73
Helper functions to substitute paths with environmental variables.
@ FRAME_FOOTPRINT_CHOOSER
Definition frame_type.h:40
@ FRAME_SYMBOL_CHOOSER
Definition frame_type.h:33
@ MAIL_SYMBOL_NETLIST
Definition mail_type.h:42
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:448
STL namespace.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
wxString UnescapeString(const wxString &aSource)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_LIBID
wxString result
Test unit parsing edge cases and error handling.