-------------------------------------------------------------------------=--- -- Name: Calculator.wx.lua -- Purpose: Calculator wxLua sample -- Author: J Winwood -- Based on the wxWidgets sample by Marco Ghislanzoni -- Created: March 2002 -- Updated January 2003 to use XML resources -- Copyright: (c) 2002-2003 Lomtick Software. All rights reserved. -- Licence: wxWidgets licence -------------------------------------------------------------------------=--- -- Load the wxLua module, does nothing if running from wxLua, wxLuaFreeze, or wxLuaEdit require("wx") -- --------------------------------------------------------------------------- -- Global variables dialog = nil -- the wxDialog main toplevel window xmlResource = nil -- the XML resource handle txtDisplay = nil -- statictext window for the display clearDisplay = nil lastNumber = 0 -- the last number pressed, 0 - 9 lastOperationId = nil -- the window id of last operation button pressed local xpmdata = { "16 15 5 1", " c None", "a c Black", "b c #FFFFFF", "c c #808080", "d c #9DBDCD", " aaaaaaaaaaaa ", " addddddddddac ", " adaaaaaaaadac ", " adabbbbbbadac ", " adabbbbbbadac ", " adaaaaaaaadac ", " addddddddddac ", " adaadaadaadac ", " adaadaadaadac ", " addddddddddac ", " adaadaadaadac ", " adaadaadaadac ", " addddddddddac ", " aaaaaaaaaaaac ", " ccccccccccccc " } -- --------------------------------------------------------------------------- -- return the path part of the currently executing file function GetExePath() local function findLast(filePath) -- find index of last / or \ in string local lastOffset = nil local offset = nil repeat offset = string.find(filePath, "\\") or string.find(filePath, "/") if offset then lastOffset = (lastOffset or 0) + offset filePath = string.sub(filePath, offset + 1) end until not offset return lastOffset end local filePath = debug.getinfo(1, "S").source if string.byte(filePath) == string.byte('@') then local offset = findLast(filePath) if offset ~= nil then -- remove the @ at the front up to just before the path separator filePath = string.sub(filePath, 2, offset - 1) else filePath = "." end else filePath = wx.wxGetCwd() end return filePath end -- --------------------------------------------------------------------------- -- Handle the clear button event function OnClear(event) txtDisplay:SetLabel("0") lastNumber = 0 lastOperationId = ID_PLUS end -- --------------------------------------------------------------------------- -- Handle all number button events function OnNumber(event) local numberId = event:GetId() local displayString = txtDisplay:GetLabel() if (displayString == "0") or (tonumber(displayString) == nil) or clearDisplay then displayString = "" end clearDisplay = nil -- Limit string length to 12 chars if string.len(displayString) < 12 then if numberId == ID_DECIMAL then if not string.find(displayString, ".", 1, 1) then -- If the first pressed char is "." then we want "0." if string.len(displayString) == 0 then displayString = displayString.."0." else displayString = displayString.."." end end else -- map button window ids to numeric values local idTable = { [ID_0] = 0, [ID_1] = 1, [ID_2] = 2, [ID_3] = 3, [ID_4] = 4, [ID_5] = 5, [ID_6] = 6, [ID_7] = 7, [ID_8] = 8, [ID_9] = 9 } local num = idTable[numberId] -- If first character entered is 0 we reject it if (num == 0) and (string.len(displayString) == 0) then displayString = "0" elseif displayString == "" then displayString = num else displayString = displayString..num end end txtDisplay:SetLabel(tostring(displayString)) end end -- --------------------------------------------------------------------------- -- Calculate the operation function DoOperation(a, b, operationId) local result = a if operationId == ID_PLUS then result = b + a elseif operationId == ID_MINUS then result = b - a elseif operationId == ID_MULTIPLY then result = b * a elseif operationId == ID_DIVIDE then if a == 0 then result = "Divide by zero error" else result = b / a end end return result end -- --------------------------------------------------------------------------- -- Handle all operation button events function OnOperator(event) -- Get display content local displayString = txtDisplay:GetLabel() local currentNumber = tonumber(displayString) -- if error message was shown, zero output and ignore operator if ((currentNumber == nil) or (lastNumber == nil)) then lastNumber = 0 return end -- Get the required lastOperationId local operationId = event:GetId() displayString = DoOperation(currentNumber, lastNumber, lastOperationId) lastNumber = tonumber(displayString) if (lastOperationId ~= ID_EQUALS) or (operationId == ID_EQUALS) then txtDisplay:SetLabel(tostring(displayString)) end clearDisplay = 1 lastOperationId = operationId end -- --------------------------------------------------------------------------- -- Handle the quit button event function OnQuit(event) event:Skip() wx.wxMessageBox("wxLua calculator sample based on the calc sample written by Marco Ghislanzoni.\n".. wxlua.wxLUA_VERSION_STRING.." built with "..wx.wxVERSION_STRING, "wxLua Calculator", wx.wxOK + wx.wxICON_INFORMATION, dialog) dialog:Show(false) dialog:Destroy() end -- --------------------------------------------------------------------------- -- The main program as a function (makes it easy to exit on error) function main() -- xml style resources (if present) xmlResource = wx.wxXmlResource() xmlResource:InitAllHandlers() local xrcFilename = GetExePath().."/calculator.xrc" -- try to load the resource and ask for path to it if not found while not xmlResource:Load(xrcFilename) do -- must unload the file before we try again xmlResource:Unload(xrcFilename) wx.wxMessageBox("Error loading xrc resources, please choose the path to 'calculator.xrc'.", "Calculator", wx.wxOK + wx.wxICON_EXCLAMATION, wx.NULL) local fileDialog = wx.wxFileDialog(wx.NULL, "Open 'calculator.xrc' resource file", "", "calculator.xrc", "XRC files (*.xrc)|*.xrc|All files (*)|*", wx.wxOPEN + wx.wxFILE_MUST_EXIST) if fileDialog:ShowModal() == wx.wxID_OK then xrcFilename = fileDialog:GetPath() else return -- quit program end end dialog = wx.wxDialog() if not xmlResource:LoadDialog(dialog, wx.NULL, "Calculator") then wx.wxMessageBox("Error loading xrc resources!", "Calculator", wx.wxOK + wx.wxICON_EXCLAMATION, wx.NULL) return -- quit program end -- ----------------------------------------------------------------------- -- This is a little awkward, but it's how it's done in C++ too bitmap = wx.wxBitmap(xpmdata) icon = wx.wxIcon() icon:CopyFromBitmap(bitmap) dialog:SetIcon(icon) bitmap:delete() icon:delete() bestSize = dialog:GetBestSize() dialog:SetSize(bestSize:GetWidth()/2, bestSize:GetHeight()) dialog:SetSizeHints(bestSize:GetWidth()/2, bestSize:GetHeight()) -- initialize the txtDisplay and verify that it's ok txtDisplay = dialog:FindWindow(xmlResource.GetXRCID("ID_TEXT")) if not txtDisplay then wx.wxMessageBox('Unable to find window "ID_TEXT" in the dialog', "Calculator", wx.wxOK + wx.wxICON_EXCLAMATION, wx.NULL) dialog:Destroy() return end if not txtDisplay:DynamicCast("wxStaticText") then wx.wxMessageBox('window "ID_TEXT" is not a "wxStaticText" or is not derived from it"', "Calculator", wx.wxOK + wx.wxICON_EXCLAMATION, wx.NULL) dialog:Destroy() return end txtDisplay:SetLabel("0") -- init global wxWindow ID values ID_0 = xmlResource.GetXRCID("ID_0") ID_1 = xmlResource.GetXRCID("ID_1") ID_2 = xmlResource.GetXRCID("ID_2") ID_3 = xmlResource.GetXRCID("ID_3") ID_4 = xmlResource.GetXRCID("ID_4") ID_5 = xmlResource.GetXRCID("ID_5") ID_6 = xmlResource.GetXRCID("ID_6") ID_7 = xmlResource.GetXRCID("ID_7") ID_8 = xmlResource.GetXRCID("ID_8") ID_9 = xmlResource.GetXRCID("ID_9") ID_DECIMAL = xmlResource.GetXRCID("ID_DECIMAL") ID_EQUALS = xmlResource.GetXRCID("ID_EQUALS") ID_PLUS = xmlResource.GetXRCID("ID_PLUS") ID_MINUS = xmlResource.GetXRCID("ID_MINUS") ID_MULTIPLY = xmlResource.GetXRCID("ID_MULTIPLY") ID_DIVIDE = xmlResource.GetXRCID("ID_DIVIDE") ID_OFF = xmlResource.GetXRCID("ID_OFF") ID_CLEAR = xmlResource.GetXRCID("ID_CLEAR") lastOperationId = ID_PLUS dialog:Connect(ID_0, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnNumber) dialog:Connect(ID_1, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnNumber) dialog:Connect(ID_2, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnNumber) dialog:Connect(ID_3, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnNumber) dialog:Connect(ID_4, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnNumber) dialog:Connect(ID_5, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnNumber) dialog:Connect(ID_6, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnNumber) dialog:Connect(ID_7, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnNumber) dialog:Connect(ID_8, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnNumber) dialog:Connect(ID_9, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnNumber) dialog:Connect(ID_DECIMAL, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnNumber) dialog:Connect(ID_EQUALS, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnOperator) dialog:Connect(ID_PLUS, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnOperator) dialog:Connect(ID_MINUS, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnOperator) dialog:Connect(ID_MULTIPLY, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnOperator) dialog:Connect(ID_DIVIDE, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnOperator) dialog:Connect(ID_OFF, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnQuit) dialog:Connect(ID_CLEAR, wx.wxEVT_COMMAND_BUTTON_CLICKED, OnClear) dialog:Connect(wx.wxEVT_CLOSE_WINDOW, OnQuit) accelTable = wx.wxAcceleratorTable({ { wx.wxACCEL_NORMAL, string.byte('0'), ID_0 }, { wx.wxACCEL_NORMAL, string.byte('1'), ID_1 }, { wx.wxACCEL_NORMAL, string.byte('2'), ID_2 }, { wx.wxACCEL_NORMAL, string.byte('3'), ID_3 }, { wx.wxACCEL_NORMAL, string.byte('4'), ID_4 }, { wx.wxACCEL_NORMAL, string.byte('5'), ID_5 }, { wx.wxACCEL_NORMAL, string.byte('6'), ID_6 }, { wx.wxACCEL_NORMAL, string.byte('7'), ID_7 }, { wx.wxACCEL_NORMAL, string.byte('8'), ID_8 }, { wx.wxACCEL_NORMAL, string.byte('9'), ID_9 }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD0, ID_0 }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD1, ID_1 }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD2, ID_2 }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD3, ID_3 }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD4, ID_4 }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD5, ID_5 }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD6, ID_6 }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD7, ID_7 }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD8, ID_8 }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD9, ID_9 }, { wx.wxACCEL_NORMAL, string.byte('.'), ID_DECIMAL }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD_DECIMAL, ID_DECIMAL }, { wx.wxACCEL_NORMAL, string.byte('='), ID_EQUALS }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD_ENTER, ID_EQUALS }, { wx.wxACCEL_NORMAL, 13, ID_EQUALS }, { wx.wxACCEL_NORMAL, string.byte('+'), ID_PLUS }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD_ADD, ID_PLUS }, { wx.wxACCEL_NORMAL, string.byte('-'), ID_MINUS }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD_SUBTRACT, ID_MINUS }, { wx.wxACCEL_NORMAL, string.byte('*'), ID_MULTIPLY }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD_MULTIPLY, ID_MULTIPLY }, { wx.wxACCEL_NORMAL, string.byte('/'), ID_DIVIDE }, { wx.wxACCEL_NORMAL, wx.VXK_NUMPAD_DIVIDE, ID_DIVIDE }, { wx.wxACCEL_NORMAL, string.byte('C'), ID_CLEAR }, { wx.wxACCEL_NORMAL, string.byte('c'), ID_CLEAR }, { wx.wxACCEL_NORMAL, wx.WXK_ESCAPE, ID_OFF } }) dialog:SetAcceleratorTable(accelTable) dialog:Centre() dialog:Show(true) 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()