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