annotate Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.lua @ 0:169f5211fc7f

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