annotate Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.lua @ 58:0682d738499b v8.0.1.058

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