///////////////////////////////////////////////////////////////////////////////
//
// wxFormBuilder - A Visual Dialog Editor for wxWidgets.
// Copyright (C) 2005 José Antonio Hurtado
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Written by
//   José Antonio Hurtado - joseantonio.hurtado@gmail.com
//   Juan Antonio Ortega  - jortegalalmolda@gmail.com
//
///////////////////////////////////////////////////////////////////////////////

#include <wx/calctrl.h>
#include <wx/clrpicker.h>
#include <wx/dataview.h>
#include <wx/datectrl.h>
#include <wx/dirctrl.h>
#include <wx/filepicker.h>
#include <wx/fontpicker.h>
#include <wx/grid.h>
#include <wx/html/htmlwin.h>
#include <wx/hyperlink.h>
#include <wx/propgrid/manager.h>
#include <wx/ribbon/buttonbar.h>
#include <wx/ribbon/gallery.h>
#include <wx/ribbon/toolbar.h>
#include <wx/richtext/richtextctrl.h>
#include <wx/spinctrl.h>
#include <wx/srchctrl.h>
#include <wx/stc/stc.h>
#include <wx/tglbtn.h>
#include <wx/timectrl.h>
#include <wx/treelist.h>

#include <common/xmlutils.h>
#include <plugin_interface/plugin.h>
#include <plugin_interface/xrcconv.h>

#include "logo.xpm"
#include "smiley.xpm"

#ifdef USE_MEDIACTRL
    #include <wx/mediactrl.h>
#endif


/**
Event handler for events generated by controls in this plugin
*/
class ComponentEvtHandler : public wxEvtHandler
{
private:
    wxWindow* m_window;
    IManager* m_manager;

public:
    ComponentEvtHandler(wxWindow* win, IManager* manager) : m_window(win), m_manager(manager) {}

protected:
    void OnGridClick([[maybe_unused]] wxGridEvent& event)
    {
        m_manager->SelectObject(m_window);
        event.Skip();
    }

    void OnGridColSize([[maybe_unused]] wxGridSizeEvent& event)
    {
        wxGrid* grid = wxDynamicCast(m_window, wxGrid);
        if (NULL == grid) {
            return;
        }

        wxString sizes;
        for (int i = 0; i < grid->GetNumberCols(); ++i) { sizes += wxString::Format(wxT("%i,"), grid->GetColSize(i)); }
        sizes = sizes.substr(0, sizes.length() - 1);

        m_manager->ModifyProperty(m_window, _("column_sizes"), sizes, true);
    }

    void OnGridRowSize([[maybe_unused]] wxGridSizeEvent& event)
    {
        wxGrid* grid = wxDynamicCast(m_window, wxGrid);
        if (NULL == grid) {
            return;
        }

        wxString sizes;
        for (int i = 0; i < grid->GetNumberRows(); ++i) { sizes += wxString::Format(wxT("%i,"), grid->GetRowSize(i)); }
        sizes = sizes.substr(0, sizes.length() - 1);

        m_manager->ModifyProperty(m_window, _("row_sizes"), sizes, true);
    }

    void OnColourPickerColourChanged([[maybe_unused]] wxColourPickerEvent& event)
    {
        wxColourPickerCtrl* window = wxDynamicCast(m_window, wxColourPickerCtrl);
        if (window != NULL) {
            wxColour colour = window->GetColour();
            m_manager->ModifyProperty(
            window, _("colour"), wxString::Format(wxT("%d,%d,%d"), colour.Red(), colour.Green(), colour.Blue()));
        }
    }

    void OnFontPickerFontChanged([[maybe_unused]] wxFontPickerEvent& event)
    {
        wxFontPickerCtrl* window = wxDynamicCast(m_window, wxFontPickerCtrl);
        if (window != NULL) {
            wxFont font = window->GetSelectedFont();
            m_manager->ModifyProperty(
            window, _("value"),
            wxString::Format(
                wxT("%s,%d,%d,%d"), font.GetFaceName().c_str(), font.GetStyle(), font.GetWeight(), font.GetPointSize()));
        }
    }

    void OnFilePickerFileChanged([[maybe_unused]] wxFileDirPickerEvent& event)
    {
        wxFilePickerCtrl* window = wxDynamicCast(m_window, wxFilePickerCtrl);
        if (window != NULL) {
            m_manager->ModifyProperty(window, _("value"), window->GetPath());
        }
    }

    void OnDirPickerDirChanged([[maybe_unused]] wxFileDirPickerEvent& event)
    {
        wxDirPickerCtrl* window = wxDynamicCast(m_window, wxDirPickerCtrl);
        if (window != NULL) {
            m_manager->ModifyProperty(window, _("value"), window->GetPath());
        }
    }

    void OnText([[maybe_unused]] wxCommandEvent& event)
    {
        wxSearchCtrl* sc = wxDynamicCast(m_window, wxSearchCtrl);
        if (sc != NULL) {
            m_manager->ModifyProperty(m_window, _("value"), sc->GetValue());
            sc->SetInsertionPointEnd();
            sc->SetFocus();
        }

        event.Skip();
    }

    // Enable folding for wxStyledTextCtrl
    void OnMarginClick([[maybe_unused]] wxStyledTextEvent& event)
    {
        wxStyledTextCtrl* scintilla = wxDynamicCast(m_window, wxStyledTextCtrl);
        if (scintilla != NULL) {
            if (event.GetMargin() == 1) {
                int lineClick = scintilla->LineFromPosition(event.GetPosition());
                int levelClick = scintilla->GetFoldLevel(lineClick);
                if ((levelClick & wxSTC_FOLDLEVELHEADERFLAG) > 0) {
                    scintilla->ToggleFold(lineClick);
                }
            }
        }
        event.Skip();
    }

    void OnRibbonBarPageChanged([[maybe_unused]] wxRibbonBarEvent& event)
    {
        if (m_window != event.GetEventObject()) {
            return;
        }

        wxRibbonBar* rb = wxDynamicCast(event.GetEventObject(), wxRibbonBar);
        if (rb == NULL) {
            return;
        }

        int selPage = rb->GetActivePage();

        size_t count = m_manager->GetChildCount(m_window);
        for (size_t i = 0; i < count; i++) {
            wxObject* wxChild = m_manager->GetChild(m_window, i);
            IObject* iChild = m_manager->GetIObject(wxChild);
            if (iChild) {
                if (int(i) == selPage && iChild->GetPropertyAsInteger(_("select")) == 0) {
                    m_manager->ModifyProperty(wxChild, _("select"), wxT("1"), false);
                } else if (int(i) != selPage && iChild->GetPropertyAsInteger(_("select")) != 0) {
                    m_manager->ModifyProperty(wxChild, _("select"), wxT("0"), false);
                }
            }
        }

        // Select the corresponding ribbon page in the object tree
        if (NULL != rb) {
            m_manager->SelectObject(rb->GetPage(selPage));
        }
    }

    wxDECLARE_EVENT_TABLE();
};

wxBEGIN_EVENT_TABLE(ComponentEvtHandler, wxEvtHandler)
EVT_COLOURPICKER_CHANGED(wxID_ANY, ComponentEvtHandler::OnColourPickerColourChanged)
EVT_FONTPICKER_CHANGED(wxID_ANY, ComponentEvtHandler::OnFontPickerFontChanged)
EVT_FILEPICKER_CHANGED(wxID_ANY, ComponentEvtHandler::OnFilePickerFileChanged)
EVT_DIRPICKER_CHANGED(wxID_ANY, ComponentEvtHandler::OnDirPickerDirChanged)
EVT_TEXT(wxID_ANY, ComponentEvtHandler::OnText)

// Grid also seems to ignore clicks
EVT_GRID_CELL_LEFT_CLICK(ComponentEvtHandler::OnGridClick)
EVT_GRID_LABEL_LEFT_CLICK(ComponentEvtHandler::OnGridClick)

EVT_GRID_COL_SIZE(ComponentEvtHandler::OnGridColSize)
EVT_GRID_ROW_SIZE(ComponentEvtHandler::OnGridRowSize)

EVT_STC_MARGINCLICK(wxID_ANY, ComponentEvtHandler::OnMarginClick)
EVT_RIBBONBAR_PAGE_CHANGED(wxID_ANY, ComponentEvtHandler::OnRibbonBarPageChanged)
wxEND_EVENT_TABLE()

/**
Event handler for events generated by wxGenericDirCtrl.
*/
class GenericDirCtrlEvtHandler : public wxEvtHandler
{
public:
    GenericDirCtrlEvtHandler(wxWindow* win, IManager* manager) : m_window(win), m_manager(manager) {}

protected:
    void OnGenericDirCtrlLeftClick([[maybe_unused]] wxMouseEvent& event)
    {
        m_manager->SelectObject(m_window);
        event.Skip();
    }

    wxDECLARE_EVENT_TABLE();

private:
    wxWindow* m_window;
    IManager* m_manager;
};

wxBEGIN_EVENT_TABLE(GenericDirCtrlEvtHandler, wxEvtHandler)
// GenericDirCtrl also seems to ignore clicks
EVT_LEFT_DOWN(GenericDirCtrlEvtHandler::OnGenericDirCtrlLeftClick)
wxEND_EVENT_TABLE()

///////////////////////////////////////////////////////////////////////////////

class CalendarCtrlComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        return new wxCalendarCtrl(
          (wxWindow*)parent, wxID_ANY, wxDefaultDateTime, obj->GetPropertyAsPoint(_("pos")),
          obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        return xfb;
    }
};

class DatePickerCtrlComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        return new wxDatePickerCtrl(
          (wxWindow*)parent, wxID_ANY, wxDefaultDateTime, obj->GetPropertyAsPoint(_("pos")),
          obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        return xfb;
    }
};

class TimePickerCtrlComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        return new wxTimePickerCtrl(
          (wxWindow*)parent, wxID_ANY, wxDefaultDateTime, obj->GetPropertyAsPoint(_("pos")),
          obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        return xfb;
    }
};

class RichTextCtrlComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {

        wxString text = obj->GetPropertyAsString(_("value"));

        wxRichTextCtrl* richText = new wxRichTextCtrl(
          (wxWindow*)parent, wxID_ANY, text, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        if (!text.empty()) {
            return richText;
        }

        wxFont textFont = wxFont(12, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
        wxFont boldFont = wxFont(12, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
        wxFont italicFont = wxFont(12, wxFONTFAMILY_ROMAN, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL);

        wxFont font(12, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);

        wxRichTextCtrl& r = *richText;
        r.Freeze();
        r.SetDefaultStyle(wxRichTextAttr());
        r.SetFont(font);
        r.BeginSuppressUndo();

        r.BeginParagraphSpacing(0, 20);

        r.BeginAlignment(wxTEXT_ALIGNMENT_CENTRE);
        r.BeginBold();

        r.BeginFontSize(14);
        r.WriteText(
          wxT("Welcome to wxRichTextCtrl, a wxWidgets control for editing and presenting styled text and images"));
        r.EndFontSize();
        r.Newline();

        r.BeginItalic();
        r.WriteText(wxT("by Julian Smart"));
        r.EndItalic();

        r.EndBold();

        r.Newline();
        r.WriteImage(wxBitmap(logo_xpm));

        r.EndAlignment();

        r.Newline();

        r.WriteText(wxT("What can you do with this thing? "));
        r.WriteImage(wxBitmap(smiley_xpm));
        r.WriteText(wxT(" Well, you can change text "));

        r.BeginTextColour(wxColour(255, 0, 0));
        r.WriteText(wxT("colour, like this red bit."));
        r.EndTextColour();

        r.BeginTextColour(wxColour(0, 0, 255));
        r.WriteText(wxT(" And this blue bit."));
        r.EndTextColour();

        r.WriteText(wxT(" Naturally you can make things "));
        r.BeginBold();
        r.WriteText(wxT("bold "));
        r.EndBold();
        r.BeginItalic();
        r.WriteText(wxT("or italic "));
        r.EndItalic();
        r.BeginUnderline();
        r.WriteText(wxT("or underlined."));
        r.EndUnderline();

        r.BeginFontSize(14);
        r.WriteText(wxT(" Different font sizes on the same line is allowed, too."));
        r.EndFontSize();

        r.WriteText(wxT(" Next we'll show an indented paragraph."));

        r.BeginLeftIndent(60);
        r.Newline();

        r.WriteText(wxT("Indented paragraph."));
        r.EndLeftIndent();

        r.Newline();

        r.WriteText(wxT("Next, we'll show a first-line indent, achieved using BeginLeftIndent(100, -40)."));

        r.BeginLeftIndent(100, -40);
        r.Newline();

        r.WriteText(wxT("It was in January, the most down-trodden month of an Edinburgh winter."));
        r.EndLeftIndent();

        r.Newline();

        r.WriteText(wxT("Numbered bullets are possible, again using subindents:"));

        r.BeginNumberedBullet(1, 100, 60);
        r.Newline();

        r.WriteText(
          wxT("This is my first item. Note that wxRichTextCtrl doesn't automatically do numbering, but this will be "
              "added later."));
        r.EndNumberedBullet();

        r.BeginNumberedBullet(2, 100, 60);
        r.Newline();

        r.WriteText(wxT("This is my second item."));
        r.EndNumberedBullet();

        r.Newline();

        r.WriteText(wxT("The following paragraph is right-indented:"));

        r.BeginRightIndent(200);
        r.Newline();

        r.WriteText(
          wxT("It was in January, the most down-trodden month of an Edinburgh winter. An attractive woman came into "
              "the cafe, which is nothing remarkable."));
        r.EndRightIndent();

        r.Newline();

        wxArrayInt tabs;
        tabs.Add(400);
        tabs.Add(600);
        tabs.Add(800);
        tabs.Add(1000);
        wxTextAttrEx attr;
        attr.SetFlags(wxTEXT_ATTR_TABS);
        attr.SetTabs(tabs);
        r.SetDefaultStyle(attr);

        r.WriteText(wxT("This line contains tabs:\tFirst tab\tSecond tab\tThird tab"));

        r.Newline();
        r.WriteText(wxT("Other notable features of wxRichTextCtrl include:"));

        r.BeginSymbolBullet(wxT('*'), 100, 60);
        r.Newline();
        r.WriteText(wxT("Compatibility with wxTextCtrl API"));
        r.EndSymbolBullet();

        r.WriteText(
          wxT("\nNote: this content was generated programmatically and copied from the sample. The images were loaded "
              "from inline XPMs. Enjoy wxRichTextCtrl!"));

        r.EndSuppressUndo();
        r.Thaw();

        return richText;
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Text, "value");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Text, "value");
        return xfb;
    }
};

class HtmlWindowComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxHtmlWindow* hw = new wxHtmlWindow(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        wxString dummy_page(wxT("<b>wxHtmlWindow</b><br />") wxT("This is a dummy page.</body></html>"));

        hw->SetPage(dummy_page);

        return hw;
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        return xfb;
    }
};

class ToggleButtonComponent : public ComponentBase, public wxEvtHandler
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxString label = obj->GetPropertyAsString(_("label"));
        wxToggleButton* button = new wxToggleButton(
          (wxWindow*)parent, wxID_ANY, label, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        if (obj->GetPropertyAsInteger(_("markup")) != 0) {
            button->SetLabelMarkup(label);
        }

        if (!obj->IsPropertyNull(_("bitmap"))) {
            button->SetBitmap(obj->GetPropertyAsBitmap(_("bitmap")));
        }

        if (!obj->IsPropertyNull(_("disabled"))) {
            button->SetBitmapDisabled(obj->GetPropertyAsBitmap(_("disabled")));
        }

        if (!obj->IsPropertyNull(_("pressed"))) {
            button->SetBitmapPressed(obj->GetPropertyAsBitmap(_("pressed")));
        }

        if (!obj->IsPropertyNull(_("focus"))) {
            button->SetBitmapFocus(obj->GetPropertyAsBitmap(_("focus")));
        }

        if (!obj->IsPropertyNull(_("current"))) {
            button->SetBitmapCurrent(obj->GetPropertyAsBitmap(_("current")));
        }

        if (!obj->IsPropertyNull(_("position"))) {
            button->SetBitmapPosition(static_cast<wxDirection>(obj->GetPropertyAsInteger(_("position"))));
        }

        if (!obj->IsPropertyNull(_("margins"))) {
            button->SetBitmapMargins(obj->GetPropertyAsSize(_("margins")));
        }

        button->SetValue((obj->GetPropertyAsInteger(_("value")) != 0));
        button->Connect(
          wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(ToggleButtonComponent::OnToggle), NULL, this);
        return button;
    }

    void OnToggle(wxCommandEvent& event)
    {
        wxToggleButton* window = dynamic_cast<wxToggleButton*>(event.GetEventObject());
        if (0 != window) {
            wxString value;
            value.Printf(wxT("%i"), window->GetValue() ? 1 : 0);
            GetManager()->ModifyProperty(window, _("value"), value);
            window->SetFocus();
        }
    }


    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Text, "label");
        filter.AddProperty(XrcFilter::Type::Bool, "markup");
        filter.AddProperty(XrcFilter::Type::Bitmap, "bitmap");
        if (!obj->IsPropertyNull("disabled")) {
            filter.AddProperty(XrcFilter::Type::Bitmap, "disabled");
        }
        if (!obj->IsPropertyNull("pressed")) {
            filter.AddProperty(XrcFilter::Type::Bitmap, "pressed");
        }
        if (!obj->IsPropertyNull("focus")) {
            filter.AddProperty(XrcFilter::Type::Bitmap, "focus");
        }
        if (!obj->IsPropertyNull("current")) {
            filter.AddProperty(XrcFilter::Type::Bitmap, "current");
        }
        if (!obj->IsPropertyNull("position")) {
            filter.AddProperty(XrcFilter::Type::Option, "position", "bitmapposition");
        }
        if (!obj->IsPropertyNull("margins")) {
            filter.AddProperty(XrcFilter::Type::Size, "margins");
        }
        filter.AddProperty(XrcFilter::Type::Bool, "value", "checked");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Text, "label");
        filter.AddProperty(XrcFilter::Type::Bool, "markup");
        filter.AddProperty(XrcFilter::Type::Bitmap, "bitmap");
        filter.AddProperty(XrcFilter::Type::Bitmap, "disabled");
        filter.AddProperty(XrcFilter::Type::Bitmap, "pressed");
        filter.AddProperty(XrcFilter::Type::Bitmap, "focus");
        filter.AddProperty(XrcFilter::Type::Bitmap, "current");
        filter.AddProperty(XrcFilter::Type::Option, "bitmapposition", "position");
        filter.AddProperty(XrcFilter::Type::Size, "margins");
        filter.AddProperty(XrcFilter::Type::Bool, "checked", "value");
        return xfb;
    }
};

class BitmapToggleButtonComponent : public ComponentBase, public wxEvtHandler
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxBitmapToggleButton* button = new wxBitmapToggleButton(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsBitmap(_("bitmap")), obj->GetPropertyAsPoint(_("pos")),
          obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        // To stay in sync what the generator templates do apply the markup label here as well
        if (obj->GetPropertyAsInteger(_("markup")) != 0) {
            button->SetLabelMarkup(obj->GetPropertyAsString(_("label")));
        }

        if (!obj->IsPropertyNull(_("disabled"))) {
            button->SetBitmapDisabled(obj->GetPropertyAsBitmap(_("disabled")));
        }

        if (!obj->IsPropertyNull(_("pressed"))) {
            button->SetBitmapPressed(obj->GetPropertyAsBitmap(_("pressed")));
        }

        if (!obj->IsPropertyNull(_("focus"))) {
            button->SetBitmapFocus(obj->GetPropertyAsBitmap(_("focus")));
        }

        if (!obj->IsPropertyNull(_("current"))) {
            button->SetBitmapCurrent(obj->GetPropertyAsBitmap(_("current")));
        }

        if (!obj->IsPropertyNull(_("position"))) {
            button->SetBitmapPosition(static_cast<wxDirection>(obj->GetPropertyAsInteger(_("position"))));
        }

        if (!obj->IsPropertyNull(_("margins"))) {
            button->SetBitmapMargins(obj->GetPropertyAsSize(_("margins")));
        }

        button->SetValue((obj->GetPropertyAsInteger(_("value")) != 0));
        button->Connect(
          wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(ToggleButtonComponent::OnToggle), NULL, this);

        return button;
    }

    void OnToggle(wxCommandEvent& event)
    {
        wxBitmapToggleButton* window = dynamic_cast<wxBitmapToggleButton*>(event.GetEventObject());
        if (0 != window) {
            wxString value;
            value.Printf(wxT("%i"), window->GetValue() ? 1 : 0);
            GetManager()->ModifyProperty(window, _("value"), value);
            window->SetFocus();
        }
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Bitmap, "bitmap");
        if (!obj->IsPropertyNull("disabled")) {
            filter.AddProperty(XrcFilter::Type::Bitmap, "disabled");
        }
        if (!obj->IsPropertyNull("pressed")) {
            filter.AddProperty(XrcFilter::Type::Bitmap, "pressed");
        }
        if (!obj->IsPropertyNull("focus")) {
            filter.AddProperty(XrcFilter::Type::Bitmap, "focus");
        }
        if (!obj->IsPropertyNull("current")) {
            filter.AddProperty(XrcFilter::Type::Bitmap, "current");
        }
        if (!obj->IsPropertyNull("position")) {
            filter.AddProperty(XrcFilter::Type::Option, "position", "bitmapposition");
        }
        if (!obj->IsPropertyNull("margins")) {
            filter.AddProperty(XrcFilter::Type::Size, "margins");
        }
        filter.AddProperty(XrcFilter::Type::Bool, "value", "checked");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Bitmap, "bitmap");
        filter.AddProperty(XrcFilter::Type::Bitmap, "disabled");
        filter.AddProperty(XrcFilter::Type::Bitmap, "pressed");
        filter.AddProperty(XrcFilter::Type::Bitmap, "focus");
        filter.AddProperty(XrcFilter::Type::Bitmap, "current");
        filter.AddProperty(XrcFilter::Type::Option, "bitmapposition", "position");
        filter.AddProperty(XrcFilter::Type::Size, "margins");
        filter.AddProperty(XrcFilter::Type::Bool, "checked", "value");
        return xfb;
    }
};

class TreeCtrlComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        int style = obj->GetPropertyAsInteger(_("style"));
        wxTreeCtrl* tc = new wxTreeCtrl(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          style | obj->GetPropertyAsInteger(_("window_style")));

        // dummy nodes
        wxTreeItemId root = tc->AddRoot(wxT("root node"));
        wxTreeItemId node1 = tc->AppendItem(root, wxT("node1"));
        wxTreeItemId node2 = tc->AppendItem(root, wxT("node2"));
        wxTreeItemId node3 = tc->AppendItem(node2, wxT("node3"));
        if ((style & wxTR_HIDE_ROOT) == 0) {
            tc->Expand(root);
        }
        tc->Expand(node1);
        tc->Expand(node2);
        tc->Expand(node3);

        return tc;
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        return xfb;
    }
};

class ScrollBarComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxScrollBar* sb = new wxScrollBar(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        sb->SetScrollbar(
          obj->GetPropertyAsInteger(_T("value")), obj->GetPropertyAsInteger(_T("thumbsize")),
          obj->GetPropertyAsInteger(_T("range")), obj->GetPropertyAsInteger(_T("pagesize")));
        return sb;
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Integer, "value");
        filter.AddProperty(XrcFilter::Type::Integer, "thumbsize");
        filter.AddProperty(XrcFilter::Type::Integer, "range");
        filter.AddProperty(XrcFilter::Type::Integer, "pagesize");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Integer, "value");
        filter.AddProperty(XrcFilter::Type::Integer, "thumbsize");
        filter.AddProperty(XrcFilter::Type::Integer, "range");
        filter.AddProperty(XrcFilter::Type::Integer, "pagesize");
        return xfb;
    }
};

