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