KiCad PCB EDA Suite
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 (C) 2018-2021 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/combo.h>
26 #include <wx/filedlg.h>
27 #include <wx/dirdlg.h>
28 
29 #include <bitmap_types.h>
30 #include <bitmaps.h>
31 #include <kiway.h>
32 #include <kiway_player.h>
33 #include <dialog_shim.h>
34 #include <common.h>
35 #include <env_paths.h>
36 #include <widgets/wx_grid.h>
38 #include <eda_doc.h>
39 
40 
41 //-------- Renderer ---------------------------------------------------------------------
42 // None required; just render as normal text.
43 
44 
45 
46 //-------- Editor Base Class ------------------------------------------------------------
47 //
48 // Note: this implementation is an adaptation of wxGridCellChoiceEditor
49 
50 
52 {
53  return Combo()->GetValue();
54 }
55 
56 
57 void GRID_CELL_TEXT_BUTTON::SetSize( const wxRect& aRect )
58 {
59  wxRect rect( aRect );
60  rect.Inflate( -1 );
61 
62 #if defined( __WXMAC__ )
63  rect.Inflate( 3 ); // no FOCUS_RING, even on Mac
64 #endif
65 
66  Combo()->SetSize( rect, wxSIZE_ALLOW_MINUS_ONE );
67 }
68 
69 
70 void GRID_CELL_TEXT_BUTTON::StartingKey( wxKeyEvent& event )
71 {
72  // Note: this is a copy of wxGridCellTextEditor's StartingKey()
73 
74  // Since this is now happening in the EVT_CHAR event EmulateKeyPress is no
75  // longer an appropriate way to get the character into the text control.
76  // Do it ourselves instead. We know that if we get this far that we have
77  // a valid character, so not a whole lot of testing needs to be done.
78 
79  // wxComboCtrl inherits from wxTextEntry, so can staticly cast
80  wxTextEntry* textEntry = static_cast<wxTextEntry*>( Combo() );
81  int ch;
82 
83  bool isPrintable;
84 
85 #if wxUSE_UNICODE
86  ch = event.GetUnicodeKey();
87 
88  if( ch != WXK_NONE )
89  isPrintable = true;
90  else
91 #endif // wxUSE_UNICODE
92  {
93  ch = event.GetKeyCode();
94  isPrintable = ch >= WXK_SPACE && ch < WXK_START;
95  }
96 
97  switch( ch )
98  {
99  case WXK_DELETE:
100  // Delete the initial character when starting to edit with DELETE.
101  textEntry->Remove( 0, 1 );
102  break;
103 
104  case WXK_BACK:
105  // Delete the last character when starting to edit with BACKSPACE.
106  {
107  const long pos = textEntry->GetLastPosition();
108  textEntry->Remove( pos - 1, pos );
109  }
110  break;
111 
112  default:
113  if( isPrintable )
114  textEntry->WriteText( static_cast<wxChar>( ch ) );
115  break;
116  }
117 }
118 
119 
120 void GRID_CELL_TEXT_BUTTON::BeginEdit( int aRow, int aCol, wxGrid* aGrid )
121 {
122  auto evtHandler = static_cast< wxGridCellEditorEvtHandler* >( m_control->GetEventHandler() );
123 
124  // Don't immediately end if we get a kill focus event within BeginEdit
125  evtHandler->SetInSetFocus( true );
126 
127  m_value = aGrid->GetTable()->GetValue( aRow, aCol );
128 
129  Combo()->SetValue( m_value );
130  Combo()->SetFocus();
131 }
132 
133 
134 bool GRID_CELL_TEXT_BUTTON::EndEdit( int, int, const wxGrid*, const wxString&, wxString *aNewVal )
135 {
136  const wxString value = Combo()->GetValue();
137 
138  if( value == m_value )
139  return false;
140 
141  m_value = value;
142 
143  if( aNewVal )
144  *aNewVal = value;
145 
146  return true;
147 }
148 
149 
150 void GRID_CELL_TEXT_BUTTON::ApplyEdit( int aRow, int aCol, wxGrid* aGrid )
151 {
152  aGrid->GetTable()->SetValue( aRow, aCol, m_value );
153 }
154 
155 
157 {
158  Combo()->SetValue( m_value );
159 }
160 
161 
162 #if wxUSE_VALIDATORS
163 void GRID_CELL_TEXT_BUTTON::SetValidator( const wxValidator& validator )
164 {
165  m_validator.reset( static_cast< wxValidator* >( validator.Clone() ) );
166 }
167 #endif
168 
169 
170 class TEXT_BUTTON_SYMBOL_CHOOSER : public wxComboCtrl
171 {
172 public:
173  TEXT_BUTTON_SYMBOL_CHOOSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg,
174  const wxString& aPreselect ) :
175  wxComboCtrl( aParent ),
176  m_dlg( aParentDlg ),
177  m_preselect( aPreselect )
178  {
179  SetButtonBitmaps( KiBitmap( small_library_xpm ) );
180  }
181 
182 protected:
183  void DoSetPopupControl( wxComboPopup* popup ) override
184  {
185  m_popup = nullptr;
186  }
187 
188  void OnButtonClick() override
189  {
190  // pick a footprint using the footprint picker.
191  wxString compid = GetValue();
192 
193  if( compid.IsEmpty() )
194  compid = m_preselect;
195 
197 
198  if( frame->ShowModal( &compid, m_dlg ) )
199  SetValue( compid );
200 
201  frame->Destroy();
202  }
203 
205  wxString m_preselect;
206 };
207 
208 
209 void GRID_CELL_SYMBOL_ID_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
210  wxEvtHandler* aEventHandler )
211 {
212  m_control = new TEXT_BUTTON_SYMBOL_CHOOSER( aParent, m_dlg, m_preselect );
213 
214  wxGridCellEditor::Create( aParent, aId, aEventHandler );
215 }
216 
217 
218 class TEXT_BUTTON_FP_CHOOSER : public wxComboCtrl
219 {
220 public:
221  TEXT_BUTTON_FP_CHOOSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg,
222  const wxString& aPreselect ) :
223  wxComboCtrl( aParent ),
224  m_dlg( aParentDlg ),
225  m_preselect( aPreselect )
226  {
227  SetButtonBitmaps( KiBitmap( small_library_xpm ) );
228  }
229 
230 protected:
231  void DoSetPopupControl( wxComboPopup* popup ) override
232  {
233  m_popup = nullptr;
234  }
235 
236  void OnButtonClick() override
237  {
238  // pick a footprint using the footprint picker.
239  wxString fpid = GetValue();
240 
241  if( fpid.IsEmpty() )
242  fpid = m_preselect;
243 
245 
246  if( frame->ShowModal( &fpid, m_dlg ) )
247  SetValue( fpid );
248 
249  frame->Destroy();
250  }
251 
253  wxString m_preselect;
254 };
255 
256 
257 void GRID_CELL_FOOTPRINT_ID_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
258  wxEvtHandler* aEventHandler )
259 {
260  m_control = new TEXT_BUTTON_FP_CHOOSER( aParent, m_dlg, m_preselect );
261 
262 #if wxUSE_VALIDATORS
263  // validate text in textctrl, if validator is set
264  if ( m_validator )
265  {
266  Combo()->SetValidator( *m_validator );
267  }
268 #endif
269 
270  wxGridCellEditor::Create( aParent, aId, aEventHandler );
271 }
272 
273 
274 class TEXT_BUTTON_URL : public wxComboCtrl
275 {
276 public:
277  TEXT_BUTTON_URL( wxWindow* aParent, DIALOG_SHIM* aParentDlg ) :
278  wxComboCtrl( aParent ),
279  m_dlg( aParentDlg )
280  {
281  SetButtonBitmaps( KiBitmap( www_xpm ) );
282  }
283 
284 protected:
285  void DoSetPopupControl( wxComboPopup* popup ) override
286  {
287  m_popup = nullptr;
288  }
289 
290  void OnButtonClick() override
291  {
292  wxString filename = GetValue();
293 
294  if( !filename.IsEmpty() && filename != wxT( "~" ) )
295  GetAssociatedDocument( m_dlg, GetValue(), &m_dlg->Prj() );
296  }
297 
299 };
300 
301 
302 void GRID_CELL_URL_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
303  wxEvtHandler* aEventHandler )
304 {
305  m_control = new TEXT_BUTTON_URL( aParent, m_dlg );
306 
307 #if wxUSE_VALIDATORS
308  // validate text in textctrl, if validator is set
309  if ( m_validator )
310  {
311  Combo()->SetValidator( *m_validator );
312  }
313 #endif
314 
315  wxGridCellEditor::Create( aParent, aId, aEventHandler );
316 }
317 
318 
319 class TEXT_BUTTON_FILE_BROWSER : public wxComboCtrl
320 {
321 public:
322  TEXT_BUTTON_FILE_BROWSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg, WX_GRID* aGrid,
323  wxString* aCurrentDir, wxString* aExt = nullptr,
324  bool aNormalize = false,
325  wxString aNormalizeBasePath = wxEmptyString ) :
326  wxComboCtrl( aParent ),
327  m_dlg( aParentDlg ),
328  m_grid( aGrid ),
329  m_currentDir( aCurrentDir ),
330  m_ext( aExt ),
331  m_normalize( aNormalize ),
332  m_normalizeBasePath( aNormalizeBasePath )
333  {
334  SetButtonBitmaps( KiBitmap( small_folder_xpm ) );
335  }
336 
337 protected:
338  void DoSetPopupControl( wxComboPopup* popup ) override
339  {
340  m_popup = nullptr;
341  }
342 
343  void OnButtonClick() override
344  {
345  wxString path = GetValue();
346 
347  if( path.IsEmpty() )
348  path = *m_currentDir;
349  else
350  path = ExpandEnvVarSubstitutions( path, &m_dlg->Prj() );
351 
352  if( m_ext )
353  {
354  wxFileName fn( path );
355  wxFileDialog dlg( nullptr, _( "Select a File" ), fn.GetPath(), fn.GetFullName(), *m_ext,
356  wxFD_FILE_MUST_EXIST | wxFD_OPEN );
357 
358  if( dlg.ShowModal() == wxID_OK )
359  {
360  wxString filePath = dlg.GetPath();
361  wxString lastPath = dlg.GetDirectory();
362  wxString relPath = wxEmptyString;
363 
364  if( m_normalize )
365  {
366  relPath = NormalizePath( filePath, &Pgm().GetLocalEnvVariables(),
368  lastPath = NormalizePath( dlg.GetDirectory(), &Pgm().GetLocalEnvVariables(),
370  }
371 
372  if( relPath.IsEmpty() )
373  relPath = filePath;
374 
375  SetValue( relPath );
376 
377  if( !m_grid->CommitPendingChanges() )
378  {;} // shouldn't happen, but Coverity doesn't know that
379 
380  *m_currentDir = lastPath;
381  }
382  }
383  else
384  {
385  wxDirDialog dlg( nullptr, _( "Select Path" ), path,
386  wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST );
387 
388  if( dlg.ShowModal() == wxID_OK )
389  {
390  wxString filePath = dlg.GetPath();
391  wxString relPath = wxEmptyString;
392 
393  if ( m_normalize )
394  {
395  relPath = NormalizePath( filePath, &Pgm().GetLocalEnvVariables(),
397  }
398 
399  if( relPath.IsEmpty() )
400  relPath = filePath;
401 
402  SetValue( relPath );
403 
404  if( !m_grid->CommitPendingChanges() )
405  {;} // shouldn't happen, but Coverity doesn't know that
406 
407  *m_currentDir = relPath;
408  }
409  }
410  }
411 
414  wxString* m_currentDir;
415  wxString* m_ext;
418 };
419 
420 
421 void GRID_CELL_PATH_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
422  wxEvtHandler* aEventHandler )
423 {
424  if( m_ext.IsEmpty() )
425  m_control = new TEXT_BUTTON_FILE_BROWSER( aParent, m_dlg, m_grid, m_currentDir, nullptr,
427  else
428  m_control = new TEXT_BUTTON_FILE_BROWSER( aParent, m_dlg, m_grid, m_currentDir, &m_ext,
430 
431 #if wxUSE_VALIDATORS
432  // validate text in textctrl, if validator is set
433  if ( m_validator )
434  {
435  Combo()->SetValidator( *m_validator );
436  }
437 #endif
438 
439  wxGridCellEditor::Create( aParent, aId, aEventHandler );
440 }
virtual bool ShowModal(wxString *aResult=nullptr, wxWindow *aResultantFocusWindow=nullptr)
Show this wxFrame as if it were a modal dialog, with all other instantiated wxFrames disabled until t...
bool GetAssociatedDocument(wxWindow *aParent, const wxString &aDocName, PROJECT *aProject)
Open a document (file) with the suitable browser.
Definition: eda_doc.cpp:79
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Definition: kiway_player.h:61
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Definition: kiway_holder.h:56
TEXT_BUTTON_FILE_BROWSER(wxWindow *aParent, DIALOG_SHIM *aParentDlg, WX_GRID *aGrid, wxString *aCurrentDir, wxString *aExt=nullptr, bool aNormalize=false, wxString aNormalizeBasePath=wxEmptyString)
void StartingKey(wxKeyEvent &event) override
const BITMAP_OPAQUE small_folder_xpm[1]
This file is part of the common library.
void Create(wxWindow *aParent, wxWindowID aId, wxEvtHandler *aEventHandler) override
void Create(wxWindow *aParent, wxWindowID aId, wxEvtHandler *aEventHandler) override
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:345
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:83
TEXT_BUTTON_URL(wxWindow *aParent, DIALOG_SHIM *aParentDlg)
void DoSetPopupControl(wxComboPopup *popup) override
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:269
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
void SetSize(const wxRect &aRect) override
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:82
void BeginEdit(int aRow, int aCol, wxGrid *aGrid) override
TEXT_BUTTON_FP_CHOOSER(wxWindow *aParent, DIALOG_SHIM *aParentDlg, const wxString &aPreselect)
TEXT_BUTTON_SYMBOL_CHOOSER(wxWindow *aParent, DIALOG_SHIM *aParentDlg, const wxString &aPreselect)
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
wxComboCtrl * Combo() const
void DoSetPopupControl(wxComboPopup *popup) override
const BITMAP_OPAQUE small_library_xpm[1]
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:182
void DoSetPopupControl(wxComboPopup *popup) override
const BITMAP_OPAQUE www_xpm[1]
Definition: www.cpp:37
wxString GetValue() const override
void Create(wxWindow *aParent, wxWindowID aId, wxEvtHandler *aEventHandler) override
#define _(s)
Definition: 3d_actions.cpp:33
void ApplyEdit(int aRow, int aCol, wxGrid *aGrid) override
The common library.
void DoSetPopupControl(wxComboPopup *popup) override
bool Destroy() override
Our version of Destroy() which is virtual from wxWidgets.
void Create(wxWindow *aParent, wxWindowID aId, wxEvtHandler *aEventHandler) override
bool EndEdit(int, int, const wxGrid *, const wxString &, wxString *aNewVal) override
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:67