class SpinCtrlComponent : public ComponentBase, public wxEvtHandler
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        int max = obj->GetPropertyAsInteger(_("max"));
        int min = obj->GetPropertyAsInteger(_("min"));
        wxSpinCtrl* window = new wxSpinCtrl(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsString(_("value")), obj->GetPropertyAsPoint(_("pos")),
          obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")), min < max ? min : max,
          max, obj->GetPropertyAsInteger(_("initial")));

        window->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(SpinCtrlComponent::OnSpin), NULL, this);
        return window;
    }

    void OnSpin(wxSpinEvent& event)
    {
        wxSpinCtrl* window = dynamic_cast<wxSpinCtrl*>(event.GetEventObject());
        if (0 != window) {
            wxString value;
            value.Printf(wxT("%i"), window->GetValue());
            GetManager()->ModifyProperty(window, _("initial"), value);
            window->SetFocus();
        }
    }

    void Cleanup(wxObject* obj) override
    {
        wxSpinCtrl* window = dynamic_cast<wxSpinCtrl*>(obj);
        if (0 != window) {
            window->Disconnect(
              wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(SpinCtrlComponent::OnSpin), NULL, this);
        }
        ComponentBase::Cleanup(obj);
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Integer, "initial", "value");
        filter.AddProperty(XrcFilter::Type::Integer, "min");
        filter.AddProperty(XrcFilter::Type::Integer, "max");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        // TODO: XRC only supports the integral initial value, filter it twice to initialize the string initial value as well
        filter.AddProperty(XrcFilter::Type::String, "value");
        filter.AddProperty(XrcFilter::Type::Integer, "value", "initial");
        filter.AddProperty(XrcFilter::Type::Integer, "min");
        filter.AddProperty(XrcFilter::Type::Integer, "max");
        return xfb;
    }
};

class SpinCtrlDoubleComponent : public ComponentBase, public wxEvtHandler
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxSpinCtrlDouble* window = new wxSpinCtrlDouble(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsString(_("value")), obj->GetPropertyAsPoint(_("pos")),
          obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")),
          obj->GetPropertyAsFloat(_("min")), obj->GetPropertyAsFloat(_("max")), obj->GetPropertyAsFloat(_("initial")),
          obj->GetPropertyAsFloat(_("inc")));

        window->SetDigits(obj->GetPropertyAsInteger(_("digits")));

        window->Connect(
          wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, wxSpinEventHandler(SpinCtrlDoubleComponent::OnSpin), NULL, this);
        return window;
    }

    void OnSpin(wxSpinEvent& event)
    {
        wxSpinCtrlDouble* window = dynamic_cast<wxSpinCtrlDouble*>(event.GetEventObject());
        if (0 != window) {
            wxString value;
            value.Printf(wxT("%f"), window->GetValue());
            GetManager()->ModifyProperty(window, _("initial"), value);
            window->SetFocus();
        }
    }

    void Cleanup(wxObject* obj) override
    {
        wxSpinCtrlDouble* window = dynamic_cast<wxSpinCtrlDouble*>(obj);
        if (0 != window) {
            window->Disconnect(
              wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, wxSpinEventHandler(SpinCtrlDoubleComponent::OnSpin), NULL, this);
        }
        ComponentBase::Cleanup(obj);
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Float, "initial", "value");
        filter.AddProperty(XrcFilter::Type::Float, "min");
        filter.AddProperty(XrcFilter::Type::Float, "max");
        filter.AddProperty(XrcFilter::Type::Float, "inc");
        filter.AddProperty(XrcFilter::Type::Integer, "digits");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        // TODO: XRC only supports the float initial value, filter it twice to initialize the string initial value as well
        filter.AddProperty(XrcFilter::Type::String, "value");
        filter.AddProperty(XrcFilter::Type::Float, "value", "initial");
        filter.AddProperty(XrcFilter::Type::Float, "min");
        filter.AddProperty(XrcFilter::Type::Float, "max");
        filter.AddProperty(XrcFilter::Type::Float, "inc");
        filter.AddProperty(XrcFilter::Type::Integer, "digits");
        return xfb;
    }
};

class SpinButtonComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        return new wxSpinButton(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        return xfb;
    }
};

class CheckListBoxComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxArrayString choices(obj->GetPropertyAsArrayString(_("choices")));
        wxCheckListBox* cl = new wxCheckListBox(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")), choices,
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        return cl;
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::TextList, "choices", "content");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::TextList, "content", "choices");
        return xfb;
    }
};

class GridComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxGrid* grid = new wxGrid(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("window_style")));

        grid->CreateGrid(obj->GetPropertyAsInteger(_("rows")), obj->GetPropertyAsInteger(_("cols")));

        grid->EnableDragColMove(obj->GetPropertyAsInteger(_("drag_col_move")) != 0);
        grid->EnableDragColSize(obj->GetPropertyAsInteger(_("drag_col_size")) != 0);
        grid->EnableDragGridSize(obj->GetPropertyAsInteger(_("drag_grid_size")) != 0);
        grid->EnableDragRowSize(obj->GetPropertyAsInteger(_("drag_row_size")) != 0);
        grid->EnableEditing(obj->GetPropertyAsInteger(_("editing")) != 0);
        grid->EnableGridLines(obj->GetPropertyAsInteger(_("grid_lines")) != 0);
        if (!obj->IsPropertyNull(_("grid_line_color"))) {
            grid->SetGridLineColour(obj->GetPropertyAsColour(_("grid_line_color")));
        }
        grid->SetMargins(obj->GetPropertyAsInteger(_("margin_width")), obj->GetPropertyAsInteger(_("margin_height")));

        // Label Properties
        grid->SetColLabelAlignment(
          obj->GetPropertyAsInteger(_("col_label_horiz_alignment")),
          obj->GetPropertyAsInteger(_("col_label_vert_alignment")));

        wxArrayString columnLabels = obj->GetPropertyAsArrayString(_("col_label_values"));
        for (int i = 0; i < (int)columnLabels.size() && i < grid->GetNumberCols(); ++i) {
            grid->SetColLabelValue(i, columnLabels[i]);
        }

        if (!obj->IsPropertyNull(_("col_label_size"))) {
            grid->SetColLabelSize(obj->GetPropertyAsInteger(_("col_label_size")));
        }

        wxArrayInt columnSizes = obj->GetPropertyAsArrayInt(_("column_sizes"));
        for (int i = 0; i < (int)columnSizes.size() && i < grid->GetNumberCols(); ++i) {
            grid->SetColSize(i, columnSizes[i]);
        }

        grid->SetRowLabelAlignment(
          obj->GetPropertyAsInteger(_("row_label_horiz_alignment")),
          obj->GetPropertyAsInteger(_("row_label_vert_alignment")));

        wxArrayString rowLabels = obj->GetPropertyAsArrayString(_("row_label_values"));
        for (int i = 0; i < (int)rowLabels.size() && i < grid->GetNumberRows(); ++i) {
            grid->SetRowLabelValue(i, rowLabels[i]);
        }

        if (!obj->IsPropertyNull(_("row_label_size"))) {
            grid->SetRowLabelSize(obj->GetPropertyAsInteger(_("row_label_size")));
        }

        wxArrayInt rowSizes = obj->GetPropertyAsArrayInt(_("row_sizes"));
        for (int i = 0; i < (int)rowSizes.size() && i < grid->GetNumberRows(); ++i) {
            grid->SetRowSize(i, rowSizes[i]);
        }

        if (!obj->IsPropertyNull(_("label_bg"))) {
            grid->SetLabelBackgroundColour(obj->GetPropertyAsColour(_("label_bg")));
        }
        if (!obj->IsPropertyNull(_("label_text"))) {
            grid->SetLabelTextColour(obj->GetPropertyAsColour(_("label_text")));
        }
        if (!obj->IsPropertyNull(_("label_font"))) {
            grid->SetLabelFont(obj->GetPropertyAsFont(_("label_font")));
        }

        // Default Cell Properties
        grid->SetDefaultCellAlignment(
          obj->GetPropertyAsInteger(_("cell_horiz_alignment")), obj->GetPropertyAsInteger(_("cell_vert_alignment")));

        if (!obj->IsPropertyNull(_("cell_bg"))) {
            grid->SetDefaultCellBackgroundColour(obj->GetPropertyAsColour(_("cell_bg")));
        }
        if (!obj->IsPropertyNull(_("cell_text"))) {
            grid->SetDefaultCellTextColour(obj->GetPropertyAsColour(_("cell_text")));
        }
        if (!obj->IsPropertyNull(_("cell_font"))) {
            grid->SetDefaultCellFont(obj->GetPropertyAsFont(_("cell_font")));
        }

        // Example Cell Values
        for (int col = 0; col < grid->GetNumberCols(); ++col) {
            for (int row = 0; row < grid->GetNumberRows(); ++row) {
                grid->SetCellValue(row, col, grid->GetColLabelValue(col) + wxT("-") + grid->GetRowLabelValue(row));
            }
        }

        if (obj->GetPropertyAsInteger(_("autosize_rows")) != 0) {
            grid->AutoSizeRows();
        }
        if (obj->GetPropertyAsInteger(_("autosize_cols")) != 0) {
            grid->AutoSizeColumns();
        }

        grid->PushEventHandler(new ComponentEvtHandler(grid, GetManager()));

        return grid;
    }

    void Cleanup(wxObject* obj) override
    {
        auto* window = wxDynamicCast(obj, wxGrid);
        if (window) {
            window->PopEventHandler(true);
        }
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        return xfb;
    }
};


class PickerComponentBase : public ComponentBase, public wxEvtHandler
{
public:
    void OnLeftClick(wxMouseEvent& event)
    {
        wxWindow* window = dynamic_cast<wxWindow*>(event.GetEventObject());
        wxPickerBase* picker = dynamic_cast<wxPickerBase*>(window->GetParent());
        if (0 != picker) {
            if (!GetManager()->SelectObject(picker)) {
                event.Skip();
            }
        }
    }

    void OnCreated(wxObject* wxobject, wxWindow* /*wxparent*/) override
    {
        wxPickerBase* picker = dynamic_cast<wxPickerBase*>(wxobject);
        if (picker != 0) {
            picker->GetPickerCtrl()->Connect(
              wxEVT_LEFT_DOWN, wxMouseEventHandler(PickerComponentBase::OnLeftClick), NULL, this);

            wxTextCtrl* text = picker->GetTextCtrl();
            if (0 != text) {
                text->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(PickerComponentBase::OnLeftClick), NULL, this);
            }
        }
    }

    void Cleanup(wxObject* obj) override
    {
        wxPickerBase* picker = dynamic_cast<wxPickerBase*>(obj);
        if (picker != 0) {
            picker->GetPickerCtrl()->Disconnect(
              wxEVT_LEFT_DOWN, wxMouseEventHandler(PickerComponentBase::OnLeftClick), NULL, this);

            wxTextCtrl* text = picker->GetTextCtrl();
            if (0 != text) {
                text->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(PickerComponentBase::OnLeftClick), NULL, this);
            }
        }
        ComponentBase::Cleanup(obj);
    }
};

class ColourPickerComponent : public PickerComponentBase
{
private:
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxColourPickerCtrl* colourpicker = new wxColourPickerCtrl(
          (wxWindow*)parent, obj->GetPropertyAsInteger(_("id")), obj->GetPropertyAsColour(_("colour")),
          obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        colourpicker->PushEventHandler(new ComponentEvtHandler(colourpicker, GetManager()));
        return colourpicker;
    }

    void Cleanup(wxObject* obj) override
    {
        auto* window = wxDynamicCast(obj, wxColourPickerCtrl);
        if (window) {
            window->PopEventHandler(true);
        }
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Colour, "colour", "value");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Colour, "value", "colour");
        return xfb;
    }
};

class FontPickerComponent : public PickerComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxFontPickerCtrl* picker = new wxFontPickerCtrl(
          (wxWindow*)parent, obj->GetPropertyAsInteger(_("id")), obj->GetPropertyAsFont(_("value")),
          obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        if (!obj->IsPropertyNull(_("max_point_size"))) {
            picker->SetMaxPointSize(obj->GetPropertyAsInteger(_("max_point_size")));
        }

        picker->PushEventHandler(new ComponentEvtHandler(picker, GetManager()));
        return picker;
    }

    void Cleanup(wxObject* obj) override
    {
        auto* window = wxDynamicCast(obj, wxFontPickerCtrl);
        if (window) {
            window->PopEventHandler(true);
        }
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        if (!obj->IsPropertyNull("value")) {
            filter.AddProperty(XrcFilter::Type::Font, "value");
        }
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Font, "value");
        return xfb;
    }
};

