-----------------------------------------------------------------------------
-- Name:        controls.wx.lua
-- Purpose:     Controls wxLua sample
-- Author:      John Labenski
-- Modified by:
-- Created:     6/19/2007
-- RCS-ID:
-- Copyright:   (c) 2007 John Labenski
-- Licence:     wxWidgets licence
-----------------------------------------------------------------------------

-- Load the wxLua module, does nothing if running from wxLua, wxLuaFreeze, or wxLuaEdit
package.cpath = package.cpath..";./?.dll;./?.so;../lib/?.so;../lib/vc_dll/?.dll;../lib/bcc_dll/?.dll;../lib/mingw_dll/?.dll;"
require("wx")

frame       = nil
textCtrl    = nil
taskbarIcon = nil -- The wxTaskBarIcon that we install

wxwindowClassInfo = wx.wxClassInfo("wxWindow")

-- wxBitmap to use for controls that need one
bmp = wx.wxArtProvider.GetBitmap(wx.wxART_INFORMATION, wx.wxART_TOOLBAR, wx.wxSize(16, 16))

-- wxImageList for any controls that need them
imageList = wx.wxImageList(16, 16)
imageList:Add(wx.wxArtProvider.GetBitmap(wx.wxART_NORMAL_FILE, wx.wxART_TOOLBAR, wx.wxSize(16, 16)))
imageList:Add(wx.wxArtProvider.GetBitmap(wx.wxART_FOLDER, wx.wxART_TOOLBAR, wx.wxSize(16, 16)))
imageList:Add(wx.wxArtProvider.GetBitmap(wx.wxART_PRINT, wx.wxART_TOOLBAR, wx.wxSize(16, 16)))
imageList:Add(wx.wxArtProvider.GetBitmap(wx.wxART_FLOPPY, wx.wxART_TOOLBAR, wx.wxSize(16, 16)))
colorList = { wx.wxColour(255, 100, 100), wx.wxColour(100, 100, 255), wx.wxColour(100, 255, 100), wx.wxWHITE }

-- wxImageList for the wxListCtrl that shows the events
listImageList = wx.wxImageList(16, 16)
listImageList:Add(wx.wxArtProvider.GetBitmap(wx.wxART_CROSS_MARK, wx.wxART_TOOLBAR, wx.wxSize(16, 16)))
listImageList:Add(wx.wxArtProvider.GetBitmap(wx.wxART_TICK_MARK, wx.wxART_TOOLBAR, wx.wxSize(16, 16)))
listImageList:Add(wx.wxArtProvider.GetBitmap(wx.wxART_ERROR, wx.wxART_TOOLBAR, wx.wxSize(16, 16)))

bindingList = wxlua.GetBindings() -- Table of {wxLuaBinding functions}

controlTable   = {} -- Table of { win_id = "win name" }
ignoreControls = {} -- Table of { win_id = "win name" } of controls to ignore events from

-- IDs for controls used by this program
ID_EVENT_LISTCTRL   = 5000
ID_CONTROL_LISTCTRL = 5001
ID_CLEAR_LOG        = 5002

-- IDs for the windows that we show
ID_PARENT_SCROLLEDWINDOW = 1000

ID_ANIMATIONCTRL    = 1001
ID_BITMAPBUTTON     = 1002
ID_BITMAPCOMBOBOX   = 1003
ID_BUTTON           = 1004
ID_CALENDARCTRL     = 1005
ID_CHECKBOX         = 1006
ID_CHECKLISTBOX     = 1007
ID_CHOICE           = 1008
ID_CHOICEBOOK       = 1009
ID_COLLAPSIBLEPANE  = 1010
ID_COMBOBOX         = 1011
ID_CONTROL          = 1012
ID_DIRPICKERCTRL    = 1013
ID_FILEPICKERCTRL   = 1014
ID_FONTPICKERCTRL   = 1015
ID_GAUGE            = 1016
ID_GENERICDIRCTRL   = 1017
ID_GRID             = 1018
ID_HYPERLINKCTRL    = 1019
ID_LISTBOX          = 1020
ID_LISTBOOK         = 1021
ID_LISTCTRL         = 1022
ID_NOTEBOOK         = 1023
ID_PANEL            = 1024
ID_RADIOBOX         = 1025
ID_RADIOBUTTON      = 1026
ID_SASHLAYOUTWINDOW = 1027
ID_SASHWINDOW       = 1028
ID_SCROLLBAR        = 1029
ID_SCROLLEDWINDOW   = 1030
ID_SLIDER           = 1031
ID_SPINBUTTON       = 1032
ID_SPINCTRL         = 1033
ID_SPLITTERWINDOW   = 1034
ID_STATICBITMAP     = 1035
ID_STATICBOX        = 1036
ID_STATICLINE       = 1037
ID_STATICTEXT       = 1038
ID_TEXTCTRL         = 1039
ID_TOGGLEBUTTON     = 1040
ID_TOOLBAR          = 1041
ID_TOOLBOOK         = 1042
ID_TREEBOOK         = 1043
ID_TREECTRL         = 1044
ID_WINDOW           = 1045
ID_WEBVIEW          = 1046

-- ---------------------------------------------------------------------------
-- Gather up some data from the bindings
-- ---------------------------------------------------------------------------

wxLuaBinding_wx = nil

do
    local bindTable = wxlua.GetBindings()
    for n = 1, #bindTable do
        if bindTable[n].name == "wx" then
            wxLuaBinding_wx = bindTable[n].binding
            break
        end
    end
end


-- Turn the array from the binding into a lookup table by event type
wxEVT_Array = bindingList[1].GetEventArray
for i = 2, #bindingList do
    local evtArr = bindingList[i].GetEventArray
    for j = 1, #evtArr do
        table.insert(wxEVT_Array, evtArr[j])
    end
end

wxEVT_List  = {}
wxEVT_TableByType = {}
for i = 1, #wxEVT_Array do
    wxEVT_TableByType[wxEVT_Array[i].eventType] = wxEVT_Array[i]
    table.insert(wxEVT_List, {wxlua.typename(wxEVT_Array[i].wxluatype), wxEVT_Array[i].name})
end
table.sort(wxEVT_List, function(t1, t2) return t1[1] > t2[1] end)

-- Turn the array from the binding into a lookup table by class name
wxCLASS_Array = bindingList[1].GetClassArray
for i = 2, #bindingList do
    local classArr = bindingList[i].GetClassArray
    for j = 1, #classArr do
        table.insert(wxCLASS_Array, classArr[j])
    end
end

wxCLASS_TableByName = {}
for i = 1, #wxCLASS_Array do
    wxCLASS_TableByName[wxCLASS_Array[i].name] = wxCLASS_Array[i]
