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