class FilePickerComponent : public PickerComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxFilePickerCtrl* picker = new wxFilePickerCtrl(
          (wxWindow*)parent, obj->GetPropertyAsInteger(_("id")), obj->GetPropertyAsString(_("value")),
          obj->GetPropertyAsString(_("message")), obj->GetPropertyAsString(_("wildcard")),
          obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        picker->PushEventHandler(new ComponentEvtHandler(picker, GetManager()));
        return picker;
    }

    void Cleanup(wxObject* obj) override
    {
        auto* window = wxDynamicCast(obj, wxFilePickerCtrl);
        if (window) {
            window->PopEventHandler(true);
        }
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::String, "value");
        filter.AddProperty(XrcFilter::Type::Text, "message");
        filter.AddProperty(XrcFilter::Type::Text, "wildcard");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::String, "value");
        filter.AddProperty(XrcFilter::Type::Text, "message");
        filter.AddProperty(XrcFilter::Type::Text, "wildcard");
        return xfb;
    }
};

class DirPickerComponent : public PickerComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxDirPickerCtrl* picker = new wxDirPickerCtrl(
          (wxWindow*)parent, obj->GetPropertyAsInteger(_("id")), obj->GetPropertyAsString(_("value")),
          obj->GetPropertyAsString(_("message")), obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        picker->PushEventHandler(new ComponentEvtHandler(picker, GetManager()));
        return picker;
    }

    void Cleanup(wxObject* obj) override
    {
        auto* window = wxDynamicCast(obj, wxDirPickerCtrl);
        if (window) {
            window->PopEventHandler(true);
        }
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::String, "value");
        filter.AddProperty(XrcFilter::Type::Text, "message");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::String, "value");
        filter.AddProperty(XrcFilter::Type::Text, "message");
        return xfb;
    }
};

class HyperlinkComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxHyperlinkCtrl* ctrl = new wxHyperlinkCtrl(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsString(_("label")), obj->GetPropertyAsString(_("url")),
          obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        if (!obj->IsPropertyNull(_("hover_color"))) {
            ctrl->SetHoverColour(obj->GetPropertyAsColour(_("hover_color")));
        }
        if (!obj->IsPropertyNull(_("normal_color"))) {
            ctrl->SetNormalColour(obj->GetPropertyAsColour(_("normal_color")));
        }
        if (!obj->IsPropertyNull(_("visited_color"))) {
            ctrl->SetVisitedColour(obj->GetPropertyAsColour(_("visited_color")));
        }

        return ctrl;
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Text, "label");
        filter.AddProperty(XrcFilter::Type::String, "url");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Text, "label");
        filter.AddProperty(XrcFilter::Type::String, "url");
        return xfb;
    }
};

class GenericDirCtrlComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxGenericDirCtrl* ctrl = new wxGenericDirCtrl(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsString(_("defaultfolder")), obj->GetPropertyAsPoint(_("pos")),
          obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")),
          obj->GetPropertyAsString(_("filter")), obj->GetPropertyAsInteger(_("defaultfilter")));

        ctrl->ShowHidden(obj->GetPropertyAsInteger(_("show_hidden")) != 0);
        ctrl->GetTreeCtrl()->PushEventHandler(new GenericDirCtrlEvtHandler(ctrl, GetManager()));
        return ctrl;
    }

    void Cleanup(wxObject* obj) override
    {
        auto* window = wxDynamicCast(obj, wxGenericDirCtrl);
        if (window) {
            window->GetTreeCtrl()->PopEventHandler(true);
        }
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::String, "defaultfolder");
        filter.AddProperty(XrcFilter::Type::Text, "filter");
        filter.AddProperty(XrcFilter::Type::Integer, "defaultfilter");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::String, "defaultfolder");
        filter.AddProperty(XrcFilter::Type::Text, "filter");
        filter.AddProperty(XrcFilter::Type::Integer, "defaultfilter");
        return xfb;
    }
};

class CustomControlComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* /*obj*/, wxObject* parent) override
    {
        return new wxPanel((wxWindow*)parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0);
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj, obj->GetPropertyAsString("class"));
        return xrc;
    }
};

class CustomCodeComponent : public ComponentBase
{
};

class SearchCtrlComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxSearchCtrl* sc = new wxSearchCtrl(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsString(_("value")), obj->GetPropertyAsPoint(_("pos")),
          obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        if (!obj->IsPropertyNull(_("search_button"))) {
            sc->ShowSearchButton(obj->GetPropertyAsInteger(_("search_button")) != 0);
        }

        if (!obj->IsPropertyNull(_("cancel_button"))) {
            sc->ShowCancelButton(obj->GetPropertyAsInteger(_("cancel_button")) != 0);
        }

        sc->PushEventHandler(new ComponentEvtHandler(sc, GetManager()));

        return sc;
    }

    void Cleanup(wxObject* obj) override
    {
        auto* window = wxDynamicCast(obj, wxSearchCtrl);
        if (window) {
            window->PopEventHandler(true);
        }
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Text, "value");
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        filter.AddProperty(XrcFilter::Type::Text, "value");
        return xfb;
    }
};

#ifdef USE_MEDIACTRL
class MediaCtrlComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxMediaCtrl* mc = new wxMediaCtrl(
          (wxWindow*)parent, wxID_ANY, wxT(""), obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        if (!obj->IsPropertyNull(_("file"))) {
            if (mc->Load(obj->GetPropertyAsString(_("file")))) {
                if (!obj->IsPropertyNull(_("playback_rate")))
                    mc->SetPlaybackRate(obj->GetPropertyAsFloat(_("playback_rate")));
                if (
                  !obj->IsPropertyNull(_("volume")) && (obj->GetPropertyAsFloat(_("volume")) >= 0) &&
                  (obj->GetPropertyAsFloat(_("volume")) <= 1))
                    mc->SetPlaybackRate(obj->GetPropertyAsFloat(_("volume")));
                if (!obj->IsPropertyNull(_("player_controls"))) {
                    if (obj->GetPropertyAsString(_("player_controls")) == wxT("STEP"))
                        mc->ShowPlayerControls(wxMEDIACTRLPLAYERCONTROLS_STEP);
                    if (obj->GetPropertyAsString(_("player_controls")) == wxT("VOLUME"))
                        mc->ShowPlayerControls(wxMEDIACTRLPLAYERCONTROLS_VOLUME);
                    if (obj->GetPropertyAsString(_("player_controls")) == wxT("DEFAULT"))
                        mc->ShowPlayerControls(wxMEDIACTRLPLAYERCONTROLS_DEFAULT);
                    if (obj->GetPropertyAsString(_("player_controls")) == wxT("NONE"))
                        mc->ShowPlayerControls(wxMEDIACTRLPLAYERCONTROLS_NONE);
                }

                if (!obj->IsPropertyNull(_("play")) && (obj->GetPropertyAsInteger(_("play")) == 1))
                    mc->Play();
                else
                    mc->Stop();

                // GetManager()->ModifyProperty( m_window, wxT("size"), mc->GetBestSize() );
            }
        }

        if (!obj->IsPropertyNull(_("style")))
            mc->ShowPlayerControls(wxMEDIACTRLPLAYERCONTROLS_STEP);

        mc->PushEventHandler(new ComponentEvtHandler(mc, GetManager()));

        return mc;
    }

    void Cleanup(wxObject* obj) override
    {
        auto* window = wxDynamicCast(obj, wxMediaCtrl);
        if (window) {
            window->PopEventHandler(true);
        }
    }

    tinyxml2::XMLElement* ExportToXrc(tinyxml2::XMLElement* xrc, const IObject* obj) override
    {
        ObjectToXrcFilter filter(xrc, GetLibrary(), obj);
        filter.AddWindowProperties();
        return xrc;
    }

    tinyxml2::XMLElement* ImportFromXrc(tinyxml2::XMLElement* xfb, const tinyxml2::XMLElement* xrc) override
    {
        XrcToXfbFilter filter(xfb, GetLibrary(), xrc);
        filter.AddWindowProperties();
        return xfb;
    }
};
#endif

class TimerComponent : public ComponentBase
{
public:
};

class PropertyGridComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxPropertyGrid* pg = new wxPropertyGrid(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(wxT("pos")), obj->GetPropertyAsSize(wxT("size")),
          obj->GetPropertyAsInteger(wxT("style")) | obj->GetPropertyAsInteger(wxT("window_style")));

        if (!obj->GetPropertyAsString(wxT("extra_style")).empty()) {
            pg->SetExtraStyle(obj->GetPropertyAsInteger(wxT("extra_style")));
        }

        return pg;
    }

    /*void Cleanup( wxObject* )
    {
            // Prevent assert for missing event handler
    }*/

    void OnCreated(wxObject* wxobject, wxWindow* /*wxparent*/) override
    {
        wxPropertyGrid* pg = wxDynamicCast(wxobject, wxPropertyGrid);
        if (NULL == pg) {
            // very very strange
            return;
        }

        size_t count = GetManager()->GetChildCount(wxobject);
        for (size_t i = 0; i < count; ++i) {
            wxObject* child = GetManager()->GetChild(wxobject, i);
            IObject* childObj = GetManager()->GetIObject(child);
            if (childObj->GetClassName() == _("propGridItem")) {
                auto propName = childObj->GetPropertyAsString(_("prop_name"));
                if (childObj->GetPropertyAsString(_("type")) == _("Category")) {
                    pg->Append(new wxPropertyCategory(
                      childObj->GetPropertyAsString(_("label")), !propName.empty() ? propName : wxPG_LABEL));
                } else {
                    wxPGProperty* prop = wxDynamicCast(
                      wxCreateDynamicObject(wxT("wx") + (childObj->GetPropertyAsString(_("type"))) + wxT("Property")),
                      wxPGProperty);
                    if (prop) {
                        prop->SetLabel(childObj->GetPropertyAsString(_("label")));
                        if (!propName.empty()) {
                            prop->SetName(propName);
                        }
                        pg->Append(prop);

                        if (childObj->GetPropertyAsString(_("help")) != wxEmptyString) {
                            pg->SetPropertyHelpString(prop, childObj->GetPropertyAsString(_("help")));
                        }
                    }
                }
            }
        }
    }
};

class PropertyGridManagerComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxPropertyGridManager* pgman = new wxPropertyGridManager(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(wxT("pos")), obj->GetPropertyAsSize(wxT("size")),
          obj->GetPropertyAsInteger(wxT("style")) | obj->GetPropertyAsInteger(wxT("window_style")));

        if (!obj->GetPropertyAsString(wxT("extra_style")).empty()) {
            pgman->SetExtraStyle(obj->GetPropertyAsInteger(wxT("extra_style")));
        }

        pgman->ShowHeader(obj->GetPropertyAsInteger(wxT("show_header")) != 0);

        // Adding a page sets target page to the one added, so
        // we don't have to call SetTargetPage if we are filling
        // it right after adding.
        /*wxPropertyGridPage* pg = pgman->AddPage( _("First Page") );

        pg->Append( new wxPropertyCategory( _("Sample Category") ) );

        // Add string property
        wxPGProperty *id = pg->Append( new wxStringProperty( _("Label"), wxPG_LABEL, _("Initial Value") ) );
        pg->SetPropertyHelpString( id, _("A string property") );

        // Add int property
        pg->Append( new wxIntProperty ( wxT("IntProperty"), wxPG_LABEL, 12345678 ) );

        // Add float property (value type is actually double)
        pg->Append( new wxFloatProperty ( wxT("FloatProperty"), wxPG_LABEL, 12345.678 ) );

        // Add a bool property
        pg->Append( new wxBoolProperty ( wxT("BoolProperty"), wxPG_LABEL, false ) );
        pg->Append( new wxBoolProperty ( wxT("BoolPropertyAsCheckbox"), wxPG_LABEL, true ) );
        pg->SetPropertyAttribute( wxT("BoolPropertyAsCheckbox"), wxPG_BOOL_USE_CHECKBOX, (long)1 );

        // Add an enum property
        wxArrayString strings;
        strings.Add( _("Herbivore") );
        strings.Add( _("Carnivore") );
        strings.Add( _("Omnivore") );

        wxArrayInt indexes;
        indexes.Add( 0 );
        indexes.Add( 1 );
        indexes.Add( 2 );

        pg->Append( new wxEnumProperty( wxT("EnumProperty"), wxPG_LABEL, strings, indexes, 0 ) );

        pg->Append( new wxPropertyCategory( _("Low Priority Properties") ) );

        // A string property that can be edited in a separate editor dialog.
        pg->Append( new wxLongStringProperty( wxT("LongStringProperty"), wxPG_LABEL,
                                                                                  wxString(_("This is much longer string
        than the ") ) + wxString(_("first one. Edit it by clicking the button.") ) ) );

        // String editor with dir selector button.
        pg->Append( new wxDirProperty( wxT("DirProperty"), wxPG_LABEL, ::wxGetUserHome() ) );

        // A file selector property.
        pg->Append( new wxFileProperty( wxT("FileProperty"), wxPG_LABEL, wxEmptyString ) );

        wxPropertyGridPage* pg2 = pgman->AddPage( _("Second Page") );

        pg2->Append( new wxPropertyCategory( _("Sample Parent Property"), wxPG_LABEL ) );

        wxPGProperty* carProp2 = pg2->Append( new wxStringProperty( _("Car"), wxPG_LABEL, wxT("<composed>") ) );
        pg2->AppendIn( carProp2, new wxStringProperty( _("Model"), wxPG_LABEL, wxT("Lamborghini Diablo SV") ) );
        pg2->AppendIn( carProp2, new wxIntProperty( _("Engine Size (cc)"), wxPG_LABEL, 5707) );

        wxPGProperty* speedsProp2 = pg2->AppendIn( carProp2, new wxStringProperty( _("Speeds"), wxPG_LABEL,
        wxT("<composed>") ) ); pg2->AppendIn( speedsProp2, new wxIntProperty( _("Max. Speed (mph)"), wxPG_LABEL, 300 )
        ); pg2->AppendIn( speedsProp2, new wxFloatProperty( _("0-100 mph (sec)"), wxPG_LABEL, 3.9 ) ); pg2->AppendIn(
        speedsProp2, new wxFloatProperty( _("1/4 mile (sec)"), wxPG_LABEL, 8.6) );

        pg2->AppendIn( carProp2, new wxIntProperty( _("Price ($)"), wxPG_LABEL, 300000 ) );

        if ( obj->GetPropertyAsInteger( wxT("include_advanced") ) )
        {
                pg2->Append( new wxPropertyCategory( _("Advanced Properties"), wxPG_LABEL ) );
                // wxArrayStringProperty embeds a wxArrayString.
                pg2->Append( new wxArrayStringProperty( _("Example of ArrayStringProperty"), wxT("ArrayStringProp") ) );

                // Image file property. Wildcard is auto-generated from available
                // image handlers, so it is not set this time.
                pg2->Append( new wxImageFileProperty( _("Example of ImageFileProperty"), wxT("ImageFileProp") ) );

                // Font property has sub-properties.
                pg2->Append( new wxFontProperty( _("Font"), wxPG_LABEL ) );

                // Colour property with arbitrary colour.
                pg2->Append( new wxColourProperty( _("My Colour 1"), wxPG_LABEL, wxColour( 242, 109, 0 ) ) );

                // System colour property.
                pg2->Append( new wxSystemColourProperty( _("My SysColour 1"), wxPG_LABEL, wxSystemSettings::GetColour(
        wxSYS_COLOUR_WINDOW ) ) );

                // System colour property with custom colour.
                pg2->Append( new wxSystemColourProperty( _("My SysColour 2"), wxPG_LABEL, wxColour( 0, 200, 160 ) ) );

                // Cursor property
                pg2->Append( new wxCursorProperty( _("My Cursor"), wxPG_LABEL, wxCURSOR_ARROW ) );
        }*/

        return pgman;
    }
    /*
            void Cleanup( wxObject* )
            {
                    // Prevent assert for missing event handler
            }*/

    void OnCreated(wxObject* wxobject, wxWindow* /*wxparent*/) override
    {
        wxPropertyGridManager* pgm = wxDynamicCast(wxobject, wxPropertyGridManager);
        if (NULL == pgm) {
            // very very strange
            return;
        }

        size_t count = GetManager()->GetChildCount(wxobject);
        for (size_t i = 0; i < count; ++i) {
            wxObject* child = GetManager()->GetChild(wxobject, i);
            IObject* childObj = GetManager()->GetIObject(child);
            if (childObj->GetClassName() == _("propGridPage")) {
                wxPropertyGridPage* page =
                  pgm->AddPage(childObj->GetPropertyAsString(_("label")), childObj->GetPropertyAsBitmap(_("bitmap")));

                for (size_t j = 0; j < childObj->GetChildCount(); ++j) {
                    auto* innerChildObj = childObj->GetChildObject(j);
                    if (innerChildObj->GetClassName() == _("propGridItem")) {
                        auto propName = innerChildObj->GetPropertyAsString(_("prop_name"));
                        if (innerChildObj->GetPropertyAsString(_("type")) == _("Category")) {
                            page->Append(new wxPropertyCategory(
                              innerChildObj->GetPropertyAsString(_("label")),
                              !propName.empty() ? propName : wxPG_LABEL));
                        } else {
                            wxPGProperty* prop = wxDynamicCast(
                              wxCreateDynamicObject(
                                wxT("wx") + (innerChildObj->GetPropertyAsString(_("type"))) + wxT("Property")),
                              wxPGProperty);
                            if (prop) {
                                prop->SetLabel(innerChildObj->GetPropertyAsString(_("label")));
                                if (!propName.empty()) {
                                    prop->SetName(propName);
                                }
                                page->Append(prop);

                                if (innerChildObj->GetPropertyAsString(_("help")) != wxEmptyString) {
                                    page->SetPropertyHelpString(prop, innerChildObj->GetPropertyAsString(_("help")));
                                }
                            }
                        }
                    }
                }
            }
        }

        if (count) {
            pgm->SelectPage(0);
        }

        pgm->Update();
    }
};

class PropertyGridItemComponent : public ComponentBase
{
};
class PropertyGridPageComponent : public ComponentBase
{
};

class StyledTextComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxStyledTextCtrl* m_code = new wxStyledTextCtrl(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("window_style")), obj->GetPropertyAsString(_("name")));

        // Line Numbers
        if (0 != obj->GetPropertyAsInteger(_("line_numbers"))) {
            m_code->SetMarginType(0, wxSTC_MARGIN_NUMBER);
            m_code->SetMarginWidth(0, m_code->TextWidth(wxSTC_STYLE_LINENUMBER, wxT("_99999")));
        } else {
            m_code->SetMarginWidth(0, 0);
        }

        // markers
        m_code->MarkerDefine(wxSTC_MARKNUM_FOLDER, wxSTC_MARK_BOXPLUS);
        m_code->MarkerSetBackground(wxSTC_MARKNUM_FOLDER, wxColour(wxT("BLACK")));
        m_code->MarkerSetForeground(wxSTC_MARKNUM_FOLDER, wxColour(wxT("WHITE")));
        m_code->MarkerDefine(wxSTC_MARKNUM_FOLDEROPEN, wxSTC_MARK_BOXMINUS);
        m_code->MarkerSetBackground(wxSTC_MARKNUM_FOLDEROPEN, wxColour(wxT("BLACK")));
        m_code->MarkerSetForeground(wxSTC_MARKNUM_FOLDEROPEN, wxColour(wxT("WHITE")));
        m_code->MarkerDefine(wxSTC_MARKNUM_FOLDERSUB, wxSTC_MARK_EMPTY);
        m_code->MarkerDefine(wxSTC_MARKNUM_FOLDEREND, wxSTC_MARK_BOXPLUS);
        m_code->MarkerSetBackground(wxSTC_MARKNUM_FOLDEREND, wxColour(wxT("BLACK")));
        m_code->MarkerSetForeground(wxSTC_MARKNUM_FOLDEREND, wxColour(wxT("WHITE")));
        m_code->MarkerDefine(wxSTC_MARKNUM_FOLDEROPENMID, wxSTC_MARK_BOXMINUS);
        m_code->MarkerSetBackground(wxSTC_MARKNUM_FOLDEROPENMID, wxColour(wxT("BLACK")));
        m_code->MarkerSetForeground(wxSTC_MARKNUM_FOLDEROPENMID, wxColour(wxT("WHITE")));
        m_code->MarkerDefine(wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_EMPTY);
        m_code->MarkerDefine(wxSTC_MARKNUM_FOLDERTAIL, wxSTC_MARK_EMPTY);

        // folding
        if (0 != obj->GetPropertyAsInteger(_("folding"))) {
            m_code->SetMarginType(1, wxSTC_MARGIN_SYMBOL);
            m_code->SetMarginMask(1, wxSTC_MASK_FOLDERS);
            m_code->SetMarginWidth(1, 16);
            m_code->SetMarginSensitive(1, true);

            m_code->SetProperty(wxT("fold"), wxT("1"));
            m_code->SetFoldFlags(wxSTC_FOLDFLAG_LINEBEFORE_CONTRACTED | wxSTC_FOLDFLAG_LINEAFTER_CONTRACTED);
        } else {
            m_code->SetMarginWidth(1, 0);
        }
        m_code->SetIndentationGuides(obj->GetPropertyAsInteger(_("indentation_guides")));

        m_code->SetMarginWidth(2, 0);

        m_code->SetLexer(wxSTC_LEX_CPP);
        m_code->SetKeyWords(0, wxT("asm auto bool break case catch char class const const_cast \
							   continue default delete do double dynamic_cast else enum explicit \
							   export extern false float for friend goto if inline int long \
							   mutable namespace new operator private protected public register \
							   reinterpret_cast return short signed sizeof static static_cast \
							   struct switch template this throw true try typedef typeid \
							   typename union unsigned using virtual void volatile wchar_t \
							   while"));

        wxFont font(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
        if (!obj->GetPropertyAsString(_("font")).empty()) {
            font = obj->GetPropertyAsFont(_("font"));
        }

        m_code->StyleSetFont(wxSTC_STYLE_DEFAULT, font);

        m_code->StyleClearAll();
        m_code->StyleSetBold(wxSTC_C_WORD, true);
        m_code->StyleSetForeground(wxSTC_C_WORD, *wxBLUE);
        m_code->StyleSetForeground(wxSTC_C_STRING, *wxRED);
        m_code->StyleSetForeground(wxSTC_C_STRINGEOL, *wxRED);
        m_code->StyleSetForeground(wxSTC_C_PREPROCESSOR, wxColour(49, 106, 197));
        m_code->StyleSetForeground(wxSTC_C_COMMENT, wxColour(0, 128, 0));
        m_code->StyleSetForeground(wxSTC_C_COMMENTLINE, wxColour(0, 128, 0));
        m_code->StyleSetForeground(wxSTC_C_COMMENTDOC, wxColour(0, 128, 0));
        m_code->StyleSetForeground(wxSTC_C_COMMENTLINEDOC, wxColour(0, 128, 0));
        m_code->StyleSetForeground(wxSTC_C_NUMBER, *wxBLUE);
        m_code->SetUseTabs((0 != obj->GetPropertyAsInteger(_("use_tabs"))));
        m_code->SetTabWidth(obj->GetPropertyAsInteger(_("tab_width")));
        m_code->SetTabIndents((0 != obj->GetPropertyAsInteger(_("tab_indents"))));
        m_code->SetBackSpaceUnIndents((0 != obj->GetPropertyAsInteger(_("backspace_unindents"))));
        m_code->SetIndent(obj->GetPropertyAsInteger(_("tab_width")));
        m_code->SetSelBackground(true, wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
        m_code->SetSelForeground(true, wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
        m_code->SetViewEOL((0 != obj->GetPropertyAsInteger(_("view_eol"))));
        m_code->SetViewWhiteSpace(obj->GetPropertyAsInteger(_("view_whitespace")));

        m_code->SetCaretWidth(2);

        m_code->SetText(wxT("/** Sample Class to Display wxScintilla */\n") wxT("class ScintillaSampleCode\n")
                          wxT("{\n") wxT("private:\n") wxT("\tint m_privateMember;\n\n") wxT("public:\n\n")
                            wxT("\t// Sample Member Function\n") wxT("\tint SampleFunction( int sample = 0 )\n")
                              wxT("\t{\n") wxT("\t\treturn sample;\n") wxT("\t}\n") wxT("};\n"));

        m_code->PushEventHandler(new ComponentEvtHandler(m_code, GetManager()));

        return m_code;
    }

    void Cleanup(wxObject* obj) override
    {
        auto* window = wxDynamicCast(obj, wxStyledTextCtrl);
        if (window) {
            window->PopEventHandler(true);
        }
    }
};

class DataViewModel : public wxDataViewModel
{
public:
    unsigned int GetChildren(const wxDataViewItem&, wxDataViewItemArray& /*children*/) const override { return 0; }
    unsigned int GetColumnCount() const override { return 0; }
    wxString GetColumnType(unsigned int /*col*/) const override { return wxVariant("Dummy").GetType(); }
    wxDataViewItem GetParent(const wxDataViewItem&) const override { return wxDataViewItem(NULL); }
    bool IsContainer(const wxDataViewItem&) const override { return false; }
    void GetValue(wxVariant&, const wxDataViewItem&, unsigned int /*col*/) const override {}
    bool SetValue(const wxVariant&, const wxDataViewItem&, unsigned int /*col*/) override { return true; }
};

class DataViewCtrl : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxDataViewCtrl* dataViewCtrl = new wxDataViewCtrl(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("window_style")));

        wxObjectDataPtr<DataViewModel> model;
        model = new DataViewModel;
        dataViewCtrl->AssociateModel(model.get());

        return dataViewCtrl;
    }

    void OnCreated(wxObject* wxobject, wxWindow* /*wxparent*/) override
    {
        wxDataViewCtrl* list = wxDynamicCast(wxobject, wxDataViewCtrl);
        if (NULL == list) {
            // very very strange
            return;
        }
        size_t count = GetManager()->GetChildCount(wxobject);
        for (size_t i = 0; i < count; ++i) {
            wxObject* child = GetManager()->GetChild(wxobject, i);
            IObject* childObj = GetManager()->GetIObject(child);
            if (childObj->GetClassName() == _("dataViewColumn")) {
                if (childObj->GetPropertyAsString(_("type")) == _("Text")) {
                    auto* col = list->AppendTextColumn(
                      childObj->GetPropertyAsString(_("label")), childObj->GetPropertyAsInteger(_("model_column")),
                      static_cast<wxDataViewCellMode>(childObj->GetPropertyAsInteger(_("mode"))),
                      childObj->GetPropertyAsInteger(_("width")),
                      static_cast<wxAlignment>(childObj->GetPropertyAsInteger(_("align"))),
                      childObj->GetPropertyAsInteger(_("flags")));
                    if (!childObj->IsPropertyNull(_("ellipsize"))) {
                        col->GetRenderer()->EnableEllipsize(
                          static_cast<wxEllipsizeMode>(childObj->GetPropertyAsInteger(_("ellipsize"))));
                    }
                } else if (childObj->GetPropertyAsString(_("type")) == _("Toggle")) {
                    auto* col = list->AppendToggleColumn(
                      childObj->GetPropertyAsString(_("label")), childObj->GetPropertyAsInteger(_("model_column")),
                      static_cast<wxDataViewCellMode>(childObj->GetPropertyAsInteger(_("mode"))),
                      childObj->GetPropertyAsInteger(_("width")),
                      static_cast<wxAlignment>(childObj->GetPropertyAsInteger(_("align"))),
                      childObj->GetPropertyAsInteger(_("flags")));
                    if (!childObj->IsPropertyNull(_("ellipsize"))) {
                        col->GetRenderer()->EnableEllipsize(
                          static_cast<wxEllipsizeMode>(childObj->GetPropertyAsInteger(_("ellipsize"))));
                    }
                } else if (childObj->GetPropertyAsString(_("type")) == _("Progress")) {
                    auto* col = list->AppendProgressColumn(
                      childObj->GetPropertyAsString(_("label")), childObj->GetPropertyAsInteger(_("model_column")),
                      static_cast<wxDataViewCellMode>(childObj->GetPropertyAsInteger(_("mode"))),
                      childObj->GetPropertyAsInteger(_("width")),
                      static_cast<wxAlignment>(childObj->GetPropertyAsInteger(_("align"))),
                      childObj->GetPropertyAsInteger(_("flags")));
                    if (!childObj->IsPropertyNull(_("ellipsize"))) {
                        col->GetRenderer()->EnableEllipsize(
                          static_cast<wxEllipsizeMode>(childObj->GetPropertyAsInteger(_("ellipsize"))));
                    }
                } else if (childObj->GetPropertyAsString(_("type")) == _("IconText")) {
                    auto* col = list->AppendIconTextColumn(
                      childObj->GetPropertyAsString(_("label")), childObj->GetPropertyAsInteger(_("model_column")),
                      static_cast<wxDataViewCellMode>(childObj->GetPropertyAsInteger(_("mode"))),
                      childObj->GetPropertyAsInteger(_("width")),
                      static_cast<wxAlignment>(childObj->GetPropertyAsInteger(_("align"))),
                      childObj->GetPropertyAsInteger(_("flags")));
                    if (!childObj->IsPropertyNull(_("ellipsize"))) {
                        col->GetRenderer()->EnableEllipsize(
                          static_cast<wxEllipsizeMode>(childObj->GetPropertyAsInteger(_("ellipsize"))));
                    }
                } else if (childObj->GetPropertyAsString(_("type")) == _("Date")) {
                    auto* col = list->AppendDateColumn(
                      childObj->GetPropertyAsString(_("label")), childObj->GetPropertyAsInteger(_("model_column")),
                      static_cast<wxDataViewCellMode>(childObj->GetPropertyAsInteger(_("mode"))),
                      childObj->GetPropertyAsInteger(_("width")),
                      static_cast<wxAlignment>(childObj->GetPropertyAsInteger(_("align"))),
                      childObj->GetPropertyAsInteger(_("flags")));
                    if (!childObj->IsPropertyNull(_("ellipsize"))) {
                        col->GetRenderer()->EnableEllipsize(
                          static_cast<wxEllipsizeMode>(childObj->GetPropertyAsInteger(_("ellipsize"))));
                    }
                } else if (childObj->GetPropertyAsString(_("type")) == _("Bitmap")) {
                    auto* col = list->AppendBitmapColumn(
                      childObj->GetPropertyAsString(_("label")), childObj->GetPropertyAsInteger(_("model_column")),
                      static_cast<wxDataViewCellMode>(childObj->GetPropertyAsInteger(_("mode"))),
                      childObj->GetPropertyAsInteger(_("width")),
                      static_cast<wxAlignment>(childObj->GetPropertyAsInteger(_("align"))),
                      childObj->GetPropertyAsInteger(_("flags")));
                    if (!childObj->IsPropertyNull(_("ellipsize"))) {
                        col->GetRenderer()->EnableEllipsize(
                          static_cast<wxEllipsizeMode>(childObj->GetPropertyAsInteger(_("ellipsize"))));
                    }
                }
            }
        }
    }
};

