Mercurial > wow > mailopener
view Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.lua @ 8:1b2d819b4fa8
Now using the global MailAddonBusy to indicate MailOpener is busy, read comments in Core.lua for more info.
Default status is now ?enabled without automatic mail opening? to let first time users look around before MailOpener messes up their heads. A StaticPopupDialog will be added later to ask if they want to auto-enable.
When ?enabled without automatic mail opening? is on and you toggle the mail opening checkbox on, mail opening will automatically start.
| author | Zerotorescue |
|---|---|
| date | Thu, 09 Sep 2010 10:53:19 +0200 |
| parents | 823e33465b6e |
| children |
line wrap: on
line source
--- AceConfigCmd-3.0 handles access to an options table through the "command line" interface via the ChatFrames. -- @class file -- @name AceConfigCmd-3.0 -- @release $Id: AceConfigCmd-3.0.lua 904 2009-12-13 11:56:37Z nevcairiel $ --[[ AceConfigCmd-3.0 Handles commandline optionstable access REQUIRES: AceConsole-3.0 for command registration (loaded on demand) ]] -- TODO: plugin args local MAJOR, MINOR = "AceConfigCmd-3.0", 12 local AceConfigCmd = LibStub:NewLibrary(MAJOR, MINOR) if not AceConfigCmd then return end AceConfigCmd.commands = AceConfigCmd.commands or {} local commands = AceConfigCmd.commands local cfgreg = LibStub("AceConfigRegistry-3.0") local AceConsole -- LoD local AceConsoleName = "AceConsole-3.0" -- Lua APIs local strsub, strsplit, strlower, strmatch, strtrim = string.sub, string.split, string.lower, string.match, string.trim local format, tonumber, tostring = string.format, tonumber, tostring local tsort, tinsert = table.sort, table.insert local select, pairs, next, type = select, pairs, next, type local error, assert = error, assert -- WoW APIs local _G = _G -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded -- List them here for Mikk's FindGlobals script -- GLOBALS: LibStub, SELECTED_CHAT_FRAME, DEFAULT_CHAT_FRAME local L = setmetatable({}, { -- TODO: replace with proper locale __index = function(self,k) return k end }) local function print(msg) (SELECTED_CHAT_FRAME or DEFAULT_CHAT_FRAME):AddMessage(msg) end -- constants used by getparam() calls below local handlertypes = {["table"]=true} local handlermsg = "expected a table" local functypes = {["function"]=true, ["string"]=true} local funcmsg = "expected function or member name" -- pickfirstset() - picks the first non-nil value and returns it local function pickfirstset(...) for i=1,select("#",...) do if select(i,...)~=nil then return select(i,...) end end end -- err() - produce real error() regarding malformed options tables etc local function err(info,inputpos,msg ) local cmdstr=" "..strsub(info.input, 1, inputpos-1) error(MAJOR..": /" ..info[0] ..cmdstr ..": "..(msg or "malformed options table"), 2) end -- usererr() - produce chatframe message regarding bad slash syntax etc local function usererr(info,inputpos,msg ) local cmdstr=strsub(info.input, 1, inputpos-1); print("/" ..info[0] .. " "..cmdstr ..": "..(msg or "malformed options table")) end -- callmethod() - call a given named method (e.g. "get", "set") with given arguments local function callmethod(info, inputpos, tab, methodtype, ...) local method = info[methodtype] if not method then err(info, inputpos, "'"..methodtype.."': not set") end info.arg = tab.arg info.option = tab info.type = tab.type if type(method)=="function" then return method(info, ...) elseif type(method)=="string" then if type(info.handler[method])~="function" then err(info, inputpos, "'"..methodtype.."': '"..method.."' is not a member function of "..tostring(info.handler)) end return info.handler[method](info.handler, info, ...) else assert(false) -- type should have already been checked on read end end -- callfunction() - call a given named function (e.g. "name", "desc") with given arguments local function callfunction(info, tab, methodtype, ...) local method = tab[methodtype] info.arg = tab.arg info.option = tab info.type = tab.type if type(method)=="function" then return method(info, ...) else assert(false) -- type should have already been checked on read end end -- do_final() - do the final step (set/execute) along with validation and confirmation local function do_final(info, inputpos, tab, methodtype, ...) if info.validate then local res = callmethod(info,inputpos,tab,"validate",...) if type(res)=="string" then usererr(info, inputpos, "'"..strsub(info.input, inputpos).."' - "..res) return end end -- console ignores .confirm callmethod(info,inputpos,tab,methodtype, ...) end -- getparam() - used by handle() to retreive and store "handler", "get", "set", etc local function getparam(info, inputpos, tab, depth, paramname, types, errormsg) local old,oldat = info[paramname], info[paramname.."_at"] local val=tab[paramname] if val~=nil then if val==false then val=nil elseif not types[type(val)] then err(info, inputpos, "'" .. paramname.. "' - "..errormsg) end info[paramname] = val info[paramname.."_at"] = depth end return old,oldat end -- iterateargs(tab) - custom iterator that iterates both t.args and t.plugins.* local dummytable={} local function iterateargs(tab) if not tab.plugins then return pairs(tab.args) end local argtabkey,argtab=next(tab.plugins) local v return function(_, k) while argtab do k,v = next(argtab, k) if k then return k,v end if argtab==tab.args then argtab=nil else argtabkey,argtab = next(tab.plugins, argtabkey) if not argtabkey then argtab=tab.args end end end end end local function checkhidden(info, inputpos, tab) if tab.cmdHidden~=nil then return tab.cmdHidden end local hidden = tab.hidden if type(hidden) == "function" or type(hidden) == "string" then info.hidden = hidden hidden = callmethod(info, inputpos, tab, 'hidden') info.hidden = nil end return hidden end local function showhelp(info, inputpos, tab, depth, noHead) if not noHead then print("|cff33ff99"..info.appName.."|r: Arguments to |cffffff78/"..info[0].."|r "..strsub(info.input,1,inputpos-1)..":") end local sortTbl = {} -- [1..n]=name local refTbl = {} -- [name]=tableref for k,v in iterateargs(tab) do if not refTbl[k] then -- a plugin overriding something in .args tinsert(sortTbl, k) refTbl[k] = v end end tsort(sortTbl, function(one, two) local o1 = refTbl[one].order or 100 local o2 = refTbl[two].order or 100 if type(o1) == "function" or type(o1) == "string" then info.order = o1 info[#info+1] = one o1 = callmethod(info, inputpos, refTbl[one], "order") info[#info] = nil info.order = nil end if type(o2) == "function" or type(o1) == "string" then info.order = o2 info[#info+1] = two o2 = callmethod(info, inputpos, refTbl[two], "order") info[#info] = nil info.order = nil end if o1<0 and o2<0 then return o1<o2 end if o2<0 then return true end if o1<0 then return false end if o1==o2 then return tostring(one)<tostring(two) end -- compare names return o1<o2 end) for i = 1, #sortTbl do local k = sortTbl[i] local v = refTbl[k] if not checkhidden(info, inputpos, v) then if v.type ~= "description" and v.type ~= "header" then -- recursively show all inline groups local name, desc = v.name, v.desc if type(name) == "function" then name = callfunction(info, v, 'name') end if type(desc) == "function" then desc = callfunction(info, v, 'desc') end if v.type == "group" and pickfirstset(v.cmdInline, v.inline, false) then print(" "..(desc or name)..":") local oldhandler,oldhandler_at = getparam(info, inputpos, v, depth, "handler", handlertypes, handlermsg) showhelp(info, inputpos, v, depth, true) info.handler,info.handler_at = oldhandler,oldhandler_at else local key = k:gsub(" ", "_") print(" |cffffff78"..key.."|r - "..(desc or name or "")) end end end end end local function keybindingValidateFunc(text) if text == nil or text == "NONE" then return nil end text = text:upper() local shift, ctrl, alt local modifier while true do if text == "-" then break end modifier, text = strsplit('-', text, 2) if text then if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then return false end if modifier == "SHIFT" then if shift then return false end shift = true end if modifier == "CTRL" then if ctrl then return false end ctrl = true end if modifier == "ALT" then if alt then return false end alt = true end else text = modifier break end end if text == "" then return false end if not text:find("^F%d+$") and text ~= "CAPSLOCK" and text:len() ~= 1 and (text:byte() < 128 or text:len() > 4) and not _G["KEY_" .. text] then return false end local s = text if shift then s = "SHIFT-" .. s end if ctrl then s = "CTRL-" .. s end if alt then s = "ALT-" .. s end return s end -- handle() - selfrecursing function that processes input->optiontable -- - depth - starts at 0 -- - retfalse - return false rather than produce error if a match is not found (used by inlined groups) local function handle(info, inputpos, tab, depth, retfalse) if not(type(tab)=="table" and type(tab.type)=="string") then err(info,inputpos) end ------------------------------------------------------------------- -- Grab hold of handler,set,get,func,etc if set (and remember old ones) -- Note that we do NOT validate if method names are correct at this stage, -- the handler may change before they're actually used! local oldhandler,oldhandler_at = getparam(info,inputpos,tab,depth,"handler",handlertypes,handlermsg) local oldset,oldset_at = getparam(info,inputpos,tab,depth,"set",functypes,funcmsg) local oldget,oldget_at = getparam(info,inputpos,tab,depth,"get",functypes,funcmsg) local oldfunc,oldfunc_at = getparam(info,inputpos,tab,depth,"func",functypes,funcmsg) local oldvalidate,oldvalidate_at = getparam(info,inputpos,tab,depth,"validate",functypes,funcmsg) --local oldconfirm,oldconfirm_at = getparam(info,inputpos,tab,depth,"confirm",functypes,funcmsg) ------------------------------------------------------------------- -- Act according to .type of this table if tab.type=="group" then ------------ group -------------------------------------------- if type(tab.args)~="table" then err(info, inputpos) end if tab.plugins and type(tab.plugins)~="table" then err(info,inputpos) end -- grab next arg from input local _,nextpos,arg = (info.input):find(" *([^ ]+) *", inputpos) if not arg then showhelp(info, inputpos, tab, depth) return end nextpos=nextpos+1 -- loop .args and try to find a key with a matching name for k,v in iterateargs(tab) do if not(type(k)=="string" and type(v)=="table" and type(v.type)=="string") then err(info,inputpos, "options table child '"..tostring(k).."' is malformed") end -- is this child an inline group? if so, traverse into it if v.type=="group" and pickfirstset(v.cmdInline, v.inline, false) then info[depth+1] = k if handle(info, inputpos, v, depth+1, true)==false then info[depth+1] = nil -- wasn't found in there, but that's ok, we just keep looking down here else return -- done, name was found in inline group end -- matching name and not a inline group elseif strlower(arg)==strlower(k:gsub(" ", "_")) then info[depth+1] = k return handle(info,nextpos,v,depth+1) end end -- no match if retfalse then -- restore old infotable members and return false to indicate failure info.handler,info.handler_at = oldhandler,oldhandler_at info.set,info.set_at = oldset,oldset_at info.get,info.get_at = oldget,oldget_at info.func,info.func_at = oldfunc,oldfunc_at info.validate,info.validate_at = oldvalidate,oldvalidate_at --info.confirm,info.confirm_at = oldconfirm,oldconfirm_at return false end -- couldn't find the command, display error usererr(info, inputpos, "'"..arg.."' - " .. L["unknown argument"]) return end local str = strsub(info.input,inputpos); if tab.type=="execute" then ------------ execute -------------------------------------------- do_final(info, inputpos, tab, "func") elseif tab.type=="input" then ------------ input -------------------------------------------- local res = true if tab.pattern then if not(type(tab.pattern)=="string") then err(info, inputpos, "'pattern' - expected a string") end if not strmatch(str, tab.pattern) then usererr(info, inputpos, "'"..str.."' - " .. L["invalid input"]) return end end do_final(info, inputpos, tab, "set", str) elseif tab.type=="toggle" then ------------ toggle -------------------------------------------- local b local str = strtrim(strlower(str)) if str=="" then b = callmethod(info, inputpos, tab, "get") if tab.tristate then --cycle in true, nil, false order if b then b = nil elseif b == nil then b = false else b = true end else b = not b end elseif str==L["on"] then b = true elseif str==L["off"] then b = false elseif tab.tristate and str==L["default"] then b = nil else if tab.tristate then usererr(info, inputpos, format(L["'%s' - expected 'on', 'off' or 'default', or no argument to toggle."], str)) else usererr(info, inputpos, format(L["'%s' - expected 'on' or 'off', or no argument to toggle."], str)) end return end do_final(info, inputpos, tab, "set", b) elseif tab.type=="range" then ------------ range -------------------------------------------- local val = tonumber(str) if not val then usererr(info, inputpos, "'"..str.."' - "..L["expected number"]) return end if type(info.step)=="number" then val = val- (val % info.step) end if type(info.min)=="number" and val<info.min then usererr(info, inputpos, val.." - "..format(L["must be equal to or higher than %s"], tostring(info.min)) ) return end if type(info.max)=="number" and val>info.max then usererr(info, inputpos, val.." - "..format(L["must be equal to or lower than %s"], tostring(info.max)) ) return end do_final(info, inputpos, tab, "set", val) elseif tab.type=="select" then ------------ select ------------------------------------ local str = strtrim(strlower(str)) local values = tab.values if type(values) == "function" or type(values) == "string" then info.values = values values = callmethod(info, inputpos, tab, "values") info.values = nil end if str == "" then local b = callmethod(info, inputpos, tab, "get") local fmt = "|cffffff78- [%s]|r %s" local fmt_sel = "|cffffff78- [%s]|r %s |cffff0000*|r" print(L["Options for |cffffff78"..info[#info].."|r:"]) for k, v in pairs(values) do if b == k then print(fmt_sel:format(k, v)) else print(fmt:format(k, v)) end end return end local ok for k,v in pairs(values) do if strlower(k)==str then str = k -- overwrite with key (in case of case mismatches) ok = true break end end if not ok then usererr(info, inputpos, "'"..str.."' - "..L["unknown selection"]) return end do_final(info, inputpos, tab, "set", str) elseif tab.type=="multiselect" then ------------ multiselect ------------------------------------------- local str = strtrim(strlower(str)) local values = tab.values if type(values) == "function" or type(values) == "string" then info.values = values values = callmethod(info, inputpos, tab, "values") info.values = nil end if str == "" then local fmt = "|cffffff78- [%s]|r %s" local fmt_sel = "|cffffff78- [%s]|r %s |cffff0000*|r" print(L["Options for |cffffff78"..info[#info].."|r (multiple possible):"]) for k, v in pairs(values) do if callmethod(info, inputpos, tab, "get", k) then print(fmt_sel:format(k, v)) else print(fmt:format(k, v)) end end return end --build a table of the selections, checking that they exist --parse for =on =off =default in the process --table will be key = true for options that should toggle, key = [on|off|default] for options to be set local sels = {} for v in str:gmatch("[^ ]+") do --parse option=on etc local opt, val = v:match('(.+)=(.+)') --get option if toggling if not opt then opt = v end --check that the opt is valid local ok for k,v in pairs(values) do if strlower(k)==opt then opt = k -- overwrite with key (in case of case mismatches) ok = true break end end if not ok then usererr(info, inputpos, "'"..opt.."' - "..L["unknown selection"]) return end --check that if val was supplied it is valid if val then if val == L["on"] or val == L["off"] or (tab.tristate and val == L["default"]) then --val is valid insert it sels[opt] = val else if tab.tristate then usererr(info, inputpos, format(L["'%s' '%s' - expected 'on', 'off' or 'default', or no argument to toggle."], v, val)) else usererr(info, inputpos, format(L["'%s' '%s' - expected 'on' or 'off', or no argument to toggle."], v, val)) end return end else -- no val supplied, toggle sels[opt] = true end end for opt, val in pairs(sels) do local newval if (val == true) then --toggle the option local b = callmethod(info, inputpos, tab, "get", opt) if tab.tristate then --cycle in true, nil, false order if b then b = nil elseif b == nil then b = false else b = true end else b = not b end newval = b else --set the option as specified if val==L["on"] then newval = true elseif val==L["off"] then newval = false elseif val==L["default"] then newval = nil end end do_final(info, inputpos, tab, "set", opt, newval) end elseif tab.type=="color" then ------------ color -------------------------------------------- local str = strtrim(strlower(str)) if str == "" then --TODO: Show current value return end local r, g, b, a if tab.hasAlpha then if str:len() == 8 and str:find("^%x*$") then --parse a hex string r,g,b,a = tonumber(str:sub(1, 2), 16) / 255, tonumber(str:sub(3, 4), 16) / 255, tonumber(str:sub(5, 6), 16) / 255, tonumber(str:sub(7, 8), 16) / 255 else --parse seperate values r,g,b,a = str:match("^([%d%.]+) ([%d%.]+) ([%d%.]+) ([%d%.]+)$") r,g,b,a = tonumber(r), tonumber(g), tonumber(b), tonumber(a) end if not (r and g and b and a) then usererr(info, inputpos, format(L["'%s' - expected 'RRGGBBAA' or 'r g b a'."], str)) return end if r >= 0.0 and r <= 1.0 and g >= 0.0 and g <= 1.0 and b >= 0.0 and b <= 1.0 and a >= 0.0 and a <= 1.0 then --values are valid elseif r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255 and a >= 0 and a <= 255 then --values are valid 0..255, convert to 0..1 r = r / 255 g = g / 255 b = b / 255 a = a / 255 else --values are invalid usererr(info, inputpos, format(L["'%s' - values must all be either in the range 0..1 or 0..255."], str)) end else a = 1.0 if str:len() == 6 and str:find("^%x*$") then --parse a hex string r,g,b = tonumber(str:sub(1, 2), 16) / 255, tonumber(str:sub(3, 4), 16) / 255, tonumber(str:sub(5, 6), 16) / 255 else --parse seperate values r,g,b = str:match("^([%d%.]+) ([%d%.]+) ([%d%.]+)$") r,g,b = tonumber(r), tonumber(g), tonumber(b) end if not (r and g and b) then usererr(info, inputpos, format(L["'%s' - expected 'RRGGBB' or 'r g b'."], str)) return end if r >= 0.0 and r <= 1.0 and g >= 0.0 and g <= 1.0 and b >= 0.0 and b <= 1.0 then --values are valid elseif r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255 then --values are valid 0..255, convert to 0..1 r = r / 255 g = g / 255 b = b / 255 else --values are invalid usererr(info, inputpos, format(L["'%s' - values must all be either in the range 0-1 or 0-255."], str)) end end do_final(info, inputpos, tab, "set", r,g,b,a) elseif tab.type=="keybinding" then ------------ keybinding -------------------------------------------- local str = strtrim(strlower(str)) if str == "" then --TODO: Show current value return end local value = keybindingValidateFunc(str:upper()) if value == false then usererr(info, inputpos, format(L["'%s' - Invalid Keybinding."], str)) return end do_final(info, inputpos, tab, "set", value) elseif tab.type=="description" then ------------ description -------------------- -- ignore description, GUI config only else err(info, inputpos, "unknown options table item type '"..tostring(tab.type).."'") end end --- Handle the chat command. -- This is usually called from a chat command handler to parse the command input as operations on an aceoptions table.\\ -- AceConfigCmd uses this function internally when a slash command is registered with `:CreateChatCommand` -- @param slashcmd The slash command WITHOUT leading slash (only used for error output) -- @param appName The application name as given to `:RegisterOptionsTable()` -- @param input The commandline input (as given by the WoW handler, i.e. without the command itself) -- @usage -- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceConsole-3.0") -- -- Use AceConsole-3.0 to register a Chat Command -- MyAddon:RegisterChatCommand("mychat", "ChatCommand") -- -- -- Show the GUI if no input is supplied, otherwise handle the chat input. -- function MyAddon:ChatCommand(input) -- -- Assuming "MyOptions" is the appName of a valid options table -- if not input or input:trim() == "" then -- LibStub("AceConfigDialog-3.0"):Open("MyOptions") -- else -- LibStub("AceConfigCmd-3.0").HandleCommand(MyAddon, "mychat", "MyOptions", input) -- end -- end function AceConfigCmd:HandleCommand(slashcmd, appName, input) local optgetter = cfgreg:GetOptionsTable(appName) if not optgetter then error([[Usage: HandleCommand("slashcmd", "appName", "input"): 'appName' - no options table "]]..tostring(appName)..[[" has been registered]], 2) end local options = assert( optgetter("cmd", MAJOR) ) local info = { -- Don't try to recycle this, it gets handed off to callbacks and whatnot [0] = slashcmd, appName = appName, options = options, input = input, self = self, handler = self, uiType = "cmd", uiName = MAJOR, } handle(info, 1, options, 0) -- (info, inputpos, table, depth) end --- Utility function to create a slash command handler. -- Also registers tab completion with AceTab -- @param slashcmd The slash command WITHOUT leading slash (only used for error output) -- @param appName The application name as given to `:RegisterOptionsTable()` function AceConfigCmd:CreateChatCommand(slashcmd, appName) if not AceConsole then AceConsole = LibStub(AceConsoleName) end if AceConsole.RegisterChatCommand(self, slashcmd, function(input) AceConfigCmd.HandleCommand(self, slashcmd, appName, input) -- upgradable end, true) then -- succesfully registered so lets get the command -> app table in commands[slashcmd] = appName end end --- Utility function that returns the options table that belongs to a slashcommand. -- Designed to be used for the AceTab interface. -- @param slashcmd The slash command WITHOUT leading slash (only used for error output) -- @return The options table associated with the slash command (or nil if the slash command was not registered) function AceConfigCmd:GetChatCommandOptions(slashcmd) return commands[slashcmd] end
