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