annotate Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.lua @ 0:98c6f55e6619

First commit
author Xiiph
date Sat, 05 Feb 2011 16:45:02 +0100
parents
children
rev   line source
Xiiph@0 1 --- AceConfigCmd-3.0 handles access to an options table through the "command line" interface via the ChatFrames.
Xiiph@0 2 -- @class file
Xiiph@0 3 -- @name AceConfigCmd-3.0
Xiiph@0 4 -- @release $Id: AceConfigCmd-3.0.lua 904 2009-12-13 11:56:37Z nevcairiel $
Xiiph@0 5
Xiiph@0 6 --[[
Xiiph@0 7 AceConfigCmd-3.0
Xiiph@0 8
Xiiph@0 9 Handles commandline optionstable access
Xiiph@0 10
Xiiph@0 11 REQUIRES: AceConsole-3.0 for command registration (loaded on demand)
Xiiph@0 12
Xiiph@0 13 ]]
Xiiph@0 14
Xiiph@0 15 -- TODO: plugin args
Xiiph@0 16
Xiiph@0 17
Xiiph@0 18 local MAJOR, MINOR = "AceConfigCmd-3.0", 12
Xiiph@0 19 local AceConfigCmd = LibStub:NewLibrary(MAJOR, MINOR)
Xiiph@0 20
Xiiph@0 21 if not AceConfigCmd then return end
Xiiph@0 22
Xiiph@0 23 AceConfigCmd.commands = AceConfigCmd.commands or {}
Xiiph@0 24 local commands = AceConfigCmd.commands
Xiiph@0 25
Xiiph@0 26 local cfgreg = LibStub("AceConfigRegistry-3.0")
Xiiph@0 27 local AceConsole -- LoD
Xiiph@0 28 local AceConsoleName = "AceConsole-3.0"
Xiiph@0 29
Xiiph@0 30 -- Lua APIs
Xiiph@0 31 local strsub, strsplit, strlower, strmatch, strtrim = string.sub, string.split, string.lower, string.match, string.trim
Xiiph@0 32 local format, tonumber, tostring = string.format, tonumber, tostring
Xiiph@0 33 local tsort, tinsert = table.sort, table.insert
Xiiph@0 34 local select, pairs, next, type = select, pairs, next, type
Xiiph@0 35 local error, assert = error, assert
Xiiph@0 36
Xiiph@0 37 -- WoW APIs
Xiiph@0 38 local _G = _G
Xiiph@0 39
Xiiph@0 40 -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
Xiiph@0 41 -- List them here for Mikk's FindGlobals script
Xiiph@0 42 -- GLOBALS: LibStub, SELECTED_CHAT_FRAME, DEFAULT_CHAT_FRAME
Xiiph@0 43
Xiiph@0 44
Xiiph@0 45 local L = setmetatable({}, { -- TODO: replace with proper locale
Xiiph@0 46 __index = function(self,k) return k end
Xiiph@0 47 })
Xiiph@0 48
Xiiph@0 49
Xiiph@0 50
Xiiph@0 51 local function print(msg)
Xiiph@0 52 (SELECTED_CHAT_FRAME or DEFAULT_CHAT_FRAME):AddMessage(msg)
Xiiph@0 53 end
Xiiph@0 54
Xiiph@0 55 -- constants used by getparam() calls below
Xiiph@0 56
Xiiph@0 57 local handlertypes = {["table"]=true}
Xiiph@0 58 local handlermsg = "expected a table"
Xiiph@0 59
Xiiph@0 60 local functypes = {["function"]=true, ["string"]=true}
Xiiph@0 61 local funcmsg = "expected function or member name"
Xiiph@0 62
Xiiph@0 63
Xiiph@0 64 -- pickfirstset() - picks the first non-nil value and returns it
Xiiph@0 65
Xiiph@0 66 local function pickfirstset(...)
Xiiph@0 67 for i=1,select("#",...) do
Xiiph@0 68 if select(i,...)~=nil then
Xiiph@0 69 return select(i,...)
Xiiph@0 70 end
Xiiph@0 71 end
Xiiph@0 72 end
Xiiph@0 73
Xiiph@0 74
Xiiph@0 75 -- err() - produce real error() regarding malformed options tables etc
Xiiph@0 76
Xiiph@0 77 local function err(info,inputpos,msg )
Xiiph@0 78 local cmdstr=" "..strsub(info.input, 1, inputpos-1)
Xiiph@0 79 error(MAJOR..": /" ..info[0] ..cmdstr ..": "..(msg or "malformed options table"), 2)
Xiiph@0 80 end
Xiiph@0 81
Xiiph@0 82
Xiiph@0 83 -- usererr() - produce chatframe message regarding bad slash syntax etc
Xiiph@0 84
Xiiph@0 85 local function usererr(info,inputpos,msg )
Xiiph@0 86 local cmdstr=strsub(info.input, 1, inputpos-1);
Xiiph@0 87 print("/" ..info[0] .. " "..cmdstr ..": "..(msg or "malformed options table"))
Xiiph@0 88 end
Xiiph@0 89
Xiiph@0 90
Xiiph@0 91 -- callmethod() - call a given named method (e.g. "get", "set") with given arguments
Xiiph@0 92
Xiiph@0 93 local function callmethod(info, inputpos, tab, methodtype, ...)
Xiiph@0 94 local method = info[methodtype]
Xiiph@0 95 if not method then
Xiiph@0 96 err(info, inputpos, "'"..methodtype.."': not set")
Xiiph@0 97 end
Xiiph@0 98
Xiiph@0 99 info.arg = tab.arg
Xiiph@0 100 info.option = tab
Xiiph@0 101 info.type = tab.type
Xiiph@0 102
Xiiph@0 103 if type(method)=="function" then
Xiiph@0 104 return method(info, ...)
Xiiph@0 105 elseif type(method)=="string" then
Xiiph@0 106 if type(info.handler[method])~="function" then
Xiiph@0 107 err(info, inputpos, "'"..methodtype.."': '"..method.."' is not a member function of "..tostring(info.handler))
Xiiph@0 108 end
Xiiph@0 109 return info.handler[method](info.handler, info, ...)
Xiiph@0 110 else
Xiiph@0 111 assert(false) -- type should have already been checked on read
Xiiph@0 112 end
Xiiph@0 113 end
Xiiph@0 114
Xiiph@0 115 -- callfunction() - call a given named function (e.g. "name", "desc") with given arguments
Xiiph@0 116
Xiiph@0 117 local function callfunction(info, tab, methodtype, ...)
Xiiph@0 118 local method = tab[methodtype]
Xiiph@0 119
Xiiph@0 120 info.arg = tab.arg
Xiiph@0 121 info.option = tab
Xiiph@0 122 info.type = tab.type
Xiiph@0 123
Xiiph@0 124 if type(method)=="function" then
Xiiph@0 125 return method(info, ...)
Xiiph@0 126 else
Xiiph@0 127 assert(false) -- type should have already been checked on read
Xiiph@0 128 end
Xiiph@0 129 end
Xiiph@0 130
Xiiph@0 131 -- do_final() - do the final step (set/execute) along with validation and confirmation
Xiiph@0 132
Xiiph@0 133 local function do_final(info, inputpos, tab, methodtype, ...)
Xiiph@0 134 if info.validate then
Xiiph@0 135 local res = callmethod(info,inputpos,tab,"validate",...)
Xiiph@0 136 if type(res)=="string" then
Xiiph@0 137 usererr(info, inputpos, "'"..strsub(info.input, inputpos).."' - "..res)
Xiiph@0 138 return
Xiiph@0 139 end
Xiiph@0 140 end
Xiiph@0 141 -- console ignores .confirm
Xiiph@0 142
Xiiph@0 143 callmethod(info,inputpos,tab,methodtype, ...)
Xiiph@0 144 end
Xiiph@0 145
Xiiph@0 146
Xiiph@0 147 -- getparam() - used by handle() to retreive and store "handler", "get", "set", etc
Xiiph@0 148
Xiiph@0 149 local function getparam(info, inputpos, tab, depth, paramname, types, errormsg)
Xiiph@0 150 local old,oldat = info[paramname], info[paramname.."_at"]
Xiiph@0 151 local val=tab[paramname]
Xiiph@0 152 if val~=nil then
Xiiph@0 153 if val==false then
Xiiph@0 154 val=nil
Xiiph@0 155 elseif not types[type(val)] then
Xiiph@0 156 err(info, inputpos, "'" .. paramname.. "' - "..errormsg)
Xiiph@0 157 end
Xiiph@0 158 info[paramname] = val
Xiiph@0 159 info[paramname.."_at"] = depth
Xiiph@0 160 end
Xiiph@0 161 return old,oldat
Xiiph@0 162 end
Xiiph@0 163
Xiiph@0 164
Xiiph@0 165 -- iterateargs(tab) - custom iterator that iterates both t.args and t.plugins.*
Xiiph@0 166 local dummytable={}
Xiiph@0 167
Xiiph@0 168 local function iterateargs(tab)
Xiiph@0 169 if not tab.plugins then
Xiiph@0 170 return pairs(tab.args)
Xiiph@0 171 end
Xiiph@0 172
Xiiph@0 173 local argtabkey,argtab=next(tab.plugins)
Xiiph@0 174 local v
Xiiph@0 175
Xiiph@0 176 return function(_, k)
Xiiph@0 177 while argtab do
Xiiph@0 178 k,v = next(argtab, k)
Xiiph@0 179 if k then return k,v end
Xiiph@0 180 if argtab==tab.args then
Xiiph@0 181 argtab=nil
Xiiph@0 182 else
Xiiph@0 183 argtabkey,argtab = next(tab.plugins, argtabkey)
Xiiph@0 184 if not argtabkey then
Xiiph@0 185 argtab=tab.args
Xiiph@0 186 end
Xiiph@0 187 end
Xiiph@0 188 end
Xiiph@0 189 end
Xiiph@0 190 end
Xiiph@0 191
Xiiph@0 192 local function checkhidden(info, inputpos, tab)
Xiiph@0 193 if tab.cmdHidden~=nil then
Xiiph@0 194 return tab.cmdHidden
Xiiph@0 195 end
Xiiph@0 196 local hidden = tab.hidden
Xiiph@0 197 if type(hidden) == "function" or type(hidden) == "string" then
Xiiph@0 198 info.hidden = hidden
Xiiph@0 199 hidden = callmethod(info, inputpos, tab, 'hidden')
Xiiph@0 200 info.hidden = nil
Xiiph@0 201 end
Xiiph@0 202 return hidden
Xiiph@0 203 end
Xiiph@0 204
Xiiph@0 205 local function showhelp(info, inputpos, tab, depth, noHead)
Xiiph@0 206 if not noHead then
Xiiph@0 207 print("|cff33ff99"..info.appName.."|r: Arguments to |cffffff78/"..info[0].."|r "..strsub(info.input,1,inputpos-1)..":")
Xiiph@0 208 end
Xiiph@0 209
Xiiph@0 210 local sortTbl = {} -- [1..n]=name
Xiiph@0 211 local refTbl = {} -- [name]=tableref
Xiiph@0 212
Xiiph@0 213 for k,v in iterateargs(tab) do
Xiiph@0 214 if not refTbl[k] then -- a plugin overriding something in .args
Xiiph@0 215 tinsert(sortTbl, k)
Xiiph@0 216 refTbl[k] = v
Xiiph@0 217 end
Xiiph@0 218 end
Xiiph@0 219
Xiiph@0 220 tsort(sortTbl, function(one, two)
Xiiph@0 221 local o1 = refTbl[one].order or 100
Xiiph@0 222 local o2 = refTbl[two].order or 100
Xiiph@0 223 if type(o1) == "function" or type(o1) == "string" then
Xiiph@0 224 info.order = o1
Xiiph@0 225 info[#info+1] = one
Xiiph@0 226 o1 = callmethod(info, inputpos, refTbl[one], "order")
Xiiph@0 227 info[#info] = nil
Xiiph@0 228 info.order = nil
Xiiph@0 229 end
Xiiph@0 230 if type(o2) == "function" or type(o1) == "string" then
Xiiph@0 231 info.order = o2
Xiiph@0 232 info[#info+1] = two
Xiiph@0 233 o2 = callmethod(info, inputpos, refTbl[two], "order")
Xiiph@0 234 info[#info] = nil
Xiiph@0 235 info.order = nil
Xiiph@0 236 end
Xiiph@0 237 if o1<0 and o2<0 then return o1<o2 end
Xiiph@0 238 if o2<0 then return true end
Xiiph@0 239 if o1<0 then return false end
Xiiph@0 240 if o1==o2 then return tostring(one)<tostring(two) end -- compare names
Xiiph@0 241 return o1<o2
Xiiph@0 242 end)
Xiiph@0 243
Xiiph@0 244 for i = 1, #sortTbl do
Xiiph@0 245 local k = sortTbl[i]
Xiiph@0 246 local v = refTbl[k]
Xiiph@0 247 if not checkhidden(info, inputpos, v) then
Xiiph@0 248 if v.type ~= "description" and v.type ~= "header" then
Xiiph@0 249 -- recursively show all inline groups
Xiiph@0 250 local name, desc = v.name, v.desc
Xiiph@0 251 if type(name) == "function" then
Xiiph@0 252 name = callfunction(info, v, 'name')
Xiiph@0 253 end
Xiiph@0 254 if type(desc) == "function" then
Xiiph@0 255 desc = callfunction(info, v, 'desc')
Xiiph@0 256 end
Xiiph@0 257 if v.type == "group" and pickfirstset(v.cmdInline, v.inline, false) then
Xiiph@0 258 print(" "..(desc or name)..":")
Xiiph@0 259 local oldhandler,oldhandler_at = getparam(info, inputpos, v, depth, "handler", handlertypes, handlermsg)
Xiiph@0 260 showhelp(info, inputpos, v, depth, true)
Xiiph@0 261 info.handler,info.handler_at = oldhandler,oldhandler_at
Xiiph@0 262 else
Xiiph@0 263 local key = k:gsub(" ", "_")
Xiiph@0 264 print(" |cffffff78"..key.."|r - "..(desc or name or ""))
Xiiph@0 265 end
Xiiph@0 266 end
Xiiph@0 267 end
Xiiph@0 268 end
Xiiph@0 269 end
Xiiph@0 270
Xiiph@0 271
Xiiph@0 272 local function keybindingValidateFunc(text)
Xiiph@0 273 if text == nil or text == "NONE" then
Xiiph@0 274 return nil
Xiiph@0 275 end
Xiiph@0 276 text = text:upper()
Xiiph@0 277 local shift, ctrl, alt
Xiiph@0 278 local modifier
Xiiph@0 279 while true do
Xiiph@0 280 if text == "-" then
Xiiph@0 281 break
Xiiph@0 282 end
Xiiph@0 283 modifier, text = strsplit('-', text, 2)
Xiiph@0 284 if text then
Xiiph@0 285 if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then
Xiiph@0 286 return false
Xiiph@0 287 end
Xiiph@0 288 if modifier == "SHIFT" then
Xiiph@0 289 if shift then
Xiiph@0 290 return false
Xiiph@0 291 end
Xiiph@0 292 shift = true
Xiiph@0 293 end
Xiiph@0 294 if modifier == "CTRL" then
Xiiph@0 295 if ctrl then
Xiiph@0 296 return false
Xiiph@0 297 end
Xiiph@0 298 ctrl = true
Xiiph@0 299 end
Xiiph@0 300 if modifier == "ALT" then
Xiiph@0 301 if alt then
Xiiph@0 302 return false
Xiiph@0 303 end
Xiiph@0 304 alt = true
Xiiph@0 305 end
Xiiph@0 306 else
Xiiph@0 307 text = modifier
Xiiph@0 308 break
Xiiph@0 309 end
Xiiph@0 310 end
Xiiph@0 311 if text == "" then
Xiiph@0 312 return false
Xiiph@0 313 end
Xiiph@0 314 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
Xiiph@0 315 return false
Xiiph@0 316 end
Xiiph@0 317 local s = text
Xiiph@0 318 if shift then
Xiiph@0 319 s = "SHIFT-" .. s
Xiiph@0 320 end
Xiiph@0 321 if ctrl then
Xiiph@0 322 s = "CTRL-" .. s
Xiiph@0 323 end
Xiiph@0 324 if alt then
Xiiph@0 325 s = "ALT-" .. s
Xiiph@0 326 end
Xiiph@0 327 return s
Xiiph@0 328 end
Xiiph@0 329
Xiiph@0 330 -- handle() - selfrecursing function that processes input->optiontable
Xiiph@0 331 -- - depth - starts at 0
Xiiph@0 332 -- - retfalse - return false rather than produce error if a match is not found (used by inlined groups)
Xiiph@0 333
Xiiph@0 334 local function handle(info, inputpos, tab, depth, retfalse)
Xiiph@0 335
Xiiph@0 336 if not(type(tab)=="table" and type(tab.type)=="string") then err(info,inputpos) end
Xiiph@0 337
Xiiph@0 338 -------------------------------------------------------------------
Xiiph@0 339 -- Grab hold of handler,set,get,func,etc if set (and remember old ones)
Xiiph@0 340 -- Note that we do NOT validate if method names are correct at this stage,
Xiiph@0 341 -- the handler may change before they're actually used!
Xiiph@0 342
Xiiph@0 343 local oldhandler,oldhandler_at = getparam(info,inputpos,tab,depth,"handler",handlertypes,handlermsg)
Xiiph@0 344 local oldset,oldset_at = getparam(info,inputpos,tab,depth,"set",functypes,funcmsg)
Xiiph@0 345 local oldget,oldget_at = getparam(info,inputpos,tab,depth,"get",functypes,funcmsg)
Xiiph@0 346 local oldfunc,oldfunc_at = getparam(info,inputpos,tab,depth,"func",functypes,funcmsg)
Xiiph@0 347 local oldvalidate,oldvalidate_at = getparam(info,inputpos,tab,depth,"validate",functypes,funcmsg)
Xiiph@0 348 --local oldconfirm,oldconfirm_at = getparam(info,inputpos,tab,depth,"confirm",functypes,funcmsg)
Xiiph@0 349
Xiiph@0 350 -------------------------------------------------------------------
Xiiph@0 351 -- Act according to .type of this table
Xiiph@0 352
Xiiph@0 353 if tab.type=="group" then
Xiiph@0 354 ------------ group --------------------------------------------
Xiiph@0 355
Xiiph@0 356 if type(tab.args)~="table" then err(info, inputpos) end
Xiiph@0 357 if tab.plugins and type(tab.plugins)~="table" then err(info,inputpos) end
Xiiph@0 358
Xiiph@0 359 -- grab next arg from input
Xiiph@0 360 local _,nextpos,arg = (info.input):find(" *([^ ]+) *", inputpos)
Xiiph@0 361 if not arg then
Xiiph@0 362 showhelp(info, inputpos, tab, depth)
Xiiph@0 363 return
Xiiph@0 364 end
Xiiph@0 365 nextpos=nextpos+1
Xiiph@0 366
Xiiph@0 367 -- loop .args and try to find a key with a matching name
Xiiph@0 368 for k,v in iterateargs(tab) do
Xiiph@0 369 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
Xiiph@0 370
Xiiph@0 371 -- is this child an inline group? if so, traverse into it
Xiiph@0 372 if v.type=="group" and pickfirstset(v.cmdInline, v.inline, false) then
Xiiph@0 373 info[depth+1] = k
Xiiph@0 374 if handle(info, inputpos, v, depth+1, true)==false then
Xiiph@0 375 info[depth+1] = nil
Xiiph@0 376 -- wasn't found in there, but that's ok, we just keep looking down here
Xiiph@0 377 else
Xiiph@0 378 return -- done, name was found in inline group
Xiiph@0 379 end
Xiiph@0 380 -- matching name and not a inline group
Xiiph@0 381 elseif strlower(arg)==strlower(k:gsub(" ", "_")) then
Xiiph@0 382 info[depth+1] = k
Xiiph@0 383 return handle(info,nextpos,v,depth+1)
Xiiph@0 384 end
Xiiph@0 385 end
Xiiph@0 386
Xiiph@0 387 -- no match
Xiiph@0 388 if retfalse then
Xiiph@0 389 -- restore old infotable members and return false to indicate failure
Xiiph@0 390 info.handler,info.handler_at = oldhandler,oldhandler_at
Xiiph@0 391 info.set,info.set_at = oldset,oldset_at
Xiiph@0 392 info.get,info.get_at = oldget,oldget_at
Xiiph@0 393 info.func,info.func_at = oldfunc,oldfunc_at
Xiiph@0 394 info.validate,info.validate_at = oldvalidate,oldvalidate_at
Xiiph@0 395 --info.confirm,info.confirm_at = oldconfirm,oldconfirm_at
Xiiph@0 396 return false
Xiiph@0 397 end
Xiiph@0 398
Xiiph@0 399 -- couldn't find the command, display error
Xiiph@0 400 usererr(info, inputpos, "'"..arg.."' - " .. L["unknown argument"])
Xiiph@0 401 return
Xiiph@0 402 end
Xiiph@0 403
Xiiph@0 404 local str = strsub(info.input,inputpos);
Xiiph@0 405
Xiiph@0 406 if tab.type=="execute" then
Xiiph@0 407 ------------ execute --------------------------------------------
Xiiph@0 408 do_final(info, inputpos, tab, "func")
Xiiph@0 409
Xiiph@0 410
Xiiph@0 411
Xiiph@0 412 elseif tab.type=="input" then
Xiiph@0 413 ------------ input --------------------------------------------
Xiiph@0 414
Xiiph@0 415 local res = true
Xiiph@0 416 if tab.pattern then
Xiiph@0 417 if not(type(tab.pattern)=="string") then err(info, inputpos, "'pattern' - expected a string") end
Xiiph@0 418 if not strmatch(str, tab.pattern) then
Xiiph@0 419 usererr(info, inputpos, "'"..str.."' - " .. L["invalid input"])
Xiiph@0 420 return
Xiiph@0 421 end
Xiiph@0 422 end
Xiiph@0 423
Xiiph@0 424 do_final(info, inputpos, tab, "set", str)
Xiiph@0 425
Xiiph@0 426
Xiiph@0 427
Xiiph@0 428 elseif tab.type=="toggle" then
Xiiph@0 429 ------------ toggle --------------------------------------------
Xiiph@0 430 local b
Xiiph@0 431 local str = strtrim(strlower(str))
Xiiph@0 432 if str=="" then
Xiiph@0 433 b = callmethod(info, inputpos, tab, "get")
Xiiph@0 434
Xiiph@0 435 if tab.tristate then
Xiiph@0 436 --cycle in true, nil, false order
Xiiph@0 437 if b then
Xiiph@0 438 b = nil
Xiiph@0 439 elseif b == nil then
Xiiph@0 440 b = false
Xiiph@0 441 else
Xiiph@0 442 b = true
Xiiph@0 443 end
Xiiph@0 444 else
Xiiph@0 445 b = not b
Xiiph@0 446 end
Xiiph@0 447
Xiiph@0 448 elseif str==L["on"] then
Xiiph@0 449 b = true
Xiiph@0 450 elseif str==L["off"] then
Xiiph@0 451 b = false
Xiiph@0 452 elseif tab.tristate and str==L["default"] then
Xiiph@0 453 b = nil
Xiiph@0 454 else
Xiiph@0 455 if tab.tristate then
Xiiph@0 456 usererr(info, inputpos, format(L["'%s' - expected 'on', 'off' or 'default', or no argument to toggle."], str))
Xiiph@0 457 else
Xiiph@0 458 usererr(info, inputpos, format(L["'%s' - expected 'on' or 'off', or no argument to toggle."], str))
Xiiph@0 459 end
Xiiph@0 460 return
Xiiph@0 461 end
Xiiph@0 462
Xiiph@0 463 do_final(info, inputpos, tab, "set", b)
Xiiph@0 464
Xiiph@0 465
Xiiph@0 466 elseif tab.type=="range" then
Xiiph@0 467 ------------ range --------------------------------------------
Xiiph@0 468 local val = tonumber(str)
Xiiph@0 469 if not val then
Xiiph@0 470 usererr(info, inputpos, "'"..str.."' - "..L["expected number"])
Xiiph@0 471 return
Xiiph@0 472 end
Xiiph@0 473 if type(info.step)=="number" then
Xiiph@0 474 val = val- (val % info.step)
Xiiph@0 475 end
Xiiph@0 476 if type(info.min)=="number" and val<info.min then
Xiiph@0 477 usererr(info, inputpos, val.." - "..format(L["must be equal to or higher than %s"], tostring(info.min)) )
Xiiph@0 478 return
Xiiph@0 479 end
Xiiph@0 480 if type(info.max)=="number" and val>info.max then
Xiiph@0 481 usererr(info, inputpos, val.." - "..format(L["must be equal to or lower than %s"], tostring(info.max)) )
Xiiph@0 482 return
Xiiph@0 483 end
Xiiph@0 484
Xiiph@0 485 do_final(info, inputpos, tab, "set", val)
Xiiph@0 486
Xiiph@0 487
Xiiph@0 488 elseif tab.type=="select" then
Xiiph@0 489 ------------ select ------------------------------------
Xiiph@0 490 local str = strtrim(strlower(str))
Xiiph@0 491
Xiiph@0 492 local values = tab.values
Xiiph@0 493 if type(values) == "function" or type(values) == "string" then
Xiiph@0 494 info.values = values
Xiiph@0 495 values = callmethod(info, inputpos, tab, "values")
Xiiph@0 496 info.values = nil
Xiiph@0 497 end
Xiiph@0 498
Xiiph@0 499 if str == "" then
Xiiph@0 500 local b = callmethod(info, inputpos, tab, "get")
Xiiph@0 501 local fmt = "|cffffff78- [%s]|r %s"
Xiiph@0 502 local fmt_sel = "|cffffff78- [%s]|r %s |cffff0000*|r"
Xiiph@0 503 print(L["Options for |cffffff78"..info[#info].."|r:"])
Xiiph@0 504 for k, v in pairs(values) do
Xiiph@0 505 if b == k then
Xiiph@0 506 print(fmt_sel:format(k, v))
Xiiph@0 507 else
Xiiph@0 508 print(fmt:format(k, v))
Xiiph@0 509 end
Xiiph@0 510 end
Xiiph@0 511 return
Xiiph@0 512 end
Xiiph@0 513
Xiiph@0 514 local ok
Xiiph@0 515 for k,v in pairs(values) do
Xiiph@0 516 if strlower(k)==str then
Xiiph@0 517 str = k -- overwrite with key (in case of case mismatches)
Xiiph@0 518 ok = true
Xiiph@0 519 break
Xiiph@0 520 end
Xiiph@0 521 end
Xiiph@0 522 if not ok then
Xiiph@0 523 usererr(info, inputpos, "'"..str.."' - "..L["unknown selection"])
Xiiph@0 524 return
Xiiph@0 525 end
Xiiph@0 526
Xiiph@0 527 do_final(info, inputpos, tab, "set", str)
Xiiph@0 528
Xiiph@0 529 elseif tab.type=="multiselect" then
Xiiph@0 530 ------------ multiselect -------------------------------------------
Xiiph@0 531 local str = strtrim(strlower(str))
Xiiph@0 532
Xiiph@0 533 local values = tab.values
Xiiph@0 534 if type(values) == "function" or type(values) == "string" then
Xiiph@0 535 info.values = values
Xiiph@0 536 values = callmethod(info, inputpos, tab, "values")
Xiiph@0 537 info.values = nil
Xiiph@0 538 end
Xiiph@0 539
Xiiph@0 540 if str == "" then
Xiiph@0 541 local fmt = "|cffffff78- [%s]|r %s"
Xiiph@0 542 local fmt_sel = "|cffffff78- [%s]|r %s |cffff0000*|r"
Xiiph@0 543 print(L["Options for |cffffff78"..info[#info].."|r (multiple possible):"])
Xiiph@0 544 for k, v in pairs(values) do
Xiiph@0 545 if callmethod(info, inputpos, tab, "get", k) then
Xiiph@0 546 print(fmt_sel:format(k, v))
Xiiph@0 547 else
Xiiph@0 548 print(fmt:format(k, v))
Xiiph@0 549 end
Xiiph@0 550 end
Xiiph@0 551 return
Xiiph@0 552 end
Xiiph@0 553
Xiiph@0 554 --build a table of the selections, checking that they exist
Xiiph@0 555 --parse for =on =off =default in the process
Xiiph@0 556 --table will be key = true for options that should toggle, key = [on|off|default] for options to be set
Xiiph@0 557 local sels = {}
Xiiph@0 558 for v in str:gmatch("[^ ]+") do
Xiiph@0 559 --parse option=on etc
Xiiph@0 560 local opt, val = v:match('(.+)=(.+)')
Xiiph@0 561 --get option if toggling
Xiiph@0 562 if not opt then
Xiiph@0 563 opt = v
Xiiph@0 564 end
Xiiph@0 565
Xiiph@0 566 --check that the opt is valid
Xiiph@0 567 local ok
Xiiph@0 568 for k,v in pairs(values) do
Xiiph@0 569 if strlower(k)==opt then
Xiiph@0 570 opt = k -- overwrite with key (in case of case mismatches)
Xiiph@0 571 ok = true
Xiiph@0 572 break
Xiiph@0 573 end
Xiiph@0 574 end
Xiiph@0 575
Xiiph@0 576 if not ok then
Xiiph@0 577 usererr(info, inputpos, "'"..opt.."' - "..L["unknown selection"])
Xiiph@0 578 return
Xiiph@0 579 end
Xiiph@0 580
Xiiph@0 581 --check that if val was supplied it is valid
Xiiph@0 582 if val then
Xiiph@0 583 if val == L["on"] or val == L["off"] or (tab.tristate and val == L["default"]) then
Xiiph@0 584 --val is valid insert it
Xiiph@0 585 sels[opt] = val
Xiiph@0 586 else
Xiiph@0 587 if tab.tristate then
Xiiph@0 588 usererr(info, inputpos, format(L["'%s' '%s' - expected 'on', 'off' or 'default', or no argument to toggle."], v, val))
Xiiph@0 589 else
Xiiph@0 590 usererr(info, inputpos, format(L["'%s' '%s' - expected 'on' or 'off', or no argument to toggle."], v, val))
Xiiph@0 591 end
Xiiph@0 592 return
Xiiph@0 593 end
Xiiph@0 594 else
Xiiph@0 595 -- no val supplied, toggle
Xiiph@0 596 sels[opt] = true
Xiiph@0 597 end
Xiiph@0 598 end
Xiiph@0 599
Xiiph@0 600 for opt, val in pairs(sels) do
Xiiph@0 601 local newval
Xiiph@0 602
Xiiph@0 603 if (val == true) then
Xiiph@0 604 --toggle the option
Xiiph@0 605 local b = callmethod(info, inputpos, tab, "get", opt)
Xiiph@0 606
Xiiph@0 607 if tab.tristate then
Xiiph@0 608 --cycle in true, nil, false order
Xiiph@0 609 if b then
Xiiph@0 610 b = nil
Xiiph@0 611 elseif b == nil then
Xiiph@0 612 b = false
Xiiph@0 613 else
Xiiph@0 614 b = true
Xiiph@0 615 end
Xiiph@0 616 else
Xiiph@0 617 b = not b
Xiiph@0 618 end
Xiiph@0 619 newval = b
Xiiph@0 620 else
Xiiph@0 621 --set the option as specified
Xiiph@0 622 if val==L["on"] then
Xiiph@0 623 newval = true
Xiiph@0 624 elseif val==L["off"] then
Xiiph@0 625 newval = false
Xiiph@0 626 elseif val==L["default"] then
Xiiph@0 627 newval = nil
Xiiph@0 628 end
Xiiph@0 629 end
Xiiph@0 630
Xiiph@0 631 do_final(info, inputpos, tab, "set", opt, newval)
Xiiph@0 632 end
Xiiph@0 633
Xiiph@0 634
Xiiph@0 635 elseif tab.type=="color" then
Xiiph@0 636 ------------ color --------------------------------------------
Xiiph@0 637 local str = strtrim(strlower(str))
Xiiph@0 638 if str == "" then
Xiiph@0 639 --TODO: Show current value
Xiiph@0 640 return
Xiiph@0 641 end
Xiiph@0 642
Xiiph@0 643 local r, g, b, a
Xiiph@0 644
Xiiph@0 645 if tab.hasAlpha then
Xiiph@0 646 if str:len() == 8 and str:find("^%x*$") then
Xiiph@0 647 --parse a hex string
Xiiph@0 648 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
Xiiph@0 649 else
Xiiph@0 650 --parse seperate values
Xiiph@0 651 r,g,b,a = str:match("^([%d%.]+) ([%d%.]+) ([%d%.]+) ([%d%.]+)$")
Xiiph@0 652 r,g,b,a = tonumber(r), tonumber(g), tonumber(b), tonumber(a)
Xiiph@0 653 end
Xiiph@0 654 if not (r and g and b and a) then
Xiiph@0 655 usererr(info, inputpos, format(L["'%s' - expected 'RRGGBBAA' or 'r g b a'."], str))
Xiiph@0 656 return
Xiiph@0 657 end
Xiiph@0 658
Xiiph@0 659 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
Xiiph@0 660 --values are valid
Xiiph@0 661 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
Xiiph@0 662 --values are valid 0..255, convert to 0..1
Xiiph@0 663 r = r / 255
Xiiph@0 664 g = g / 255
Xiiph@0 665 b = b / 255
Xiiph@0 666 a = a / 255
Xiiph@0 667 else
Xiiph@0 668 --values are invalid
Xiiph@0 669 usererr(info, inputpos, format(L["'%s' - values must all be either in the range 0..1 or 0..255."], str))
Xiiph@0 670 end
Xiiph@0 671 else
Xiiph@0 672 a = 1.0
Xiiph@0 673 if str:len() == 6 and str:find("^%x*$") then
Xiiph@0 674 --parse a hex string
Xiiph@0 675 r,g,b = tonumber(str:sub(1, 2), 16) / 255, tonumber(str:sub(3, 4), 16) / 255, tonumber(str:sub(5, 6), 16) / 255
Xiiph@0 676 else
Xiiph@0 677 --parse seperate values
Xiiph@0 678 r,g,b = str:match("^([%d%.]+) ([%d%.]+) ([%d%.]+)$")
Xiiph@0 679 r,g,b = tonumber(r), tonumber(g), tonumber(b)
Xiiph@0 680 end
Xiiph@0 681 if not (r and g and b) then
Xiiph@0 682 usererr(info, inputpos, format(L["'%s' - expected 'RRGGBB' or 'r g b'."], str))
Xiiph@0 683 return
Xiiph@0 684 end
Xiiph@0 685 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
Xiiph@0 686 --values are valid
Xiiph@0 687 elseif r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255 then
Xiiph@0 688 --values are valid 0..255, convert to 0..1
Xiiph@0 689 r = r / 255
Xiiph@0 690 g = g / 255
Xiiph@0 691 b = b / 255
Xiiph@0 692 else
Xiiph@0 693 --values are invalid
Xiiph@0 694 usererr(info, inputpos, format(L["'%s' - values must all be either in the range 0-1 or 0-255."], str))
Xiiph@0 695 end
Xiiph@0 696 end
Xiiph@0 697
Xiiph@0 698 do_final(info, inputpos, tab, "set", r,g,b,a)
Xiiph@0 699
Xiiph@0 700 elseif tab.type=="keybinding" then
Xiiph@0 701 ------------ keybinding --------------------------------------------
Xiiph@0 702 local str = strtrim(strlower(str))
Xiiph@0 703 if str == "" then
Xiiph@0 704 --TODO: Show current value
Xiiph@0 705 return
Xiiph@0 706 end
Xiiph@0 707 local value = keybindingValidateFunc(str:upper())
Xiiph@0 708 if value == false then
Xiiph@0 709 usererr(info, inputpos, format(L["'%s' - Invalid Keybinding."], str))
Xiiph@0 710 return
Xiiph@0 711 end
Xiiph@0 712
Xiiph@0 713 do_final(info, inputpos, tab, "set", value)
Xiiph@0 714
Xiiph@0 715 elseif tab.type=="description" then
Xiiph@0 716 ------------ description --------------------
Xiiph@0 717 -- ignore description, GUI config only
Xiiph@0 718 else
Xiiph@0 719 err(info, inputpos, "unknown options table item type '"..tostring(tab.type).."'")
Xiiph@0 720 end
Xiiph@0 721 end
Xiiph@0 722
Xiiph@0 723 --- Handle the chat command.
Xiiph@0 724 -- This is usually called from a chat command handler to parse the command input as operations on an aceoptions table.\\
Xiiph@0 725 -- AceConfigCmd uses this function internally when a slash command is registered with `:CreateChatCommand`
Xiiph@0 726 -- @param slashcmd The slash command WITHOUT leading slash (only used for error output)
Xiiph@0 727 -- @param appName The application name as given to `:RegisterOptionsTable()`
Xiiph@0 728 -- @param input The commandline input (as given by the WoW handler, i.e. without the command itself)
Xiiph@0 729 -- @usage
Xiiph@0 730 -- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceConsole-3.0")
Xiiph@0 731 -- -- Use AceConsole-3.0 to register a Chat Command
Xiiph@0 732 -- MyAddon:RegisterChatCommand("mychat", "ChatCommand")
Xiiph@0 733 --
Xiiph@0 734 -- -- Show the GUI if no input is supplied, otherwise handle the chat input.
Xiiph@0 735 -- function MyAddon:ChatCommand(input)
Xiiph@0 736 -- -- Assuming "MyOptions" is the appName of a valid options table
Xiiph@0 737 -- if not input or input:trim() == "" then
Xiiph@0 738 -- LibStub("AceConfigDialog-3.0"):Open("MyOptions")
Xiiph@0 739 -- else
Xiiph@0 740 -- LibStub("AceConfigCmd-3.0").HandleCommand(MyAddon, "mychat", "MyOptions", input)
Xiiph@0 741 -- end
Xiiph@0 742 -- end
Xiiph@0 743 function AceConfigCmd:HandleCommand(slashcmd, appName, input)
Xiiph@0 744
Xiiph@0 745 local optgetter = cfgreg:GetOptionsTable(appName)
Xiiph@0 746 if not optgetter then
Xiiph@0 747 error([[Usage: HandleCommand("slashcmd", "appName", "input"): 'appName' - no options table "]]..tostring(appName)..[[" has been registered]], 2)
Xiiph@0 748 end
Xiiph@0 749 local options = assert( optgetter("cmd", MAJOR) )
Xiiph@0 750
Xiiph@0 751 local info = { -- Don't try to recycle this, it gets handed off to callbacks and whatnot
Xiiph@0 752 [0] = slashcmd,
Xiiph@0 753 appName = appName,
Xiiph@0 754 options = options,
Xiiph@0 755 input = input,
Xiiph@0 756 self = self,
Xiiph@0 757 handler = self,
Xiiph@0 758 uiType = "cmd",
Xiiph@0 759 uiName = MAJOR,
Xiiph@0 760 }
Xiiph@0 761
Xiiph@0 762 handle(info, 1, options, 0) -- (info, inputpos, table, depth)
Xiiph@0 763 end
Xiiph@0 764
Xiiph@0 765 --- Utility function to create a slash command handler.
Xiiph@0 766 -- Also registers tab completion with AceTab
Xiiph@0 767 -- @param slashcmd The slash command WITHOUT leading slash (only used for error output)
Xiiph@0 768 -- @param appName The application name as given to `:RegisterOptionsTable()`
Xiiph@0 769 function AceConfigCmd:CreateChatCommand(slashcmd, appName)
Xiiph@0 770 if not AceConsole then
Xiiph@0 771 AceConsole = LibStub(AceConsoleName)
Xiiph@0 772 end
Xiiph@0 773 if AceConsole.RegisterChatCommand(self, slashcmd, function(input)
Xiiph@0 774 AceConfigCmd.HandleCommand(self, slashcmd, appName, input) -- upgradable
Xiiph@0 775 end,
Xiiph@0 776 true) then -- succesfully registered so lets get the command -> app table in
Xiiph@0 777 commands[slashcmd] = appName
Xiiph@0 778 end
Xiiph@0 779 end
Xiiph@0 780
Xiiph@0 781 --- Utility function that returns the options table that belongs to a slashcommand.
Xiiph@0 782 -- Designed to be used for the AceTab interface.
Xiiph@0 783 -- @param slashcmd The slash command WITHOUT leading slash (only used for error output)
Xiiph@0 784 -- @return The options table associated with the slash command (or nil if the slash command was not registered)
Xiiph@0 785 function AceConfigCmd:GetChatCommandOptions(slashcmd)
Xiiph@0 786 return commands[slashcmd]
Xiiph@0 787 end