end

-- ---------------------------------------------------------------------------
-- wxEventTypes that we don't want to initially handle
-- ---------------------------------------------------------------------------

ignoreEVTs = {
    ["wxEVT_CREATE"]           = true,
    ["wxEVT_DESTROY"]          = true,
    ["wxEVT_ENTER_WINDOW"]     = true,
    ["wxEVT_ERASE_BACKGROUND"] = true,
    ["wxEVT_IDLE"]             = true,
    ["wxEVT_LEAVE_WINDOW"]     = true,
    ["wxEVT_LEFT_DOWN"]        = true,
    ["wxEVT_LEFT_UP"]          = true,
    ["wxEVT_MOTION"]           = true,
    ["wxEVT_MOVE"]             = true,
    ["wxEVT_PAINT"]            = true,
    ["wxEVT_RIGHT_DOWN"]       = true,
    ["wxEVT_RIGHT_UP"]         = true,
    ["wxEVT_SET_CURSOR"]       = true,
    ["wxEVT_SHOW"]             = true,
    ["wxEVT_SIZE"]             = true,
    ["wxEVT_TIMER"]            = true,
    ["wxEVT_UPDATE_UI"]        = true,
}

-- ---------------------------------------------------------------------------
-- wxEventTypes that we shouldn't ever handle because they cause problems
-- ---------------------------------------------------------------------------

skipEVTs = {
    ["wxEVT_PAINT"] = true, -- controls don't redraw if we connect to this in MSW, even if we Skip() it
}

-- ---------------------------------------------------------------------------
-- All wxEvent derived classes and their GetXXX functions (none modify event)
-- ---------------------------------------------------------------------------

function OnSplitterEvent(event)
    -- asserts if these are called inappropriately
    -- {"GetSashPosition", "GetX", "GetY", "GetWindowBeingRemoved"}
    local typ = event:GetEventType()
    local s = ""

    if (typ == wx.wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING) or (typ == wx.wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED) then
        s = s.."GetSashPosition="..tostring(event:GetSashPosition())
    end
    if (typ == wx.wxEVT_COMMAND_SPLITTER_DOUBLECLICKED) then
        s = s.." GetX="..tostring(event:GetX())
        s = s.." GetY="..tostring(event:GetY())
    end
    if (typ == wx.wxEVT_COMMAND_SPLITTER_UNSPLIT) then
        s = s.." GetWindowBeingRemoved="..tostring(event:GetWindowBeingRemoved())
    end

    return s
end

wxEvent_GetFuncs = {
    ["wxEvent"]                    = {"GetEventType", "GetId", "GetSkipped", "GetTimestamp", "IsCommandEvent", "ShouldPropagate"},
    ["wxActivateEvent"]            = {"GetActive"},
    ["wxBookCtrlBaseEvent"]        = {"GetOldSelection", "GetSelection"},
    ["wxCalculateLayoutEvent"]     = {"GetFlags", "GetRect"},
    ["wxCalendarEvent"]            = {"GetWeekDay"},
    ["wxChildFocusEvent"]          = {"GetWindow"},
    ["wxChoicebookEvent"]          = {},
    ["wxClipboardTextEvent"]       = {},
    ["wxCloseEvent"]               = {"CanVeto", "GetLoggingOff"},
    ["wxCollapsiblePaneEvent"]     = {"GetCollapsed"},
    ["wxColourPickerEvent"]        = {"GetColour"},
    ["wxCommandEvent"]             = {"GetExtraLong", "GetInt", "GetSelection", "GetString", "IsChecked", "IsSelection"},
    ["wxContextMenuEvent"]         = {"GetPosition"},
    ["wxDateEvent"]                = {"GetDate"},
    ["wxDisplayChangedEvent"]      = {"GetPosition", "GetNumberOfFiles", "GetFiles"},
    ["wxDropFilesEvent"]           = {},
    ["wxEraseEvent"]               = {},
    ["wxFileDirPickerEvent"]       = {"GetPath"},
    ["wxFindDialogEvent"]          = {"GetFlags", "GetFindString", "GetReplaceString", "GetDialog"},
    ["wxFocusEvent"]               = {"GetWindow"},
    ["wxFontPickerEvent"]          = {"GetFont"},
    ["wxGridEditorCreatedEvent"]   = {"GetRow", "GetCol", "GetControl"},
    ["wxGridEvent"]                = {"GetRow", "GetCol", "GetPosition", "Selecting", "ControlDown", "MetaDown", "ShiftDown", "AltDown"},
    ["wxGridRangeSelectEvent"]     = {"GetTopRow", "GetBottomRow", "GetLeftCol", "GetRightCol", "Selecting", "ControlDown", "MetaDown", "ShiftDown", "AltDown"},
    ["wxGridSizeEvent"]            = {"GetRowOrCol", "GetPosition", "ControlDown", "MetaDown", "ShiftDown", "AltDown"},
    ["wxHelpEvent"]                = {"GetLink", "GetPosition", "GetTarget", "GetOrigin"},
    ["wxHyperlinkEvent"]           = {"GetURL"},
    ["wxIconizeEvent"]             = {"Iconized"},
    ["wxIdleEvent"]                = {"GetMode", "MoreRequested"},
    ["wxInitDialogEvent"]          = {},
    ["wxJoystickEvent"]            = nil,
    ["wxKeyEvent"]                 = {"AltDown", "CmdDown", "ControlDown", "MetaDown", "ShiftDown", "HasModifiers", "GetModifiers", "GetKeyCode", "GetPosition"},
    ["wxListbookEvent"]            = {},
    ["wxListEvent"]                = {"GetKeyCode", "GetIndex", "GetColumn", "GetPoint", "GetLabel", "GetText", "GetImage", "GetData", "GetMask", "GetItem", "IsEditCancelled"}, -- "GetCacheFrom", "GetCacheTo", FIXME? do we want these?
    ["wxLuaHtmlWinTagEvent"]       = {"GetHtmlTag", "GetHtmlParser", "GetParseInnerCalled"},
    ["wxMaximizeEvent"]            = {},
    ["wxMenuEvent"]                = {"GetMenuId", "IsPopup", "GetMenu"},
    ["wxMouseCaptureChangedEvent"] = {"GetCapturedWindow"},
    ["wxMouseCaptureLostEvent"]    = {},
    ["wxMouseEvent"]               = {"GetPosition", "AltDown", "ButtonDClick", "ButtonDown", "ButtonUp", "CmdDown", "ControlDown", "Dragging", "Entering"},
    ["wxMoveEvent"]                = {"GetPosition"},
    ["wxNavigationKeyEvent"]       = {"GetDirection", "IsWindowChange", "IsFromTab", "GetCurrentFocus"},
    ["wxNotebookEvent"]            = {},
    ["wxNotifyEvent"]              = {"IsAllowed"},
    ["wxPaintEvent"]               = {},
    ["wxPaletteChangedEvent"]      = {"GetChangedWindow"},
    ["wxProcessEvent"]             = nil,
    ["wxQueryLayoutInfoEvent"]     = {"GetAlignment", "GetFlags", "GetOrientation", "GetRequestedLength", "GetSize"},
    ["wxQueryNewPaletteEvent"]     = {"GetPaletteRealized"},
    ["wxSashEvent"]                = {"GetEdge", "GetDragRect", "GetDragStatus"},
    ["wxScrollEvent"]              = {"GetOrientation", "GetPosition"},
    ["wxScrollWinEvent"]           = {"GetOrientation", "GetPosition"},
    ["wxSetCursorEvent"]           = {"GetX", "GetY", "HasCursor"},
    ["wxShowEvent"]                = {"GetShow"},
    ["wxSizeEvent"]                = {"GetSize"},
    ["wxSocketEvent"]              = nil,
    ["wxSpinEvent"]                = {"GetPosition"},
    ["wxSplitterEvent"]            = OnSplitterEvent, -- {"GetSashPosition", "GetX", "GetY", "GetWindowBeingRemoved"} asserts if these are called inappropriately
    ["wxSysColourChangedEvent"]    = {},
    ["wxTaskBarIconEvent"]         = {},
    ["wxTimerEvent"]               = {"GetInterval"},
    ["wxToolbookEvent"]            = {},
    ["wxTreebookEvent"]            = {},
    ["wxTreeEvent"]                = {"GetKeyCode", "GetItem", "GetOldItem", "GetLabel", "GetPoint", "IsEditCancelled"},
    ["wxUpdateUIEvent"]            = {"GetText", "GetChecked", "GetEnabled", "GetShown", "GetSetChecked", "GetSetEnabled", "GetSetShown", "GetSetText"},
    ["wxWebViewEvent"]             = {"GetURL", "GetTarget"},
    ["wxWindowCreateEvent"]        = {"GetWindow"},
    ["wxWindowDestroyEvent"]       = {"GetWindow"},
    ["wxWizardEvent"]              = {"GetDirection", "GetPage"}
}