class DataViewTreeCtrl : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxDataViewTreeCtrl* dataViewTreeCtrl = new wxDataViewTreeCtrl(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("window_style")));

        return dataViewTreeCtrl;
    }
};

class DataViewListCtrl : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxDataViewListCtrl* dataViewListCtrl = new wxDataViewListCtrl(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("window_style")));

        return dataViewListCtrl;
    }

    void OnCreated(wxObject* wxobject, wxWindow* /*wxparent*/) override
    {
        wxDataViewListCtrl* list = wxDynamicCast(wxobject, wxDataViewListCtrl);
        if (NULL == list) {
            // very very strange
            return;
        }
        size_t count = GetManager()->GetChildCount(wxobject);
        for (size_t i = 0; i < count; ++i) {
            wxObject* child = GetManager()->GetChild(wxobject, i);
            IObject* childObj = GetManager()->GetIObject(child);
            if (childObj->GetClassName() == _("dataViewListColumn")) {
                if (childObj->GetPropertyAsString(_("type")) == _("Text")) {
                    auto* col = list->AppendTextColumn(
                      childObj->GetPropertyAsString(_("label")),
                      static_cast<wxDataViewCellMode>(childObj->GetPropertyAsInteger(_("mode"))),
                      childObj->GetPropertyAsInteger(_("width")),
                      static_cast<wxAlignment>(childObj->GetPropertyAsInteger(_("align"))),
                      childObj->GetPropertyAsInteger(_("flags")));
                    if (!childObj->IsPropertyNull(_("ellipsize"))) {
                        col->GetRenderer()->EnableEllipsize(
                          static_cast<wxEllipsizeMode>(childObj->GetPropertyAsInteger(_("ellipsize"))));
                    }
                } else if (childObj->GetPropertyAsString(_("type")) == _("Toggle")) {
                    auto* col = list->AppendToggleColumn(
                      childObj->GetPropertyAsString(_("label")),
                      static_cast<wxDataViewCellMode>(childObj->GetPropertyAsInteger(_("mode"))),
                      childObj->GetPropertyAsInteger(_("width")),
                      static_cast<wxAlignment>(childObj->GetPropertyAsInteger(_("align"))),
                      childObj->GetPropertyAsInteger(_("flags")));
                    if (!childObj->IsPropertyNull(_("ellipsize"))) {
                        col->GetRenderer()->EnableEllipsize(
                          static_cast<wxEllipsizeMode>(childObj->GetPropertyAsInteger(_("ellipsize"))));
                    }
                } else if (childObj->GetPropertyAsString(_("type")) == _("Progress")) {
                    auto* col = list->AppendProgressColumn(
                      childObj->GetPropertyAsString(_("label")),
                      static_cast<wxDataViewCellMode>(childObj->GetPropertyAsInteger(_("mode"))),
                      childObj->GetPropertyAsInteger(_("width")),
                      static_cast<wxAlignment>(childObj->GetPropertyAsInteger(_("align"))),
                      childObj->GetPropertyAsInteger(_("flags")));
                    if (!childObj->IsPropertyNull(_("ellipsize"))) {
                        col->GetRenderer()->EnableEllipsize(
                          static_cast<wxEllipsizeMode>(childObj->GetPropertyAsInteger(_("ellipsize"))));
                    }
                } else if (childObj->GetPropertyAsString(_("type")) == _("IconText")) {
                    auto* col = list->AppendIconTextColumn(
                      childObj->GetPropertyAsString(_("label")),
                      static_cast<wxDataViewCellMode>(childObj->GetPropertyAsInteger(_("mode"))),
                      childObj->GetPropertyAsInteger(_("width")),
                      static_cast<wxAlignment>(childObj->GetPropertyAsInteger(_("align"))),
                      childObj->GetPropertyAsInteger(_("flags")));
                    if (!childObj->IsPropertyNull(_("ellipsize"))) {
                        col->GetRenderer()->EnableEllipsize(
                          static_cast<wxEllipsizeMode>(childObj->GetPropertyAsInteger(_("ellipsize"))));
                    }
                }
            }
        }
    }
};

class DataViewListColumn : public ComponentBase
{
};

class DataViewColumn : public ComponentBase
{
};

class wxcoreTreeListCtrlComponent : public ComponentBase
{
public:
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxTreeListCtrl* treeListCtrl = new wxTreeListCtrl(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        return treeListCtrl;
    }

    void OnCreated(wxObject* wxobject, wxWindow* /*wxparent*/) override
    {
        // initialize tree
        wxTreeListCtrl* treeListCtrl = wxDynamicCast(wxobject, wxTreeListCtrl);
        int colCount = treeListCtrl->GetColumnCount();

        // Check for columns
        if (0 == colCount) {
            return;
        }

        wxTreeListItem root = treeListCtrl->GetRootItem();

        wxTreeListItem treeListParent;
        wxTreeListItem treeListItem;
        int n = 0;

        // Build tree.
        treeListItem = treeListCtrl->AppendItem(root, wxString::Format(_("Item #%d"), ++n));
        FillItem(treeListCtrl, treeListItem, colCount, n);

        treeListParent = treeListItem;
        for (int i = 0; i < 5; ++i) {
            treeListItem = treeListCtrl->AppendItem(treeListParent, wxString::Format(_("Item #%d"), ++n));
            FillItem(treeListCtrl, treeListItem, colCount, n);
        }
        treeListCtrl->Expand(treeListParent);

        treeListParent = treeListItem;
        for (int i = 0; i < 5; ++i) {
            treeListItem = treeListCtrl->AppendItem(treeListParent, wxString::Format(_("Item #%d"), ++n));
            FillItem(treeListCtrl, treeListItem, colCount, n);
        }
        treeListCtrl->Expand(treeListParent);
    }

    void FillItem(wxTreeListCtrl* treeListCtrl, wxTreeListItem itemId, int colCount, int row)
    {
        for (int i = 0; i < colCount; ++i) {
            treeListCtrl->SetItemText(itemId, i, wxString::Format(_("Item #%d, column #%d"), row, i));
        }
    }
};

