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 
63 #if defined( __WXMAC__ )
64  rect.Inflate( 2 ); // ignore FOCUS_RING
65 #elif defined( __WXGTK__ )
66  rect.Inflate( -3 ); // The -3 is a very sad hack here. Some GTK themes overrun the
67  // default -1, preventing display. Unfortunately, we don't appear to
68  // have a good method of finding the current margin needed.
69  // Some GTK resize events seem to update the cell size but not all and
70  // not consistently.
71 #else
72  rect.Inflate( -1 );
73 #endif
74 
75  Combo()->SetSize( rect, wxSIZE_ALLOW_MINUS_ONE );
76 }
77 
78 
79 void GRID_CELL_TEXT_BUTTON::StartingKey( wxKeyEvent& event )
80 {
81  // Note: this is a copy of wxGridCellTextEditor's StartingKey()
82 
83  // Since this is now happening in the EVT_CHAR event EmulateKeyPress is no
84  // longer an appropriate way to get the character into the text control.
85  // Do it ourselves instead. We know that if we get this far that we have
86  // a valid character, so not a whole lot of testing needs to be done.
87 
88  // wxComboCtrl inherits from wxTextEntry, so can statically cast
89  wxTextEntry* textEntry = static_cast<wxTextEntry*>( Combo() );
90  int ch;
91 
92  bool isPrintable;
93 
94 #if wxUSE_UNICODE
95  ch = event.GetUnicodeKey();
96 
97  if( ch != WXK_NONE )
98  isPrintable = true;
99  else
100 #endif // wxUSE_UNICODE
101  {
102  ch = event.GetKeyCode();
103  isPrintable = ch >= WXK_SPACE && ch < WXK_START;
104  }
105 
106  switch( ch )
107  {
108  case WXK_DELETE:
109  // Delete the initial character when starting to edit with DELETE.
110  textEntry->Remove( 0, 1 );
111  break;
112 
113  case WXK_BACK:
114  // Delete the last character when starting to edit with BACKSPACE.
115  {
116  const long pos = textEntry->GetLastPosition();
117  textEntry->Remove( pos - 1, pos );
118  }
119  break;
120 
121  default:
122  if( isPrintable )
123  textEntry->WriteText( static_cast<wxChar>( ch ) );
124  break;
125  }
126 }
127 
128 
129 void GRID_CELL_TEXT_BUTTON::BeginEdit( int aRow, int aCol, wxGrid* aGrid )
130 {
131  auto evtHandler = static_cast< wxGridCellEditorEvtHandler* >( m_control->GetEventHandler() );
132 
133  // Don't immediately end if we get a kill focus event within BeginEdit
134  evtHandler->SetInSetFocus( true );
135 
136  m_value = aGrid->GetTable()->GetValue( aRow, aCol );
137 
138  Combo()->SetValue( m_value );
139  Combo()->SetFocus();
140 }
141 
142 
143 bool GRID_CELL_TEXT_BUTTON::EndEdit( int, int, const wxGrid*, const wxString&, wxString *aNewVal )
144 {
145  const wxString value = Combo()->GetValue();
146 
147  if( value == m_value )
148  return false;
149 
150  m_value = value;
151 
152  if( aNewVal )
153  *aNewVal = value;
154 
155  return true;
156 }
157 
158 
159 void GRID_CELL_TEXT_BUTTON::ApplyEdit( int aRow, int aCol, wxGrid* aGrid )
160 {
161  aGrid->GetTable()->SetValue( aRow, aCol, m_value );
162 }
163 
164 
166 {
167  Combo()->SetValue( m_value );
168 }
169 
170 
171 #if wxUSE_VALIDATORS
172 void GRID_CELL_TEXT_BUTTON::SetValidator( const wxValidator& validator )
173 {
174  m_validator.reset( static_cast< wxValidator* >( validator.Clone() ) );
175 }
176 #endif
177 
178 
179 class TEXT_BUTTON_SYMBOL_CHOOSER : public wxComboCtrl
180 {
181 public:
182  TEXT_BUTTON_SYMBOL_CHOOSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg,
183  const wxString& aPreselect ) :
184  wxComboCtrl( aParent ),
185  m_dlg( aParentDlg ),
186  m_preselect( aPreselect )
187  {
188  SetButtonBitmaps( KiBitmap( BITMAPS::small_library ) );
189 
190  // win32 fix, avoids drawing the "native dropdown caret"
191  Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
192  }
193 
194 protected:
195  void DoSetPopupControl( wxComboPopup* popup ) override
196  {
197  m_popup = nullptr;
198  }
199 
200  wxString escapeLibId( const wxString& aRawValue )
201  {
202  wxString itemName;
203  wxString libName = aRawValue.BeforeFirst( ':', &itemName );
204  return EscapeString( libName, CTX_LIBID ) + ':' + EscapeString( itemName, CTX_LIBID );
205  }
206 
207  void OnButtonClick() override
208  {
209  // pick a symbol using the symbol picker.
210  wxString rawValue = GetValue();
211 
212  if( rawValue.IsEmpty() )
213  rawValue = m_preselect;
214 
215  wxString symbolId = escapeLibId( rawValue );
217 
218  if( frame->ShowModal( &symbolId, m_dlg ) )
219  SetValue( UnescapeString( symbolId ) );
220 
221  frame->Destroy();
222  }
223 
225  wxString m_preselect;
226 };
227 
228 
229 void GRID_CELL_SYMBOL_ID_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
230  wxEvtHandler* aEventHandler )
231 {
232  m_control = new TEXT_BUTTON_SYMBOL_CHOOSER( aParent, m_dlg, m_preselect );
233 
234  wxGridCellEditor::Create( aParent, aId, aEventHandler );
235 }
236 
237 
238 class TEXT_BUTTON_FP_CHOOSER : public wxComboCtrl
239 {
240 public:
241  TEXT_BUTTON_FP_CHOOSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg,
242  const wxString& aPreselect ) :
243  wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
244  wxTE_PROCESS_ENTER ),
245  m_dlg( aParentDlg ),
246  m_preselect( aPreselect )
247  {
248  SetButtonBitmaps( KiBitmap( BITMAPS::small_library ) );
249 
250  // win32 fix, avoids drawing the "native dropdown caret"
251  Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
252  }
253 
254 protected:
255  void DoSetPopupControl( wxComboPopup* popup ) override
256  {
257  m_popup = nullptr;
258  }
259 
260  void OnButtonClick() override
261  {
262  // pick a footprint using the footprint picker.
263  wxString fpid = GetValue();
264 
265  if( fpid.IsEmpty() )
266  fpid = m_preselect;
267 
269 
270  if( frame->ShowModal( &fpid, m_dlg ) )
271  SetValue( fpid );
272 
273  frame->Destroy();
274  }
275 
277  wxString m_preselect;
278 };
279 
280 
281 void GRID_CELL_FOOTPRINT_ID_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
282  wxEvtHandler* aEventHandler )
283 {
284  m_control = new TEXT_BUTTON_FP_CHOOSER( aParent, m_dlg, m_preselect );
285 
286 #if wxUSE_VALIDATORS
287  // validate text in textctrl, if validator is set
288  if ( m_validator )
289  {
290  Combo()->SetValidator( *m_validator );
291  }
292 #endif
293 
294  wxGridCellEditor::Create( aParent, aId, aEventHandler );
295 }
296 
297 
298 class TEXT_BUTTON_URL : public wxComboCtrl
299 {
300 public:
301  TEXT_BUTTON_URL( wxWindow* aParent, DIALOG_SHIM* aParentDlg, SEARCH_STACK* aSearchStack ) :
302  wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
303  wxTE_PROCESS_ENTER ),
304  m_dlg( aParentDlg ),
305  m_searchStack( aSearchStack )
306  {
307  SetButtonBitmaps( KiBitmap( BITMAPS::www ) );
308 
309  // win32 fix, avoids drawing the "native dropdown caret"
310  Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
311  }
312 
313 protected:
314  void DoSetPopupControl( wxComboPopup* popup ) override
315  {
316  m_popup = nullptr;
317  }
318 
319  void OnButtonClick() override
320  {
321  wxString filename = GetValue();
322 
323  if( !filename.IsEmpty() && filename != wxT( "~" ) )
324  GetAssociatedDocument( m_dlg, GetValue(), &m_dlg->Prj(), m_searchStack );
325  }
326 
329 };
330 
331 
332 void GRID_CELL_URL_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
333  wxEvtHandler* aEventHandler )
334 {
335  m_control = new TEXT_BUTTON_URL( aParent, m_dlg, m_searchStack );
336 
337 #if wxUSE_VALIDATORS
338  // validate text in textctrl, if validator is set
339  if ( m_validator )
340  {
341  Combo()->SetValidator( *m_validator );
342  }
343 #endif
344 
345  wxGridCellEditor::Create( aParent, aId, aEventHandler );
346 }
347 
348 
349 class TEXT_BUTTON_FILE_BROWSER : public wxComboCtrl
350 {
351 public:
352  TEXT_BUTTON_FILE_BROWSER( wxWindow* aParent, DIALOG_SHIM* aParentDlg, WX_GRID* aGrid,
353  wxString* aCurrentDir, wxString* aExt = nullptr,
354  bool aNormalize = false,
355  wxString aNormalizeBasePath = wxEmptyString ) :
356  wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
357  wxTE_PROCESS_ENTER ),
358  m_dlg( aParentDlg ),
359  m_grid( aGrid ),
360  m_currentDir( aCurrentDir ),
361  m_ext( aExt ),
362  m_normalize( aNormalize ),
363  m_normalizeBasePath( aNormalizeBasePath )
364  {
365  SetButtonBitmaps( KiBitmap( BITMAPS::small_folder ) );
366 
367  // win32 fix, avoids drawing the "native dropdown caret"
368  Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
369  }
370 
371 protected:
372  void DoSetPopupControl( wxComboPopup* popup ) override
373  {
374  m_popup = nullptr;
375  }
376 
377  void OnButtonClick() override
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_ext )
387  {
388  wxFileDialog dlg( m_dlg, _( "Select a File" ), fn.GetPath(), fn.GetFullName(), *m_ext,
389  wxFD_FILE_MUST_EXIST | wxFD_OPEN );
390 
391  if( dlg.ShowModal() == wxID_OK )
392  {
393  wxString filePath = dlg.GetPath();
394  wxString lastPath = dlg.GetDirectory();
395  wxString relPath = wxEmptyString;
396 
397  if( m_normalize )
398  {
399  relPath = NormalizePath( filePath, &Pgm().GetLocalEnvVariables(),
401  lastPath = NormalizePath( dlg.GetDirectory(), &Pgm().GetLocalEnvVariables(),
403  }
404  else
405  {
406  relPath = filePath;
407  }
408 
409  SetValue( relPath );
410 
411  if( !m_grid->CommitPendingChanges() )
412  {;} // shouldn't happen, but Coverity doesn't know that
413 
414  if( m_currentDir )
415  *m_currentDir = lastPath;
416  }
417  }
418  else
419  {
420  wxDirDialog dlg( m_dlg, _( "Select Path" ), fn.GetPath(),
421  wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST );
422 
423  if( dlg.ShowModal() == wxID_OK )
424  {
425  wxString filePath = dlg.GetPath();
426  wxString relPath = wxEmptyString;
427 
428  if ( m_normalize )
429  {
430  relPath = NormalizePath( filePath, &Pgm().GetLocalEnvVariables(),
432  }
433  else
434  {
435  relPath = filePath;
436  }
437 
438  SetValue( relPath );
439 
440  if( !m_grid->CommitPendingChanges() )
441  {;} // shouldn't happen, but Coverity doesn't know that
442 
443  *m_currentDir = relPath;
444  }
445  }
446  }
447 
450  wxString* m_currentDir;
451  wxString* m_ext;
454 };
455 
456 
457 void GRID_CELL_PATH_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
458  wxEvtHandler* aEventHandler )
459 {
460  if( m_ext.IsEmpty() )
461  m_control = new TEXT_BUTTON_FILE_BROWSER( aParent, m_dlg, m_grid, m_currentDir, nullptr,
463  else
464  m_control = new TEXT_BUTTON_FILE_BROWSER( aParent, m_dlg, m_grid, m_currentDir, &m_ext,
466 
467 #if wxUSE_VALIDATORS
468  // validate text in textctrl, if validator is set
469  if ( m_validator )
470  {
471  Combo()->SetValidator( *m_validator );
472  }
473 #endif
474 
475  wxGridCellEditor::Create( aParent, aId, aEventHandler );
476 }
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...
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)
TEXT_BUTTON_URL(wxWindow *aParent, DIALOG_SHIM *aParentDlg, SEARCH_STACK *aSearchStack)
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:393
Look for files in a number of paths.
Definition: search_stack.h:41
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:82
bool GetAssociatedDocument(wxWindow *aParent, const wxString &aDocName, PROJECT *aProject, SEARCH_STACK *aPaths)
Open a document (file) with the suitable browser.
Definition: eda_doc.cpp:74
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:267
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:226
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:71