-- ---------------------------------------------------------------------------
-- Format the values you can get from different event types
-- ---------------------------------------------------------------------------

function FuncsToString(event, funcTable, evtClassName)
    local t = {}

    for n = 1, #funcTable do
        local v = event[funcTable[n]](event) -- each item is a function name

        local s = funcTable[n].."="

        local typ_name, typ = wxlua.type(v)

        if typ == wxlua.WXLUA_TSTRING then
            s = s.."'"..tostring(v).."'"
        elseif typ == wxlua.WXLUA_TTABLE then
            s = s.."("..table.concat(v, ",")..")"
        elseif typ <= wxlua.WXLUA_T_MAX then -- the rest of generic lua types
            s = s..tostring(v)
        elseif typ_name == "wxPoint" then
            s = s..string.format("(%d, %d) ", v:GetX(), v:GetY())
        elseif typ_name == "wxSize" then
            s = s..string.format("(%d, %d) ", v:GetWidth(), v:GetHeight())
        elseif typ_name == "wxRect" then
            s = s..string.format("(%d, %d, %d, %d)", v:GetX(), v:GetY(), v:GetWidth(), v:GetHeight())
        elseif typ_name == "wxColour" then
            s = s..v:GetAsString()
            v:delete()
        elseif typ_name == "wxFont" then
            s = s..v:GetNativeFontInfoDesc()
            v:delete()
        elseif typ_name == "wxDateTime" then
            s = s..v:Format()
        elseif typ_name == "wxTreeItemId" then
            local tree = event:GetEventObject():DynamicCast("wxTreeCtrl")
            if v:IsOk() then
                s = s..typ_name.."(tree:GetItemText='"..tree:GetItemText(v).."')"
            else
                s = s..typ_name.."!IsOk"
            end
        elseif typ_name == "wxListItem" then
            s = s..typ_name.."(GetId='"..v:GetId().."')"
        elseif typ_name == "wxWindow" then
            s = s..typ_name.."(GetName="..v:GetName()..")"
        elseif typ_name == "wxObject" then
            local obj_name = "nil"
            if v then
                obj_name = v:GetClassInfo():GetClassName()
            end
            s = s..obj_name
        else
            s = s..tostring(v)
            --v:delete()
            -- If we haven't handled it yet, we probably should
            print("Unhandled wxLua data type in FuncsToString from ", wxlua.type(event), typ_name, s, evtClassName)
        end

        table.insert(t, s)
    end

    return table.concat(t, ", ")
end

-- ---------------------------------------------------------------------------
-- Handle all wxEvents
-- ---------------------------------------------------------------------------

function OnEvent(event)
    local skip = true
    local evtClassName = wxlua.type(event) 
    local evtTypeStr   = wxEVT_TableByType[event:GetEventType()].name

    -- You absolutely must create a wxPaintDC for a wxEVT_PAINT in MSW
    -- to clear the region to be updated, otherwise you'll keep getting them
    -- Note: we always skip this anyway, see skipEVTs, but just to be sure...
    if event:GetEventType() == wx.wxEVT_PAINT then
        local dc = wx.wxPaintDC(event:GetEventObject():DynamicCast("wxWindow"))
        dc:delete()
    end

    -- try to figure out where this came from using the GetEventObject()
    local obj_str = "nil"
    local win_id  = -100000000
    if event:GetEventObject() then
    
        -- get the window id from the object since some events don't properly set the id.
        if event:GetEventObject():IsKindOf(wxwindowClassInfo) then
            local win = event:GetEventObject():DynamicCast("wxWindow")
            if win then
                win_id = win:GetId();
            end
        end
    
        --if win_id ~= event:GetId() then
        --    print(string.format("WARNING : wxEvent ID=%d does not match wxWindow ID=%d", event:GetId(), win_id))
        --end
    
        local classInfo = event:GetEventObject():GetClassInfo()
        if classInfo then
            obj_str = classInfo:GetClassName()
        else
            obj_str = "No wxClassInfo"
        end
    end

    -- during shutdown, we nil textCtrl since events are sent and we don't want them anymore
    if (not textCtrl) or ignoreEVTs[evtTypeStr] or ignoreControls[event:GetId()] or ignoreControls[win_id] then
        event:Skip(skip)
        return
    end

    --print(evtClassName, wxEVT_TableByType[event:GetEventType()].name)

    local s = string.format("%s %s(%s) GetEventObject=%s", wx.wxNow(), evtClassName, evtTypeStr, obj_str)

    -- Gather up all the info from the functions for the event and it's base classes
    while wxEvent_GetFuncs[evtClassName] do
        if type(wxEvent_GetFuncs[evtClassName]) == "table" then
            s = s.."\n\t"..evtClassName.." - "..FuncsToString(event, wxEvent_GetFuncs[evtClassName], evtClassName)
        else
            s = s.."\n\t"..evtClassName.." - "..wxEvent_GetFuncs[evtClassName](event)
        end
        evtClassName = wxCLASS_TableByName[evtClassName].baseclassNames[1]
    end

    -- for debugging, this means we need to add it to the wxEvent_GetFuncs table
    if evtClassName ~= "wxObject" then
        print("Unhandled wxEventXXX type in OnEvent:", evtClassName, evtTypeStr)
    end

    textCtrl:AppendText(s.."\n\n")

    event:Skip(skip)
