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 #include <wx/textctrl.h>
29 
30 #include <bitmaps.h>
31 #include <kiway.h>
32 #include <kiway_player.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>
40 #include <eda_doc.h>
41 
42 
43 //-------- Renderer ---------------------------------------------------------------------
44 // None required; just render as normal text.
45 
46 
47 
48 //-------- Editor Base Class ------------------------------------------------------------
49 //
50 // Note: this implementation is an adaptation of wxGridCellChoiceEditor
51 
52 
54 {
55  return Combo()->GetValue();
56 }
57 
58 
59 void GRID_CELL_TEXT_BUTTON::SetSize( const wxRect& aRect )
60 {
61  wxRect rect( aRect );
62  rect.Inflate( -1 );
63 
64 #if defined( __WXMAC__ )
65  rect.Inflate( 3 ); // no FOCUS_RING, even on Mac
66 #endif
67 
68  Combo()->SetSize( rect, wxSIZE_ALLOW_MINUS_ONE );
69 }
70 
71 
72 void GRID_CELL_TEXT_BUTTON::StartingKey( wxKeyEvent& event )
73 {
74  // Note: this is a copy of wxGridCellTextEditor's StartingKey()
75 
76  // Since this is now happening in the EVT_CHAR event EmulateKeyPress is no
77  // longer an appropriate way to get the character into the text control.
78  // Do it ourselves instead. We know that if we get this far that we have
79  // a valid character, so not a whole lot of testing needs to be done.
80 
81  // wxComboCtrl inherits from wxTextEntry, so can statically cast
82  wxTextEntry* textEntry = static_cast<wxTextEntry*>( Combo() );
83  int ch;
84 
85  bool isPrintable;
86 
87 #if wxUSE_UNICODE
88  ch = event.GetUnicodeKey();
89 
90  if( ch != WXK_NONE )
91  isPrintable = true;
92  else
93 #endif // wxUSE_UNICODE
94  {
95  ch = event.GetKeyCode();
96  isPrintable = ch >= WXK_SPACE && ch < WXK_START;
97  }
98 
99  switch( ch )
100  {
101  case WXK_DELETE:
102  // Delete the initial character when starting to edit with DELETE.
103  textEntry->Remove( 0, 1 );
104  break;
105 
106  case WXK_BACK:
107  // Delete the last character when starting to edit with BACKSPACE.
108  {
109  const long pos = textEntry->GetLastPosition();
110  textEntry->Remove( pos - 1, pos );
111  }
112  break;
113 
114  default:
115  if( isPrintable )
116  textEntry->WriteText( static_cast<wxChar>( ch ) );
117  break;
118  }
119 }
120 
121 
122 void GRID_CELL_TEXT_BUTTON::BeginEdit( int aRow, int aCol, wxGrid* aGrid )
123 {
124  auto evtHandler = static_cast< wxGridCellEditorEvtHandler* >( m_control->GetEventHandler() );
125 
126  // Don't immediately end if we get a kill focus event within BeginEdit
127  evtHandler->SetInSetFocus( true );
128 
129  m_value = aGrid->GetTable()->GetValue( aRow, aCol );
130 
131  Combo()->SetValue( m_value );
132  Combo()->SetFocus();
133 }
134 
135 
136 bool GRID_CELL_TEXT_BUTTON::EndEdit( int, int, const wxGrid*, const wxString&, wxString *aNewVal )
137 {
138  const wxString value = Combo()->GetValue();
139 
140  if( value == m_value )
141  return false;
142 
143  m_value = value;
144 
145  if( aNewVal )
146  *aNewVal = value;
147 
148  return true;
149 }
150 
151 
152 void GRID_CELL_TEXT_BUTTON::ApplyEdit( int aRow, int aCol, wxGrid* aGrid )
153 {
154  aGrid->GetTable()->SetValue( aRow, aCol, m_value );
155 }
156 
157 
159 {
160  Combo()->SetValue( m_value );
161 }
162 
163 
164 #if wxUSE_VALIDATORS
165 void GRID_CELL_TEXT_BUTTON::SetValidator( const wxValidator& validator )
166 {
167  m_validator.reset( static_cast< wxValidator* >( validator.Clone() ) );
168 }
169 #endif
170 
171 
172 class TEXT_BUTTON_SYMBOL_CHOOSER : public wxComboCtrl
173 {
174 public:
175  TEXT_BUTTON_SYMBOL_CHOOSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg,
176  const wxString& aPreselect ) :
177  wxComboCtrl( aParent ),
178  m_dlg( aParentDlg ),
179  m_preselect( aPreselect )
180  {
181  SetButtonBitmaps( KiBitmap( BITMAPS::small_library ) );
182 
183  // win32 fix, avoids drawing the "native dropdown caret"
184  Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
185  }
186 
187 protected:
188  void DoSetPopupControl( wxComboPopup* popup ) override
189  {
190  m_popup = nullptr;
191  }
192 
193  wxString escapeLibId( const wxString& aRawValue )
194  {
195  wxString itemName;
196  wxString libName = aRawValue.BeforeFirst( ':', &itemName );
197  return EscapeString( libName, CTX_LIBID ) + ':' + EscapeString( itemName, CTX_LIBID );
198  }
199 
200  void OnButtonClick() override
201  {
202  // pick a symbol using the symbol picker.
203  wxString rawValue = GetValue();
204 
205  if( rawValue.IsEmpty() )
206  rawValue = m_preselect;
207 
208  wxString symbolId = escapeLibId( rawValue );
210 
211  if( frame->ShowModal( &symbolId, m_dlg ) )
212  SetValue( UnescapeString( symbolId ) );
213 
214  frame->Destroy();
215  }
216 
218  wxString m_preselect;
219 };
220 
221 
222 void GRID_CELL_SYMBOL_ID_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
223  wxEvtHandler* aEventHandler )
224 {
225  m_control = new TEXT_BUTTON_SYMBOL_CHOOSER( aParent, m_dlg, m_preselect );
226 
227  wxGridCellEditor::Create( aParent, aId, aEventHandler );
228 }
229 
230 
231 class TEXT_BUTTON_FP_CHOOSER : public wxComboCtrl
232 {
233 public:
234  TEXT_BUTTON_FP_CHOOSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg,
235  const wxString& aPreselect ) :
236  wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
237  wxTE_PROCESS_ENTER ),
238  m_dlg( aParentDlg ),
239  m_preselect( aPreselect )
240  {
241  SetButtonBitmaps( KiBitmap( BITMAPS::small_library ) );
242 
243  // win32 fix, avoids drawing the "native dropdown caret"
244  Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
245  }
246 
247 protected:
248  void DoSetPopupControl( wxComboPopup* popup ) override
249  {
250  m_popup = nullptr;
251  }
252 
253  void OnButtonClick() override
254  {
255  // pick a footprint using the footprint picker.
256  wxString fpid = GetValue();
257 
258  if( fpid.IsEmpty() )
259  fpid = m_preselect;
260 
262 
263  if( frame->ShowModal( &fpid, m_dlg ) )
264  SetValue( fpid );
265 
266  frame->Destroy();
267  }
268 
270  wxString m_preselect;
271 };
272 
273 
274 void GRID_CELL_FOOTPRINT_ID_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
275  wxEvtHandler* aEventHandler )
276 {
277  m_control = new TEXT_BUTTON_FP_CHOOSER( aParent, m_dlg, m_preselect );
278 
279 #if wxUSE_VALIDATORS
280  // validate text in textctrl, if validator is set
281  if ( m_validator )
282  {
283  Combo()->SetValidator( *m_validator );
284  }
285 #endif
286 
287  wxGridCellEditor::Create( aParent, aId, aEventHandler );
288 }
289 
290 
291 class TEXT_BUTTON_URL : public wxComboCtrl
292 {
293 public:
294  TEXT_BUTTON_URL( wxWindow* aParent, DIALOG_SHIM* aParentDlg ) :
295  wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
296  wxTE_PROCESS_ENTER ),
297  m_dlg( aParentDlg )
298  {
299  SetButtonBitmaps( KiBitmap( BITMAPS::www ) );
300 
301  // win32 fix, avoids drawing the "native dropdown caret"
302  Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
303  }
304 
305 protected:
306  void DoSetPopupControl( wxComboPopup* popup ) override
307  {
308  m_popup = nullptr;
309  }
310 
311  void OnButtonClick() override
312  {
313  wxString filename = GetValue();
314 
315  if( !filename.IsEmpty() && filename != wxT( "~" ) )
316  GetAssociatedDocument( m_dlg, GetValue(), &m_dlg->Prj() );
317  }
318 
320 };
321 
322 
323 void GRID_CELL_URL_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
324  wxEvtHandler* aEventHandler )
325 {
326  m_control = new TEXT_BUTTON_URL( aParent, m_dlg );
327 
328 #if wxUSE_VALIDATORS
329  // validate text in textctrl, if validator is set
330  if ( m_validator )
331  {
332  Combo()->SetValidator( *m_validator );
333  }
334 #endif
335 
336  wxGridCellEditor::Create( aParent, aId, aEventHandler );
337 }
338 
339 
340 class TEXT_BUTTON_FILE_BROWSER : public wxComboCtrl
341 {
342 public:
343  TEXT_BUTTON_FILE_BROWSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg, WX_GRID* aGrid,
344  wxString* aCurrentDir, wxString* aExt = nullptr,
345  bool aNormalize = false,
346  wxString aNormalizeBasePath = wxEmptyString ) :
347  wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
348  wxTE_PROCESS_ENTER ),
349  m_dlg( aParentDlg ),
350  m_grid( aGrid ),
351  m_currentDir( aCurrentDir ),
352  m_ext( aExt ),
353  m_normalize( aNormalize ),
354  m_normalizeBasePath( aNormalizeBasePath )
355  {
356  SetButtonBitmaps( KiBitmap( BITMAPS::small_folder ) );
357 
358  // win32 fix, avoids drawing the "native dropdown caret"
359  Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
360  }
361 
362 protected:
363  void DoSetPopupControl( wxComboPopup* popup ) override
364  {
365  m_popup = nullptr;
366  }
367 
368  void OnButtonClick() override
369  {
370  wxFileName fn = GetValue();
371 
372  if( fn.GetPath().IsEmpty() && m_currentDir )
373  fn.SetPath( *m_currentDir );
374  else
375  fn.SetPath( ExpandEnvVarSubstitutions( fn.GetPath(), &m_dlg->Prj() ) );
376 
377  if( m_ext )
378  {
379  wxFileDialog dlg( m_dlg, _( "Select a File" ), fn.GetPath(), fn.GetFullName(), *m_ext,
380  wxFD_FILE_MUST_EXIST | wxFD_OPEN );
381 
382  if( dlg.ShowModal() == wxID_OK )
383  {
384  wxString filePath = dlg.GetPath();
385  wxString lastPath = dlg.GetDirectory();
386  wxString relPath = wxEmptyString;
387 
388  if( m_normalize )
389  {
390  relPath = NormalizePath( filePath, &Pgm().GetLocalEnvVariables(),
392  lastPath = NormalizePath( dlg.GetDirectory(), &Pgm().GetLocalEnvVariables(),
394  }
395 
396  if( relPath.IsEmpty() )
397  relPath = filePath;
398 
399  SetValue( relPath );
400 
401  if( !m_grid->CommitPendingChanges() )
402  {;} // shouldn't happen, but Coverity doesn't know that
403 
404  if( m_currentDir )
405  *m_currentDir = lastPath;
406  }
407  }
408  else
409  {
410  wxDirDialog dlg( m_dlg, _( "Select Path" ), fn.GetPath(),
411  wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST );
412 
413  if( dlg.ShowModal() == wxID_OK )
414  {
415  wxString filePath = dlg.GetPath();
416  wxString relPath = wxEmptyString;
417 
418  if ( m_normalize )
419  {
420  relPath = NormalizePath( filePath, &Pgm().GetLocalEnvVariables(),
422  }
423 
424  if( relPath.IsEmpty() )
425  relPath = filePath;
426 
427  SetValue( relPath );
428 
429  if( !m_grid->CommitPendingChanges() )
430  {;} // shouldn't happen, but Coverity doesn't know that
431 
432  *m_currentDir = relPath;
433  }
434  }
435  }
436 
439  wxString* m_currentDir;
440  wxString* m_ext;
443 };
444 
445 
446 void GRID_CELL_PATH_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
447  wxEvtHandler* aEventHandler )
448 {
449  if( m_ext.IsEmpty() )
450  m_control = new TEXT_BUTTON_FILE_BROWSER( aParent, m_dlg, m_grid, m_currentDir, nullptr,
452  else
453  m_control = new TEXT_BUTTON_FILE_BROWSER( aParent, m_dlg, m_grid, m_currentDir, &m_ext,
455 
456 #if wxUSE_VALIDATORS
457  // validate text in textctrl, if validator is set
458  if ( m_validator )
459  {
460  Combo()->SetValidator( *m_validator );
461  }
462 #endif
463 
464  wxGridCellEditor::Create( aParent, aId, aEventHandler );
465 }
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:64
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Definition: kiway_holder.h:53
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
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:382
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:82
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:279
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
void SetSize(const wxRect &aRect) override
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
#define _(s)
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:190
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
wxString escapeLibId(const wxString &aRawValue)
void DoSetPopupControl(wxComboPopup *popup) override
wxString UnescapeString(const wxString &aSource)
wxString GetValue() const override
see class PGM_BASE
void Create(wxWindow *aParent, wxWindowID aId, wxEvtHandler *aEventHandler) override
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
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
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:69