class wxcoreTreeListCtrlColumnComponent : public ComponentBase
{
public:
    void OnCreated(wxObject* wxobject, wxWindow* wxparent) override
    {
        IObject* obj = GetManager()->GetIObject(wxobject);
        wxTreeListCtrl* treeList = wxDynamicCast(wxparent, wxTreeListCtrl);

        if (!(obj && treeList)) {
            wxLogError(
              _("wxcoreTreeListCtrlColumnComponent is missing its wxFormBuilder object(%i) or its parent(%i)"), obj,
              treeList);
            return;
        }

        treeList->AppendColumn(
          obj->GetPropertyAsString(_("name")), obj->GetPropertyAsInteger(_("width")),
          static_cast<wxAlignment>(obj->GetPropertyAsInteger(_("alignment"))), obj->GetPropertyAsInteger(_("flag")));
    }

    void OnSelected(wxObject*) override {}
};

class RibbonBarComponent : public ComponentBase
{
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxRibbonBar* rb = new wxRibbonBar(
          (wxWindow*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        if (obj->GetPropertyAsString(_("theme")) == _("Default"))
            rb->SetArtProvider(new wxRibbonDefaultArtProvider);
        else if (obj->GetPropertyAsString(_("theme")) == _("Generic"))
            rb->SetArtProvider(new wxRibbonAUIArtProvider);
        else if (obj->GetPropertyAsString(_("theme")) == _("MSW"))
            rb->SetArtProvider(new wxRibbonMSWArtProvider);

        rb->PushEventHandler(new ComponentEvtHandler(rb, GetManager()));

        return rb;
    }

    void Cleanup(wxObject* obj) override
    {
        auto* window = wxDynamicCast(obj, wxRibbonBar);
        if (window) {
            window->PopEventHandler(true);
        }
    }

    void OnCreated(wxObject* wxobject, wxWindow* /*wxparent*/) override
    {
        wxRibbonBar* rb = wxDynamicCast(wxobject, wxRibbonBar);
        if (NULL == rb) {
            // very very strange
            return;
        }

        rb->Realize();
    }
};

class RibbonPageComponent : public ComponentBase
{
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxRibbonPage* rbpage = new wxRibbonPage(
          (wxRibbonBar*)parent, wxID_ANY, obj->GetPropertyAsString(_("label")), obj->GetPropertyAsBitmap(_("bitmap")),
          0);

        if (obj->GetPropertyAsInteger(_("select")) != 0) {
            ((wxRibbonBar*)parent)->SetActivePage(rbpage);
        }

        // rbpage->PushEventHandler( new ComponentEvtHandler( rbpage, GetManager() ) );

        return rbpage;
    }

    /*
    void Cleanup(wxObject* obj) override
    {
            auto* window = wxDynamicCast(obj, wxRibbonPage);
            if (window)
            {
                    window->PopEventHandler(true);
            }
    }
    */
};

class RibbonPanelComponent : public ComponentBase
{
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxRibbonPanel* rbp = new wxRibbonPanel(
          (wxRibbonPage*)parent, wxID_ANY, obj->GetPropertyAsString(_("label")), obj->GetPropertyAsBitmap(_("bitmap")),
          obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")),
          obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));

        // rbp->PushEventHandler( new ComponentEvtHandler( rbp, GetManager() ) );

        return rbp;
    }

    /*
    void Cleanup(wxObject* obj) override
    {
            auto* window = wxDynamicCast(obj, wxRibbonPanel);
            if (window)
            {
                    window->PopEventHandler(true);
            }
    }
    */
};

class RibbonButtonBarComponent : public ComponentBase
{
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxRibbonButtonBar* rbb = new wxRibbonButtonBar(
          (wxRibbonPanel*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")), 0);

        // rbb->PushEventHandler( new ComponentEvtHandler( rbb, GetManager() ) );

        return rbb;
    }

    /*
    void Cleanup(wxObject* obj) override
    {
            auto* window = wxDynamicCast(obj, wxRibbonButtonBar);
            if (window)
            {
                    window->PopEventHandler(true);
            }
    }
    */

    void OnCreated(wxObject* wxobject, wxWindow* /*wxparent*/) override
    {
        wxRibbonButtonBar* rb = wxDynamicCast(wxobject, wxRibbonButtonBar);
        if (NULL == rb) {
            // very very strange
            return;
        }

        size_t count = GetManager()->GetChildCount(wxobject);
        for (size_t i = 0; i < count; ++i) {
            wxObject* child = GetManager()->GetChild(wxobject, i);
            IObject* childObj = GetManager()->GetIObject(child);
            if (childObj->GetClassName() == wxT("ribbonButton")) {
                rb->AddButton(
                  wxID_ANY, childObj->GetPropertyAsString(_("label")), childObj->GetPropertyAsBitmap(_("bitmap")),
                  childObj->GetPropertyAsString(_("help")));
            } else if (childObj->GetClassName() == wxT("ribbonDropdownButton")) {
                rb->AddDropdownButton(
                  wxID_ANY, childObj->GetPropertyAsString(_("label")), childObj->GetPropertyAsBitmap(_("bitmap")),
                  childObj->GetPropertyAsString(_("help")));
            } else if (childObj->GetClassName() == wxT("ribbonHybridButton")) {
                rb->AddHybridButton(
                  wxID_ANY, childObj->GetPropertyAsString(_("label")), childObj->GetPropertyAsBitmap(_("bitmap")),
                  childObj->GetPropertyAsString(_("help")));
            } else if (childObj->GetClassName() == wxT("ribbonToggleButton")) {
                rb->AddToggleButton(
                  wxID_ANY, childObj->GetPropertyAsString(_("label")), childObj->GetPropertyAsBitmap(_("bitmap")),
                  childObj->GetPropertyAsString(_("help")));
            }
        }
    }
};

class RibbonButtonComponent : public ComponentBase
{
};
class RibbonDropdownButtonComponent : public ComponentBase
{
};
class RibbonHybridButtonComponent : public ComponentBase
{
};
class RibbonToggleButtonComponent : public ComponentBase
{
};

class RibbonToolBarComponent : public ComponentBase
{
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxRibbonToolBar* rbb = new wxRibbonToolBar(
          (wxRibbonPanel*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")), 0);

        // rbb->PushEventHandler( new ComponentEvtHandler( rbb, GetManager() ) );

        return rbb;
    }

    /*
    void Cleanup(wxObject* obj) override
    {
            auto* window = wxDynamicCast(obj, wxRibbonToolBar);
            if (window)
            {
                    window->PopEventHandler(true);
            }
    }
    */

    void OnCreated(wxObject* wxobject, wxWindow* /*wxparent*/) override
    {
        wxRibbonToolBar* rb = wxDynamicCast(wxobject, wxRibbonToolBar);
        if (NULL == rb) {
            // very very strange
            return;
        }

        size_t count = GetManager()->GetChildCount(wxobject);
        for (size_t i = 0; i < count; ++i) {
            wxObject* child = GetManager()->GetChild(wxobject, i);
            IObject* childObj = GetManager()->GetIObject(child);
            if (wxT("ribbonTool") == childObj->GetClassName()) {
                rb->AddTool(
                  wxID_ANY, childObj->GetPropertyAsBitmap(_("bitmap")), childObj->GetPropertyAsString(_("help")));
            } else if (wxT("ribbonDropdownTool") == childObj->GetClassName()) {
                rb->AddDropdownTool(
                  wxID_ANY, childObj->GetPropertyAsBitmap(_("bitmap")), childObj->GetPropertyAsString(_("help")));
            } else if (wxT("ribbonHybridTool") == childObj->GetClassName()) {
                rb->AddHybridTool(
                  wxID_ANY, childObj->GetPropertyAsBitmap(_("bitmap")), childObj->GetPropertyAsString(_("help")));
            } else if (wxT("ribbonToggleTool") == childObj->GetClassName()) {
                rb->AddToggleTool(
                  wxID_ANY, childObj->GetPropertyAsBitmap(_("bitmap")), childObj->GetPropertyAsString(_("help")));
            }
        }
    }
};

class RibbonToolComponent : public ComponentBase
{
};

class RibbonDropdownToolComponent : public ComponentBase
{
};

class RibbonHybridToolComponent : public ComponentBase
{
};

class RibbonToggleToolComponent : public ComponentBase
{
};

class RibbonGalleryComponent : public ComponentBase
{
    wxObject* Create(IObject* obj, wxObject* parent) override
    {
        wxRibbonGallery* ribbonGallery = new wxRibbonGallery(
          (wxRibbonPanel*)parent, wxID_ANY, obj->GetPropertyAsPoint(_("pos")), obj->GetPropertyAsSize(_("size")), 0);

        // ribbonGallery->PushEventHandler( new ComponentEvtHandler( ribbonGallery, GetManager() ) );

        return ribbonGallery;
    }

    /*
    void Cleanup(wxObject* obj) override
    {
            auto* window = wxDynamicCast(obj, wxRibbonGallery);
            if (window)
            {
                    window->PopEventHandler(true);
            }
    }
    */

