Mercurial > wow > inventory
comparison Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.lua @ 0:c6ff7ba0e8f6
Reasonably functional now. Cleaning up some stuff which might have to be reverted.
| author | Zerotorescue | 
|---|---|
| date | Thu, 07 Oct 2010 17:17:43 +0200 | 
| parents | |
| children | 
   comparison
  equal
  deleted
  inserted
  replaced
| -1:000000000000 | 0:c6ff7ba0e8f6 | 
|---|---|
| 1 --- AceConfigDialog-3.0 generates AceGUI-3.0 based windows based on option tables. | |
| 2 -- @class file | |
| 3 -- @name AceConfigDialog-3.0 | |
| 4 -- @release $Id: AceConfigDialog-3.0.lua 958 2010-07-03 10:22:29Z nevcairiel $ | |
| 5 | |
| 6 local LibStub = LibStub | |
| 7 local MAJOR, MINOR = "AceConfigDialog-3.0", 49 | |
| 8 local AceConfigDialog, oldminor = LibStub:NewLibrary(MAJOR, MINOR) | |
| 9 | |
| 10 if not AceConfigDialog then return end | |
| 11 | |
| 12 AceConfigDialog.OpenFrames = AceConfigDialog.OpenFrames or {} | |
| 13 AceConfigDialog.Status = AceConfigDialog.Status or {} | |
| 14 AceConfigDialog.frame = AceConfigDialog.frame or CreateFrame("Frame") | |
| 15 | |
| 16 AceConfigDialog.frame.apps = AceConfigDialog.frame.apps or {} | |
| 17 AceConfigDialog.frame.closing = AceConfigDialog.frame.closing or {} | |
| 18 AceConfigDialog.frame.closeAllOverride = AceConfigDialog.frame.closeAllOverride or {} | |
| 19 | |
| 20 local gui = LibStub("AceGUI-3.0") | |
| 21 local reg = LibStub("AceConfigRegistry-3.0") | |
| 22 | |
| 23 -- Lua APIs | |
| 24 local tconcat, tinsert, tsort, tremove = table.concat, table.insert, table.sort, table.remove | |
| 25 local strmatch, format = string.match, string.format | |
| 26 local assert, loadstring, error = assert, loadstring, error | |
| 27 local pairs, next, select, type, unpack, wipe = pairs, next, select, type, unpack, wipe | |
| 28 local rawset, tostring, tonumber = rawset, tostring, tonumber | |
| 29 local math_min, math_max, math_floor = math.min, math.max, math.floor | |
| 30 | |
| 31 -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded | |
| 32 -- List them here for Mikk's FindGlobals script | |
| 33 -- GLOBALS: NORMAL_FONT_COLOR, GameTooltip, StaticPopupDialogs, ACCEPT, CANCEL, StaticPopup_Show | |
| 34 -- GLOBALS: PlaySound, GameFontHighlight, GameFontHighlightSmall, GameFontHighlightLarge | |
| 35 -- GLOBALS: CloseSpecialWindows, InterfaceOptions_AddCategory, geterrorhandler | |
| 36 | |
| 37 local emptyTbl = {} | |
| 38 | |
| 39 --[[ | |
| 40 xpcall safecall implementation | |
| 41 ]] | |
| 42 local xpcall = xpcall | |
| 43 | |
| 44 local function errorhandler(err) | |
| 45 return geterrorhandler()(err) | |
| 46 end | |
| 47 | |
| 48 local function CreateDispatcher(argCount) | |
| 49 local code = [[ | |
| 50 local xpcall, eh = ... | |
| 51 local method, ARGS | |
| 52 local function call() return method(ARGS) end | |
| 53 | |
| 54 local function dispatch(func, ...) | |
| 55 method = func | |
| 56 if not method then return end | |
| 57 ARGS = ... | |
| 58 return xpcall(call, eh) | |
| 59 end | |
| 60 | |
| 61 return dispatch | |
| 62 ]] | |
| 63 | |
| 64 local ARGS = {} | |
| 65 for i = 1, argCount do ARGS[i] = "arg"..i end | |
| 66 code = code:gsub("ARGS", tconcat(ARGS, ", ")) | |
| 67 return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) | |
| 68 end | |
| 69 | |
| 70 local Dispatchers = setmetatable({}, {__index=function(self, argCount) | |
| 71 local dispatcher = CreateDispatcher(argCount) | |
| 72 rawset(self, argCount, dispatcher) | |
| 73 return dispatcher | |
| 74 end}) | |
| 75 Dispatchers[0] = function(func) | |
| 76 return xpcall(func, errorhandler) | |
| 77 end | |
| 78 | |
| 79 local function safecall(func, ...) | |
| 80 return Dispatchers[select("#", ...)](func, ...) | |
| 81 end | |
| 82 | |
| 83 local width_multiplier = 170 | |
| 84 | |
| 85 --[[ | |
| 86 Group Types | |
| 87 Tree - All Descendant Groups will all become nodes on the tree, direct child options will appear above the tree | |
| 88 - Descendant Groups with inline=true and thier children will not become nodes | |
| 89 | |
| 90 Tab - Direct Child Groups will become tabs, direct child options will appear above the tab control | |
| 91 - Grandchild groups will default to inline unless specified otherwise | |
| 92 | |
| 93 Select- Same as Tab but with entries in a dropdown rather than tabs | |
| 94 | |
| 95 | |
| 96 Inline Groups | |
| 97 - Will not become nodes of a select group, they will be effectivly part of thier parent group seperated by a border | |
| 98 - If declared on a direct child of a root node of a select group, they will appear above the group container control | |
| 99 - When a group is displayed inline, all descendants will also be inline members of the group | |
| 100 | |
| 101 ]] | |
| 102 | |
| 103 -- Recycling functions | |
| 104 local new, del, copy | |
| 105 --newcount, delcount,createdcount,cached = 0,0,0 | |
| 106 do | |
| 107 local pool = setmetatable({},{__mode="k"}) | |
| 108 function new() | |
| 109 --newcount = newcount + 1 | |
| 110 local t = next(pool) | |
| 111 if t then | |
| 112 pool[t] = nil | |
| 113 return t | |
| 114 else | |
| 115 --createdcount = createdcount + 1 | |
| 116 return {} | |
| 117 end | |
| 118 end | |
| 119 function copy(t) | |
| 120 local c = new() | |
| 121 for k, v in pairs(t) do | |
| 122 c[k] = v | |
| 123 end | |
| 124 return c | |
| 125 end | |
| 126 function del(t) | |
| 127 --delcount = delcount + 1 | |
| 128 for k in pairs(t) do | |
| 129 t[k] = nil | |
| 130 end | |
| 131 pool[t] = true | |
| 132 end | |
| 133 -- function cached() | |
| 134 -- local n = 0 | |
| 135 -- for k in pairs(pool) do | |
| 136 -- n = n + 1 | |
| 137 -- end | |
| 138 -- return n | |
| 139 -- end | |
| 140 end | |
| 141 | |
| 142 -- picks the first non-nil value and returns it | |
| 143 local function pickfirstset(...) | |
| 144 for i=1,select("#",...) do | |
| 145 if select(i,...)~=nil then | |
| 146 return select(i,...) | |
| 147 end | |
| 148 end | |
| 149 end | |
| 150 | |
| 151 --gets an option from a given group, checking plugins | |
| 152 local function GetSubOption(group, key) | |
| 153 if group.plugins then | |
| 154 for plugin, t in pairs(group.plugins) do | |
| 155 if t[key] then | |
| 156 return t[key] | |
| 157 end | |
| 158 end | |
| 159 end | |
| 160 | |
| 161 return group.args[key] | |
| 162 end | |
| 163 | |
| 164 --Option member type definitions, used to decide how to access it | |
| 165 | |
| 166 --Is the member Inherited from parent options | |
| 167 local isInherited = { | |
| 168 set = true, | |
| 169 get = true, | |
| 170 func = true, | |
| 171 confirm = true, | |
| 172 validate = true, | |
| 173 disabled = true, | |
| 174 hidden = true | |
| 175 } | |
| 176 | |
| 177 --Does a string type mean a literal value, instead of the default of a method of the handler | |
| 178 local stringIsLiteral = { | |
| 179 name = true, | |
| 180 desc = true, | |
| 181 icon = true, | |
| 182 usage = true, | |
| 183 width = true, | |
| 184 image = true, | |
| 185 fontSize = true, | |
| 186 } | |
| 187 | |
| 188 --Is Never a function or method | |
| 189 local allIsLiteral = { | |
| 190 type = true, | |
| 191 descStyle = true, | |
| 192 imageWidth = true, | |
| 193 imageHeight = true, | |
| 194 } | |
| 195 | |
| 196 --gets the value for a member that could be a function | |
| 197 --function refs are called with an info arg | |
| 198 --every other type is returned | |
| 199 local function GetOptionsMemberValue(membername, option, options, path, appName, ...) | |
| 200 --get definition for the member | |
| 201 local inherits = isInherited[membername] | |
| 202 | |
| 203 | |
| 204 --get the member of the option, traversing the tree if it can be inherited | |
| 205 local member | |
| 206 | |
| 207 if inherits then | |
| 208 local group = options | |
| 209 if group[membername] ~= nil then | |
| 210 member = group[membername] | |
| 211 end | |
| 212 for i = 1, #path do | |
| 213 group = GetSubOption(group, path[i]) | |
| 214 if group[membername] ~= nil then | |
| 215 member = group[membername] | |
| 216 end | |
| 217 end | |
| 218 else | |
| 219 member = option[membername] | |
| 220 end | |
| 221 | |
| 222 --check if we need to call a functon, or if we have a literal value | |
| 223 if ( not allIsLiteral[membername] ) and ( type(member) == "function" or ((not stringIsLiteral[membername]) and type(member) == "string") ) then | |
| 224 --We have a function to call | |
| 225 local info = new() | |
| 226 --traverse the options table, picking up the handler and filling the info with the path | |
| 227 local handler | |
| 228 local group = options | |
| 229 handler = group.handler or handler | |
| 230 | |
| 231 for i = 1, #path do | |
| 232 group = GetSubOption(group, path[i]) | |
| 233 info[i] = path[i] | |
| 234 handler = group.handler or handler | |
| 235 end | |
| 236 | |
| 237 info.options = options | |
| 238 info.appName = appName | |
| 239 info[0] = appName | |
| 240 info.arg = option.arg | |
| 241 info.handler = handler | |
| 242 info.option = option | |
| 243 info.type = option.type | |
| 244 info.uiType = "dialog" | |
| 245 info.uiName = MAJOR | |
| 246 | |
| 247 local a, b, c ,d | |
| 248 --using 4 returns for the get of a color type, increase if a type needs more | |
| 249 if type(member) == "function" then | |
| 250 --Call the function | |
| 251 a,b,c,d = member(info, ...) | |
| 252 else | |
| 253 --Call the method | |
| 254 if handler and handler[member] then | |
| 255 a,b,c,d = handler[member](handler, info, ...) | |
| 256 else | |
| 257 error(format("Method %s doesn't exist in handler for type %s", member, membername)) | |
| 258 end | |
| 259 end | |
| 260 del(info) | |
| 261 return a,b,c,d | |
| 262 else | |
| 263 --The value isnt a function to call, return it | |
| 264 return member | |
| 265 end | |
| 266 end | |
| 267 | |
| 268 --[[calls an options function that could be inherited, method name or function ref | |
| 269 local function CallOptionsFunction(funcname ,option, options, path, appName, ...) | |
| 270 local info = new() | |
| 271 | |
| 272 local func | |
| 273 local group = options | |
| 274 local handler | |
| 275 | |
| 276 --build the info table containing the path | |
| 277 -- pick up functions while traversing the tree | |
| 278 if group[funcname] ~= nil then | |
| 279 func = group[funcname] | |
| 280 end | |
| 281 handler = group.handler or handler | |
| 282 | |
| 283 for i, v in ipairs(path) do | |
| 284 group = GetSubOption(group, v) | |
| 285 info[i] = v | |
| 286 if group[funcname] ~= nil then | |
| 287 func = group[funcname] | |
| 288 end | |
| 289 handler = group.handler or handler | |
| 290 end | |
| 291 | |
| 292 info.options = options | |
| 293 info[0] = appName | |
| 294 info.arg = option.arg | |
| 295 | |
| 296 local a, b, c ,d | |
| 297 if type(func) == "string" then | |
| 298 if handler and handler[func] then | |
| 299 a,b,c,d = handler[func](handler, info, ...) | |
| 300 else | |
| 301 error(string.format("Method %s doesn't exist in handler for type func", func)) | |
| 302 end | |
| 303 elseif type(func) == "function" then | |
| 304 a,b,c,d = func(info, ...) | |
| 305 end | |
| 306 del(info) | |
| 307 return a,b,c,d | |
| 308 end | |
| 309 --]] | |
| 310 | |
| 311 --tables to hold orders and names for options being sorted, will be created with new() | |
| 312 --prevents needing to call functions repeatedly while sorting | |
| 313 local tempOrders | |
| 314 local tempNames | |
| 315 | |
| 316 local function compareOptions(a,b) | |
| 317 if not a then | |
| 318 return true | |
| 319 end | |
| 320 if not b then | |
| 321 return false | |
| 322 end | |
| 323 local OrderA, OrderB = tempOrders[a] or 100, tempOrders[b] or 100 | |
| 324 if OrderA == OrderB then | |
| 325 local NameA = (type(tempNames[a] == "string") and tempNames[a]) or "" | |
| 326 local NameB = (type(tempNames[b] == "string") and tempNames[b]) or "" | |
| 327 return NameA:upper() < NameB:upper() | |
| 328 end | |
| 329 if OrderA < 0 then | |
| 330 if OrderB > 0 then | |
| 331 return false | |
| 332 end | |
| 333 else | |
| 334 if OrderB < 0 then | |
| 335 return true | |
| 336 end | |
| 337 end | |
| 338 return OrderA < OrderB | |
| 339 end | |
| 340 | |
| 341 | |
| 342 | |
| 343 --builds 2 tables out of an options group | |
| 344 -- keySort, sorted keys | |
| 345 -- opts, combined options from .plugins and args | |
| 346 local function BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
| 347 tempOrders = new() | |
| 348 tempNames = new() | |
| 349 | |
| 350 if group.plugins then | |
| 351 for plugin, t in pairs(group.plugins) do | |
| 352 for k, v in pairs(t) do | |
| 353 if not opts[k] then | |
| 354 tinsert(keySort, k) | |
| 355 opts[k] = v | |
| 356 | |
| 357 path[#path+1] = k | |
| 358 tempOrders[k] = GetOptionsMemberValue("order", v, options, path, appName) | |
| 359 tempNames[k] = GetOptionsMemberValue("name", v, options, path, appName) | |
| 360 path[#path] = nil | |
| 361 end | |
| 362 end | |
| 363 end | |
| 364 end | |
| 365 | |
| 366 for k, v in pairs(group.args) do | |
| 367 if not opts[k] then | |
| 368 tinsert(keySort, k) | |
| 369 opts[k] = v | |
| 370 | |
| 371 path[#path+1] = k | |
| 372 tempOrders[k] = GetOptionsMemberValue("order", v, options, path, appName) | |
| 373 tempNames[k] = GetOptionsMemberValue("name", v, options, path, appName) | |
| 374 path[#path] = nil | |
| 375 end | |
| 376 end | |
| 377 | |
| 378 tsort(keySort, compareOptions) | |
| 379 | |
| 380 del(tempOrders) | |
| 381 del(tempNames) | |
| 382 end | |
| 383 | |
| 384 local function DelTree(tree) | |
| 385 if tree.children then | |
| 386 local childs = tree.children | |
| 387 for i = 1, #childs do | |
| 388 DelTree(childs[i]) | |
| 389 del(childs[i]) | |
| 390 end | |
| 391 del(childs) | |
| 392 end | |
| 393 end | |
| 394 | |
| 395 local function CleanUserData(widget, event) | |
| 396 | |
| 397 local user = widget:GetUserDataTable() | |
| 398 | |
| 399 if user.path then | |
| 400 del(user.path) | |
| 401 end | |
| 402 | |
| 403 if widget.type == "TreeGroup" then | |
| 404 local tree = user.tree | |
| 405 widget:SetTree(nil) | |
| 406 if tree then | |
| 407 for i = 1, #tree do | |
| 408 DelTree(tree[i]) | |
| 409 del(tree[i]) | |
| 410 end | |
| 411 del(tree) | |
| 412 end | |
| 413 end | |
| 414 | |
| 415 if widget.type == "TabGroup" then | |
| 416 widget:SetTabs(nil) | |
| 417 if user.tablist then | |
| 418 del(user.tablist) | |
| 419 end | |
| 420 end | |
| 421 | |
| 422 if widget.type == "DropdownGroup" then | |
| 423 widget:SetGroupList(nil) | |
| 424 if user.grouplist then | |
| 425 del(user.grouplist) | |
| 426 end | |
| 427 end | |
| 428 end | |
| 429 | |
| 430 -- - Gets a status table for the given appname and options path. | |
| 431 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
| 432 -- @param path The path to the options (a table with all group keys) | |
| 433 -- @return | |
| 434 function AceConfigDialog:GetStatusTable(appName, path) | |
| 435 local status = self.Status | |
| 436 | |
| 437 if not status[appName] then | |
| 438 status[appName] = {} | |
| 439 status[appName].status = {} | |
| 440 status[appName].children = {} | |
| 441 end | |
| 442 | |
| 443 status = status[appName] | |
| 444 | |
| 445 if path then | |
| 446 for i = 1, #path do | |
| 447 local v = path[i] | |
| 448 if not status.children[v] then | |
| 449 status.children[v] = {} | |
| 450 status.children[v].status = {} | |
| 451 status.children[v].children = {} | |
| 452 end | |
| 453 status = status.children[v] | |
| 454 end | |
| 455 end | |
| 456 | |
| 457 return status.status | |
| 458 end | |
| 459 | |
| 460 --- Selects the specified path in the options window. | |
| 461 -- The path specified has to match the keys of the groups in the table. | |
| 462 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
| 463 -- @param ... The path to the key that should be selected | |
| 464 function AceConfigDialog:SelectGroup(appName, ...) | |
| 465 local path = new() | |
| 466 | |
| 467 | |
| 468 local app = reg:GetOptionsTable(appName) | |
| 469 if not app then | |
| 470 error(("%s isn't registed with AceConfigRegistry, unable to open config"):format(appName), 2) | |
| 471 end | |
| 472 local options = app("dialog", MAJOR) | |
| 473 local group = options | |
| 474 local status = self:GetStatusTable(appName, path) | |
| 475 if not status.groups then | |
| 476 status.groups = {} | |
| 477 end | |
| 478 status = status.groups | |
| 479 local treevalue | |
| 480 local treestatus | |
| 481 | |
| 482 for n = 1, select("#",...) do | |
| 483 local key = select(n, ...) | |
| 484 | |
| 485 if group.childGroups == "tab" or group.childGroups == "select" then | |
| 486 --if this is a tab or select group, select the group | |
| 487 status.selected = key | |
| 488 --children of this group are no longer extra levels of a tree | |
| 489 treevalue = nil | |
| 490 else | |
| 491 --tree group by default | |
| 492 if treevalue then | |
| 493 --this is an extra level of a tree group, build a uniquevalue for it | |
| 494 treevalue = treevalue.."\001"..key | |
| 495 else | |
| 496 --this is the top level of a tree group, the uniquevalue is the same as the key | |
| 497 treevalue = key | |
| 498 if not status.groups then | |
| 499 status.groups = {} | |
| 500 end | |
| 501 --save this trees status table for any extra levels or groups | |
| 502 treestatus = status | |
| 503 end | |
| 504 --make sure that the tree entry is open, and select it. | |
| 505 --the selected group will be overwritten if a child is the final target but still needs to be open | |
| 506 treestatus.selected = treevalue | |
| 507 treestatus.groups[treevalue] = true | |
| 508 | |
| 509 end | |
| 510 | |
| 511 --move to the next group in the path | |
| 512 group = GetSubOption(group, key) | |
| 513 if not group then | |
| 514 break | |
| 515 end | |
| 516 tinsert(path, key) | |
| 517 status = self:GetStatusTable(appName, path) | |
| 518 if not status.groups then | |
| 519 status.groups = {} | |
| 520 end | |
| 521 status = status.groups | |
| 522 end | |
| 523 | |
| 524 del(path) | |
| 525 reg:NotifyChange(appName) | |
| 526 end | |
| 527 | |
| 528 local function OptionOnMouseOver(widget, event) | |
| 529 --show a tooltip/set the status bar to the desc text | |
| 530 local user = widget:GetUserDataTable() | |
| 531 local opt = user.option | |
| 532 local options = user.options | |
| 533 local path = user.path | |
| 534 local appName = user.appName | |
| 535 | |
| 536 GameTooltip:SetOwner(widget.frame, "ANCHOR_TOPRIGHT") | |
| 537 local name = GetOptionsMemberValue("name", opt, options, path, appName) | |
| 538 local desc = GetOptionsMemberValue("desc", opt, options, path, appName) | |
| 539 local usage = GetOptionsMemberValue("usage", opt, options, path, appName) | |
| 540 local descStyle = opt.descStyle | |
| 541 | |
| 542 if descStyle and descStyle ~= "tooltip" then return end | |
| 543 | |
| 544 GameTooltip:SetText(name, 1, .82, 0, 1) | |
| 545 | |
| 546 if opt.type == "multiselect" then | |
| 547 GameTooltip:AddLine(user.text,0.5, 0.5, 0.8, 1) | |
| 548 end | |
| 549 if type(desc) == "string" then | |
| 550 GameTooltip:AddLine(desc, 1, 1, 1, 1) | |
| 551 end | |
| 552 if type(usage) == "string" then | |
| 553 GameTooltip:AddLine("Usage: "..usage, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1) | |
| 554 end | |
| 555 | |
| 556 GameTooltip:Show() | |
| 557 end | |
| 558 | |
| 559 local function OptionOnMouseLeave(widget, event) | |
| 560 GameTooltip:Hide() | |
| 561 end | |
| 562 | |
| 563 local function GetFuncName(option) | |
| 564 local type = option.type | |
| 565 if type == "execute" then | |
| 566 return "func" | |
| 567 else | |
| 568 return "set" | |
| 569 end | |
| 570 end | |
| 571 local function confirmPopup(appName, rootframe, basepath, info, message, func, ...) | |
| 572 if not StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] then | |
| 573 StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] = {} | |
| 574 end | |
| 575 local t = StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] | |
| 576 for k in pairs(t) do | |
| 577 t[k] = nil | |
| 578 end | |
| 579 t.text = message | |
| 580 t.button1 = ACCEPT | |
| 581 t.button2 = CANCEL | |
| 582 local dialog, oldstrata | |
| 583 t.OnAccept = function() | |
| 584 safecall(func, unpack(t)) | |
| 585 if dialog and oldstrata then | |
| 586 dialog:SetFrameStrata(oldstrata) | |
| 587 end | |
| 588 AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl)) | |
| 589 del(info) | |
| 590 end | |
| 591 t.OnCancel = function() | |
| 592 if dialog and oldstrata then | |
| 593 dialog:SetFrameStrata(oldstrata) | |
| 594 end | |
| 595 AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl)) | |
| 596 del(info) | |
| 597 end | |
| 598 for i = 1, select("#", ...) do | |
| 599 t[i] = select(i, ...) or false | |
| 600 end | |
| 601 t.timeout = 0 | |
| 602 t.whileDead = 1 | |
| 603 t.hideOnEscape = 1 | |
| 604 | |
| 605 dialog = StaticPopup_Show("ACECONFIGDIALOG30_CONFIRM_DIALOG") | |
| 606 if dialog then | |
| 607 oldstrata = dialog:GetFrameStrata() | |
| 608 dialog:SetFrameStrata("TOOLTIP") | |
| 609 end | |
| 610 end | |
| 611 | |
| 612 local function ActivateControl(widget, event, ...) | |
| 613 --This function will call the set / execute handler for the widget | |
| 614 --widget:GetUserDataTable() contains the needed info | |
| 615 local user = widget:GetUserDataTable() | |
| 616 local option = user.option | |
| 617 local options = user.options | |
| 618 local path = user.path | |
| 619 local info = new() | |
| 620 | |
| 621 local func | |
| 622 local group = options | |
| 623 local funcname = GetFuncName(option) | |
| 624 local handler | |
| 625 local confirm | |
| 626 local validate | |
| 627 --build the info table containing the path | |
| 628 -- pick up functions while traversing the tree | |
| 629 if group[funcname] ~= nil then | |
| 630 func = group[funcname] | |
| 631 end | |
| 632 handler = group.handler or handler | |
| 633 confirm = group.confirm | |
| 634 validate = group.validate | |
| 635 for i = 1, #path do | |
| 636 local v = path[i] | |
| 637 group = GetSubOption(group, v) | |
| 638 info[i] = v | |
| 639 if group[funcname] ~= nil then | |
| 640 func = group[funcname] | |
| 641 end | |
| 642 handler = group.handler or handler | |
| 643 if group.confirm ~= nil then | |
| 644 confirm = group.confirm | |
| 645 end | |
| 646 if group.validate ~= nil then | |
| 647 validate = group.validate | |
| 648 end | |
| 649 end | |
| 650 | |
| 651 info.options = options | |
| 652 info.appName = user.appName | |
| 653 info.arg = option.arg | |
| 654 info.handler = handler | |
| 655 info.option = option | |
| 656 info.type = option.type | |
| 657 info.uiType = "dialog" | |
| 658 info.uiName = MAJOR | |
| 659 | |
| 660 local name | |
| 661 if type(option.name) == "function" then | |
| 662 name = option.name(info) | |
| 663 elseif type(option.name) == "string" then | |
| 664 name = option.name | |
| 665 else | |
| 666 name = "" | |
| 667 end | |
| 668 local usage = option.usage | |
| 669 local pattern = option.pattern | |
| 670 | |
| 671 local validated = true | |
| 672 | |
| 673 if option.type == "input" then | |
| 674 if type(pattern)=="string" then | |
| 675 if not strmatch(..., pattern) then | |
| 676 validated = false | |
| 677 end | |
| 678 end | |
| 679 end | |
| 680 | |
| 681 local success | |
| 682 if validated and option.type ~= "execute" then | |
| 683 if type(validate) == "string" then | |
| 684 if handler and handler[validate] then | |
| 685 success, validated = safecall(handler[validate], handler, info, ...) | |
| 686 if not success then validated = false end | |
| 687 else | |
| 688 error(format("Method %s doesn't exist in handler for type execute", validate)) | |
| 689 end | |
| 690 elseif type(validate) == "function" then | |
| 691 success, validated = safecall(validate, info, ...) | |
| 692 if not success then validated = false end | |
| 693 end | |
| 694 end | |
| 695 | |
| 696 local rootframe = user.rootframe | |
| 697 if type(validated) == "string" then | |
| 698 --validate function returned a message to display | |
| 699 if rootframe.SetStatusText then | |
| 700 rootframe:SetStatusText(validated) | |
| 701 else | |
| 702 -- TODO: do something else. | |
| 703 end | |
| 704 PlaySound("igPlayerInviteDecline") | |
| 705 del(info) | |
| 706 return true | |
| 707 elseif not validated then | |
| 708 --validate returned false | |
| 709 if rootframe.SetStatusText then | |
| 710 if usage then | |
| 711 rootframe:SetStatusText(name..": "..usage) | |
| 712 else | |
| 713 if pattern then | |
| 714 rootframe:SetStatusText(name..": Expected "..pattern) | |
| 715 else | |
| 716 rootframe:SetStatusText(name..": Invalid Value") | |
| 717 end | |
| 718 end | |
| 719 else | |
| 720 -- TODO: do something else | |
| 721 end | |
| 722 PlaySound("igPlayerInviteDecline") | |
| 723 del(info) | |
| 724 return true | |
| 725 else | |
| 726 | |
| 727 local confirmText = option.confirmText | |
| 728 --call confirm func/method | |
| 729 if type(confirm) == "string" then | |
| 730 if handler and handler[confirm] then | |
| 731 success, confirm = safecall(handler[confirm], handler, info, ...) | |
| 732 if success and type(confirm) == "string" then | |
| 733 confirmText = confirm | |
| 734 confirm = true | |
| 735 elseif not success then | |
| 736 confirm = false | |
| 737 end | |
| 738 else | |
| 739 error(format("Method %s doesn't exist in handler for type confirm", confirm)) | |
| 740 end | |
| 741 elseif type(confirm) == "function" then | |
| 742 success, confirm = safecall(confirm, info, ...) | |
| 743 if success and type(confirm) == "string" then | |
| 744 confirmText = confirm | |
| 745 confirm = true | |
| 746 elseif not success then | |
| 747 confirm = false | |
| 748 end | |
| 749 end | |
| 750 | |
| 751 --confirm if needed | |
| 752 if type(confirm) == "boolean" then | |
| 753 if confirm then | |
| 754 if not confirmText then | |
| 755 local name, desc = option.name, option.desc | |
| 756 if type(name) == "function" then | |
| 757 name = name(info) | |
| 758 end | |
| 759 if type(desc) == "function" then | |
| 760 desc = desc(info) | |
| 761 end | |
| 762 confirmText = name | |
| 763 if desc then | |
| 764 confirmText = confirmText.." - "..desc | |
| 765 end | |
| 766 end | |
| 767 | |
| 768 local iscustom = user.rootframe:GetUserData("iscustom") | |
| 769 local rootframe | |
| 770 | |
| 771 if iscustom then | |
| 772 rootframe = user.rootframe | |
| 773 end | |
| 774 local basepath = user.rootframe:GetUserData("basepath") | |
| 775 if type(func) == "string" then | |
| 776 if handler and handler[func] then | |
| 777 confirmPopup(user.appName, rootframe, basepath, info, confirmText, handler[func], handler, info, ...) | |
| 778 else | |
| 779 error(format("Method %s doesn't exist in handler for type func", func)) | |
| 780 end | |
| 781 elseif type(func) == "function" then | |
| 782 confirmPopup(user.appName, rootframe, basepath, info, confirmText, func, info, ...) | |
| 783 end | |
| 784 --func will be called and info deleted when the confirm dialog is responded to | |
| 785 return | |
| 786 end | |
| 787 end | |
| 788 | |
| 789 --call the function | |
| 790 if type(func) == "string" then | |
| 791 if handler and handler[func] then | |
| 792 safecall(handler[func],handler, info, ...) | |
| 793 else | |
| 794 error(format("Method %s doesn't exist in handler for type func", func)) | |
| 795 end | |
| 796 elseif type(func) == "function" then | |
| 797 safecall(func,info, ...) | |
| 798 end | |
| 799 | |
| 800 | |
| 801 | |
| 802 local iscustom = user.rootframe:GetUserData("iscustom") | |
| 803 local basepath = user.rootframe:GetUserData("basepath") or emptyTbl | |
| 804 --full refresh of the frame, some controls dont cause this on all events | |
| 805 if option.type == "color" then | |
| 806 if event == "OnValueConfirmed" then | |
| 807 | |
| 808 if iscustom then | |
| 809 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
| 810 else | |
| 811 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
| 812 end | |
| 813 end | |
| 814 elseif option.type == "range" then | |
| 815 if event == "OnMouseUp" then | |
| 816 if iscustom then | |
| 817 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
| 818 else | |
| 819 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
| 820 end | |
| 821 end | |
| 822 --multiselects don't cause a refresh on 'OnValueChanged' only 'OnClosed' | |
| 823 elseif option.type == "multiselect" then | |
| 824 user.valuechanged = true | |
| 825 else | |
| 826 if iscustom then | |
| 827 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
| 828 else | |
| 829 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
| 830 end | |
| 831 end | |
| 832 | |
| 833 end | |
| 834 del(info) | |
| 835 end | |
| 836 | |
| 837 local function ActivateSlider(widget, event, value) | |
| 838 local option = widget:GetUserData("option") | |
| 839 local min, max, step = option.min or (not option.softMin and 0 or nil), option.max or (not option.softMax and 100 or nil), option.step | |
| 840 if min then | |
| 841 if step then | |
| 842 value = math_floor((value - min) / step + 0.5) * step + min | |
| 843 end | |
| 844 value = math_max(value, min) | |
| 845 end | |
| 846 if max then | |
| 847 value = math_min(value, max) | |
| 848 end | |
| 849 ActivateControl(widget,event,value) | |
| 850 end | |
| 851 | |
| 852 --called from a checkbox that is part of an internally created multiselect group | |
| 853 --this type is safe to refresh on activation of one control | |
| 854 local function ActivateMultiControl(widget, event, ...) | |
| 855 ActivateControl(widget, event, widget:GetUserData("value"), ...) | |
| 856 local user = widget:GetUserDataTable() | |
| 857 local iscustom = user.rootframe:GetUserData("iscustom") | |
| 858 local basepath = user.rootframe:GetUserData("basepath") or emptyTbl | |
| 859 if iscustom then | |
| 860 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
| 861 else | |
| 862 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
| 863 end | |
| 864 end | |
| 865 | |
| 866 local function MultiControlOnClosed(widget, event, ...) | |
| 867 local user = widget:GetUserDataTable() | |
| 868 if user.valuechanged then | |
| 869 local iscustom = user.rootframe:GetUserData("iscustom") | |
| 870 local basepath = user.rootframe:GetUserData("basepath") or emptyTbl | |
| 871 if iscustom then | |
| 872 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
| 873 else | |
| 874 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
| 875 end | |
| 876 end | |
| 877 end | |
| 878 | |
| 879 local function FrameOnClose(widget, event) | |
| 880 local appName = widget:GetUserData("appName") | |
| 881 AceConfigDialog.OpenFrames[appName] = nil | |
| 882 gui:Release(widget) | |
| 883 end | |
| 884 | |
| 885 local function CheckOptionHidden(option, options, path, appName) | |
| 886 --check for a specific boolean option | |
| 887 local hidden = pickfirstset(option.dialogHidden,option.guiHidden) | |
| 888 if hidden ~= nil then | |
| 889 return hidden | |
| 890 end | |
| 891 | |
| 892 return GetOptionsMemberValue("hidden", option, options, path, appName) | |
| 893 end | |
| 894 | |
| 895 local function CheckOptionDisabled(option, options, path, appName) | |
| 896 --check for a specific boolean option | |
| 897 local disabled = pickfirstset(option.dialogDisabled,option.guiDisabled) | |
| 898 if disabled ~= nil then | |
| 899 return disabled | |
| 900 end | |
| 901 | |
| 902 return GetOptionsMemberValue("disabled", option, options, path, appName) | |
| 903 end | |
| 904 --[[ | |
| 905 local function BuildTabs(group, options, path, appName) | |
| 906 local tabs = new() | |
| 907 local text = new() | |
| 908 local keySort = new() | |
| 909 local opts = new() | |
| 910 | |
| 911 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
| 912 | |
| 913 for i = 1, #keySort do | |
| 914 local k = keySort[i] | |
| 915 local v = opts[k] | |
| 916 if v.type == "group" then | |
| 917 path[#path+1] = k | |
| 918 local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
| 919 local hidden = CheckOptionHidden(v, options, path, appName) | |
| 920 if not inline and not hidden then | |
| 921 tinsert(tabs, k) | |
| 922 text[k] = GetOptionsMemberValue("name", v, options, path, appName) | |
| 923 end | |
| 924 path[#path] = nil | |
| 925 end | |
| 926 end | |
| 927 | |
| 928 del(keySort) | |
| 929 del(opts) | |
| 930 | |
| 931 return tabs, text | |
| 932 end | |
| 933 ]] | |
| 934 local function BuildSelect(group, options, path, appName) | |
| 935 local groups = new() | |
| 936 local keySort = new() | |
| 937 local opts = new() | |
| 938 | |
| 939 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
| 940 | |
| 941 for i = 1, #keySort do | |
| 942 local k = keySort[i] | |
| 943 local v = opts[k] | |
| 944 if v.type == "group" then | |
| 945 path[#path+1] = k | |
| 946 local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
| 947 local hidden = CheckOptionHidden(v, options, path, appName) | |
| 948 if not inline and not hidden then | |
| 949 groups[k] = GetOptionsMemberValue("name", v, options, path, appName) | |
| 950 end | |
| 951 path[#path] = nil | |
| 952 end | |
| 953 end | |
| 954 | |
| 955 del(keySort) | |
| 956 del(opts) | |
| 957 | |
| 958 return groups | |
| 959 end | |
| 960 | |
| 961 local function BuildSubGroups(group, tree, options, path, appName) | |
| 962 local keySort = new() | |
| 963 local opts = new() | |
| 964 | |
| 965 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
| 966 | |
| 967 for i = 1, #keySort do | |
| 968 local k = keySort[i] | |
| 969 local v = opts[k] | |
| 970 if v.type == "group" then | |
| 971 path[#path+1] = k | |
| 972 local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
| 973 local hidden = CheckOptionHidden(v, options, path, appName) | |
| 974 if not inline and not hidden then | |
| 975 local entry = new() | |
| 976 entry.value = k | |
| 977 entry.text = GetOptionsMemberValue("name", v, options, path, appName) | |
| 978 entry.icon = GetOptionsMemberValue("icon", v, options, path, appName) | |
| 979 entry.iconCoords = GetOptionsMemberValue("iconCoords", v, options, path, appName) | |
| 980 entry.disabled = CheckOptionDisabled(v, options, path, appName) | |
| 981 if not tree.children then tree.children = new() end | |
| 982 tinsert(tree.children,entry) | |
| 983 if (v.childGroups or "tree") == "tree" then | |
| 984 BuildSubGroups(v,entry, options, path, appName) | |
| 985 end | |
| 986 end | |
| 987 path[#path] = nil | |
| 988 end | |
| 989 end | |
| 990 | |
| 991 del(keySort) | |
| 992 del(opts) | |
| 993 end | |
| 994 | |
| 995 local function BuildGroups(group, options, path, appName, recurse) | |
| 996 local tree = new() | |
| 997 local keySort = new() | |
| 998 local opts = new() | |
| 999 | |
| 1000 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
| 1001 | |
| 1002 for i = 1, #keySort do | |
| 1003 local k = keySort[i] | |
| 1004 local v = opts[k] | |
| 1005 if v.type == "group" then | |
| 1006 path[#path+1] = k | |
| 1007 local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
| 1008 local hidden = CheckOptionHidden(v, options, path, appName) | |
| 1009 if not inline and not hidden then | |
| 1010 local entry = new() | |
| 1011 entry.value = k | |
| 1012 entry.text = GetOptionsMemberValue("name", v, options, path, appName) | |
| 1013 entry.icon = GetOptionsMemberValue("icon", v, options, path, appName) | |
| 1014 entry.disabled = CheckOptionDisabled(v, options, path, appName) | |
| 1015 tinsert(tree,entry) | |
| 1016 if recurse and (v.childGroups or "tree") == "tree" then | |
| 1017 BuildSubGroups(v,entry, options, path, appName) | |
| 1018 end | |
| 1019 end | |
| 1020 path[#path] = nil | |
| 1021 end | |
| 1022 end | |
| 1023 del(keySort) | |
| 1024 del(opts) | |
| 1025 return tree | |
| 1026 end | |
| 1027 | |
| 1028 local function InjectInfo(control, options, option, path, rootframe, appName) | |
| 1029 local user = control:GetUserDataTable() | |
| 1030 for i = 1, #path do | |
| 1031 user[i] = path[i] | |
| 1032 end | |
| 1033 user.rootframe = rootframe | |
| 1034 user.option = option | |
| 1035 user.options = options | |
| 1036 user.path = copy(path) | |
| 1037 user.appName = appName | |
| 1038 control:SetCallback("OnRelease", CleanUserData) | |
| 1039 control:SetCallback("OnLeave", OptionOnMouseLeave) | |
| 1040 control:SetCallback("OnEnter", OptionOnMouseOver) | |
| 1041 end | |
| 1042 | |
| 1043 | |
| 1044 --[[ | |
| 1045 options - root of the options table being fed | |
| 1046 container - widget that controls will be placed in | |
| 1047 rootframe - Frame object the options are in | |
| 1048 path - table with the keys to get to the group being fed | |
| 1049 --]] | |
| 1050 | |
| 1051 local function FeedOptions(appName, options,container,rootframe,path,group,inline) | |
| 1052 local keySort = new() | |
| 1053 local opts = new() | |
| 1054 | |
| 1055 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
| 1056 | |
| 1057 for i = 1, #keySort do | |
| 1058 local k = keySort[i] | |
| 1059 local v = opts[k] | |
| 1060 tinsert(path, k) | |
| 1061 local hidden = CheckOptionHidden(v, options, path, appName) | |
| 1062 local name = GetOptionsMemberValue("name", v, options, path, appName) | |
| 1063 if not hidden then | |
| 1064 if v.type == "group" then | |
| 1065 if inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then | |
| 1066 --Inline group | |
| 1067 local GroupContainer | |
| 1068 if name and name ~= "" then | |
| 1069 GroupContainer = gui:Create("InlineGroup") | |
| 1070 GroupContainer:SetTitle(name or "") | |
| 1071 else | |
| 1072 GroupContainer = gui:Create("SimpleGroup") | |
| 1073 end | |
| 1074 | |
| 1075 GroupContainer.width = "fill" | |
| 1076 GroupContainer:SetLayout("flow") | |
| 1077 container:AddChild(GroupContainer) | |
| 1078 FeedOptions(appName,options,GroupContainer,rootframe,path,v,true) | |
| 1079 end | |
| 1080 else | |
| 1081 --Control to feed | |
| 1082 local control | |
| 1083 | |
| 1084 local name = GetOptionsMemberValue("name", v, options, path, appName) | |
| 1085 | |
| 1086 if v.type == "execute" then | |
| 1087 | |
| 1088 local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName) | |
| 1089 local image, width, height = GetOptionsMemberValue("image",v, options, path, appName) | |
| 1090 | |
| 1091 if type(image) == "string" then | |
| 1092 control = gui:Create("Icon") | |
| 1093 if not width then | |
| 1094 width = GetOptionsMemberValue("imageWidth",v, options, path, appName) | |
| 1095 end | |
| 1096 if not height then | |
| 1097 height = GetOptionsMemberValue("imageHeight",v, options, path, appName) | |
| 1098 end | |
| 1099 if type(imageCoords) == "table" then | |
| 1100 control:SetImage(image, unpack(imageCoords)) | |
| 1101 else | |
| 1102 control:SetImage(image) | |
| 1103 end | |
| 1104 if type(width) ~= "number" then | |
| 1105 width = 32 | |
| 1106 end | |
| 1107 if type(height) ~= "number" then | |
| 1108 height = 32 | |
| 1109 end | |
| 1110 control:SetImageSize(width, height) | |
| 1111 control:SetLabel(name) | |
| 1112 else | |
| 1113 control = gui:Create("Button") | |
| 1114 control:SetText(name) | |
| 1115 end | |
| 1116 control:SetCallback("OnClick",ActivateControl) | |
| 1117 | |
| 1118 elseif v.type == "input" then | |
| 1119 local controlType = v.dialogControl or v.control or (v.multiline and "MultiLineEditBox") or "EditBox" | |
| 1120 control = gui:Create(controlType) | |
| 1121 if not control then | |
| 1122 geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) | |
| 1123 control = gui:Create(v.multiline and "MultiLineEditBox" or "EditBox") | |
| 1124 end | |
| 1125 | |
| 1126 if v.multiline and control.SetNumLines then | |
| 1127 control:SetNumLines(tonumber(v.multiline) or 4) | |
| 1128 end | |
| 1129 control:SetLabel(name) | |
| 1130 control:SetCallback("OnEnterPressed",ActivateControl) | |
| 1131 local text = GetOptionsMemberValue("get",v, options, path, appName) | |
| 1132 if type(text) ~= "string" then | |
| 1133 text = "" | |
| 1134 end | |
| 1135 control:SetText(text) | |
| 1136 | |
| 1137 elseif v.type == "toggle" then | |
| 1138 control = gui:Create("CheckBox") | |
| 1139 control:SetLabel(name) | |
| 1140 control:SetTriState(v.tristate) | |
| 1141 local value = GetOptionsMemberValue("get",v, options, path, appName) | |
| 1142 control:SetValue(value) | |
| 1143 control:SetCallback("OnValueChanged",ActivateControl) | |
| 1144 | |
| 1145 if v.descStyle == "inline" then | |
| 1146 local desc = GetOptionsMemberValue("desc", v, options, path, appName) | |
| 1147 control:SetDescription(desc) | |
| 1148 end | |
| 1149 | |
| 1150 local image = GetOptionsMemberValue("image", v, options, path, appName) | |
| 1151 local imageCoords = GetOptionsMemberValue("imageCoords", v, options, path, appName) | |
| 1152 | |
| 1153 if type(image) == "string" then | |
| 1154 if type(imageCoords) == "table" then | |
| 1155 control:SetImage(image, unpack(imageCoords)) | |
| 1156 else | |
| 1157 control:SetImage(image) | |
| 1158 end | |
| 1159 end | |
| 1160 elseif v.type == "range" then | |
| 1161 control = gui:Create("Slider") | |
| 1162 control:SetLabel(name) | |
| 1163 control:SetSliderValues(v.softMin or v.min or 0, v.softMax or v.max or 100, v.bigStep or v.step or 0) | |
| 1164 control:SetIsPercent(v.isPercent) | |
| 1165 local value = GetOptionsMemberValue("get",v, options, path, appName) | |
| 1166 if type(value) ~= "number" then | |
| 1167 value = 0 | |
| 1168 end | |
| 1169 control:SetValue(value) | |
| 1170 control:SetCallback("OnValueChanged",ActivateSlider) | |
| 1171 control:SetCallback("OnMouseUp",ActivateSlider) | |
| 1172 | |
| 1173 elseif v.type == "select" then | |
| 1174 local values = GetOptionsMemberValue("values", v, options, path, appName) | |
| 1175 local controlType = v.dialogControl or v.control or "Dropdown" | |
| 1176 control = gui:Create(controlType) | |
| 1177 if not control then | |
| 1178 geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) | |
| 1179 control = gui:Create("Dropdown") | |
| 1180 end | |
| 1181 control:SetLabel(name) | |
| 1182 control:SetList(values) | |
| 1183 local value = GetOptionsMemberValue("get",v, options, path, appName) | |
| 1184 if not values[value] then | |
| 1185 value = nil | |
| 1186 end | |
| 1187 control:SetValue(value) | |
| 1188 control:SetCallback("OnValueChanged",ActivateControl) | |
| 1189 | |
| 1190 elseif v.type == "multiselect" then | |
| 1191 local values = GetOptionsMemberValue("values", v, options, path, appName) | |
| 1192 local disabled = CheckOptionDisabled(v, options, path, appName) | |
| 1193 | |
| 1194 local controlType = v.dialogControl or v.control | |
| 1195 | |
| 1196 local valuesort = new() | |
| 1197 if values then | |
| 1198 for value, text in pairs(values) do | |
| 1199 tinsert(valuesort, value) | |
| 1200 end | |
| 1201 end | |
| 1202 tsort(valuesort) | |
| 1203 | |
| 1204 if controlType then | |
| 1205 control = gui:Create(controlType) | |
| 1206 if not control then | |
| 1207 geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) | |
| 1208 end | |
| 1209 end | |
| 1210 if control then | |
| 1211 control:SetMultiselect(true) | |
| 1212 control:SetLabel(name) | |
| 1213 control:SetList(values) | |
| 1214 control:SetDisabled(disabled) | |
| 1215 control:SetCallback("OnValueChanged",ActivateControl) | |
| 1216 control:SetCallback("OnClosed", MultiControlOnClosed) | |
| 1217 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
| 1218 if width == "double" then | |
| 1219 control:SetWidth(width_multiplier * 2) | |
| 1220 elseif width == "half" then | |
| 1221 control:SetWidth(width_multiplier / 2) | |
| 1222 elseif width == "full" then | |
| 1223 control.width = "fill" | |
| 1224 else | |
| 1225 control:SetWidth(width_multiplier) | |
| 1226 end | |
| 1227 --check:SetTriState(v.tristate) | |
| 1228 for i = 1, #valuesort do | |
| 1229 local key = valuesort[i] | |
| 1230 local value = GetOptionsMemberValue("get",v, options, path, appName, key) | |
| 1231 control:SetItemValue(key,value) | |
| 1232 end | |
| 1233 else | |
| 1234 control = gui:Create("InlineGroup") | |
| 1235 control:SetLayout("Flow") | |
| 1236 control:SetTitle(name) | |
| 1237 control.width = "fill" | |
| 1238 | |
| 1239 control:PauseLayout() | |
| 1240 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
| 1241 for i = 1, #valuesort do | |
| 1242 local value = valuesort[i] | |
| 1243 local text = values[value] | |
| 1244 local check = gui:Create("CheckBox") | |
| 1245 check:SetLabel(text) | |
| 1246 check:SetUserData("value", value) | |
| 1247 check:SetUserData("text", text) | |
| 1248 check:SetDisabled(disabled) | |
| 1249 check:SetTriState(v.tristate) | |
| 1250 check:SetValue(GetOptionsMemberValue("get",v, options, path, appName, value)) | |
| 1251 check:SetCallback("OnValueChanged",ActivateMultiControl) | |
| 1252 InjectInfo(check, options, v, path, rootframe, appName) | |
| 1253 control:AddChild(check) | |
| 1254 if width == "double" then | |
| 1255 check:SetWidth(width_multiplier * 2) | |
| 1256 elseif width == "half" then | |
| 1257 check:SetWidth(width_multiplier / 2) | |
| 1258 elseif width == "full" then | |
| 1259 check.width = "fill" | |
| 1260 else | |
| 1261 check:SetWidth(width_multiplier) | |
| 1262 end | |
| 1263 end | |
| 1264 control:ResumeLayout() | |
| 1265 control:DoLayout() | |
| 1266 | |
| 1267 | |
| 1268 end | |
| 1269 | |
| 1270 del(valuesort) | |
| 1271 | |
| 1272 elseif v.type == "color" then | |
| 1273 control = gui:Create("ColorPicker") | |
| 1274 control:SetLabel(name) | |
| 1275 control:SetHasAlpha(v.hasAlpha) | |
| 1276 control:SetColor(GetOptionsMemberValue("get",v, options, path, appName)) | |
| 1277 control:SetCallback("OnValueChanged",ActivateControl) | |
| 1278 control:SetCallback("OnValueConfirmed",ActivateControl) | |
| 1279 | |
| 1280 elseif v.type == "keybinding" then | |
| 1281 control = gui:Create("Keybinding") | |
| 1282 control:SetLabel(name) | |
| 1283 control:SetKey(GetOptionsMemberValue("get",v, options, path, appName)) | |
| 1284 control:SetCallback("OnKeyChanged",ActivateControl) | |
| 1285 | |
| 1286 elseif v.type == "header" then | |
| 1287 control = gui:Create("Heading") | |
| 1288 control:SetText(name) | |
| 1289 control.width = "fill" | |
| 1290 | |
| 1291 elseif v.type == "description" then | |
| 1292 control = gui:Create("Label") | |
| 1293 control:SetText(name) | |
| 1294 | |
| 1295 local fontSize = GetOptionsMemberValue("fontSize",v, options, path, appName) | |
| 1296 if fontSize == "medium" then | |
| 1297 control:SetFontObject(GameFontHighlight) | |
| 1298 elseif fontSize == "large" then | |
| 1299 control:SetFontObject(GameFontHighlightLarge) | |
| 1300 else -- small or invalid | |
| 1301 control:SetFontObject(GameFontHighlightSmall) | |
| 1302 end | |
| 1303 | |
| 1304 local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName) | |
| 1305 local image, width, height = GetOptionsMemberValue("image",v, options, path, appName) | |
| 1306 | |
| 1307 if type(image) == "string" then | |
| 1308 if not width then | |
| 1309 width = GetOptionsMemberValue("imageWidth",v, options, path, appName) | |
| 1310 end | |
| 1311 if not height then | |
| 1312 height = GetOptionsMemberValue("imageHeight",v, options, path, appName) | |
| 1313 end | |
| 1314 if type(imageCoords) == "table" then | |
| 1315 control:SetImage(image, unpack(imageCoords)) | |
| 1316 else | |
| 1317 control:SetImage(image) | |
| 1318 end | |
| 1319 if type(width) ~= "number" then | |
| 1320 width = 32 | |
| 1321 end | |
| 1322 if type(height) ~= "number" then | |
| 1323 height = 32 | |
| 1324 end | |
| 1325 control:SetImageSize(width, height) | |
| 1326 end | |
| 1327 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
| 1328 control.width = not width and "fill" | |
| 1329 end | |
| 1330 | |
| 1331 --Common Init | |
| 1332 if control then | |
| 1333 if control.width ~= "fill" then | |
| 1334 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
| 1335 if width == "double" then | |
| 1336 control:SetWidth(width_multiplier * 2) | |
| 1337 elseif width == "half" then | |
| 1338 control:SetWidth(width_multiplier / 2) | |
| 1339 elseif width == "full" then | |
| 1340 control.width = "fill" | |
| 1341 else | |
| 1342 control:SetWidth(width_multiplier) | |
| 1343 end | |
| 1344 end | |
| 1345 if control.SetDisabled then | |
| 1346 local disabled = CheckOptionDisabled(v, options, path, appName) | |
| 1347 control:SetDisabled(disabled) | |
| 1348 end | |
| 1349 | |
| 1350 InjectInfo(control, options, v, path, rootframe, appName) | |
| 1351 container:AddChild(control) | |
| 1352 end | |
| 1353 | |
| 1354 end | |
| 1355 end | |
| 1356 tremove(path) | |
| 1357 end | |
| 1358 container:ResumeLayout() | |
| 1359 container:DoLayout() | |
| 1360 del(keySort) | |
| 1361 del(opts) | |
| 1362 end | |
| 1363 | |
| 1364 local function BuildPath(path, ...) | |
| 1365 for i = 1, select("#",...) do | |
| 1366 tinsert(path, (select(i,...))) | |
| 1367 end | |
| 1368 end | |
| 1369 | |
| 1370 | |
| 1371 local function TreeOnButtonEnter(widget, event, uniquevalue, button) | |
| 1372 local user = widget:GetUserDataTable() | |
| 1373 if not user then return end | |
| 1374 local options = user.options | |
| 1375 local option = user.option | |
| 1376 local path = user.path | |
| 1377 local appName = user.appName | |
| 1378 | |
| 1379 local feedpath = new() | |
| 1380 for i = 1, #path do | |
| 1381 feedpath[i] = path[i] | |
| 1382 end | |
| 1383 | |
| 1384 BuildPath(feedpath, ("\001"):split(uniquevalue)) | |
| 1385 local group = options | |
| 1386 for i = 1, #feedpath do | |
| 1387 if not group then return end | |
| 1388 group = GetSubOption(group, feedpath[i]) | |
| 1389 end | |
| 1390 | |
| 1391 local name = GetOptionsMemberValue("name", group, options, feedpath, appName) | |
| 1392 local desc = GetOptionsMemberValue("desc", group, options, feedpath, appName) | |
| 1393 | |
| 1394 GameTooltip:SetOwner(button, "ANCHOR_NONE") | |
| 1395 if widget.type == "TabGroup" then | |
| 1396 GameTooltip:SetPoint("BOTTOM",button,"TOP") | |
| 1397 else | |
| 1398 GameTooltip:SetPoint("LEFT",button,"RIGHT") | |
| 1399 end | |
| 1400 | |
| 1401 GameTooltip:SetText(name, 1, .82, 0, 1) | |
| 1402 | |
| 1403 if type(desc) == "string" then | |
| 1404 GameTooltip:AddLine(desc, 1, 1, 1, 1) | |
| 1405 end | |
| 1406 | |
| 1407 GameTooltip:Show() | |
| 1408 end | |
| 1409 | |
| 1410 local function TreeOnButtonLeave(widget, event, value, button) | |
| 1411 GameTooltip:Hide() | |
| 1412 end | |
| 1413 | |
| 1414 | |
| 1415 local function GroupExists(appName, options, path, uniquevalue) | |
| 1416 if not uniquevalue then return false end | |
| 1417 | |
| 1418 local feedpath = new() | |
| 1419 local temppath = new() | |
| 1420 for i = 1, #path do | |
| 1421 feedpath[i] = path[i] | |
| 1422 end | |
| 1423 | |
| 1424 BuildPath(feedpath, ("\001"):split(uniquevalue)) | |
| 1425 | |
| 1426 local group = options | |
| 1427 for i = 1, #feedpath do | |
| 1428 local v = feedpath[i] | |
| 1429 temppath[i] = v | |
| 1430 group = GetSubOption(group, v) | |
| 1431 | |
| 1432 if not group or group.type ~= "group" or CheckOptionHidden(group, options, temppath, appName) then | |
| 1433 del(feedpath) | |
| 1434 del(temppath) | |
| 1435 return false | |
| 1436 end | |
| 1437 end | |
| 1438 del(feedpath) | |
| 1439 del(temppath) | |
| 1440 return true | |
| 1441 end | |
| 1442 | |
| 1443 local function GroupSelected(widget, event, uniquevalue) | |
| 1444 | |
| 1445 local user = widget:GetUserDataTable() | |
| 1446 | |
| 1447 local options = user.options | |
| 1448 local option = user.option | |
| 1449 local path = user.path | |
| 1450 local rootframe = user.rootframe | |
| 1451 | |
| 1452 local feedpath = new() | |
| 1453 for i = 1, #path do | |
| 1454 feedpath[i] = path[i] | |
| 1455 end | |
| 1456 | |
| 1457 BuildPath(feedpath, ("\001"):split(uniquevalue)) | |
| 1458 local group = options | |
| 1459 for i = 1, #feedpath do | |
| 1460 group = GetSubOption(group, feedpath[i]) | |
| 1461 end | |
| 1462 widget:ReleaseChildren() | |
| 1463 AceConfigDialog:FeedGroup(user.appName,options,widget,rootframe,feedpath) | |
| 1464 | |
| 1465 del(feedpath) | |
| 1466 end | |
| 1467 | |
| 1468 | |
| 1469 | |
| 1470 --[[ | |
| 1471 -- INTERNAL -- | |
| 1472 This function will feed one group, and any inline child groups into the given container | |
| 1473 Select Groups will only have the selection control (tree, tabs, dropdown) fed in | |
| 1474 and have a group selected, this event will trigger the feeding of child groups | |
| 1475 | |
| 1476 Rules: | |
| 1477 If the group is Inline, FeedOptions | |
| 1478 If the group has no child groups, FeedOptions | |
| 1479 | |
| 1480 If the group is a tab or select group, FeedOptions then add the Group Control | |
| 1481 If the group is a tree group FeedOptions then | |
| 1482 its parent isnt a tree group: then add the tree control containing this and all child tree groups | |
| 1483 if its parent is a tree group, its already a node on a tree | |
| 1484 --]] | |
| 1485 | |
| 1486 function AceConfigDialog:FeedGroup(appName,options,container,rootframe,path, isRoot) | |
| 1487 local group = options | |
| 1488 --follow the path to get to the curent group | |
| 1489 local inline | |
| 1490 local grouptype, parenttype = options.childGroups, "none" | |
| 1491 | |
| 1492 | |
| 1493 for i = 1, #path do | |
| 1494 local v = path[i] | |
| 1495 group = GetSubOption(group, v) | |
| 1496 inline = inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
| 1497 parenttype = grouptype | |
| 1498 grouptype = group.childGroups | |
| 1499 end | |
| 1500 | |
| 1501 if not parenttype then | |
| 1502 parenttype = "tree" | |
| 1503 end | |
| 1504 | |
| 1505 --check if the group has child groups | |
| 1506 local hasChildGroups | |
| 1507 for k, v in pairs(group.args) do | |
| 1508 if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) and not CheckOptionHidden(v, options, path, appName) then | |
| 1509 hasChildGroups = true | |
| 1510 end | |
| 1511 end | |
| 1512 if group.plugins then | |
| 1513 for plugin, t in pairs(group.plugins) do | |
| 1514 for k, v in pairs(t) do | |
| 1515 if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) and not CheckOptionHidden(v, options, path, appName) then | |
| 1516 hasChildGroups = true | |
| 1517 end | |
| 1518 end | |
| 1519 end | |
| 1520 end | |
| 1521 | |
| 1522 container:SetLayout("flow") | |
| 1523 local scroll | |
| 1524 | |
| 1525 --Add a scrollframe if we are not going to add a group control, this is the inverse of the conditions for that later on | |
| 1526 if (not (hasChildGroups and not inline)) or (grouptype ~= "tab" and grouptype ~= "select" and (parenttype == "tree" and not isRoot)) then | |
| 1527 if container.type ~= "InlineGroup" and container.type ~= "SimpleGroup" then | |
| 1528 scroll = gui:Create("ScrollFrame") | |
| 1529 scroll:SetLayout("flow") | |
| 1530 scroll.width = "fill" | |
| 1531 scroll.height = "fill" | |
| 1532 container:SetLayout("fill") | |
| 1533 container:AddChild(scroll) | |
| 1534 container = scroll | |
| 1535 end | |
| 1536 end | |
| 1537 | |
| 1538 FeedOptions(appName,options,container,rootframe,path,group,nil) | |
| 1539 | |
| 1540 if scroll then | |
| 1541 container:PerformLayout() | |
| 1542 local status = self:GetStatusTable(appName, path) | |
| 1543 if not status.scroll then | |
| 1544 status.scroll = {} | |
| 1545 end | |
| 1546 scroll:SetStatusTable(status.scroll) | |
| 1547 end | |
| 1548 | |
| 1549 if hasChildGroups and not inline then | |
| 1550 local name = GetOptionsMemberValue("name", group, options, path, appName) | |
| 1551 if grouptype == "tab" then | |
| 1552 | |
| 1553 local tab = gui:Create("TabGroup") | |
| 1554 InjectInfo(tab, options, group, path, rootframe, appName) | |
| 1555 tab:SetCallback("OnGroupSelected", GroupSelected) | |
| 1556 tab:SetCallback("OnTabEnter", TreeOnButtonEnter) | |
| 1557 tab:SetCallback("OnTabLeave", TreeOnButtonLeave) | |
| 1558 | |
| 1559 local status = AceConfigDialog:GetStatusTable(appName, path) | |
| 1560 if not status.groups then | |
| 1561 status.groups = {} | |
| 1562 end | |
| 1563 tab:SetStatusTable(status.groups) | |
| 1564 tab.width = "fill" | |
| 1565 tab.height = "fill" | |
| 1566 | |
| 1567 local tabs = BuildGroups(group, options, path, appName) | |
| 1568 tab:SetTabs(tabs) | |
| 1569 tab:SetUserData("tablist", tabs) | |
| 1570 | |
| 1571 for i = 1, #tabs do | |
| 1572 local entry = tabs[i] | |
| 1573 if not entry.disabled then | |
| 1574 tab:SelectTab((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value) | |
| 1575 break | |
| 1576 end | |
| 1577 end | |
| 1578 | |
| 1579 container:AddChild(tab) | |
| 1580 | |
| 1581 elseif grouptype == "select" then | |
| 1582 | |
| 1583 local select = gui:Create("DropdownGroup") | |
| 1584 select:SetTitle(name) | |
| 1585 InjectInfo(select, options, group, path, rootframe, appName) | |
| 1586 select:SetCallback("OnGroupSelected", GroupSelected) | |
| 1587 local status = AceConfigDialog:GetStatusTable(appName, path) | |
| 1588 if not status.groups then | |
| 1589 status.groups = {} | |
| 1590 end | |
| 1591 select:SetStatusTable(status.groups) | |
| 1592 local grouplist = BuildSelect(group, options, path, appName) | |
| 1593 select:SetGroupList(grouplist) | |
| 1594 select:SetUserData("grouplist", grouplist) | |
| 1595 local firstgroup | |
| 1596 for k, v in pairs(grouplist) do | |
| 1597 if not firstgroup or k < firstgroup then | |
| 1598 firstgroup = k | |
| 1599 end | |
| 1600 end | |
| 1601 | |
| 1602 if firstgroup then | |
| 1603 select:SetGroup((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or firstgroup) | |
| 1604 end | |
| 1605 | |
| 1606 select.width = "fill" | |
| 1607 select.height = "fill" | |
| 1608 | |
| 1609 container:AddChild(select) | |
| 1610 | |
| 1611 --assume tree group by default | |
| 1612 --if parenttype is tree then this group is already a node on that tree | |
| 1613 elseif (parenttype ~= "tree") or isRoot then | |
| 1614 local tree = gui:Create("TreeGroup") | |
| 1615 InjectInfo(tree, options, group, path, rootframe, appName) | |
| 1616 tree:EnableButtonTooltips(false) | |
| 1617 | |
| 1618 tree.width = "fill" | |
| 1619 tree.height = "fill" | |
| 1620 | |
| 1621 tree:SetCallback("OnGroupSelected", GroupSelected) | |
| 1622 tree:SetCallback("OnButtonEnter", TreeOnButtonEnter) | |
| 1623 tree:SetCallback("OnButtonLeave", TreeOnButtonLeave) | |
| 1624 | |
| 1625 local status = AceConfigDialog:GetStatusTable(appName, path) | |
| 1626 if not status.groups then | |
| 1627 status.groups = {} | |
| 1628 end | |
| 1629 local treedefinition = BuildGroups(group, options, path, appName, true) | |
| 1630 tree:SetStatusTable(status.groups) | |
| 1631 | |
| 1632 tree:SetTree(treedefinition) | |
| 1633 tree:SetUserData("tree",treedefinition) | |
| 1634 | |
| 1635 for i = 1, #treedefinition do | |
| 1636 local entry = treedefinition[i] | |
| 1637 if not entry.disabled then | |
| 1638 tree:SelectByValue((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value) | |
| 1639 break | |
| 1640 end | |
| 1641 end | |
| 1642 | |
| 1643 container:AddChild(tree) | |
| 1644 end | |
| 1645 end | |
| 1646 end | |
| 1647 | |
| 1648 local old_CloseSpecialWindows | |
| 1649 | |
| 1650 | |
| 1651 local function RefreshOnUpdate(this) | |
| 1652 for appName in pairs(this.closing) do | |
| 1653 if AceConfigDialog.OpenFrames[appName] then | |
| 1654 AceConfigDialog.OpenFrames[appName]:Hide() | |
| 1655 end | |
| 1656 if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then | |
| 1657 for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do | |
| 1658 if not widget:IsVisible() then | |
| 1659 widget:ReleaseChildren() | |
| 1660 end | |
| 1661 end | |
| 1662 end | |
| 1663 this.closing[appName] = nil | |
| 1664 end | |
| 1665 | |
| 1666 if this.closeAll then | |
| 1667 for k, v in pairs(AceConfigDialog.OpenFrames) do | |
| 1668 if not this.closeAllOverride[k] then | |
| 1669 v:Hide() | |
| 1670 end | |
| 1671 end | |
| 1672 this.closeAll = nil | |
| 1673 wipe(this.closeAllOverride) | |
| 1674 end | |
| 1675 | |
| 1676 for appName in pairs(this.apps) do | |
| 1677 if AceConfigDialog.OpenFrames[appName] then | |
| 1678 local user = AceConfigDialog.OpenFrames[appName]:GetUserDataTable() | |
| 1679 AceConfigDialog:Open(appName, unpack(user.basepath or emptyTbl)) | |
| 1680 end | |
| 1681 if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then | |
| 1682 for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do | |
| 1683 local user = widget:GetUserDataTable() | |
| 1684 if widget:IsVisible() then | |
| 1685 AceConfigDialog:Open(widget:GetUserData("appName"), widget, unpack(user.basepath or emptyTbl)) | |
| 1686 end | |
| 1687 end | |
| 1688 end | |
| 1689 this.apps[appName] = nil | |
| 1690 end | |
| 1691 this:SetScript("OnUpdate", nil) | |
| 1692 end | |
| 1693 | |
| 1694 -- Upgrade the OnUpdate script as well, if needed. | |
| 1695 if AceConfigDialog.frame:GetScript("OnUpdate") then | |
| 1696 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
| 1697 end | |
| 1698 | |
| 1699 --- Close all open options windows | |
| 1700 function AceConfigDialog:CloseAll() | |
| 1701 AceConfigDialog.frame.closeAll = true | |
| 1702 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
| 1703 if next(self.OpenFrames) then | |
| 1704 return true | |
| 1705 end | |
| 1706 end | |
| 1707 | |
| 1708 --- Close a specific options window. | |
| 1709 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
| 1710 function AceConfigDialog:Close(appName) | |
| 1711 if self.OpenFrames[appName] then | |
| 1712 AceConfigDialog.frame.closing[appName] = true | |
| 1713 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
| 1714 return true | |
| 1715 end | |
| 1716 end | |
| 1717 | |
| 1718 -- Internal -- Called by AceConfigRegistry | |
| 1719 function AceConfigDialog:ConfigTableChanged(event, appName) | |
| 1720 AceConfigDialog.frame.apps[appName] = true | |
| 1721 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
| 1722 end | |
| 1723 | |
| 1724 reg.RegisterCallback(AceConfigDialog, "ConfigTableChange", "ConfigTableChanged") | |
| 1725 | |
| 1726 --- Sets the default size of the options window for a specific application. | |
| 1727 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
| 1728 -- @param width The default width | |
| 1729 -- @param height The default height | |
| 1730 function AceConfigDialog:SetDefaultSize(appName, width, height) | |
| 1731 local status = AceConfigDialog:GetStatusTable(appName) | |
| 1732 if type(width) == "number" and type(height) == "number" then | |
| 1733 status.width = width | |
| 1734 status.height = height | |
| 1735 end | |
| 1736 end | |
| 1737 | |
| 1738 --- Open an option window at the specified path (if any). | |
| 1739 -- This function can optionally feed the group into a pre-created container | |
| 1740 -- instead of creating a new container frame. | |
| 1741 -- @paramsig appName [, container][, ...] | |
| 1742 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
| 1743 -- @param container An optional container frame to feed the options into | |
| 1744 -- @param ... The path to open after creating the options window (see `:SelectGroup` for details) | |
| 1745 function AceConfigDialog:Open(appName, container, ...) | |
| 1746 if not old_CloseSpecialWindows then | |
| 1747 old_CloseSpecialWindows = CloseSpecialWindows | |
| 1748 CloseSpecialWindows = function() | |
| 1749 local found = old_CloseSpecialWindows() | |
| 1750 return self:CloseAll() or found | |
| 1751 end | |
| 1752 end | |
| 1753 local app = reg:GetOptionsTable(appName) | |
| 1754 if not app then | |
| 1755 error(("%s isn't registed with AceConfigRegistry, unable to open config"):format(appName), 2) | |
| 1756 end | |
| 1757 local options = app("dialog", MAJOR) | |
| 1758 | |
| 1759 local f | |
| 1760 | |
| 1761 local path = new() | |
| 1762 local name = GetOptionsMemberValue("name", options, options, path, appName) | |
| 1763 | |
| 1764 --If an optional path is specified add it to the path table before feeding the options | |
| 1765 --as container is optional as well it may contain the first element of the path | |
| 1766 if type(container) == "string" then | |
| 1767 tinsert(path, container) | |
| 1768 container = nil | |
| 1769 end | |
| 1770 for n = 1, select("#",...) do | |
| 1771 tinsert(path, (select(n, ...))) | |
| 1772 end | |
| 1773 | |
| 1774 --if a container is given feed into that | |
| 1775 if container then | |
| 1776 f = container | |
| 1777 f:ReleaseChildren() | |
| 1778 f:SetUserData("appName", appName) | |
| 1779 f:SetUserData("iscustom", true) | |
| 1780 if #path > 0 then | |
| 1781 f:SetUserData("basepath", copy(path)) | |
| 1782 end | |
| 1783 local status = AceConfigDialog:GetStatusTable(appName) | |
| 1784 if not status.width then | |
| 1785 status.width = 700 | |
| 1786 end | |
| 1787 if not status.height then | |
| 1788 status.height = 500 | |
| 1789 end | |
| 1790 if f.SetStatusTable then | |
| 1791 f:SetStatusTable(status) | |
| 1792 end | |
| 1793 if f.SetTitle then | |
| 1794 f:SetTitle(name or "") | |
| 1795 end | |
| 1796 else | |
| 1797 if not self.OpenFrames[appName] then | |
| 1798 f = gui:Create("Frame") | |
| 1799 self.OpenFrames[appName] = f | |
| 1800 else | |
| 1801 f = self.OpenFrames[appName] | |
| 1802 end | |
| 1803 f:ReleaseChildren() | |
| 1804 f:SetCallback("OnClose", FrameOnClose) | |
| 1805 f:SetUserData("appName", appName) | |
| 1806 if #path > 0 then | |
| 1807 f:SetUserData("basepath", copy(path)) | |
| 1808 end | |
| 1809 f:SetTitle(name or "") | |
| 1810 local status = AceConfigDialog:GetStatusTable(appName) | |
| 1811 f:SetStatusTable(status) | |
| 1812 end | |
| 1813 | |
| 1814 self:FeedGroup(appName,options,f,f,path,true) | |
| 1815 if f.Show then | |
| 1816 f:Show() | |
| 1817 end | |
| 1818 del(path) | |
| 1819 | |
| 1820 if AceConfigDialog.frame.closeAll then | |
| 1821 -- close all is set, but thats not good, since we're just opening here, so force it | |
| 1822 AceConfigDialog.frame.closeAllOverride[appName] = true | |
| 1823 end | |
| 1824 end | |
| 1825 | |
| 1826 -- convert pre-39 BlizOptions structure to the new format | |
| 1827 if oldminor and oldminor < 39 and AceConfigDialog.BlizOptions then | |
| 1828 local old = AceConfigDialog.BlizOptions | |
| 1829 local new = {} | |
| 1830 for key, widget in pairs(old) do | |
| 1831 local appName = widget:GetUserData("appName") | |
| 1832 if not new[appName] then new[appName] = {} end | |
| 1833 new[appName][key] = widget | |
| 1834 end | |
| 1835 AceConfigDialog.BlizOptions = new | |
| 1836 else | |
| 1837 AceConfigDialog.BlizOptions = AceConfigDialog.BlizOptions or {} | |
| 1838 end | |
| 1839 | |
| 1840 local function FeedToBlizPanel(widget, event) | |
| 1841 local path = widget:GetUserData("path") | |
| 1842 AceConfigDialog:Open(widget:GetUserData("appName"), widget, unpack(path or emptyTbl)) | |
| 1843 end | |
| 1844 | |
| 1845 local function ClearBlizPanel(widget, event) | |
| 1846 local appName = widget:GetUserData("appName") | |
| 1847 AceConfigDialog.frame.closing[appName] = true | |
| 1848 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
| 1849 end | |
| 1850 | |
| 1851 --- Add an option table into the Blizzard Interface Options panel. | |
| 1852 -- You can optionally supply a descriptive name to use and a parent frame to use, | |
| 1853 -- as well as a path in the options table.\\ | |
| 1854 -- If no name is specified, the appName will be used instead. | |
| 1855 -- | |
| 1856 -- If you specify a proper `parent` (by name), the interface options will generate a | |
| 1857 -- tree layout. Note that only one level of children is supported, so the parent always | |
| 1858 -- has to be a head-level note. | |
| 1859 -- | |
| 1860 -- This function returns a reference to the container frame registered with the Interface | |
| 1861 -- Options. You can use this reference to open the options with the API function | |
| 1862 -- `InterfaceOptionsFrame_OpenToCategory`. | |
| 1863 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
| 1864 -- @param name A descriptive name to display in the options tree (defaults to appName) | |
| 1865 -- @param parent The parent to use in the interface options tree. | |
| 1866 -- @param ... The path in the options table to feed into the interface options panel. | |
| 1867 -- @return The reference to the frame registered into the Interface Options. | |
| 1868 function AceConfigDialog:AddToBlizOptions(appName, name, parent, ...) | |
| 1869 local BlizOptions = AceConfigDialog.BlizOptions | |
| 1870 | |
| 1871 local key = appName | |
| 1872 for n = 1, select("#", ...) do | |
| 1873 key = key.."\001"..select(n, ...) | |
| 1874 end | |
| 1875 | |
| 1876 if not BlizOptions[appName] then | |
| 1877 BlizOptions[appName] = {} | |
| 1878 end | |
| 1879 | |
| 1880 if not BlizOptions[appName][key] then | |
| 1881 local group = gui:Create("BlizOptionsGroup") | |
| 1882 BlizOptions[appName][key] = group | |
| 1883 group:SetName(name or appName, parent) | |
| 1884 | |
| 1885 group:SetTitle(name or appName) | |
| 1886 group:SetUserData("appName", appName) | |
| 1887 if select("#", ...) > 0 then | |
| 1888 local path = {} | |
| 1889 for n = 1, select("#",...) do | |
| 1890 tinsert(path, (select(n, ...))) | |
| 1891 end | |
| 1892 group:SetUserData("path", path) | |
| 1893 end | |
| 1894 group:SetCallback("OnShow", FeedToBlizPanel) | |
| 1895 group:SetCallback("OnHide", ClearBlizPanel) | |
| 1896 InterfaceOptions_AddCategory(group.frame) | |
| 1897 return group.frame | |
| 1898 else | |
| 1899 error(("%s has already been added to the Blizzard Options Window with the given path"):format(appName), 2) | |
| 1900 end | |
| 1901 end | 
