--[[-------------------------------------------------------------------------- Copyright (c) 2007, James Whitehead II All rights reserved. WowLua is an interactive interpreter for World of Warcraft --------------------------------------------------------------------------]]-- -- TODO: -- * Make the scroll bars hide/show as necessary -- * Implement each button as required -- * Make line numbers line up with soft-wrapped lines -- * Disable selection of line numbers. Actually, would it be possible to make -- it select lines a la most other edtiors? -- * There seems to be a missing background texture in the upper-left 6th or so of the window -- * Resizing the window should grow the edit box vertically and leave the output window static. -- * Profit!!! WowLua = { VERSION = "WowLua 1.0 Interactive Interpreter", } WowLuaDB = { pages = { ["Untitled 1"] = "", [1] = "Untitled 1"}, currentPage = 1, untitled = 1, } local function wowpad_print(...) local out = "" for i=1,select("#", ...) do -- Comma seperate values if i > 1 then out = out .. ", " end out = out .. tostring(select(i, ...)) end WowLuaFrameOutput:AddMessage("|cff999999" .. out .. "|r") end if not print then print = wowpad_print end local function processSpecialCommands(txt) if txt == "/reload" then ReloadUI() return true elseif txt == "/reset" then WowLuaFrame:ClearAllPoints() WowLuaFrame:SetPoint("CENTER") WowLuaFrame:SetWidth(640) WowLuaFrame:SetHeight(512) WowLuaFrameResizeBar:ClearAllPoints() WowLuaFrameResizeBar:SetPoint("TOPLEFT", 14, -220) WowLuaFrameResizeBar:SetPoint("TOPRIGHT", 0, -220) return true end end function WowLua:ProcessLine(text) WowLuaFrameCommandEditBox:SetText("") if processSpecialCommands(text) then return end -- escape any color codes: local output = text:gsub("\124", "\124\124") WowLuaFrameOutput:AddMessage(WowLuaFrameCommandPrompt:GetText() .. output) WowLuaFrameCommandEditBox:AddHistoryLine(output) -- If they're using "= value" syntax, just print it text = text:gsub("^%s*=%s*(.+)", "print(%1)") -- Store this command into self.cmd in case we have multiple lines if self.cmd then self.cmd = self.cmd .. "\n" .. text self.orig = self.orig .. "\n" .. text else self.cmd = text self.orig = text end -- Trim the command before we run it self.cmd = string.trim(self.cmd) -- Process the current command local func,err = loadstring(self.cmd) -- Fail to compile? Give it a return -- Check to see if this just needs a return in front of it if not func then local newfunc,newerr = loadstring("print(" .. self.cmd .. ")") if newfunc then func,err = newfunc,newerr end end if not func then -- Check to see if this is just an unfinished block if err:sub(-7, -1) == "'<eof>'" then -- Change the prompt WowLuaFrameCommandPrompt:SetText(">> ") return end WowLuaFrameOutput:AddMessage("|cffff0000" .. err .. "|r") self.cmd = nil WowLuaFrameCommandPrompt:SetText("> ") else -- Make print a global function local old_print = print print = wowpad_print -- Call the function local succ,err = pcall(func) -- Restore the value of print print = old_print if not succ then WowLuaFrameOutput:AddMessage("|cffff0000" .. err .. "|r") end self.cmd = nil WowLuaFrameCommandPrompt:SetText("> ") end end function WowLua.RunScript(text) -- escape any color codes: local output = text:gsub("\124", "\124\124") if text == "/reload" then ReloadUI() end -- If they're using "= value" syntax, just print it text = text:gsub("^%s*=%s*(.+)", "print(%1)") -- Trim the command before we run it text = string.trim(text) -- Process the current command local func,err = loadstring(text, "WowLua") if not func then WowLuaFrameOutput:AddMessage("|cffff0000" .. err .. "|r") return false, err else -- Make print a global function local old_print = print print = wowpad_print -- Call the function local succ,err = pcall(func) -- Restore the value of print print = old_print if not succ then WowLuaFrameOutput:AddMessage("|cffff0000" .. err .. "|r") return false, err end end return true end function WowLua.Initialize(self) WowLua.OnSizeChanged(self) table.insert(UISpecialFrames, "WowLuaFrame") PlaySound("igMainMenuOpen"); end local tooltips = { ["New"] = "Create a new script page", ["Open"] = "Open an existing script page", ["Save As"] = "Save the current page with a name", ["Undo"] = "Revert to the last saved version", ["Delete"] = "Delete the current page", ["Lock"] = "Locks/unlocks the current page from being changed", ["Previous"] = "Navigate back one page", ["Next"] = "Navigate forward one page", ["Run"] = "Run the current script", } function WowLua.Button_OnEnter(self) GameTooltip:SetOwner(this, "ANCHOR_BOTTOM"); local operation = self:GetName():match("WowLuaButton_(.+)"):gsub("_", " ") GameTooltip:SetText(operation) if tooltips[operation] then GameTooltip:AddLine(tooltips[operation], 1, 1, 1) end GameTooltip:Show(); end function WowLua.Button_OnLeave(self) GameTooltip:Hide() end function WowLua.Button_OnClick(self) local operation = self:GetName():match("WowLuaButton_(.+)") if operation == "New" then local page = WowLuaDB.pages[WowLuaDB.currentPage] local text = WowLuaFrameEditBox:GetText() WowLuaDB.pages[page] = text WowLuaFrameEditBox:SetText("") WowLuaDB.untitled = WowLuaDB.untitled + 1 WowLuaDB.pages[#WowLuaDB.pages + 1] = string.format("Untitled %d", WowLuaDB.untitled) WowLuaDB.currentPage = #WowLuaDB.pages WowLuaButton_Next:Disable() SetDesaturation(WowLuaButton_Next:GetNormalTexture(),true) WowLuaButton_Previous:Enable() SetDesaturation(WowLuaButton_Previous:GetNormalTexture(),false) elseif operation == "Open" then elseif operation == "Save_As" then elseif operation == "Undo" then local page = WowLuaDB.pages[WowLuaDB.currentPage] WowLuaFrameEditBox:SetText(WowLuaDB.pages[page]) elseif operation == "Delete" then local page = WowLuaDB.pages[WowLuaDB.currentPage] WowLuaDB.pages[page] = nil table.remove(WowLuaDB.pages, WowLuaDB.currentPage) if WowLuaDB.currentPage > 1 then WowLuaDB.currentPage = WowLuaDB.currentPage - 1 end local page = WowLuaDB.pages[WowLuaDB.currentPage] WowLuaFrameEditBox:SetText(WowLuaDB.pages[page]) if WowLuaDB.currentPage == 1 then WowLuaButton_Previous:Disable() SetDesaturation(WowLuaButton_Previous:GetNormalTexture(),true) else WowLuaButton_Previous:Enable() SetDesaturation(WowLuaButton_Previous:GetNormalTexture(),false) end if WowLuaDB.currentPage == #WowLuaDB.pages then WowLuaButton_Next:Disable() SetDesaturation(WowLuaButton_Next:GetNormalTexture(),true) else WowLuaButton_Next:Enable() SetDesaturation(WowLuaButton_Next:GetNormalTexture(),false) end elseif operation == "Lock" then elseif operation == "Previous" then local cPage = WowLuaDB.pages[WowLuaDB.currentPage] local text = WowLuaFrameEditBox:GetText() WowLuaDB.pages[cPage] = text WowLuaDB.currentPage = WowLuaDB.currentPage - 1 cPage = WowLuaDB.pages[WowLuaDB.currentPage] WowLuaFrameEditBox:SetText(WowLuaDB.pages[cPage] or "") if WowLuaDB.currentPage == 1 then WowLuaButton_Previous:Disable() SetDesaturation(WowLuaButton_Previous:GetNormalTexture(),true) else WowLuaButton_Previous:Enable() SetDesaturation(WowLuaButton_Previous:GetNormalTexture(),false) end if WowLuaDB.currentPage == #WowLuaDB.pages then WowLuaButton_Next:Disable() SetDesaturation(WowLuaButton_Next:GetNormalTexture(),true) else WowLuaButton_Next:Enable() SetDesaturation(WowLuaButton_Next:GetNormalTexture(),false) end elseif operation == "Next" then local cPage = WowLuaDB.pages[WowLuaDB.currentPage] local text = WowLuaFrameEditBox:GetText() WowLuaDB.pages[cPage] = text WowLuaDB.currentPage = WowLuaDB.currentPage + 1 cPage = WowLuaDB.pages[WowLuaDB.currentPage] WowLuaFrameEditBox:SetText(WowLuaDB.pages[cPage] or "") if WowLuaDB.currentPage == #WowLuaDB.pages then WowLuaButton_Next:Disable() SetDesaturation(WowLuaButton_Next:GetNormalTexture(),true) else WowLuaButton_Next:Enable() SetDesaturation(WowLuaButton_Next:GetNormalTexture(),true) end if WowLuaDB.currentPage == 1 then WowLuaButton_Previous:Disable() SetDesaturation(WowLuaButton_Previous:GetNormalTexture(),true) else WowLuaButton_Previous:Enable() SetDesaturation(WowLuaButton_Previous:GetNormalTexture(),true) end elseif operation == "Run" then -- Run the script, if there is an error then highlight it local text = WowLuaFrameEditBox:GetText() if text then local succ,err = WowLua.RunScript(text) if not succ then local chunkName,lineNum = err:match("(%b[]):(%d+):") lineNum = tonumber(lineNum) WowLua.UpdateLineNums(lineNum) -- Highlight the text in the editor by finding the char of the line number we're on text = WowLua.indent.coloredGetText(WowLuaFrameEditBox) local curLine,start = 1,1 while curLine < lineNum do local s,e = text:find("\n", start) start = e + 1 curLine = curLine + 1 end local nextLine = select(2, text:find("\n", start)) WowLuaFrameEditBox:SetFocus() WowLuaFrameEditBox:SetCursorPosition(start - 1) end local page = WowLuaDB.pages[WowLuaDB.currentPage] WowLuaDB.pages[page] = text end end end local function slashHandler(txt) local page = WowLuaDB.pages[WowLuaDB.currentPage] WowLuaFrameEditBox:SetText(WowLuaDB.pages[page] or "") if WowLuaDB.currentPage == 1 then WowLuaButton_Previous:Disable() SetDesaturation(WowLuaButton_Previous:GetNormalTexture(),true) end if WowLuaDB.currentPage == #WowLuaDB.pages then WowLuaButton_Next:Disable() SetDesaturation(WowLuaButton_Next:GetNormalTexture(),true) end --WowLua:CreateFrame() WowLuaFrame:Show() if processSpecialCommands(txt) then return end if txt:match("%S") then WowLua:ProcessLine(txt) end WowLuaFrameCommandEditBox:SetFocus() end SLASH_WOWLUA1 = "/wowlua" SLASH_WOWLUA2 = "/lua" SlashCmdList["WOWLUA"] = slashHandler function WowLua.OnSizeChanged(self) -- The first graphic is offset 13 pixels to the right local width = self:GetWidth() - 13 local bg2w,bg3w,bg4w = 0,0,0 -- Resize bg2 up to 256 width local bg2w = width - 256 if bg2w > 256 then bg3w = bg2w - 256 bg2w = 256 end if bg3w > 256 then bg4w = bg3w - 256 bg3w = 256 end local bg2 = WowLuaFrameBG2 local bg3 = WowLuaFrameBG3 local bg4 = WowLuaFrameBG4 if bg2w > 0 then bg2:SetWidth(bg2w) bg2:SetTexCoord(0, (bg2w / 256), 0, 1) bg2:Show() else bg2:Hide() end if bg3w and bg3w > 0 then bg3:SetWidth(bg3w) bg3:SetTexCoord(0, (bg3w / 256), 0, 1) bg3:Show() else bg3:Hide() end if bg4w and bg4w > 0 then bg4:SetWidth(bg4w) bg4:SetTexCoord(0, (bg4w / 256), 0, 1) bg4:Show() else bg4:Hide() end if WowLuaFrameResizeBar then -- Don't move too high, or too low local parent = WowLuaFrameResizeBar:GetParent() local top = parent:GetTop() local bot = parent:GetBottom() local maxpoint = (top - bot - 80) * -1 -- This is the current point, actually local newPoint = select(5, WowLuaFrameResizeBar:GetPoint()) -- Don't move past the edges of the frame if newPoint < maxpoint then newPoint = maxpoint elseif newPoint > -125 then newPoint = -125 end WowLuaFrameResizeBar:ClearAllPoints() WowLuaFrameResizeBar:SetPoint("TOPLEFT", 14, newPoint) WowLuaFrameResizeBar:SetPoint("TOPRIGHT", 0, newPoint) --[[ -- Get our bottom, and the bottom of the frame local sbot,pbot = WowLuaFrameResizeBar:GetBottom(), parent:GetBottom() -- Diff local diff = pbot - sbot local numLines = math.abs((diff / 14) + 1.3) if numLines <= 1 then numLines = 1 end WowLuaFrameOutput:SetMaxLines(numLines) --]] end end function WowLua.ResizeBar_OnMouseDown(self, button) self.cursorStart = select(2, GetCursorPosition()) self.anchorStart = select(5, self:GetPoint()) self:SetScript("OnUpdate", WowLua.ResizeBar_OnUpdate) end function WowLua.ResizeBar_OnMouseUp(self, button) self:SetScript("OnUpdate", nil) end function WowLua.ResizeBar_OnUpdate(self, elapsed) local cursorY = select(2, GetCursorPosition()) local newPoint = self.anchorStart - (self.cursorStart - cursorY)/self:GetEffectiveScale() -- Don't move too high, or too low local parent = self:GetParent() local top = parent:GetTop() local bot = parent:GetBottom() local maxpoint = (top - bot - 80) * -1 -- Don't move past the edges of the frame if newPoint < maxpoint then newPoint = maxpoint elseif newPoint > -125 then newPoint = -125 end self:ClearAllPoints() self:SetPoint("TOPLEFT", 14, newPoint) self:SetPoint("TOPRIGHT", 0, newPoint) --[[ -- Get our bottom, and the bottom of the frame local sbot,pbot = self:GetBottom(), parent:GetBottom() -- Diff local diff = pbot - sbot local numLines = math.abs((diff / 14) + 1.3) if numLines <= 1 then numLines = 1 end WowLuaFrameOutput:SetMaxLines(numLines) --]] end function WowLua.OnVerticalScroll(scrollFrame) local offset = scrollFrame:GetVerticalScroll(); local scrollbar = getglobal(scrollFrame:GetName().."ScrollBar"); scrollbar:SetValue(offset); local min, max = scrollbar:GetMinMaxValues(); local display = false; if ( offset == 0 ) then getglobal(scrollbar:GetName().."ScrollUpButton"):Disable(); else getglobal(scrollbar:GetName().."ScrollUpButton"):Enable(); display = true; end if ((scrollbar:GetValue() - max) == 0) then getglobal(scrollbar:GetName().."ScrollDownButton"):Disable(); else getglobal(scrollbar:GetName().."ScrollDownButton"):Enable(); display = true; end if ( display ) then scrollbar:Show(); else scrollbar:Hide(); end end function WowLua.UpdateLineNums(highlightNum) -- highlightNum is the line number indicated by the error message if highlightNum then WowLua.highlightNum = highlightNum else highlightNum = WowLua.highlightNum end -- Since we know this is FAIAP enabled, we need to pass true in order -- to get the raw values local editbox = WowLuaFrameEditBox local linebox = WowLuaFrameLineNumEditBox local linetest = WowLuaFrameEditBoxLineTest local linescroll = WowLuaFrameLineNumScrollFrame local width = editbox:GetWidth() local text = editbox:GetText(true) local linetext = "" local count = 1 for line in text:gmatch("([^\n]*\n?)") do if #line > 0 then ChatFrame1:AddMessage(count .. " hi: " .. tostring(highlightNum)) if count == highlightNum then ChatFrame1:AddMessage("got highlight") linetext = linetext .. "|cFFFF1111" .. count .. "|r" .. "\n" else linetext = linetext .. count .. "\n" end count = count + 1 -- Check to see if the line of text spans more than one actual line linetest:SetText(line:gsub("|", "||")) local testwidth = linetest:GetWidth() if testwidth >= width then linetext = linetext .. string.rep("\n", testwidth / width) end end end if text:sub(-1, -1) == "\n" then linetext = linetext .. count .. "\n" count = count + 1 end -- Make the line number frame wider as necessary local offset = tostring(count):len() * 10 linescroll:ClearAllPoints() linescroll:SetPoint("TOPLEFT", WowLuaFrame, "TOPLEFT", 18, -74) linescroll:SetPoint("BOTTOMRIGHT", WowLuaFrameResizeBar, "TOPLEFT", 15 + offset, -4) linebox:SetText(linetext) linetest:SetText(text) end local function canScroll(scroll, direction) local num, displayed, currScroll = scroll:GetNumMessages(), scroll:GetNumLinesDisplayed(), scroll:GetCurrentScroll(); if ( direction == "up" and ( num == displayed or num == ( currScroll + displayed ) ) ) then return false; elseif ( direction == "down" and currScroll == 0 ) then return false; end return true; end function WowLua.UpdateScrollingMessageFrame(frame) local name = frame:GetName(); local display = false; if ( canScroll(frame, "up") ) then getglobal(name.."UpButton"):Enable(); display = true; else getglobal(name.."UpButton"):Disable(); end if ( canScroll(frame, "down") ) then getglobal(name.."DownButton"):Enable(); display = true; else getglobal(name.."DownButton"):Disable(); end if ( display ) then getglobal(name.."UpButton"):Show(); getglobal(name.."DownButton"):Show(); else getglobal(name.."UpButton"):Hide(); getglobal(name.."DownButton"):Hide(); end end local scrollMethods = { ["line"] = { ["up"] = "ScrollUp", ["down"] = "ScrollDown" }, ["page"] = { ["up"] = "PageUp", ["down"] = "PageDown" }, ["end"] = { ["up"] = "ScrollToTop", ["down"] = "ScrollToBottom" }, }; function WowLua.ScrollingMessageFrameScroll(scroll, direction, type) -- Make sure we can scroll first if ( not canScroll(scroll, direction) ) then return; end local method = scrollMethods[type][direction]; scroll[method](scroll); end function WowLua.OnTextChanged(self) self.highlightNum = nil end function WowLua.OnCursorChanged(self) WowLua.dirty = true end