end

-- ---------------------------------------------------------------------------
-- Create the window with the controls
-- ---------------------------------------------------------------------------
function CreateControlsWindow(parent)

    local scrollWin = wx.wxScrolledWindow(parent, ID_PARENT_SCROLLEDWINDOW,
                                    wx.wxDefaultPosition, wx.wxDefaultSize,
                                    wx.wxHSCROLL + wx.wxVSCROLL)

    -- Give the scrollwindow enough size so sizer works when calling Fit()
    scrollWin:SetScrollbars(15, 15, 400, 1000, 0, 0, false)

    -- try to slightly change the background colour, doesn't work in GTK
    if false then
        local c = scrollWin:GetBackgroundColour()
        local d = 20
        if (c:Red() >= 255-d) and (c:Green() >= 255-d) and (c:Blue() >= 255-d) then
            d = -d
        end

        local c2 = wx.wxColour(c:Red()+d, c:Green()+d, c:Blue()+d)
        scrollWin:SetBackgroundColour(c2)
        c:delete()
        c2:delete()
    end

    local flexSizer = wx.wxFlexGridSizer(0, 2, 5, 5)
    flexSizer:AddGrowableCol(1)

    local control = nil -- not used outside of this function

    -- -----------------------------------------------------------------------

    -- Connect ALL events to the window
    local function ConnectEvents(control)
        -- Note this is the same as doing this, but we connect all of them
        -- win:Connect(wx.wxID_ANY, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnCommandEvent)

        for i = 1, #wxEVT_Array do
            if not skipEVTs[wxEVT_Array[i].name] then
                control:Connect(wx.wxID_ANY, wxEVT_Array[i].eventType, OnEvent)
            end
        end
    end

    -- -----------------------------------------------------------------------

    local function AddControl(txt, control, real_control)
        local statText = wx.wxStaticText(scrollWin, wx.wxID_ANY, txt)

        flexSizer:Add(statText, 0, wx.wxALIGN_CENTER_VERTICAL+wx.wxALL, 5)
        flexSizer:Add(control, 0, wx.wxALIGN_LEFT+wx.wxALL, 5)

        flexSizer:Add(wx.wxStaticLine(scrollWin, wx.wxID_ANY), 0, wx.wxEXPAND+wx.wxALL, 5)
        flexSizer:Add(wx.wxStaticLine(scrollWin, wx.wxID_ANY), 0, wx.wxEXPAND+wx.wxALL, 5)

        ConnectEvents(real_control or control) -- connect to the real control

        local a = string.find(txt, "\n", 1, 1)
        if a then txt = string.sub(txt, 1, a-1) end

        if real_control and real_control:IsKindOf(wx.wxClassInfo.FindClass("wxWindow")) then
            controlTable[real_control:GetId()] = txt
        else
            controlTable[control:GetId()] = txt
        end
    end

    -- -----------------------------------------------------------------------

    local function CreateBookPage(parent, num)
        local p = wx.wxPanel(parent, wx.wxID_ANY)
        local s = wx.wxBoxSizer(wx.wxVERTICAL)
        local t = wx.wxStaticText(p, wx.wxID_ANY, "Window "..num)
        s:Add(t, 0, wx.wxCENTER, 5)
        s:SetMinSize(200,200) -- force it to be some reasonable size
        p:SetSizer(s)

        p:SetBackgroundColour(colorList[num]) -- make them easy to find

        return p
    end

    local function SetupBook(control)
        -- Note we can't just use a static text here since it does not obey
        -- any set size, set min size calls and always shrinks to the
        -- size that just fits the text

        control:SetImageList(imageList)
        control:AddPage(CreateBookPage(control, 1), "Page 1", true, 0)
        control:AddPage(CreateBookPage(control, 2), "Page 2", false, 1)
        control:AddPage(CreateBookPage(control, 3), "Page 3", false, 2)
    end

    -- -----------------------------------------------------------------------

    local path = nil
    local paths = {"throbber.gif", "../art/throbber.gif", "../../art/throbber.gif", "../../../art/throbber.gif"}
    for n = 1, #paths do
        if wx.wxFileExists(paths[n]) then path = paths[n]; break; end
    end

    if wx.wxAnimation and path then
        local ani = wx.wxAnimation() -- note cannot load from constuctor in GTK
        ani:LoadFile(path)

        control = wx.wxAnimationCtrl(scrollWin, ID_ANIMATIONCTRL, ani,
                                            wx.wxDefaultPosition, wx.wxDefaultSize)
        control:Play()
        ani:delete()
    else
        control = wx.wxStaticText(scrollWin, wx.wxID_ANY, "wxAnimation is missing or unable to load [../art/]throbber.gif")
    end
    AddControl("wxAnimationCtrl", control)

    -- -----------------------------------------------------------------------

    control = wx.wxBitmapButton(scrollWin, ID_BITMAPBUTTON, bmp,
                                         wx.wxDefaultPosition, wx.wxDefaultSize)
    AddControl("wxBitmapButton", control)

    -- -----------------------------------------------------------------------

    control = wx.wxBitmapComboBox(scrollWin, ID_BITMAPCOMBOBOX, "wxBitmapComboBox",
                                         wx.wxDefaultPosition, wx.wxDefaultSize,
                                         {"Item 1", "Item 2", "Item 3 text is long to check default size"},
                                         wx.wxTE_PROCESS_ENTER) -- generates event when enter is pressed
    control:Append("Appended w/ bitmap", bmp)
    control:Insert("Inserted at 0 w/ bitmap", bmp, 0)
    control:SetItemBitmap(2, bmp)
    AddControl("wxBitmapComboBox", control)

    -- -----------------------------------------------------------------------

    control = wx.wxButton(scrollWin, ID_BUTTON, "wxButton",
                          wx.wxDefaultPosition, wx.wxDefaultSize)
    AddControl("wxButton", control)

    -- -----------------------------------------------------------------------

    do
    -- Note: the wxCalendar control needs some help since it is made up of
    -- separate controls, put in on a panel first and that way the sizer that
    -- lays out all of these windows doesn't have a problem
    local p = wx.wxPanel(scrollWin, wx.wxID_ANY)
    local s = wx.wxBoxSizer(wx.wxVERTICAL)

    control = wx.wxCalendarCtrl(p, ID_CALENDARCTRL, wx.wxDefaultDateTime,
                                         wx.wxDefaultPosition, wx.wxDefaultSize,
                                         wx.wxCAL_SHOW_HOLIDAYS+wx.wxCAL_BORDER_SQUARE)
    s:Add(control, 1, wx.wxEXPAND, 5)
    p:SetSizer(s)
    s:SetSizeHints(p)
    AddControl("wxCalendarCtrl", p, control)
    end

    -- -----------------------------------------------------------------------

    control = wx.wxCheckBox(scrollWin, ID_CHECKBOX, "wxCheckBox",
                                         wx.wxDefaultPosition, wx.wxDefaultSize)
    AddControl("wxCheckBox", control)

    -- -----------------------------------------------------------------------

    control = wx.wxCheckListBox(scrollWin, ID_CHECKLISTBOX,
                                         wx.wxDefaultPosition, wx.wxDefaultSize,
                                         {"Item 1", "Item 2", "Item 3"})
    AddControl("wxCheckListBox", control)

    -- -----------------------------------------------------------------------

    control = wx.wxChoice(scrollWin, ID_CHOICE,
                                         wx.wxDefaultPosition, wx.wxDefaultSize,
                                         {"Item 1", "Item 2", "Item 3"})
    AddControl("wxChoice", control)

    -- -----------------------------------------------------------------------

    control = wx.wxChoicebook(scrollWin, ID_CHOICEBOOK,
                                         wx.wxDefaultPosition, wx.wxDefaultSize)
    SetupBook(control)
    AddControl("wxChoicebook", control)

    -- -----------------------------------------------------------------------

    control = wx.wxCollapsiblePane(scrollWin, ID_COLLAPSIBLEPANE, "wxCollapsiblePane",
                                         wx.wxDefaultPosition, wx.wxDefaultSize)
    AddControl("wxCollapsiblePane", control)

    -- -----------------------------------------------------------------------

    control = wx.wxComboBox(scrollWin, ID_COMBOBOX, "wxComboBox",
                                         wx.wxDefaultPosition, wx.wxDefaultSize,
                                         {"Item 1", "Item 2", "Item 3 text is long to check default size"},
                                         wx.wxTE_PROCESS_ENTER) -- generates event when enter is pressed
    control:Append("Appended item")
    control:Insert("Inserted at 0", 0)
    AddControl("wxComboBox", control)

    -- -----------------------------------------------------------------------

    control = wx.wxControl(scrollWin, ID_CONTROL,
                            wx.wxDefaultPosition, wx.wxDefaultSize,
                            wx.wxSUNKEN_BORDER)
    AddControl("wxControl", control)

    -- -----------------------------------------------------------------------

    control = wx.wxDirPickerCtrl(scrollWin, ID_DIRPICKERCTRL, wx.wxGetCwd(), "I'm the message parameter",
                                         wx.wxDefaultPosition, wx.wxDefaultSize,
                                         wx.wxDIRP_USE_TEXTCTRL)
    AddControl("wxDirPickerCtrl", control)

    -- -----------------------------------------------------------------------

    control = wx.wxFilePickerCtrl(scrollWin, ID_FILEPICKERCTRL, wx.wxGetCwd(), wx.wxFileSelectorPromptStr, wx.wxFileSelectorDefaultWildcardStr,
                                         wx.wxDefaultPosition, wx.wxDefaultSize,
                                         wx.wxFLP_USE_TEXTCTRL)
    AddControl("wxFilePickerCtrl", control)

    -- -----------------------------------------------------------------------

    control = wx.wxFontPickerCtrl(scrollWin, ID_FONTPICKERCTRL, wx.wxITALIC_FONT,
                                         wx.wxDefaultPosition, wx.wxDefaultSize,
                                         wx.wxFNTP_USEFONT_FOR_LABEL)
    AddControl("wxFontPickerCtrl", control)

    -- -----------------------------------------------------------------------

    control = wx.wxGauge(scrollWin, ID_GAUGE, 100,
                            wx.wxDefaultPosition, wx.wxDefaultSize)
    control:SetValue(30)
    AddControl("wxGauge", control)

    -- -----------------------------------------------------------------------

    control = wx.wxGenericDirCtrl(scrollWin, ID_GENERICDIRCTRL, wx.wxDirDialogDefaultFolderStr,
                            wx.wxDefaultPosition, wx.wxSize(200,200))
    AddControl("wxGenericDirCtrl", control)

    -- -----------------------------------------------------------------------

    control = wx.wxGrid(scrollWin, ID_GRID,
                            wx.wxDefaultPosition, wx.wxSize(200,200))
    control:CreateGrid(10, 20)
    AddControl("wxGrid", control)

    -- -----------------------------------------------------------------------

    control = wx.wxStaticText(scrollWin, wx.wxID_ANY, "TODO - wxHtml windows")
    AddControl("wxHtml", control)

    -- -----------------------------------------------------------------------

    control = wx.wxHyperlinkCtrl(scrollWin, ID_HYPERLINKCTRL,
                            "Goto wxlua.sourceforge.net", "http://wxlua.sourceforge.net",
                            wx.wxDefaultPosition, wx.wxDefaultSize)
    AddControl("wxHyperlinkCtrl", control)

    -- -----------------------------------------------------------------------

    control = wx.wxListBox(scrollWin, ID_LISTBOX,
                            wx.wxDefaultPosition, wx.wxDefaultSize,
                            {"Item 1", "Item 2", "Item 3"},
                            wx.wxLB_EXTENDED)
    AddControl("wxListBox", control)

    -- -----------------------------------------------------------------------

    control = wx.wxListCtrl(scrollWin, ID_LISTCTRL,
                            wx.wxDefaultPosition, wx.wxSize(200, 200),
                            wx.wxLC_REPORT)
    control:InsertColumn(0, "Col 1")
    control:InsertColumn(1, "Col 2")
    control:InsertItem(0, "Item 1")
    control:InsertItem(1, "Item 2")
    control:InsertItem(2, "Item 3")
    AddControl("wxListCtrl", control)

    -- -----------------------------------------------------------------------

    control = wx.wxStaticText(scrollWin, wx.wxID_ANY, "wxListView is a wxListCtrl with a couple of methods added")
    AddControl("wxListView", control)

    -- -----------------------------------------------------------------------

    control = wx.wxListbook(scrollWin, ID_LISTBOOK,
                                         wx.wxDefaultPosition, wx.wxSize(200,200))
    SetupBook(control)
    AddControl("wxListbook", control)

    -- -----------------------------------------------------------------------

    -- wxMediaCtrl

    -- -----------------------------------------------------------------------

    do
    -- Note: The wxNotebook in GTK will not draw it's tabs correctly if placed
    -- directly on the scrolled window, put it in a panel first.
    local p = wx.wxPanel(scrollWin, wx.wxID_ANY)
    local s = wx.wxBoxSizer(wx.wxVERTICAL)

    control = wx.wxNotebook(p, ID_NOTEBOOK,
                            wx.wxDefaultPosition, wx.wxSize(200,200))
    SetupBook(control)

    s:Add(control, 1, wx.wxEXPAND)
    s:SetMinSize(200,200)
    p:SetSizer(s)
    s:SetSizeHints(p)
    AddControl("wxNotebook", p, control)
    end

    -- -----------------------------------------------------------------------

    control = wx.wxPanel(scrollWin, ID_PANEL,
                            wx.wxDefaultPosition, wx.wxDefaultSize,
                            wx.wxSUNKEN_BORDER)
    AddControl("wxPanel", control)

    -- -----------------------------------------------------------------------

    control = wx.wxRadioBox(scrollWin, ID_RADIOBOX, "wxRadioBox",
                            wx.wxDefaultPosition, wx.wxDefaultSize,
                            {"Item 1", "Item 2", "Item 3"}, 1,
                            wx.wxSUNKEN_BORDER)
    AddControl("wxRadioBox", control)

    -- -----------------------------------------------------------------------

    control = wx.wxRadioButton(scrollWin, ID_RADIOBUTTON, "wxRadioButton",
                            wx.wxDefaultPosition, wx.wxDefaultSize)
    AddControl("wxRadioButton", control)

    -- -----------------------------------------------------------------------

    control = wx.wxStaticText(scrollWin, wx.wxID_ANY, "wxSashLayoutWindow must have a top level window as parent")
    AddControl("wxSashLayoutWindow", control)

    -- -----------------------------------------------------------------------

    control = wx.wxStaticText(scrollWin, wx.wxID_ANY, "wxSashWindow must have a top level window as parent")
    AddControl("wxSashWindow", control)

    -- -----------------------------------------------------------------------

    control = wx.wxScrollBar(scrollWin, ID_SCROLLBAR,
                            wx.wxDefaultPosition, wx.wxSize(200, -1))
    control:SetScrollbar(10, 10, 100, 20)
    AddControl("wxScrollBar\n range=100\n thumb=10\n pageSize=20", control)

    -- -----------------------------------------------------------------------

    control = wx.wxScrolledWindow(scrollWin, ID_SCROLLEDWINDOW,
                            wx.wxDefaultPosition, wx.wxSize(200, 200))
    control:SetScrollbars(10, 10, 100, 100)
    control:SetBackgroundColour(colorList[1])
    wx.wxButton(control, wx.wxID_ANY, "Child button of wxScrolledWindow", wx.wxPoint(50, 50))
    AddControl("wxScrolledWindow\n pixelsPerUnit=10\n noUnits=100", control)
    flexSizer:SetItemMinSize(control, 200, 200)

    -- -----------------------------------------------------------------------

    control = wx.wxSlider(scrollWin, ID_SLIDER, 10, 0, 100,
                            wx.wxDefaultPosition, wx.wxSize(200, -1))
    AddControl("wxSlider", control)

    -- -----------------------------------------------------------------------

    control = wx.wxSpinButton(scrollWin, ID_SPINBUTTON,
                            wx.wxDefaultPosition, wx.wxDefaultSize)
    AddControl("wxSpinButton", control)

    -- -----------------------------------------------------------------------

    control = wx.wxSpinCtrl(scrollWin, ID_SPINCTRL, "wxSpinCtrl",
                            wx.wxDefaultPosition, wx.wxDefaultSize)
    AddControl("wxSpinCtrl", control)

    -- -----------------------------------------------------------------------

    do
    -- Note: putting the splitter window directly on the scrolled window
    -- and in it's sizer makes the sash undraggable, put it in a panel first
    local p = wx.wxPanel(scrollWin, wx.wxID_ANY)
    local s = wx.wxBoxSizer(wx.wxVERTICAL)

    control = wx.wxSplitterWindow(p, ID_SPLITTERWINDOW,
                            wx.wxDefaultPosition, wx.wxSize(300, 200))
    control:SplitVertically(CreateBookPage(control, 1),
                            CreateBookPage(control, 2),
                            100)
    s:SetMinSize(300, 200)
    p:SetSizer(s)
    s:SetSizeHints(p)

    AddControl("wxSplitterWindow", p, control)
    end

    -- -----------------------------------------------------------------------

    control = wx.wxStaticBitmap(scrollWin, ID_STATICBITMAP, bmp,
                            wx.wxDefaultPosition, wx.wxDefaultSize)
    AddControl("wxStaticBitmap", control)

    -- -----------------------------------------------------------------------

    control = wx.wxStaticBox(scrollWin, ID_STATICBOX, "wxStaticBox",
                            wx.wxDefaultPosition, wx.wxSize(200, 100))
    control:SetBackgroundColour(colorList[1])
    AddControl("wxStaticBox", control)

    -- -----------------------------------------------------------------------

    control = wx.wxStaticLine(scrollWin, ID_STATICLINE,
                            wx.wxDefaultPosition, wx.wxSize(200, -1))
    AddControl("wxStaticLine", control)

    -- -----------------------------------------------------------------------

    control = wx.wxStaticText(scrollWin, ID_STATICTEXT, "Text for the wxStaticText control")
    AddControl("wxStaticText", control)

    -- -----------------------------------------------------------------------

    do
    local p = wx.wxStaticText(scrollWin, wx.wxID_ANY, "See OS taskbar for lightbulb icon")
    taskbarIcon = wx.wxTaskBarIcon()
    local icon = wx.wxIcon()
    icon:CopyFromBitmap(bmp)
    taskbarIcon:SetIcon(icon, "Tooltop for wxTaskBarIcon from controls.wx.lua")
    icon:delete()
    AddControl("wxTaskBarIcon", p, taskbarIcon)
    end

    -- -----------------------------------------------------------------------

    control = wx.wxTextCtrl(scrollWin, ID_TEXTCTRL, "wxTextCtrl",
                            wx.wxDefaultPosition, wx.wxDefaultSize,
                            wx.wxTE_PROCESS_ENTER)
    AddControl("wxTextCtrl", control)

    -- -----------------------------------------------------------------------

    control = wx.wxToggleButton(scrollWin, ID_TOGGLEBUTTON, "wxToggleButton",
                            wx.wxDefaultPosition, wx.wxDefaultSize)
    AddControl("wxToggleButton", control)

    -- -----------------------------------------------------------------------

    control = wx.wxToolBar(scrollWin, ID_TOOLBAR,
                            wx.wxDefaultPosition, wx.wxDefaultSize)
    control:AddTool(wx.wxID_ANY, "A tool 1", bmp, "Help for a tool 1", wx.wxITEM_NORMAL)
    control:AddTool(wx.wxID_ANY, "A tool 2", bmp, "Help for a tool 2", wx.wxITEM_NORMAL)
    control:AddSeparator()
    control:AddCheckTool(wx.wxID_ANY, "A check tool 1", bmp, wx.wxNullBitmap, "Short help for checktool 1", "Long help for checktool ")
    control:AddCheckTool(wx.wxID_ANY, "A check tool 2", bmp, wx.wxNullBitmap, "Short help for checktool 2", "Long help for checktool 2")
    AddControl("wxToolBar", control)

    -- -----------------------------------------------------------------------

    control = wx.wxToolbook(scrollWin, ID_TOOLBOOK,
                                         wx.wxDefaultPosition, wx.wxSize(200,200))
    SetupBook(control)
    AddControl("wxToolbook", control)

    -- -----------------------------------------------------------------------

    control = wx.wxTreebook(scrollWin, ID_TREEBOOK,
                                         wx.wxDefaultPosition, wx.wxSize(200,200))
    SetupBook(control)
    -- Now add special pages for the treebook
    control:AddSubPage(CreateBookPage(control, 4), "Subpage 1", false, 3)
    AddControl("wxTreebook", control)

    -- -----------------------------------------------------------------------

    do
    control = wx.wxTreeCtrl(scrollWin, ID_TREECTRL,
                            wx.wxDefaultPosition, wx.wxSize(200, 200),
                            wx.wxTR_HAS_BUTTONS+wx.wxTR_MULTIPLE)
    control:SetImageList(imageList)
    local item = control:AddRoot("Root Node", 0)
    control:AppendItem(item, "Item 1", 1)
    control:AppendItem(item, "Item 2")
    item = control:AppendItem(item, "Item 3", 2)
    item = control:AppendItem(item, "Item 3:1")
    item = control:AppendItem(item, "Item 3:2", 3)

    AddControl("wxTreeCtrl", control)
    end

    -- -----------------------------------------------------------------------

    control = wx.wxWindow(scrollWin, ID_WINDOW,
                            wx.wxDefaultPosition, wx.wxDefaultSize,
                            wx.wxSUNKEN_BORDER)
    AddControl("wxWindow", control)

    -- -----------------------------------------------------------------------

    if wxwebview and wxwebview.wxWebView then
    control = wxwebview.wxWebView.New(scrollWin, ID_WEBVIEW, "http://wxlua.sourceforge.net",
                                      wx.wxDefaultPosition, wx.wxSize(200, 200))
    control:LoadURL("http://wxlua.sourceforge.net")
    AddControl("wxWebView", control)
    end

    -- -----------------------------------------------------------------------

    scrollWin:SetSizer(flexSizer)
    flexSizer:Fit(scrollWin)

    return scrollWin
