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