    void OnCreated(wxObject* wxobject, wxWindow* /*wxparent*/) override
    {
        wxRibbonGallery* rg = wxDynamicCast(wxobject, wxRibbonGallery);
        if (NULL == rg) {
            // very very strange
            return;
        }

        size_t count = GetManager()->GetChildCount(wxobject);
        for (size_t i = 0; i < count; ++i) {
            wxObject* child = GetManager()->GetChild(wxobject, i);
            IObject* childObj = GetManager()->GetIObject(child);
            if (wxT("ribbonGalleryItem") == childObj->GetClassName()) {
                rg->Append(childObj->GetPropertyAsBitmap(_("bitmap")), wxID_ANY);
            }
        }
    }
};

class RibbonGalleryItemComponent : public ComponentBase
{
};

///////////////////////////////////////////////////////////////////////////////

BEGIN_LIBRARY()

WINDOW_COMPONENT("wxCalendarCtrl", CalendarCtrlComponent)
WINDOW_COMPONENT("wxDatePickerCtrl", DatePickerCtrlComponent)
WINDOW_COMPONENT("wxTimePickerCtrl", TimePickerCtrlComponent)
WINDOW_COMPONENT("wxHtmlWindow", HtmlWindowComponent)
WINDOW_COMPONENT("wxToggleButton", ToggleButtonComponent)
WINDOW_COMPONENT("wxBitmapToggleButton", BitmapToggleButtonComponent)
WINDOW_COMPONENT("wxTreeCtrl", TreeCtrlComponent)
WINDOW_COMPONENT("wxGrid", GridComponent)
WINDOW_COMPONENT("wxScrollBar", ScrollBarComponent)
WINDOW_COMPONENT("wxSpinCtrl", SpinCtrlComponent)
WINDOW_COMPONENT("wxSpinButton", SpinButtonComponent)
WINDOW_COMPONENT("CustomControl", CustomControlComponent)
ABSTRACT_COMPONENT("CustomCode", CustomCodeComponent)
WINDOW_COMPONENT("wxDataViewCtrl", DataViewCtrl)
WINDOW_COMPONENT("wxDataViewTreeCtrl", DataViewTreeCtrl)
WINDOW_COMPONENT("wxDataViewListCtrl", DataViewListCtrl)
ABSTRACT_COMPONENT("dataViewListColumn", DataViewListColumn)
ABSTRACT_COMPONENT("dataViewColumn", DataViewColumn)
WINDOW_COMPONENT("wxRibbonBar", RibbonBarComponent)
WINDOW_COMPONENT("wxRibbonPage", RibbonPageComponent)
WINDOW_COMPONENT("wxRibbonPanel", RibbonPanelComponent)
WINDOW_COMPONENT("wxRibbonButtonBar", RibbonButtonBarComponent)
WINDOW_COMPONENT("wxRibbonToolBar", RibbonToolBarComponent)
WINDOW_COMPONENT("wxRibbonGallery", RibbonGalleryComponent)
ABSTRACT_COMPONENT("ribbonButton", RibbonButtonComponent)
ABSTRACT_COMPONENT("ribbonDropdownButton", RibbonDropdownButtonComponent)
ABSTRACT_COMPONENT("ribbonHybridButton", RibbonHybridButtonComponent)
ABSTRACT_COMPONENT("ribbonToggleButton", RibbonToggleButtonComponent)
ABSTRACT_COMPONENT("ribbonTool", RibbonToolComponent)
ABSTRACT_COMPONENT("ribbonDropdownTool", RibbonDropdownToolComponent)
ABSTRACT_COMPONENT("ribbonHybridTool", RibbonHybridToolComponent)
ABSTRACT_COMPONENT("ribbonToggleTool", RibbonToggleToolComponent)
ABSTRACT_COMPONENT("ribbonGalleryItem", RibbonGalleryItemComponent)

// wxCheckListBox
WINDOW_COMPONENT("wxCheckListBox", CheckListBoxComponent)

#ifdef USE_MEDIACTRL
WINDOW_COMPONENT("wxMediaCtrl", MediaCtrlComponent)
#endif

// wxRichTextCtrl
WINDOW_COMPONENT("wxRichTextCtrl", RichTextCtrlComponent)
MACRO(wxTE_PROCESS_ENTER);
MACRO(wxTE_PROCESS_TAB);
MACRO(wxTE_READONLY);
MACRO(wxTE_AUTO_URL);

// wxColourPickerCtrl
WINDOW_COMPONENT("wxColourPickerCtrl", ColourPickerComponent)
MACRO(wxCLRP_DEFAULT_STYLE)
MACRO(wxCLRP_USE_TEXTCTRL)
MACRO(wxCLRP_SHOW_LABEL)

// wxFontPickerCtrl
WINDOW_COMPONENT("wxFontPickerCtrl", FontPickerComponent)
MACRO(wxFNTP_DEFAULT_STYLE)
MACRO(wxFNTP_USE_TEXTCTRL)
MACRO(wxFNTP_FONTDESC_AS_LABEL)
MACRO(wxFNTP_USEFONT_FOR_LABEL)

// wxFilePickerCtrl
WINDOW_COMPONENT("wxFilePickerCtrl", FilePickerComponent)
MACRO(wxFLP_DEFAULT_STYLE)
MACRO(wxFLP_USE_TEXTCTRL)
MACRO(wxFLP_OPEN)
MACRO(wxFLP_SAVE)
MACRO(wxFLP_OVERWRITE_PROMPT)
MACRO(wxFLP_FILE_MUST_EXIST)
MACRO(wxFLP_CHANGE_DIR)
MACRO(wxFLP_SMALL)

// wxDirPickerCtrl
WINDOW_COMPONENT("wxDirPickerCtrl", DirPickerComponent)
MACRO(wxDIRP_DEFAULT_STYLE)
MACRO(wxDIRP_USE_TEXTCTRL)
MACRO(wxDIRP_DIR_MUST_EXIST)
MACRO(wxDIRP_CHANGE_DIR)
MACRO(wxDIRP_SMALL)

// wxHyperlinkCtrl
WINDOW_COMPONENT("wxHyperlinkCtrl", HyperlinkComponent)
MACRO(wxHL_ALIGN_LEFT)
MACRO(wxHL_ALIGN_RIGHT)
MACRO(wxHL_ALIGN_CENTRE)
MACRO(wxHL_CONTEXTMENU)
MACRO(wxHL_DEFAULT_STYLE)

// wxSearchCtrl
WINDOW_COMPONENT("wxSearchCtrl", SearchCtrlComponent)
MACRO(wxTE_PROCESS_ENTER);
MACRO(wxTE_PROCESS_TAB);
MACRO(wxTE_NOHIDESEL);
MACRO(wxTE_LEFT);
MACRO(wxTE_CENTER);
MACRO(wxTE_RIGHT);
MACRO(wxTE_CAPITALIZE);

WINDOW_COMPONENT("wxSpinCtrlDouble", SpinCtrlDoubleComponent)

// wxCalendarCtrl
MACRO(wxCAL_SUNDAY_FIRST)
MACRO(wxCAL_MONDAY_FIRST)
MACRO(wxCAL_SHOW_HOLIDAYS)
MACRO(wxCAL_NO_YEAR_CHANGE)
MACRO(wxCAL_NO_MONTH_CHANGE)
MACRO(wxCAL_SHOW_SURROUNDING_WEEKS)
MACRO(wxCAL_SEQUENTIAL_MONTH_SELECTION)
MACRO(wxCAL_SHOW_WEEK_NUMBERS)

// wxDatePickerCtrl
MACRO(wxDP_SPIN)
MACRO(wxDP_DROPDOWN)
MACRO(wxDP_SHOWCENTURY)
MACRO(wxDP_ALLOWNONE)
MACRO(wxDP_DEFAULT)

// wxTimePickerCtrl
MACRO(wxTP_DEFAULT)

// wxHtmlWindow
MACRO(wxHW_SCROLLBAR_NEVER)
MACRO(wxHW_SCROLLBAR_AUTO)
MACRO(wxHW_NO_SELECTION)

// wxTreeCtrl
MACRO(wxTR_EDIT_LABELS)
MACRO(wxTR_NO_BUTTONS)
MACRO(wxTR_HAS_BUTTONS)
MACRO(wxTR_TWIST_BUTTONS)
MACRO(wxTR_NO_LINES)
MACRO(wxTR_FULL_ROW_HIGHLIGHT)
MACRO(wxTR_LINES_AT_ROOT)
MACRO(wxTR_HIDE_ROOT)
MACRO(wxTR_ROW_LINES)
MACRO(wxTR_HAS_VARIABLE_ROW_HEIGHT)
MACRO(wxTR_SINGLE)
MACRO(wxTR_MULTIPLE)
MACRO(wxTR_DEFAULT_STYLE)

// wxGrid
MACRO(wxALIGN_LEFT)
MACRO(wxALIGN_CENTER)
MACRO(wxALIGN_RIGHT)
MACRO(wxALIGN_TOP)
MACRO(wxALIGN_BOTTOM)
MACRO(wxGRID_AUTOSIZE)

// wxScrollBar
MACRO(wxSB_HORIZONTAL)
MACRO(wxSB_VERTICAL)

// wxSpinCtrl and wxSpinButton
MACRO(wxSP_ARROW_KEYS)
MACRO(wxSP_WRAP)
MACRO(wxSP_HORIZONTAL)
MACRO(wxSP_VERTICAL)

// wxGenericDirCtrl
WINDOW_COMPONENT("wxGenericDirCtrl", GenericDirCtrlComponent)
MACRO(wxDIRCTRL_DIR_ONLY)
MACRO(wxDIRCTRL_3D_INTERNAL)
MACRO(wxDIRCTRL_SELECT_FIRST)
MACRO(wxDIRCTRL_EDIT_LABELS)
MACRO(wxDIRCTRL_MULTIPLE)

// wxTimer
ABSTRACT_COMPONENT("wxTimer", TimerComponent)

// wxPropertyGrid
WINDOW_COMPONENT("wxPropertyGrid", PropertyGridComponent)
ABSTRACT_COMPONENT("propGridItem", PropertyGridItemComponent)
MACRO(wxPG_DEFAULT_STYLE)
MACRO(wxPG_AUTO_SORT)
MACRO(wxPG_HIDE_CATEGORIES)
MACRO(wxPG_ALPHABETIC_MODE)
MACRO(wxPG_BOLD_MODIFIED)
MACRO(wxPG_SPLITTER_AUTO_CENTER)
MACRO(wxPG_TOOLTIPS)
MACRO(wxPG_HIDE_MARGIN)
MACRO(wxPG_STATIC_SPLITTER)
MACRO(wxPG_STATIC_LAYOUT)
MACRO(wxPG_LIMITED_EDITING)
MACRO(wxPG_EX_INIT_NOCAT)
MACRO(wxPG_EX_HELP_AS_TOOLTIPS)
MACRO(wxPG_EX_NATIVE_DOUBLE_BUFFERING)
MACRO(wxPG_EX_AUTO_UNSPECIFIED_VALUES)
MACRO(wxPG_EX_WRITEONLY_BUILTIN_ATTRIBUTES)
MACRO(wxPG_EX_MULTIPLE_SELECTION)
MACRO(wxPG_EX_ENABLE_TLP_TRACKING)

// wxPropertyGridManager
WINDOW_COMPONENT("wxPropertyGridManager", PropertyGridManagerComponent)
ABSTRACT_COMPONENT("propGridPage", PropertyGridPageComponent)
MACRO(wxPG_EX_NO_FLAT_TOOLBAR)
MACRO(wxPG_EX_MODE_BUTTONS)
MACRO(wxPG_EX_HIDE_PAGE_BUTTONS)
MACRO(wxPG_EX_NO_TOOLBAR_DIVIDER)
MACRO(wxPG_EX_TOOLBAR_SEPARATOR)
MACRO(wxPGMAN_DEFAULT_STYLE)
MACRO(wxPG_DESCRIPTION)
MACRO(wxPG_TOOLBAR)
MACRO(wxPG_NO_INTERNAL_BORDER)

// wxStyledTextCtrl
WINDOW_COMPONENT("wxStyledTextCtrl", StyledTextComponent)

// wxDataViewCtrl
MACRO(wxDV_SINGLE)
MACRO(wxDV_MULTIPLE)
MACRO(wxDV_ROW_LINES)
MACRO(wxDV_HORIZ_RULES)
MACRO(wxDV_VERT_RULES)
MACRO(wxDV_VARIABLE_LINE_HEIGHT)
MACRO(wxDV_NO_HEADER)

MACRO(wxDATAVIEW_CELL_INERT)
MACRO(wxDATAVIEW_CELL_ACTIVATABLE)
MACRO(wxDATAVIEW_CELL_EDITABLE)

MACRO(wxDATAVIEW_COL_RESIZABLE)
MACRO(wxDATAVIEW_COL_SORTABLE)
MACRO(wxDATAVIEW_COL_REORDERABLE)
MACRO(wxDATAVIEW_COL_HIDDEN)

MACRO(wxELLIPSIZE_NONE)
MACRO(wxELLIPSIZE_START)
MACRO(wxELLIPSIZE_MIDDLE)
MACRO(wxELLIPSIZE_END)

MACRO(wxALIGN_LEFT)
MACRO(wxALIGN_TOP)
MACRO(wxALIGN_RIGHT)
MACRO(wxALIGN_BOTTOM)
MACRO(wxALIGN_CENTER)
MACRO(wxALIGN_CENTER_HORIZONTAL)
MACRO(wxALIGN_CENTER_VERTICAL)

// wxRibbonBar
MACRO(wxRIBBON_BAR_DEFAULT_STYLE)
MACRO(wxRIBBON_BAR_FOLDBAR_STYLE)
MACRO(wxRIBBON_BAR_SHOW_PAGE_LABELS)
MACRO(wxRIBBON_BAR_SHOW_PAGE_ICONS)
MACRO(wxRIBBON_BAR_FLOW_HORIZONTAL)
MACRO(wxRIBBON_BAR_FLOW_VERTICAL)
MACRO(wxRIBBON_BAR_SHOW_PANEL_EXT_BUTTONS)
MACRO(wxRIBBON_BAR_SHOW_PANEL_MINIMISE_BUTTONS)
MACRO(wxRIBBON_BAR_SHOW_TOGGLE_BUTTON)
MACRO(wxRIBBON_BAR_SHOW_HELP_BUTTON)

// wxRibbonPanel
MACRO(wxRIBBON_PANEL_DEFAULT_STYLE)
MACRO(wxRIBBON_PANEL_NO_AUTO_MINIMISE)
MACRO(wxRIBBON_PANEL_EXT_BUTTON)
MACRO(wxRIBBON_PANEL_MINIMISE_BUTTON)
MACRO(wxRIBBON_PANEL_STRETCH)
MACRO(wxRIBBON_PANEL_FLEXIBLE)

// wxTreeListCtrl
WINDOW_COMPONENT("wxTreeListCtrl", wxcoreTreeListCtrlComponent)
MACRO(wxTL_SINGLE)
MACRO(wxTL_MULTIPLE)
MACRO(wxTL_CHECKBOX)
MACRO(wxTL_3STATE)
MACRO(wxTL_USER_3STATE)
MACRO(wxTR_DEFAULT_STYLE)

ABSTRACT_COMPONENT("wxTreeListCtrlColumn", wxcoreTreeListCtrlColumnComponent)
MACRO(wxCOL_RESIZABLE)
MACRO(wxCOL_SORTABLE)
MACRO(wxCOL_REORDERABLE)
MACRO(wxCOL_HIDDEN)

END_LIBRARY()