end

-- ---------------------------------------------------------------------------
-- Main function of the program
-- ---------------------------------------------------------------------------
function main()

    frame = wx.wxFrame( wx.NULL,              -- no parent needed for toplevel windows
                        wx.wxID_ANY,          -- don't need a wxWindow ID
                        "wxLua Controls Demo",-- caption on the frame
                        wx.wxDefaultPosition, -- let system place the frame
                        wx.wxSize(550, 450),  -- set the size of the frame
                        wx.wxDEFAULT_FRAME_STYLE ) -- use default frame styles

    frame:Connect(wx.wxEVT_CLOSE_WINDOW,
            function(event)
                event:Skip();
                textCtrl = nil -- stop processing events
                imageList:delete()
                if taskbarIcon then
                    --if taskbarIcon:IsIconInstalled() then
                    --    taskbarIcon:RemoveIcon()
                    --end

                    taskbarIcon:delete() -- must delete() it for program to exit in MSW
                end
            end)

    local fileMenu = wx.wxMenu()
    fileMenu:Append(ID_CLEAR_LOG, "&Clear log\tCtrl-C", "Clear text in the log window")
    fileMenu:AppendSeparator()
    fileMenu:Append(wx.wxID_EXIT, "E&xit", "Quit the program")

    local helpMenu = wx.wxMenu()
    helpMenu:Append(wx.wxID_ABOUT, "&About", "About the wxLua Controls Application")

    local menuBar = wx.wxMenuBar()
    menuBar:Append(fileMenu, "&File")
    menuBar:Append(helpMenu, "&Help")
    frame:SetMenuBar(menuBar)

    frame:CreateStatusBar(1)
    frame:SetStatusText("Welcome to wxLua.")


    frame:Connect(ID_CLEAR_LOG, wx.wxEVT_COMMAND_MENU_SELECTED,
                  function (event) textCtrl:Clear() end )

    -- connect the selection event of the exit menu item to an
    -- event handler that closes the window
    frame:Connect(wx.wxID_EXIT, wx.wxEVT_COMMAND_MENU_SELECTED,
                  function (event) frame:Close(true) end )

    -- connect the selection event of the about menu item
    frame:Connect(wx.wxID_ABOUT, wx.wxEVT_COMMAND_MENU_SELECTED,
        function (event)
            wx.wxMessageBox('This is the "About" dialog of the Controls wxLua sample.\n\n'..
                            'Check or uncheck events and windows you want logged.\n'..
                            wxlua.wxLUA_VERSION_STRING.." built with "..wx.wxVERSION_STRING,
                            "About wxLua",
                            wx.wxOK + wx.wxICON_INFORMATION,
                            frame)
        end )

    -- -----------------------------------------------------------------------
    splitter = wx.wxSplitterWindow(frame, wx.wxID_ANY)
    splitter:SetMinimumPaneSize(50) -- don't let it unsplit
    splitter:SetSashGravity(.8)

    splitter2 = wx.wxSplitterWindow(splitter, wx.wxID_ANY)
    splitter2:SetMinimumPaneSize(50) -- don't let it unsplit
    splitter2:SetSashGravity(.1)

    -- -----------------------------------------------------------------------

    noteBook = wx.wxNotebook(splitter2, wx.wxID_ANY)

    -- -----------------------------------------------------------------------
    eventListCtrl = wx.wxListCtrl(noteBook, ID_EVENT_LISTCTRL,
                                  wx.wxDefaultPosition, wx.wxDefaultSize,
                                  wx.wxLC_REPORT)
    eventListCtrl:SetImageList(listImageList, wx.wxIMAGE_LIST_SMALL)
    eventListCtrl:InsertColumn(0, "wxEvent Class")
    eventListCtrl:InsertColumn(1, "wxEventType")

    -- Add all the initial items and find the best fitting col widths
    local li = 0
    local col_widths = {200, 300}
    for n = 1, #wxEVT_List do
        local img = 1
        if     skipEVTs[wxEVT_List[n][2]]   then img = 2
        elseif ignoreEVTs[wxEVT_List[n][2]] then img = 0 end

        li = eventListCtrl:InsertItem(li, wxEVT_List[n][1], img)
        eventListCtrl:SetItem(li, 1, wxEVT_List[n][2])

        for i = 1, #col_widths do
            local w = eventListCtrl:GetTextExtent(wxEVT_List[n][i])
            if w > col_widths[i] + 16 then w = col_widths[i] + 16 end
        end
    end
    for i = 1, #col_widths do
        eventListCtrl:SetColumnWidth(i-1, col_widths[i])
    end

    -- Handle selecting or deselecting events
    function OnCheckListCtrl(event)
        local listCtrl = event:GetEventObject():DynamicCast("wxListCtrl")
        local win_id = event:GetId()
        event:Skip(false)
        local ignored_count = 0
        local sel = {}

        -- Find all the selected items
        for n = 1, listCtrl:GetItemCount() do
            local s = listCtrl:GetItemState(n-1, wx.wxLIST_STATE_SELECTED)
            if s ~= 0 then
                local litem = wx.wxListItem()
                litem:SetId(n-1)
                litem:SetMask(wx.wxLIST_MASK_IMAGE)
                listCtrl:GetItem(litem)
                if litem:GetImage() < 2 then -- skipEVTs
                    if litem:GetImage() == 0 then
                        ignored_count = ignored_count + 1
                    end

                    litem:SetMask(wx.wxLIST_MASK_TEXT)
                    litem:SetColumn(1)
                    listCtrl:GetItem(litem)
                    table.insert(sel, {n-1, litem:GetText()})
                end
            end
        end

        local img = 0
        if (#sel) < 2*ignored_count then img = 1 end

        for n = 1, #sel do
            listCtrl:SetItemImage(sel[n][1], img)

            if win_id == ID_EVENT_LISTCTRL then
                if img == 0 then
                    ignoreEVTs[sel[n][2]] = true
                else
                    ignoreEVTs[sel[n][2]] = nil
                end
            elseif win_id == ID_CONTROL_LISTCTRL then
                if img == 0 then
                    ignoreControls[tonumber(sel[n][2])] = true
                else
                    ignoreControls[tonumber(sel[n][2])] = nil
                end
            end
        end
    end

    eventListCtrl:Connect(wx.wxEVT_COMMAND_LIST_KEY_DOWN,
            function(event)
                if event:GetKeyCode() == wx.WXK_SPACE then
                    OnCheckListCtrl(event)
                else
                    event:Skip()
                end
            end)
    eventListCtrl:Connect(wx.wxEVT_COMMAND_LIST_ITEM_ACTIVATED, OnCheckListCtrl)

    -- -----------------------------------------------------------------------

    controlListCtrl = wx.wxListCtrl(noteBook, ID_CONTROL_LISTCTRL,
                                  wx.wxDefaultPosition, wx.wxDefaultSize,
                                  wx.wxLC_REPORT)
    controlListCtrl:SetImageList(listImageList, wx.wxIMAGE_LIST_SMALL)
    controlListCtrl:InsertColumn(0, "wxWindow Class")
    controlListCtrl:InsertColumn(1, "wxWindowID")

    -- We add the items after creating all the controls

    controlListCtrl:Connect(wx.wxEVT_COMMAND_LIST_KEY_DOWN,
            function(event)
                if event:GetKeyCode() == wx.WXK_SPACE then
                    OnCheckListCtrl(event)
                else
                    event:Skip()
                end
            end)
    controlListCtrl:Connect(wx.wxEVT_COMMAND_LIST_ITEM_ACTIVATED, OnCheckListCtrl)

    -- -----------------------------------------------------------------------

    noteBook:AddPage(eventListCtrl, "wxEvents")
    noteBook:AddPage(controlListCtrl, "wxWindows")

    -- -----------------------------------------------------------------------

    controlsWin = CreateControlsWindow(splitter2)

    -- Add all the initial items and find the best fitting col widths
    local li = 0
    local col_widths = {200, 300}
    local cTable = {}
    for k, v in pairs(controlTable) do table.insert(cTable, { k, v }) end
    table.sort(cTable, function(t1, t2) return t1[2] > t2[2] end)
    for n = 1, #cTable do
        local img = 1

        li = controlListCtrl:InsertItem(li, cTable[n][2], img)
        controlListCtrl:SetItem(li, 1, tostring(cTable[n][1]))

        for i = 1, #col_widths do
            local w = controlListCtrl:GetTextExtent(tostring(cTable[n][i]))
            if w > col_widths[i] + 16 then w = col_widths[i] + 16 end
        end
    end
    for i = 1, #col_widths do
        controlListCtrl:SetColumnWidth(i-1, col_widths[i])
    end

    -- -----------------------------------------------------------------------

    textCtrl = wx.wxTextCtrl(splitter, wx.wxID_ANY, "",
                             wx.wxDefaultPosition, wx.wxDefaultSize,
                             wx.wxTE_MULTILINE+wx.wxTE_DONTWRAP)

    -- -----------------------------------------------------------------------

    splitter:SplitHorizontally(splitter2, textCtrl, 300)
    splitter2:SplitVertically(noteBook, controlsWin, 300)

    -- -----------------------------------------------------------------------

    frame:Show(true) -- show the frame window
end

main()

-- Call wx.wxGetApp():MainLoop() last to start the wxWidgets event loop,
-- otherwise the wxLua program will exit immediately.
-- Does nothing if running from wxLua, wxLuaFreeze, or wxLuaEdit since the
-- MainLoop is already running or will be started by the C++ program.
wx.wxGetApp():MainLoop()
