NumeRe v1.1.4
NumeRe: Framework für Numerische Rechnungen
customwindow.cpp
Go to the documentation of this file.
1/*****************************************************************************
2 NumeRe: Framework fuer Numerische Rechnungen
3 Copyright (C) 2021 Erik Haenel et al.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17******************************************************************************/
18
19#include "customwindow.hpp"
20#include "../NumeReWindow.h" // Already includes NumeRe::Window
21#include "../../kernel/core/utils/tinyxml2.h"
22#include "../../kernel/core/utils/stringtools.hpp"
23#include "grouppanel.hpp"
24#include <wx/tokenzr.h>
25#include <wx/dataview.h>
26#include <wx/statline.h>
27#include "../wx.h"
28
29#include <string>
30
39static wxString toWxString(const wxColor& c)
40{
41 return wxString::Format("{%d,%d,%d}", c.Red(), c.Green(), c.Blue());
42}
43
44
53static wxColour toWxColour(const wxString& s)
54{
55 wxColour c;
56 c.Set("rgb(" + s + ")");
57
58 return c;
59}
60
61
71static wxString convertToCodeString(wxString s)
72{
73 s.Replace("\"", "\\\"");
74 return "\"" + s + "\"";
75}
76
77
85static wxString nextItemValue(wxString& sItem)
86{
87 wxString sValue = sItem.substr(0, sItem.find('\t'));
88
89 if (sItem.find('\t') != std::string::npos)
90 sItem.erase(0, sItem.find('\t')+1);
91 else
92 sItem.clear();
93
94 return sValue;
95}
96
97
108static void populateTreeListCtrl(wxTreeListCtrl* listCtrl, const wxArrayString& values)
109{
110 if (!values.size())
111 return;
112
113 bool useCheckBoxes = listCtrl->HasFlag(wxTL_CHECKBOX);
114 size_t nColumns = 1;
115 size_t pos = 0;
116
117 while ((pos = values[0].find('\t', pos)) != std::string::npos)
118 {
119 nColumns++;
120 pos++;
121 }
122
123 if (useCheckBoxes)
124 nColumns--;
125
126 listCtrl->DeleteAllItems();
127 //wxSize ctrlSize = listCtrl->GetClientSize();
128
129 while (listCtrl->GetColumnCount() < nColumns)
130 listCtrl->AppendColumn("");
131
132 for (size_t i = 0; i < values.size(); i++)
133 {
134 wxString sItem = values[i];
135 size_t currCol = 1u;
136 bool check = false;
137
138 if (useCheckBoxes)
139 check = nextItemValue(sItem) != "0";
140
141 wxTreeListItem item = listCtrl->AppendItem(listCtrl->GetRootItem(), nextItemValue(sItem));
142
143 if (check && useCheckBoxes)
144 listCtrl->CheckItem(item);
145
146 while (sItem.length() && currCol < nColumns)
147 {
148 listCtrl->SetItemText(item, currCol, nextItemValue(sItem));
149 currCol++;
150 }
151 }
152
153 //for (size_t i = 0; i < 1u; i++)
154 //{
155 // listCtrl->SetColumnWidth(i, -1);//ctrlSize.x / nColumns - 2);
156 //}
157}
158
159
171static wxString getTreeListCtrlValue(wxTreeListCtrl* listCtrl)
172{
173 wxString values;
174 bool useCheckBoxes = listCtrl->HasFlag(wxTL_CHECKBOX);
175 wxTreeListItems items;
176
177 // Get selections if any and no checkboxes are used
178 if (!useCheckBoxes)
179 {
180 if (listCtrl->GetSelections(items))
181 {
182 for (size_t i = 0; i < items.size(); i++)
183 {
184 if (values.length())
185 values += ", ";
186
187 wxString sItem;
188
189 for (size_t j = 0; j < listCtrl->GetColumnCount(); j++)
190 {
191 sItem += listCtrl->GetItemText(items[i], j);
192
193 if (j+1 < listCtrl->GetColumnCount())
194 sItem += "\t";
195 }
196
197 values += convertToCodeString(sItem);
198 }
199
200 if (listCtrl->GetColumnCount() > 1)
201 return convertToCodeString(values);
202 }
203 else
204 values = "\"\"";
205
206 return values;
207 }
208
209 // Get the complete list or the states of the checkboxes
210 for (wxTreeListItem item = listCtrl->GetFirstItem(); item.IsOk(); item = listCtrl->GetNextItem(item))
211 {
212 if (values.length())
213 values += ",";
214
215 values += listCtrl->GetCheckedState(item) == wxCHK_CHECKED ? "1" : "0";
216 }
217
218 return "\"{" + values + "}\"";
219}
220
221
227{
230 HIDDEN
232
233
234BEGIN_EVENT_TABLE(CustomWindow, wxFrame)
235 EVT_BUTTON(-1, CustomWindow::OnClick)
236 EVT_CHECKBOX(-1, CustomWindow::OnChange)
237 EVT_RADIOBOX(-1, CustomWindow::OnChange)
238 EVT_CHOICE(-1, CustomWindow::OnChange)
239 EVT_COMBOBOX(-1, CustomWindow::OnChange)
240 EVT_TEXT(-1, CustomWindow::OnChange)
241 EVT_TEXT_ENTER(-1, CustomWindow::OnChange)
242 EVT_SPINCTRL(-1, CustomWindow::OnSpin)
243 EVT_CLOSE(CustomWindow::OnClose)
244 EVT_GRID_SELECT_CELL(CustomWindow::OnCellSelect)
245 EVT_LEFT_DOWN(CustomWindow::OnMouseLeftDown)
246 EVT_TREELIST_ITEM_CHECKED(-1, CustomWindow::OnTreeListEvent)
247 EVT_TREELIST_SELECTION_CHANGED(-1, CustomWindow::OnTreeListEvent)
249 EVT_SLIDER(-1, CustomWindow::OnChange)
250 EVT_MENU(-1, CustomWindow::OnMenuEvent)
252
253
254
255
256
257
266CustomWindow::CustomWindow(wxWindow* parent, const NumeRe::Window& windowRef) : wxFrame(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxFRAME_FLOAT_ON_PARENT | wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX | wxMAXIMIZE_BOX | wxMINIMIZE_BOX), m_windowRef(windowRef)
267{
268 m_windowRef.connect(this);
269
270 layout();
271}
272
273
283{
284 // Get the layout group from the layout script
286
287 // Evaluate possible background color information
288 if (layoutGroup->Attribute("color"))
289 {
290 wxString sColor = layoutGroup->Attribute("color");
291 wxColour background;
292
293 background.Set("rgb(" + sColor + ")");
294
295 SetBackgroundColour(background);
296 }
297
298 // Evaluate the title information
299 if (layoutGroup->Attribute("title"))
300 SetTitle(layoutGroup->Attribute("title"));
301 else
302 SetTitle("NumeRe: Custom Window");
303
304 // Evaluate the user-supplied icon or use
305 // the standard icon
306 if (layoutGroup->Attribute("icon"))
307 {
308 wxFileName iconfile(layoutGroup->Attribute("icon"));
309
310 if (iconfile.GetExt() == "ico")
311 SetIcon(wxIcon(layoutGroup->Attribute("icon"), wxBITMAP_TYPE_ICO));
312 else if (iconfile.GetExt() == "png")
313 SetIcon(wxIcon(layoutGroup->Attribute("icon"), wxBITMAP_TYPE_PNG));
314 else if (iconfile.GetExt() == "bmp")
315 SetIcon(wxIcon(layoutGroup->Attribute("icon"), wxBITMAP_TYPE_BMP));
316 }
317 else
318 SetIcon(static_cast<NumeReWindow*>(m_parent)->getStandardIcon());
319
320 // Create the GroupPanel instance and bind the mouse event handler
321 // the frame's event handler
322 GroupPanel* _groupPanel = new GroupPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxBORDER_STATIC);
323 _groupPanel->Bind(wxEVT_LEFT_DOWN, &CustomWindow::OnMouseLeftDown, this);
324
325 // Recursively layout the child elements of the current XMLElement
326 // object
327 layoutChild(layoutGroup->FirstChildElement(), _groupPanel, _groupPanel->getMainSizer(), _groupPanel);
328
329 // Define the scrollbars
330 _groupPanel->SetScrollbars(20, 20, 200, 200);
331
332 // Create a status bar
333 CreateStatusBar();
334
335 // Evaluate the size information
336 if (layoutGroup->Attribute("size"))
337 {
338 wxString sSize = layoutGroup->Attribute("size");
339 long int x,y;
340 sSize.substr(0, sSize.find(',')).ToLong(&x);
341 sSize.substr(sSize.find(',')+1).ToLong(&y);
342
343 SetClientSize(wxSize(x,y));
344 }
345 else
346 SetClientSize(wxSize(800,600));
347
348
349 //wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
350 //vsizer->Add(_groupPanel, 1, wxEXPAND, 0);
351 //SetSizer(vsizer);
352 //vsizer->SetSizeHints(_groupPanel);
353}
354
355
368void CustomWindow::layoutChild(const tinyxml2::XMLElement* currentChild, wxWindow* currParent, wxSizer* currSizer, GroupPanel* _groupPanel)
369{
370 wxNotebook* noteBook = nullptr;
371
372 // As long as another child (i.e. sibling) can be found
373 while (currentChild)
374 {
375 // Evaluate the id attribute
376 int id = currentChild->Attribute("id") ?
377 currentChild->DoubleAttribute("id") : m_windowItems.size() + 1000;
378
379 // Get the text used by some controls
380 wxString text = currentChild->GetText() ? currentChild->GetText() : "";
381
382 wxFont font = GetFont();
383 WindowState state = ENABLED;
384 int alignment = wxALIGN_CENTER_VERTICAL;
385
386 // Evaluate the state attribute
387 if (currentChild->Attribute("state"))
388 state = currentChild->Attribute("state", "disabled") ? DISABLED : (currentChild->Attribute("state", "hidden") ? HIDDEN : ENABLED);
389
390 // evaluat the font attribute
391 if (currentChild->Attribute("font"))
392 {
393 wxString sFont = currentChild->Attribute("font");
394
395 if (sFont.find('i') != std::string::npos)
396 font.MakeItalic();
397
398 if (sFont.find('b') != std::string::npos)
399 font.MakeBold();
400 }
401
402 // Get the alignment of the elements
403 if (currentChild->Attribute("align"))
404 {
405 wxString sAlign = currentChild->Attribute("align");
406 alignment = 0;
407
408 // LRCTB
409 if (sAlign.find_first_of("TB") == std::string::npos)
410 alignment |= wxALIGN_CENTER_VERTICAL;
411
412 if (sAlign.find_first_of("LR") == std::string::npos)
413 alignment |= wxALIGN_CENTER_HORIZONTAL;
414
415 if (sAlign.find("L") != std::string::npos)
416 alignment |= wxALIGN_LEFT;
417
418 if (sAlign.find("R") != std::string::npos)
419 alignment |= wxALIGN_RIGHT;
420
421 if (sAlign.find("T") != std::string::npos)
422 alignment |= wxALIGN_TOP;
423
424 if (sAlign.find("B") != std::string::npos)
425 alignment |= wxALIGN_BOTTOM;
426
427 }
428
429 // Now check for the current XMLElement's
430 // value (i.e. the XML-Tag name) and create
431 // a corresponding control (if available)
432 if (std::string(currentChild->Value()) == "button")
433 {
434 // Add a button
435 wxButton* button = _groupPanel->CreateButton(currParent, currSizer, removeQuotationMarks(text), id, alignment);
436 button->SetFont(font);
437 m_windowItems[id] = std::make_pair(CustomWindow::BUTTON, button);
438
439 if (currentChild->Attribute("onclick"))
440 m_eventTable[id] = currentChild->Attribute("onclick");
441
442 if (currentChild->Attribute("color"))
443 button->SetForegroundColour(toWxColour(currentChild->Attribute("color")));
444
445 if (state == DISABLED)
446 button->Disable();
447 else if (state == HIDDEN)
448 button->Hide();
449 }
450 else if (std::string(currentChild->Value()) == "checkbox")
451 {
452 // Add a checkbox
453 wxCheckBox* checkbox = _groupPanel->CreateCheckBox(currParent, currSizer, removeQuotationMarks(text), id, alignment);
454 checkbox->SetFont(font);
455 m_windowItems[id] = std::make_pair(CustomWindow::CHECKBOX, checkbox);
456
457 if (currentChild->Attribute("value"))
458 checkbox->SetValue(currentChild->Attribute("value", "1"));
459
460 if (currentChild->Attribute("onchange"))
461 m_eventTable[id] = currentChild->Attribute("onchange");
462
463 if (currentChild->Attribute("color"))
464 checkbox->SetBackgroundColour(toWxColour(currentChild->Attribute("color")));
465
466 if (state == DISABLED)
467 checkbox->Disable();
468 else if (state == HIDDEN)
469 checkbox->Hide();
470 }
471 else if (std::string(currentChild->Value()) == "radio")
472 {
473 // Add a radio group
474 wxString label;
475 int style = wxHORIZONTAL;
476
477 if (currentChild->Attribute("label"))
478 label = currentChild->Attribute("label");
479
480 if (currentChild->Attribute("type"))
481 style = currentChild->Attribute("type", "horizontal") ? wxHORIZONTAL : wxVERTICAL;
482
483 wxRadioBox* radiobox = _groupPanel->CreateRadioBox(currParent, currSizer, label, getChoices(text), style, id, alignment);
484 radiobox->SetFont(font);
485 m_windowItems[id] = std::make_pair(CustomWindow::RADIOGROUP, radiobox);
486
487 if (currentChild->Attribute("value"))
488 radiobox->SetSelection(currentChild->IntAttribute("value")-1);
489
490 if (currentChild->Attribute("onchange"))
491 m_eventTable[id] = currentChild->Attribute("onchange");
492
493 if (state == DISABLED)
494 radiobox->Disable();
495 else if (state == HIDDEN)
496 radiobox->Hide();
497 }
498 else if (std::string(currentChild->Value()) == "spinbut")
499 {
500 // Add a spincontrol group
501 wxString label;
502 int nMin = 0, nMax = 100, nValue = 0;
503
504 if (currentChild->Attribute("label"))
505 label = currentChild->Attribute("label");
506
507 if (currentChild->Attribute("min"))
508 nMin = currentChild->DoubleAttribute("min");
509
510 if (currentChild->Attribute("max"))
511 nMax = currentChild->DoubleAttribute("max");
512
513 if (currentChild->Attribute("value"))
514 nValue = currentChild->DoubleAttribute("value");
515
516 SpinBut* spinctrl = _groupPanel->CreateSpinControl(currParent, currSizer, label, nMin, nMax, nValue, id, alignment);
517 spinctrl->SetFont(font);
518 m_windowItems[id] = std::make_pair(CustomWindow::SPINCTRL, spinctrl);
519
520 if (currentChild->Attribute("onchange"))
521 m_eventTable[id] = currentChild->Attribute("onchange");
522
523 if (currentChild->Attribute("color"))
524 spinctrl->SetBackgroundColour(toWxColour(currentChild->Attribute("color")));
525
526 if (state == DISABLED)
527 spinctrl->Enable(false);
528 else if (state == HIDDEN)
529 spinctrl->Show(false);
530 }
531 else if (std::string(currentChild->Value()) == "slider")
532 {
533 // Add a slider
534 int nMin = 0, nMax = 100, nValue = 0;
535 int style = wxHORIZONTAL;
536
537 if (currentChild->Attribute("type"))
538 style = currentChild->Attribute("type", "horizontal") ? wxHORIZONTAL | wxSL_LABELS | wxSL_AUTOTICKS : wxVERTICAL | wxSL_LABELS | wxSL_AUTOTICKS;
539
540 if (currentChild->Attribute("min"))
541 nMin = currentChild->DoubleAttribute("min");
542
543 if (currentChild->Attribute("max"))
544 nMax = currentChild->DoubleAttribute("max");
545
546 if (currentChild->Attribute("value"))
547 nValue = currentChild->DoubleAttribute("value");
548
549 wxSlider* slider = _groupPanel->CreateSlider(currParent, currSizer, nMin, nMax, nValue, style, id, alignment);
550 slider->SetFont(font);
551 m_windowItems[id] = std::make_pair(CustomWindow::SLIDER, slider);
552
553 if (currentChild->Attribute("onchange"))
554 m_eventTable[id] = currentChild->Attribute("onchange");
555
556 if (currentChild->Attribute("color"))
557 slider->SetForegroundColour(toWxColour(currentChild->Attribute("color")));
558
559 if (state == DISABLED)
560 slider->Enable(false);
561 else if (state == HIDDEN)
562 slider->Show(false);
563 }
564 else if (std::string(currentChild->Value()) == "gauge")
565 {
566 // Add a gauge
567 wxString label;
568 int style = wxHORIZONTAL;
569
570 if (currentChild->Attribute("type"))
571 style = currentChild->Attribute("type", "horizontal") ? wxGA_HORIZONTAL | wxGA_SMOOTH : wxGA_VERTICAL | wxGA_SMOOTH;
572
573 wxGauge* gauge = _groupPanel->CreateGauge(currParent, currSizer, style, id, alignment);
574 m_windowItems[id] = std::make_pair(CustomWindow::GAUGE, gauge);
575
576 if (currentChild->Attribute("value"))
577 gauge->SetValue(currentChild->DoubleAttribute("value"));
578
579 if (state == DISABLED)
580 gauge->Disable();
581 else if (state == HIDDEN)
582 gauge->Hide();
583 }
584 else if (std::string(currentChild->Value()) == "dropdown")
585 {
586 // Add a dropdown
587 wxChoice* choice = _groupPanel->CreateChoices(currParent, currSizer, getChoices(text), id, alignment);
588 m_windowItems[id] = std::make_pair(CustomWindow::DROPDOWN, choice);
589
590 if (currentChild->Attribute("value"))
591 choice->SetSelection(currentChild->IntAttribute("value")-1);
592
593 if (currentChild->Attribute("onchange"))
594 m_eventTable[id] = currentChild->Attribute("onchange");
595
596 if (currentChild->Attribute("color"))
597 choice->SetBackgroundColour(toWxColour(currentChild->Attribute("color")));
598
599 if (state == DISABLED)
600 choice->Disable();
601 else if (state == HIDDEN)
602 choice->Hide();
603 }
604 else if (std::string(currentChild->Value()) == "combobox")
605 {
606 // Add a combobox
607 wxComboBox* combo = _groupPanel->CreateComboBox(currParent, currSizer, getChoices(text), id, alignment);
608 m_windowItems[id] = std::make_pair(CustomWindow::COMBOBOX, combo);
609
610 if (currentChild->Attribute("value"))
611 combo->SetSelection(currentChild->IntAttribute("value")-1);
612
613 if (currentChild->Attribute("onchange"))
614 m_eventTable[id] = currentChild->Attribute("onchange");
615
616 if (currentChild->Attribute("color"))
617 combo->SetBackgroundColour(toWxColour(currentChild->Attribute("color")));
618
619 if (state == DISABLED)
620 combo->Disable();
621 else if (state == HIDDEN)
622 combo->Hide();
623 }
624 else if (std::string(currentChild->Value()) == "textfield")
625 {
626 // Add a textctrl
627 int style = wxTE_PROCESS_ENTER;
628 wxSize size(310,-1);
629
630 if (currentChild->Attribute("size"))
631 {
632 wxString sSize = currentChild->Attribute("size");
633 long int x,y;
634 sSize.substr(0, sSize.find(',')).ToLong(&x);
635 sSize.substr(sSize.find(',')+1).ToLong(&y);
636
637 size.x = x;
638 size.y = y;
639 }
640
641 if (currentChild->Attribute("type"))
642 style = currentChild->Attribute("type", "multiline") ? wxTE_MULTILINE | wxTE_BESTWRAP : wxTE_PROCESS_ENTER;
643
644 wxString label;
645
646 if (currentChild->Attribute("label"))
647 label = currentChild->Attribute("label");
648
649 TextField* textctrl = _groupPanel->CreateTextInput(currParent, currSizer, label, removeQuotationMarks(text), style, id, size, alignment);
650 textctrl->SetFont(font);
651 m_windowItems[id] = std::make_pair(CustomWindow::TEXTCTRL, textctrl);
652
653 if (currentChild->Attribute("onchange"))
654 m_eventTable[id] = currentChild->Attribute("onchange");
655
656 if (currentChild->Attribute("color"))
657 textctrl->SetBackgroundColour(toWxColour(currentChild->Attribute("color")));
658
659 if (state == DISABLED)
660 textctrl->Enable(false);
661 else if (state == HIDDEN)
662 textctrl->Show(false);
663 }
664 else if (std::string(currentChild->Value()) == "statictext" || std::string(currentChild->Value()) == "text")
665 {
666 // Add a static test
667 wxStaticText* statictext = _groupPanel->AddStaticText(currParent, currSizer, removeQuotationMarks(text), id, alignment);
668 statictext->SetFont(font);
669 m_windowItems[id] = std::make_pair(CustomWindow::TEXT, statictext);
670
671 if (currentChild->Attribute("color"))
672 statictext->SetForegroundColour(toWxColour(currentChild->Attribute("color")));
673 }
674 else if (std::string(currentChild->Value()) == "prop")
675 {
676 // Create internal variables
677 wxArrayString varList = getChoices(text);
678
679 for (size_t i = 0; i < varList.size(); i++)
680 {
681 if (varList[i].find('=') != std::string::npos)
682 {
683 wxString name = varList[i].substr(0, varList[i].find('='));
684 wxString value = varList[i].substr(varList[i].find('=')+1);
685
686 m_varTable[name.Trim()] = value.Trim(false);
687 }
688 else
689 m_varTable[varList[i]] = "0";
690 }
691 }
692 else if (std::string(currentChild->Value()) == "bitmap")
693 {
694 // Add an image
695 wxStaticBitmap* bitmap = _groupPanel->CreateBitmap(currParent, currSizer, removeQuotationMarks(text), id, alignment);
696 m_windowItems[id] = std::make_pair(CustomWindow::IMAGE, bitmap);
697 }
698 else if (std::string(currentChild->Value()) == "separator")
699 {
700 // Add a separator
701 int style = wxHORIZONTAL;
702 bool created = false;
703
704 if (currentChild->Attribute("type"))
705 {
706 if (currentChild->Attribute("type", "space"))
707 {
708 // Add a spacer
709 long int space;
710 removeQuotationMarks(text).ToLong(&space);
711 _groupPanel->AddSpacer(space, currSizer);
712 created = true;
713 }
714 else
715 style = currentChild->Attribute("type", "vertical") ? wxVERTICAL : wxHORIZONTAL;
716 }
717
718 if (!created)
719 {
720 // Add a static line (i.e. the default separator)
721 wxStaticLine* line = new wxStaticLine(currParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, style);
722 currSizer->Add(line, 0, wxEXPAND | wxALL, 5);
723 }
724 }
725 else if (std::string(currentChild->Value()) == "grapher")
726 {
727 // Add a grapher object
728 wxMGL* mgl = new wxMGL(currParent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_THEME, true);
729 m_windowItems[id] = std::make_pair(CustomWindow::GRAPHER, mgl);
730
731 if (currentChild->Attribute("size"))
732 {
733 wxString sSize = currentChild->Attribute("size");
734 long int x,y;
735 sSize.substr(0, sSize.find(',')).ToLong(&x);
736 sSize.substr(sSize.find(',')+1).ToLong(&y);
737
738 mgl->SetMinClientSize(wxSize(x,y));
739 }
740 else
741 mgl->SetMinClientSize(wxSize(640,480));
742
743 currSizer->Add(mgl, 1, alignment | wxALL | wxEXPAND | wxRESERVE_SPACE_EVEN_IF_HIDDEN, 5);
744
745 if (currentChild->Attribute("onclick"))
746 m_eventTable[id] = currentChild->Attribute("onclick");
747 }
748 else if (std::string(currentChild->Value()) == "tablegrid")
749 {
750 // Add a table grid
751 TableViewer* table = new TableViewer(currParent, id, nullptr, nullptr, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxBORDER_THEME);
752 currSizer->Add(table, 1, alignment | wxALL | wxEXPAND | wxRESERVE_SPACE_EVEN_IF_HIDDEN, 5);
753 m_windowItems[id] = std::make_pair(CustomWindow::TABLE, table);
754 table->SetTableReadOnly(false);
755
756 if (currentChild->Attribute("size"))
757 {
758 wxString sSize = currentChild->Attribute("size");
759 long int x,y;
760 sSize.substr(0, sSize.find(',')).ToLong(&x);
761 sSize.substr(sSize.find(',')+1).ToLong(&y);
762 NumeRe::Table data(x,y);
763 table->SetData(data, "", "");
764 }
765 else
766 {
767 NumeRe::Table data(1,1);
768 table->SetData(data, "", "");
769 }
770
771 if (currentChild->Attribute("onclick"))
772 m_eventTable[id] = currentChild->Attribute("onclick");
773 }
774 else if (std::string(currentChild->Value()) == "treelist")
775 {
776 // Add a treelist control
777 int style = wxTL_SINGLE;
778
779 if (currentChild->Attribute("type"))
780 style |= currentChild->Attribute("type", "checkmark") ? wxTL_CHECKBOX : wxTL_MULTIPLE;
781
782 wxArrayString labels;
783 wxArrayString values;
784
785 wxTreeListCtrl* listCtrl = _groupPanel->CreateTreeListCtrl(currParent, currSizer, style, wxDefaultSize, id, alignment);
786 m_windowItems[id] = std::make_pair(CustomWindow::TREELIST, listCtrl);
787 listCtrl->SetFont(font);
788
789 if (currentChild->Attribute("label"))
790 {
791 wxString label = currentChild->Attribute("label");
792 labels = getChoices(label);
793 }
794
795 if (currentChild->Attribute("value"))
796 {
797 wxString value = currentChild->Attribute("value");
798 values = getChoices(value);
799 }
800
801 if (values.size())
802 {
803 if (labels.size())
804 {
805 for (size_t j = 0; j < labels.size(); j++)
806 {
807 listCtrl->AppendColumn(labels[j]);
808 }
809 }
810
811 populateTreeListCtrl(listCtrl, values);
812 }
813 else if (currentChild->Attribute("size"))
814 {
815 wxString sSize = currentChild->Attribute("size");
816 long int row,col;
817 sSize.substr(0, sSize.find(',')).ToLong(&row);
818 sSize.substr(sSize.find(',')+1).ToLong(&col);
819
820 wxSize ctrlSize = GetClientSize();
821
822 for (size_t j = 0; j < (size_t)col; j++)
823 {
824 if (labels.size() > j)
825 listCtrl->AppendColumn(labels[j], ctrlSize.x/col - 2);
826 else
827 listCtrl->AppendColumn("", ctrlSize.x/col - 2);
828 }
829
830 for (int i = 0; i < row; i++)
831 {
832 listCtrl->AppendItem(listCtrl->GetRootItem(), "ITEM " + wxString::Format("%d", i+1));
833 }
834 }
835 else if (labels.size())
836 {
837 wxSize ctrlSize = GetClientSize();
838
839 for (size_t j = 0; j < labels.size(); j++)
840 {
841 listCtrl->AppendColumn(labels[j], ctrlSize.x / labels.size() - 2);
842 }
843 }
844
845 if (currentChild->Attribute("onclick"))
846 m_eventTable[id] = currentChild->Attribute("onclick");
847 }
848 else if (std::string(currentChild->Value()) == "group")
849 {
850 // Add a group. A group is a recursive control,
851 // which contains further controls (including further groups).
852 // Will call this function recursively.
853 wxString label;
854 int style = wxHORIZONTAL;
855 bool isCollapsible = false;
856 bool isNotebook = false;
857 bool isMenu = false;
858 int expand = 0;
859
860 if (currentChild->Attribute("label"))
861 label = currentChild->Attribute("label");
862
863 if (currentChild->Attribute("type"))
864 style = currentChild->Attribute("type", "horizontal") ? wxHORIZONTAL : wxVERTICAL;
865
866 if (currentChild->Attribute("style"))
867 {
868 isCollapsible = currentChild->Attribute("style", "collapse");
869 isNotebook = currentChild->Attribute("style", "tabs");
870 isMenu = currentChild->Attribute("style", "menu");
871 }
872
873 if (currentChild->Attribute("expand"))
874 expand = currentChild->Attribute("expand", "true") ? 1 : 0;
875
876 // A collapsible group is currently very buggy (if used
877 // with the current GroupPanel).
878#warning TODO (numere#1#08/15/21): Fix the collapsible group
879 if (label.length())
880 {
881 if (isCollapsible)
882 {
883 wxCollapsiblePane* pane = _groupPanel->createCollapsibleGroup(label, currParent, currSizer);
884 wxBoxSizer* sizer = new wxBoxSizer(style);
885
886 layoutChild(currentChild->FirstChildElement(), pane->GetPane(), sizer, _groupPanel);
887
888 pane->GetPane()->SetSizer(sizer);
889 sizer->SetSizeHints(pane->GetPane());
890 }
891 else if (isNotebook)
892 {
893 if (!noteBook)
894 {
895 noteBook = new wxNotebook(currParent, wxID_ANY);
896 currSizer->Add(noteBook, 1, wxEXPAND | wxRESERVE_SPACE_EVEN_IF_HIDDEN | wxALL, 5);
897 }
898
899 GroupPanel* _newPanel = new GroupPanel(noteBook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, style == wxHORIZONTAL ? false : true);
900
901 layoutChild(currentChild->FirstChildElement(), _newPanel, _newPanel->getMainSizer(), _newPanel);
902
903 noteBook->AddPage(_newPanel, label);
904 }
905 else if (isMenu)
906 {
907 wxMenuBar* menuBar = GetMenuBar();
908
909 if (!menuBar)
910 {
911 menuBar = new wxMenuBar();
912 SetMenuBar(menuBar);
913 }
914
915 wxMenu* currMenu = new wxMenu();
916 menuBar->Append(currMenu, label);
917 layoutMenu(currentChild->FirstChildElement(), currMenu);
918 }
919 else
920 {
921 wxStaticBoxSizer* sizer = _groupPanel->createGroup(label, style, currParent, currSizer, expand);
922 layoutChild(currentChild->FirstChildElement(), sizer->GetStaticBox(), sizer, _groupPanel);
923 }
924 }
925 else
926 {
927 wxBoxSizer* sizer = _groupPanel->createGroup(style, currSizer, expand);
928 layoutChild(currentChild->FirstChildElement(), currParent, sizer, _groupPanel);
929 }
930
931 }
932
933 // Find the next sibling from the current
934 // child element
935 if (currentChild->NextSibling())
936 currentChild = currentChild->NextSibling()->ToElement();
937 else
938 break;
939 }
940}
941
942
953void CustomWindow::layoutMenu(const tinyxml2::XMLElement* currentChild, wxMenu* currMenu)
954{
955 while (currentChild)
956 {
957 // Evaluate the id attribute
958 int id = currentChild->Attribute("id") ?
959 currentChild->DoubleAttribute("id") : m_windowItems.size() + 1000;
960
961 // Get the text used by some controls
962 wxString text = currentChild->GetText() ? currentChild->GetText() : "";
963
964 wxFont font = GetFont();
965 WindowState state = ENABLED;
966
967 // Evaluate the state attribute
968 if (currentChild->Attribute("state"))
969 state = (currentChild->Attribute("state", "disabled") || currentChild->Attribute("state", "hidden")) ? DISABLED : ENABLED;
970
971 // evaluat the font attribute
972 if (currentChild->Attribute("font"))
973 {
974 wxString sFont = currentChild->Attribute("font");
975
976 if (sFont.find('i') != std::string::npos)
977 font.MakeItalic();
978
979 if (sFont.find('b') != std::string::npos)
980 font.MakeBold();
981 }
982
983 if (std::string(currentChild->Value()) == "menuitem")
984 {
985 // Create a menu item
986 bool isCheck = false;
987 bool isChecked = false;
988
989 if (currentChild->Attribute("type"))
990 isCheck = currentChild->Attribute("type", "checkmark") ? true : false;
991
992 if (currentChild->Attribute("value"))
993 isChecked = currentChild->Attribute("value", "0") ? false : true;
994
995 wxMenuItem* item = currMenu->Append(id, removeQuotationMarks(text), wxEmptyString, isCheck ? wxITEM_CHECK : wxITEM_NORMAL);
996 item->SetFont(font);
997
998 if (isCheck && isChecked)
999 item->Check(true);
1000
1001 m_windowItems[id] = std::make_pair(CustomWindow::MENUITEM, item);
1002
1003 if (currentChild->Attribute("onclick"))
1004 m_eventTable[id] = currentChild->Attribute("onclick");
1005
1006 if (currentChild->Attribute("color"))
1007 item->SetTextColour(toWxColour(currentChild->Attribute("color")));
1008
1009 if (state == DISABLED)
1010 item->Enable(false);
1011 }
1012 else if (std::string(currentChild->Value()) == "checkbox")
1013 {
1014 // Create a checkable menu item
1015 bool isChecked = false;
1016
1017 if (currentChild->Attribute("value"))
1018 isChecked = currentChild->Attribute("value", "0") ? false : true;
1019
1020 wxMenuItem* item = currMenu->Append(id, removeQuotationMarks(text), wxEmptyString, wxITEM_CHECK);
1021 item->SetFont(font);
1022
1023 if (isChecked)
1024 item->Check(true);
1025
1026 m_windowItems[id] = std::make_pair(CustomWindow::MENUITEM, item);
1027
1028 if (currentChild->Attribute("onclick"))
1029 m_eventTable[id] = currentChild->Attribute("onclick");
1030
1031 if (currentChild->Attribute("color"))
1032 item->SetTextColour(toWxColour(currentChild->Attribute("color")));
1033
1034 if (state == DISABLED)
1035 item->Enable(false);
1036 }
1037 else if (std::string(currentChild->Value()) == "separator")
1038 currMenu->AppendSeparator();
1039 else if (std::string(currentChild->Value()) == "group")
1040 {
1041 // Add a group. A group is a recursive control,
1042 // which contains further controls (including further groups).
1043 // Will call this function recursively.
1044 wxString label;
1045 bool isMenu = false;
1046
1047 if (currentChild->Attribute("label"))
1048 label = currentChild->Attribute("label");
1049
1050 if (currentChild->Attribute("style"))
1051 isMenu = currentChild->Attribute("style", "menu");
1052
1053 if (label.length() && isMenu)
1054 {
1055 wxMenu* subMenu = new wxMenu();
1056 currMenu->AppendSubMenu(subMenu, label);
1057 layoutMenu(currentChild->FirstChildElement(), subMenu);
1058 }
1059
1060 }
1061
1062 // Find the next sibling from the current
1063 // child element
1064 if (currentChild->NextSibling())
1065 currentChild = currentChild->NextSibling()->ToElement();
1066 else
1067 break;
1068
1069 }
1070}
1071
1072
1084void CustomWindow::handleEvent(wxEvent& event, const wxString& sEventType)
1085{
1086#warning NOTE (numere#1#08/15/21): The "onclose" event is still undefined
1087 if (sEventType == "onclose")
1088 return;
1089
1090 // Try to find a defined event handler for
1091 // the current window item element
1092 auto iter = m_eventTable.find(event.GetId());
1093
1094 if (iter != m_eventTable.end())
1095 {
1096 if (iter->second[0] == '$' && iter->second.length() > 1 && (wxIsalnum(iter->second[1]) || iter->second[1] == '_'))
1097 {
1098 // If the event handler starts with an
1099 // dollar, it must be a procedure
1100 NumeReWindow* mainWindow = static_cast<NumeReWindow*>(m_parent);
1101 WindowItemParams params;
1102
1103 // Get the parameters for the selected
1104 // window item
1105 getItemParameters(event.GetId(), params);
1106
1107 wxString kvl_event;
1108
1109 // Create the corresponding key-value-list
1110 // syntax
1111 if (event.GetEventType() == wxEVT_GRID_SELECT_CELL)
1112 {
1113 kvl_event = "{\"event\",\"onclick\",\"object\",\"" + params.type
1114 + "\",\"value\"," + sEventType
1115 + ",\"state\",\"" + params.state + "\"}";
1116 }
1117 else
1118 {
1119 kvl_event = "{\"event\",\"" + sEventType
1120 + "\",\"object\",\"" + params.type
1121 + "\",\"value\"," + params.value
1122 + ",\"state\",\"" + params.state + "\"}";
1123 }
1124
1125 // Call the procedure with the following syntax:
1126 // $PROCEDURE(winid, objectid, event{})
1127 mainWindow->pass_command(iter->second + "(" + toString(m_windowRef.getId()) + ","
1128 + toString(event.GetId()) + ","
1129 + kvl_event + ")", true);
1130 }
1131 else if (iter->second.find('(') != std::string::npos)
1132 {
1133 wxArrayString funcDef = decodeEventHandlerFunction(iter->second);
1134
1135 if (funcDef.front() == "evt_close")
1136 closeWindow();
1137 else if (funcDef.front() == "evt_sendvaltoitem" && funcDef.size() >= 2)
1138 {
1139 long int targetID;
1140 WindowItemParams params;
1141
1142 getItemParameters(event.GetId(), params);
1143 WindowItemValue val;
1144 val.stringValue = params.value;
1145 val.tableValue = params.table;
1146 val.type = params.type;
1147
1148 for (size_t i = 1; i < funcDef.size(); i++)
1149 {
1150 funcDef[i].ToLong(&targetID);
1151 setItemValue(val, targetID);
1152 }
1153 }
1154 else if (funcDef.front() == "evt_copyvalues" && funcDef.size() >= 3)
1155 {
1156 long int sourceID, targetID;
1157 funcDef[1].ToLong(&sourceID);
1158 WindowItemParams params;
1159 getItemParameters(sourceID, params);
1160
1161 WindowItemValue val;
1162 val.stringValue = params.value;
1163 val.tableValue = params.table;
1164 val.type = params.type;
1165
1166 for (size_t i = 2; i < funcDef.size(); i++)
1167 {
1168 funcDef[i].ToLong(&targetID);
1169 setItemValue(val, targetID);
1170 }
1171 }
1172 else if (funcDef.front() == "evt_changestate" && funcDef.size() >= 3)
1173 {
1174 long int id;
1175 funcDef[1].ToLong(&id);
1176 setItemState(funcDef[2], id);
1177 }
1178 }
1179 }
1180}
1181
1182
1191{
1192 params.type = "window";
1193 params.value = wxString::Format("{%d,%d}", GetClientSize().x, GetClientSize().y);
1194 params.state = "running";
1195 params.color = toWxString(GetBackgroundColour());
1196 params.label = "\"" + GetTitle() + "\"";
1197
1198 return true;
1199}
1200
1201
1211bool CustomWindow::getItemParameters(int windowItemID, WindowItemParams& params) const
1212{
1213 // If the ID equals -1, return the window parameters instead
1214 if (windowItemID == -1)
1215 return getWindowParameters(params);
1216
1217 auto iter = m_windowItems.find(windowItemID);
1218
1219 if (iter == m_windowItems.end())
1220 return false;
1221
1222 std::pair<CustomWindow::WindowItemType, wxObject*> object = iter->second;
1223
1224 switch (object.first)
1225 {
1227 params.type = "button";
1228 params.value = convertToCodeString(static_cast<wxButton*>(object.second)->GetLabel());
1229 params.label = params.value;
1230 params.color = toWxString(static_cast<wxButton*>(object.second)->GetForegroundColour());
1231
1232 break;
1234 params.type = "checkbox";
1235 params.value = static_cast<wxCheckBox*>(object.second)->IsChecked() ? "true" : "false";
1236 params.label = convertToCodeString(static_cast<wxCheckBox*>(object.second)->GetLabel());
1237 params.color = toWxString(static_cast<wxCheckBox*>(object.second)->GetBackgroundColour());
1238
1239 break;
1240 case CustomWindow::TEXT:
1241 params.type = "statictext";
1242 params.value = convertToCodeString(static_cast<wxStaticText*>(object.second)->GetLabel());
1243 params.label = params.value;
1244 params.color = toWxString(static_cast<wxStaticText*>(object.second)->GetForegroundColour());
1245
1246 break;
1248 params.type = "textfield";
1249 params.value = convertToCodeString(static_cast<TextField*>(object.second)->GetValue());
1250 params.label = convertToCodeString(static_cast<TextField*>(object.second)->GetLabel());
1251 params.color = toWxString(static_cast<TextField*>(object.second)->GetBackgroundColour());
1252
1253 break;
1255 {
1256 params.type = "radio";
1257 wxRadioBox* box = static_cast<wxRadioBox*>(object.second);
1258 params.value = convertToCodeString(box->GetString(box->GetSelection()));
1259 params.label = convertToCodeString(box->GetLabel());
1260 params.color = toWxString(static_cast<wxRadioBox*>(object.second)->GetBackgroundColour());
1261
1262 break;
1263 }
1265 {
1266 params.type = "dropdown";
1267 wxChoice* choices = static_cast<wxChoice*>(object.second);
1268 params.value = convertToCodeString(choices->GetString(choices->GetSelection()));
1269 params.label = params.value;
1270 params.color = toWxString(static_cast<wxChoice*>(object.second)->GetBackgroundColour());
1271
1272 break;
1273 }
1275 {
1276 params.type = "combobox";
1277 wxComboBox* combo = static_cast<wxComboBox*>(object.second);
1278
1279 if (combo->GetSelection() != wxNOT_FOUND)
1280 params.value = convertToCodeString(combo->GetString(combo->GetSelection()));
1281 else
1282 params.value = convertToCodeString(combo->GetValue());
1283
1284 params.label = params.value;
1285 params.color = toWxString(static_cast<wxComboBox*>(object.second)->GetBackgroundColour());
1286
1287 break;
1288 }
1290 params.type = "gauge";
1291 params.value = wxString::Format("%d", static_cast<wxGauge*>(object.second)->GetValue());
1292 params.label = params.value;
1293 params.color = toWxString(static_cast<wxGauge*>(object.second)->GetBackgroundColour());
1294
1295 break;
1297 params.type = "spinbut";
1298 params.value = wxString::Format("%d", static_cast<SpinBut*>(object.second)->GetValue());
1299 params.label = convertToCodeString(static_cast<SpinBut*>(object.second)->GetLabel());
1300 params.color = toWxString(static_cast<SpinBut*>(object.second)->GetBackgroundColour());
1301
1302 break;
1304 params.type = "slider";
1305 params.value = wxString::Format("%d", static_cast<wxSlider*>(object.second)->GetValue());
1306
1307 break;
1309 {
1310 TableViewer* table = static_cast<TableViewer*>(object.second);
1311 params.table = table->GetDataCopy();
1312 params.value = convertToCodeString(table->getSelectedValues());
1313 params.type = "tablegrid";
1314
1315 break;
1316 }
1318 {
1319 wxMGL* grapher = static_cast<wxMGL*>(object.second);
1320 params.value = convertToCodeString(grapher->getClickedCoords());
1321 params.type = "grapher";
1322
1323 break;
1324 }
1326 {
1327 wxTreeListCtrl* listCtrl = static_cast<wxTreeListCtrl*>(object.second);
1328
1329 for (size_t i = 0; i < listCtrl->GetColumnCount(); i++)
1330 {
1331 if (params.label.length())
1332 params.label += ", ";
1333
1334 params.label += convertToCodeString(listCtrl->GetDataView()->GetColumn(i)->GetTitle());
1335 }
1336 params.value = getTreeListCtrlValue(listCtrl);
1337 params.type = "treelist";
1338
1339 break;
1340 }
1342 {
1343 wxMenuItem* item = static_cast<wxMenuItem*>(object.second);
1344 params.type = "menuitem";
1345 params.label = convertToCodeString(item->GetItemLabel());
1346
1347 if (item->IsCheckable())
1348 params.value = item->IsChecked() ? "true" : "false";
1349 else
1350 params.value = convertToCodeString(item->GetLabelText(item->GetItemLabel()));
1351
1352 params.color = toWxString(item->GetTextColour());
1353 params.state = item->IsEnabled() ? "enabled" : "disabled";
1354
1355 break;
1356 }
1358 break;
1359 }
1360
1361 if (object.first != CustomWindow::MENUITEM)
1362 params.state = !static_cast<wxWindow*>(object.second)->IsShown() ? "hidden" : static_cast<wxWindow*>(object.second)->IsEnabled() ? "enabled" : "disabled";
1363
1364 return true;
1365}
1366
1367
1376wxArrayString CustomWindow::getChoices(wxString& choices) const
1377{
1378 wxArrayString choicesArray;
1379 size_t nQuotes = 0;
1380
1381 for (int i = 0; i < (int)choices.length(); i++)
1382 {
1383 if (choices[i] == '"' && (!i || choices[i-1] != '\\'))
1384 nQuotes++;
1385
1386 if (!(nQuotes % 2) && choices[i] == ',')
1387 {
1388 choicesArray.Add(removeQuotationMarks(choices.substr(0, i)));
1389 choices.erase(0, i+1);
1390 i = -1;
1391 }
1392 }
1393
1394 if (choices.length())
1395 choicesArray.Add(removeQuotationMarks(choices));
1396
1397 return choicesArray;
1398}
1399
1400
1410wxArrayString CustomWindow::decodeEventHandlerFunction(const wxString& sEventHandler) const
1411{
1412 wxArrayString funcDef;
1413 wxString sParams = sEventHandler.substr(sEventHandler.find('(')+1);
1414 sParams.erase(sParams.rfind(')'));
1415
1416 funcDef.Add(sEventHandler.substr(0, sEventHandler.find('(')));
1417
1418 if (sParams.length())
1419 {
1420 wxArrayString choices = getChoices(sParams);
1421
1422 for (size_t i = 0; i < choices.size(); i++)
1423 funcDef.Add(choices[i]);
1424 }
1425
1426 return funcDef;
1427}
1428
1429
1438wxString CustomWindow::removeQuotationMarks(wxString sString) const
1439{
1440 sString.Trim(false);
1441 sString.Trim(true);
1442
1443 if (sString.length() && sString[0] == '"' && sString[sString.length()-1] == '"')
1444 sString = sString.substr(1, sString.length()-2);
1445
1446 sString.Replace("\\\"", "\"");
1447
1448 return sString;
1449}
1450
1451
1460{
1461 wxWindow::Refresh();
1462}
1463
1464
1475{
1476 std::vector<int> vIDs;
1477
1478 for (auto iter : m_windowItems)
1479 {
1480 if (iter.second.first == _type)
1481 vIDs.push_back(iter.first);
1482 }
1483
1484 return vIDs;
1485}
1486
1487
1495{
1496 return Close();
1497}
1498
1499
1508{
1509 WindowItemParams params;
1510 WindowItemValue value;
1511
1512 if (getItemParameters(windowItemID, params))
1513 {
1514 if (params.value.substr(0, 2) == "\"{" && params.value.substr(params.value.length()-2) == "}\"")
1515 params.value = params.value.substr(1, params.value.length()-2);
1516
1517 value.stringValue = params.value;
1518 value.tableValue = params.table;
1519 value.type = params.type;
1520 return value;
1521 }
1522
1523 return value;
1524}
1525
1526
1534wxString CustomWindow::getItemLabel(int windowItemID) const
1535{
1536 WindowItemParams params;
1537
1538 if (getItemParameters(windowItemID, params))
1539 {
1540 if (params.label.substr(0, 2) == "\"{" && params.label.substr(params.label.length()-2) == "}\"")
1541 params.label = params.label.substr(1, params.label.length()-2);
1542
1543 return params.label;
1544 }
1545
1546 return "";
1547}
1548
1549
1557wxString CustomWindow::getItemState(int windowItemID) const
1558{
1559 WindowItemParams params;
1560
1561 if (getItemParameters(windowItemID, params))
1562 return params.state;
1563
1564 return "";
1565}
1566
1567
1575wxString CustomWindow::getItemColor(int windowItemID) const
1576{
1577 WindowItemParams params;
1578
1579 if (getItemParameters(windowItemID, params))
1580 return params.color;
1581
1582 return "";
1583}
1584
1585
1594wxString CustomWindow::getPropValue(const wxString& varName) const
1595{
1596 auto iter = m_varTable.find(varName);
1597
1598 if (iter != m_varTable.end())
1599 return iter->second;
1600
1601 return "nan";
1602}
1603
1604
1613{
1614 wxString sProperties;
1615
1616 for (auto iter : m_varTable)
1617 {
1618 if (sProperties.length())
1619 sProperties += ",";
1620
1621 sProperties += "\"" + iter.first + "\"";
1622 }
1623
1624 if (!sProperties.length())
1625 return "\"\"";
1626
1627 return sProperties;
1628}
1629
1630
1639bool CustomWindow::setItemValue(WindowItemValue& _value, int windowItemID)
1640{
1641 if (windowItemID == -1)
1642 {
1643 long int x,y;
1644 _value.stringValue.substr(0, _value.stringValue.find(',')).ToLong(&x);
1645 _value.stringValue.substr(_value.stringValue.find(',')+1).ToLong(&y);
1646
1647 SetClientSize(wxSize(x,y));
1648 Refresh();
1649
1650 return true;
1651 }
1652
1653 auto iter = m_windowItems.find(windowItemID);
1654
1655 if (iter == m_windowItems.end())
1656 return false;
1657
1658 std::pair<CustomWindow::WindowItemType, wxObject*> object = iter->second;
1659
1660 switch (object.first)
1661 {
1663 static_cast<wxButton*>(object.second)->SetLabel(removeQuotationMarks(_value.stringValue));
1664 break;
1666 static_cast<wxCheckBox*>(object.second)->SetValue(removeQuotationMarks(_value.stringValue) == "1");
1667 break;
1668 case CustomWindow::TEXT:
1669 static_cast<wxStaticText*>(object.second)->SetLabel(removeQuotationMarks(_value.stringValue));
1670 break;
1672 static_cast<TextField*>(object.second)->ChangeValue(removeQuotationMarks(_value.stringValue));
1673 break;
1675 {
1676 long int nVal;
1677 removeQuotationMarks(_value.stringValue).ToLong(&nVal);
1678
1679 if (nVal == -1)
1680 static_cast<wxGauge*>(object.second)->Pulse();
1681 else
1682 static_cast<wxGauge*>(object.second)->SetValue(nVal);
1683
1684 break;
1685 }
1687 {
1688 long int nVal;
1689 removeQuotationMarks(_value.stringValue).ToLong(&nVal);
1690 static_cast<SpinBut*>(object.second)->SetValue(nVal);
1691 break;
1692 }
1694 {
1695 long int nVal;
1696 removeQuotationMarks(_value.stringValue).ToLong(&nVal);
1697 static_cast<wxSlider*>(object.second)->SetValue(nVal);
1698 break;
1699 }
1701 {
1702 wxRadioBox* box = static_cast<wxRadioBox*>(object.second);
1703 int sel = box->FindString(removeQuotationMarks(_value.stringValue), true);
1704
1705 if (sel != wxNOT_FOUND)
1706 box->SetSelection(sel);
1707
1708 break;
1709 }
1711 {
1712 wxChoice* choices = static_cast<wxChoice*>(object.second);
1713 int sel = choices->FindString(removeQuotationMarks(_value.stringValue), true);
1714
1715 if (sel != wxNOT_FOUND)
1716 choices->SetSelection(sel);
1717
1718 break;
1719 }
1721 {
1722 wxComboBox* combo = static_cast<wxComboBox*>(object.second);
1723 int sel = combo->FindString(removeQuotationMarks(_value.stringValue), true);
1724
1725 if (sel != wxNOT_FOUND)
1726 combo->SetSelection(sel);
1727 else
1728 combo->SetValue(removeQuotationMarks(_value.stringValue));
1729
1730 break;
1731 }
1733 {
1734 wxStaticBitmap* bitmap = static_cast<wxStaticBitmap*>(object.second);
1735 bitmap->SetBitmap(wxBitmap(removeQuotationMarks(_value.stringValue), wxBITMAP_TYPE_ANY));
1736 break;
1737 }
1739 {
1740 TableViewer* table = static_cast<TableViewer*>(object.second);
1741 table->SetData(_value.tableValue, "", "");
1742 break;
1743 }
1745 {
1746 wxTreeListCtrl* listCtrl = static_cast<wxTreeListCtrl*>(object.second);
1747 populateTreeListCtrl(listCtrl, getChoices(_value.stringValue));
1748 break;
1749 }
1751 {
1752 wxMenuItem* item = static_cast<wxMenuItem*>(object.second);
1753
1754 if (item->IsCheckable())
1755 item->Check(removeQuotationMarks(_value.stringValue) != "0");
1756 else
1757 item->SetItemLabel(removeQuotationMarks(_value.stringValue));
1758
1759 break;
1760 }
1762 break;
1763 }
1764
1765 return true;
1766}
1767
1768
1777bool CustomWindow::setItemLabel(const wxString& _label, int windowItemID)
1778{
1779 if (windowItemID == -1)
1780 {
1781 SetTitle(removeQuotationMarks(_label));
1782 return true;
1783 }
1784
1785 auto iter = m_windowItems.find(windowItemID);
1786
1787 if (iter == m_windowItems.end())
1788 return false;
1789
1790 std::pair<CustomWindow::WindowItemType, wxObject*> object = iter->second;
1791
1792 switch (object.first)
1793 {
1795 static_cast<wxButton*>(object.second)->SetLabel(removeQuotationMarks(_label));
1796 break;
1798 static_cast<wxCheckBox*>(object.second)->SetLabel(removeQuotationMarks(_label));
1799 break;
1800 case CustomWindow::TEXT:
1801 static_cast<wxStaticText*>(object.second)->SetLabel(removeQuotationMarks(_label));
1802 break;
1804 static_cast<TextField*>(object.second)->SetLabel(removeQuotationMarks(_label));
1805 break;
1807 static_cast<SpinBut*>(object.second)->SetLabel(removeQuotationMarks(_label));
1808 break;
1810 static_cast<wxRadioBox*>(object.second)->SetLabel(removeQuotationMarks(_label));
1811 break;
1813 {
1814 wxString lab = _label;
1815 wxArrayString labels = getChoices(lab);
1816 wxTreeListCtrl* listCtrl = static_cast<wxTreeListCtrl*>(object.second);
1817
1818 for (size_t i = 0; i < labels.size(); i++)
1819 {
1820 if (listCtrl->GetColumnCount() <= i)
1821 listCtrl->AppendColumn(labels[i]);
1822 else
1823 listCtrl->GetDataView()->GetColumn(i)->SetTitle(labels[i]);
1824 }
1825 }
1827 static_cast<wxMenuItem*>(object.second)->SetItemLabel(removeQuotationMarks(_label));
1828 break;
1836 break;
1837 }
1838
1839 return true;
1840}
1841
1842
1851bool CustomWindow::setItemState(const wxString& _state, int windowItemID)
1852{
1853 if (windowItemID == -1)
1854 return false;
1855
1856 auto iter = m_windowItems.find(windowItemID);
1857
1858 if (iter == m_windowItems.end())
1859 return false;
1860
1861 std::pair<CustomWindow::WindowItemType, wxObject*> object = iter->second;
1862
1863 if (object.first == CustomWindow::MENUITEM)
1864 {
1865 if (_state == "hidden" || _state == "disabled")
1866 static_cast<wxMenuItem*>(object.second)->Enable(false);
1867 else
1868 static_cast<wxMenuItem*>(object.second)->Enable(true);
1869
1870 return true;
1871 }
1872
1873 wxWindow* window = static_cast<wxWindow*>(object.second);
1874
1875 if (_state == "hidden")
1876 window->Show(false);
1877 else if (_state == "disabled")
1878 {
1879 window->Show(true);
1880 window->Enable(false);
1881 }
1882 else if (_state == "enabled")
1883 {
1884 window->Show(true);
1885 window->Enable(true);
1886 }
1887
1888 return true;
1889}
1890
1891
1900bool CustomWindow::setItemColor(const wxString& _color, int windowItemID)
1901{
1902 wxColour color = toWxColour(_color);
1903
1904 if (windowItemID == -1)
1905 {
1906 SetBackgroundColour(color);
1907 Refresh();
1908
1909 return true;
1910 }
1911
1912 auto iter = m_windowItems.find(windowItemID);
1913
1914 if (iter == m_windowItems.end())
1915 return false;
1916
1917 std::pair<CustomWindow::WindowItemType, wxObject*> object = iter->second;
1918
1919 switch (object.first)
1920 {
1922 static_cast<wxButton*>(object.second)->SetForegroundColour(color);
1923 break;
1925 static_cast<wxCheckBox*>(object.second)->SetBackgroundColour(color);
1926 break;
1927 case CustomWindow::TEXT:
1928 static_cast<wxStaticText*>(object.second)->SetForegroundColour(color);
1929 break;
1931 static_cast<wxTextCtrl*>(object.second)->SetBackgroundColour(color);
1932 break;
1934 static_cast<wxSpinCtrl*>(object.second)->SetBackgroundColour(color);
1935 break;
1937 static_cast<wxRadioBox*>(object.second)->SetBackgroundColour(color);
1938 break;
1940 static_cast<wxChoice*>(object.second)->SetBackgroundColour(color);
1941 break;
1943 static_cast<wxComboBox*>(object.second)->SetBackgroundColour(color);
1944 break;
1946 static_cast<wxMenuItem*>(object.second)->SetTextColour(color);
1947 break;
1954 break;
1955 }
1956
1957 Refresh();
1958
1959 return true;
1960}
1961
1962
1971bool CustomWindow::setItemGraph(GraphHelper* _helper, int windowItemID)
1972{
1973 auto iter = m_windowItems.find(windowItemID);
1974
1975 if (iter == m_windowItems.end() || iter->second.first != CustomWindow::GRAPHER)
1976 return false;
1977
1978 wxMGL* mgl = static_cast<wxMGL*>(iter->second.second);
1979 wxSize s = mgl->GetSize();
1980 mgl->Animation(false);
1981
1982 mgl->SetDraw(_helper);
1983 mgl->SetGraph(_helper->setGrapher());
1984 mgl->SetSize(s.x, s.y);
1985 mgl->Refresh();
1986
1987 if (mgl->getNumFrames() > 1)
1988 mgl->AnimateAsynch();
1989
1990 return true;
1991}
1992
1993
2003bool CustomWindow::setPropValue(const wxString& _value, const wxString& varName)
2004{
2005 auto iter = m_varTable.find(varName);
2006
2007 if (iter != m_varTable.end())
2008 {
2009 iter->second = _value;
2010 return true;
2011 }
2012
2013 return false;
2014}
2015
2016
2024void CustomWindow::OnMenuEvent(wxCommandEvent& event)
2025{
2026 handleEvent(event, "onclick");
2027}
2028
2029
2037void CustomWindow::OnClick(wxCommandEvent& event)
2038{
2039 handleEvent(event, "onclick");
2040}
2041
2042
2050void CustomWindow::OnChange(wxCommandEvent& event)
2051{
2052 auto iter = m_windowItems.find(event.GetId());
2053
2054 if (iter != m_windowItems.end() && iter->second.first != CustomWindow::SPINCTRL)
2055 handleEvent(event, "onchange");
2056}
2057
2058
2066void CustomWindow::OnSpin(wxSpinEvent& event)
2067{
2068 handleEvent(event, "onchange");
2069}
2070
2071
2079void CustomWindow::OnCellSelect(wxGridEvent& event)
2080{
2081 TableViewer* table = static_cast<TableViewer*>(m_windowItems[event.GetId()].second);
2082 handleEvent(event, convertToCodeString(table->GetCellValue(event.GetRow(), event.GetCol())));
2083}
2084
2085
2093void CustomWindow::OnClose(wxCloseEvent& event)
2094{
2095 handleEvent(event, "onclose");
2097 static_cast<NumeReWindow*>(m_parent)->unregisterWindow(this);
2098 Destroy();
2099}
2100
2101
2109void CustomWindow::OnMouseLeftDown(wxMouseEvent& event)
2110{
2111 handleEvent(event, "onclick");
2112}
2113
2114
2122void CustomWindow::OnTreeListEvent(wxTreeListEvent& event)
2123{
2124 if (static_cast<wxTreeListCtrl*>(m_windowItems[event.GetId()].second)->HasFlag(wxTL_CHECKBOX)
2125 && event.GetEventType() == wxEVT_TREELIST_SELECTION_CHANGED)
2126 return;
2127
2128 handleEvent(event, "onclick");
2129}
2130
2131
2139void CustomWindow::OnSizeEvent(wxSizeEvent& event)
2140{
2141 // Inform the window to refresh its contents
2142 // asynchronously
2143 CallAfter(&CustomWindow::Refresh);
2144 event.Skip();
2145}
2146
#define wxCLOSE_BOX
This class represents a window, which can be created by the user during run-time by using a layout sc...
bool setItemState(const wxString &_state, int windowItemID)
Change the state of the selected item.
void layoutChild(const tinyxml2::XMLElement *currentChild, wxWindow *currParent, wxSizer *currSizer, GroupPanel *_groupPanel)
This member function can be called recursively and creates the layout for the current XMLElement's ch...
void OnChange(wxCommandEvent &event)
Generic onchange event handler.
bool setItemValue(WindowItemValue &_value, int windowItemID)
Change the value of the selected item.
void OnSpin(wxSpinEvent &event)
wxSpinCtrl event handler.
wxString getItemLabel(int windowItemID) const
Get the label of the selected item.
void handleEvent(wxEvent &event, const wxString &sEventType)
This member function is the central kernel interaction event handler. Will be called from the wxWidge...
void OnMouseLeftDown(wxMouseEvent &event)
Mouse event handler.
wxString removeQuotationMarks(wxString sString) const
Private member function to convert a kernel string into a usual string.
void OnMenuEvent(wxCommandEvent &event)
Menu event handler.
bool setItemGraph(GraphHelper *_helper, int windowItemID)
Updates the selected grapher item.
std::vector< int > getWindowItems(WindowItemType _type) const
Returns a list of all window item IDs, which correspond to the selected WindowItemType.
bool getItemParameters(int windowItemID, WindowItemParams &params) const
Returns the parameters of the selected window item.
bool setPropValue(const wxString &_value, const wxString &varName)
Sets the value of the selected window property.
bool setItemLabel(const wxString &_label, int windowItemID)
Change the label of the selected item.
bool getWindowParameters(WindowItemParams &params) const
Returns the parameters of this window.
wxString getProperties() const
Returns a list of all available window properties.
void layoutMenu(const tinyxml2::XMLElement *currentChild, wxMenu *currMenu)
This member function can be called recursively and creates menus and submenus for the current window ...
wxString getPropValue(const wxString &varName) const
Returns the value of the selected window property.
void OnTreeListEvent(wxTreeListEvent &event)
Tree list control event handler.
std::map< wxString, wxString > m_varTable
NumeRe::Window m_windowRef
wxString getItemColor(int windowItemID) const
Get the color of the selected item.
void OnClose(wxCloseEvent &event)
OnClose event handler.
wxString getItemState(int windowItemID) const
Get the state of the selected item.
void OnCellSelect(wxGridEvent &event)
wxGrid event handler.
bool setItemColor(const wxString &_color, int windowItemID)
Change the color of the selected item.
wxArrayString decodeEventHandlerFunction(const wxString &sEventHandler) const
This member function decodes the arguments of a event handler function and returns them as a wxArrayS...
void OnSizeEvent(wxSizeEvent &event)
On size event handler.
std::map< int, std::pair< WindowItemType, wxObject * > > m_windowItems
std::map< int, wxString > m_eventTable
void OnClick(wxCommandEvent &event)
Button click event handler.
wxArrayString getChoices(wxString &choices) const
A simple tokenizer to separate a list of strings into multiple strings.
void Refresh()
Wrapper for wxWindow::Refresh to use it together with CallAfter().
void layout()
This member function evaluates the layout supplied by the user via a tinyxml2::XMLDocument instance.
WindowItemValue getItemValue(int windowItemID) const
Get the value of the selected item.
bool closeWindow()
Close this window.
This class encapsulates the mglGraph object during transmission from the kernel to the GUI.
mglGraph * setGrapher()
This class simplifies the creation of simple windows and creates a common layout among all windows.
Definition: grouppanel.hpp:188
wxStaticText * AddStaticText(wxWindow *parent, wxSizer *sizer, const wxString &text, int id=wxID_STATIC, int alignment=wxALIGN_CENTER_VERTICAL)
Add some static test to the current sizer and window.
Definition: grouppanel.cpp:140
wxGauge * CreateGauge(wxWindow *parent, wxSizer *sizer, int style, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a progress bar.
Definition: grouppanel.cpp:545
wxStaticBitmap * CreateBitmap(wxWindow *parent, wxSizer *sizer, const wxString &filename, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a static bitmap.
Definition: grouppanel.cpp:566
SpinBut * CreateSpinControl(wxWindow *parent, wxSizer *sizer, const wxString &description, int nMin, int nMax, int nInitial, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a spin control including the assigned text.
Definition: grouppanel.cpp:342
wxTreeListCtrl * CreateTreeListCtrl(wxWindow *parent, wxSizer *sizer, int nStyle=wxTL_SINGLE, wxSize size=wxDefaultSize, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a treelist control.
Definition: grouppanel.cpp:399
void AddSpacer(int nSize=10, wxSizer *sizer=nullptr)
Add extra space between the last added (main) element and the next element to be added.
Definition: grouppanel.cpp:120
wxChoice * CreateChoices(wxWindow *parent, wxSizer *sizer, const wxArrayString &choices, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a dropdown list.
Definition: grouppanel.cpp:495
wxRadioBox * CreateRadioBox(wxWindow *parent, wxSizer *sizer, const wxString &description, const wxArrayString &choices, int style=wxHORIZONTAL, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a radio box.
Definition: grouppanel.cpp:474
wxSlider * CreateSlider(wxWindow *parent, wxSizer *sizer, int nMin, int nMax, int nInitial, int style, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a slider.
Definition: grouppanel.cpp:590
wxCheckBox * CreateCheckBox(wxWindow *parent, wxSizer *sizer, const wxString &description, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a usual checkbox.
Definition: grouppanel.cpp:316
wxBoxSizer * getMainSizer()
Return the pointer to the current main layout sizer.
Definition: grouppanel.cpp:104
wxStaticBoxSizer * createGroup(const wxString &sGroupName, int orient=wxVERTICAL, wxWindow *parent=nullptr, wxSizer *sizer=nullptr, int expand=0)
Member function to create a group (a static box with a label) in the panel.
Definition: grouppanel.cpp:161
wxComboBox * CreateComboBox(wxWindow *parent, wxSizer *sizer, const wxArrayString &choices, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a combobox.
Definition: grouppanel.cpp:520
wxCollapsiblePane * createCollapsibleGroup(const wxString &label, wxWindow *parent=nullptr, wxSizer *sizer=nullptr)
Member function to create a collapsible group.
Definition: grouppanel.cpp:215
wxButton * CreateButton(wxWindow *parent, wxSizer *sizer, const wxString &description, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a button.
Definition: grouppanel.cpp:447
TextField * CreateTextInput(wxWindow *parent, wxSizer *sizer, const wxString &description, const wxString &sDefault=wxEmptyString, int nStyle=0, int id=wxID_ANY, const wxSize &size=wxSize(310,-1), int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a text input.
Definition: grouppanel.cpp:285
This data container is a copy- efficient table to interchange data between Kernel and GUI.
Definition: table.hpp:87
void updateWindowInformation(int status, const std::string &_return)
This public member function can be used to update the stored window information in the window manager...
const tinyxml2::XMLDocument * getLayout() const
size_t getId() const
This class is the actual NumeRe main frame. The application's logic is implemented here.
Definition: NumeReWindow.h:177
void pass_command(const wxString &command, bool isEvent=false)
This member function is a wrapper for the corresponding terminal function to pass a command to the ke...
This class is a extension to the standard wxSpinCtrl to combine it with a read- and changeable label.
Definition: grouppanel.hpp:38
virtual bool Enable(bool enable)
Enable or disable the label and the control.
Definition: grouppanel.hpp:96
virtual bool Show(bool show)
Show or hide the label and the control.
Definition: grouppanel.hpp:80
This class is an adaption of the wxGrid class to present the tabular data in NumeRe's memory and enab...
Definition: tableviewer.hpp:42
void SetData(NumeRe::Container< std::string > &_stringTable, const std::string &sName, const std::string &sIntName)
This member function is the data setter for string and cluster tables.
void SetTableReadOnly(bool isReadOnly=true)
This member function declares the table to be read-only and enables the context menu entries,...
wxString getSelectedValues()
Returns the values of all selected cells as a string list.
NumeRe::Table GetDataCopy()
This member function returns a safe copy of the internal NumeRe::Table.
This class is a extension to the standard wxTextCtrl to combine it with a read- and changeable label.
Definition: grouppanel.hpp:113
virtual bool Show(bool show)
Show or hide the label and the control.
Definition: grouppanel.hpp:155
virtual bool Enable(bool enable)
Enable or disable the label and the control.
Definition: grouppanel.hpp:171
const char * GetText() const
Definition: tinyxml2.cpp:1656
double DoubleAttribute(const char *name, double defaultValue=0) const
See IntAttribute()
Definition: tinyxml2.cpp:1642
const char * Attribute(const char *name, const char *value=0) const
Definition: tinyxml2.cpp:1595
int IntAttribute(const char *name, int defaultValue=0) const
Definition: tinyxml2.cpp:1607
const char * Value() const
Definition: tinyxml2.cpp:817
const XMLElement * FirstChildElement(const char *name=0) const
Definition: tinyxml2.cpp:994
virtual XMLElement * ToElement()
Safely cast to an Element, or null.
Definition: tinyxml2.h:692
const XMLNode * FirstChild() const
Get the first child node, or null if none exists.
Definition: tinyxml2.h:769
const XMLNode * NextSibling() const
Get the next (right) sibling node of this node.
Definition: tinyxml2.h:821
Class is Wx widget which display MathGL graphics.
Definition: wx.h:39
wxString getClickedCoords()
Return the clicked coordinates as a parsable string. This is used by the custom GUI event handler.
Definition: wx.cpp:1591
void SetSize(int w, int h)
Set popup menu pointer.
Definition: wx.cpp:1424
int getNumFrames()
Definition: wx.h:115
void SetGraph(mglGraph *GR)
Definition: wx.h:49
void Animation(bool st=true)
Start animation.
Definition: wx.cpp:1517
void SetDraw(int(*func)(mglBase *gr, void *par), void *par=0)
Set drawing functions and its parameter.
Definition: wx.h:60
void AnimateAsynch()
Definition: wx.h:125
int GetColumnCount() const
wxString GetItemText(const wxTreeItemId &item) const
Definition: treelistctrl.h:283
wxTreeItemId AppendItem(const wxTreeItemId &parent, const wxString &text, int image=-1, int selectedImage=-1, wxTreeItemData *data=NULL)
wxTreeListColumnInfo GetColumn(int column)
size_t GetSelections(wxArrayTreeItemIds &) const
wxTreeItemId GetRootItem() const
virtual bool SetFont(const wxFont &font)
void SetItemText(const wxTreeItemId &item, const wxString &text)
static wxString nextItemValue(wxString &sItem)
Separates the different item values.
static wxColour toWxColour(const wxString &s)
This static function converts wxStrings to colors.
static wxString convertToCodeString(wxString s)
This static function conversts usual strings into "Kernel strings" (i.e. usual NumeRe code strings).
WindowState
Enumeration to define possible states of window items.
@ DISABLED
@ HIDDEN
@ ENABLED
static void populateTreeListCtrl(wxTreeListCtrl *listCtrl, const wxArrayString &values)
This static function converts the list of values into items for the passed tree list control and popu...
static wxString toWxString(const wxColor &c)
This static function converts colors to wxStrings.
static wxString getTreeListCtrlValue(wxTreeListCtrl *listCtrl)
This static function returns the current value of the passed tree list control, whereas the value is ...
char name[32]
Definition: resampler.cpp:371
A structure to simplify the reading of all parameter values of each window item.
NumeRe::Table table
A structure to simplify reading and updating window item values.
wxString stringValue
NumeRe::Table tableValue
std::string toString(int)
Converts an integer to a string without the Settings bloat.
END_EVENT_TABLE()