Mercurial > wow > hotcorners
comparison Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.lua @ 0:fc346da3afd9
First commit Hot Corners standalone.
| author | tercio |
|---|---|
| date | Fri, 08 Aug 2014 12:35:17 -0300 |
| parents | |
| children | c31ee4251181 |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:fc346da3afd9 |
|---|---|
| 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 1089 2013-09-13 14:32:35Z nevcairiel $ | |
| 5 | |
| 6 local LibStub = LibStub | |
| 7 local MAJOR, MINOR = "AceConfigDialog-3.0", 58 | |
| 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, tsort = table.concat, table.insert, table.sort, table.remove, table.sort | |
| 25 local strmatch, format = string.match, string.format | |
| 26 local assert, loadstring, error = assert, loadstring, error | |
| 27 local pairs, next, select, type, unpack, wipe, ipairs = pairs, next, select, type, unpack, wipe, ipairs | |
| 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 t.preferredIndex = STATICPOPUP_NUMDIALOGS | |
| 584 local dialog, oldstrata | |
| 585 t.OnAccept = function() | |
| 586 safecall(func, unpack(t)) | |
| 587 if dialog and oldstrata then | |
| 588 dialog:SetFrameStrata(oldstrata) | |
| 589 end | |
| 590 AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl)) | |
| 591 del(info) | |
| 592 end | |
| 593 t.OnCancel = function() | |
| 594 if dialog and oldstrata then | |
| 595 dialog:SetFrameStrata(oldstrata) | |
| 596 end | |
| 597 AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl)) | |
| 598 del(info) | |
| 599 end | |
| 600 for i = 1, select("#", ...) do | |
| 601 t[i] = select(i, ...) or false | |
| 602 end | |
| 603 t.timeout = 0 | |
| 604 t.whileDead = 1 | |
| 605 t.hideOnEscape = 1 | |
| 606 | |
| 607 dialog = StaticPopup_Show("ACECONFIGDIALOG30_CONFIRM_DIALOG") | |
| 608 if dialog then | |
| 609 oldstrata = dialog:GetFrameStrata() | |
| 610 dialog:SetFrameStrata("TOOLTIP") | |
| 611 end | |
| 612 end | |
| 613 | |
| 614 local function ActivateControl(widget, event, ...) | |
| 615 --This function will call the set / execute handler for the widget | |
| 616 --widget:GetUserDataTable() contains the needed info | |
| 617 local user = widget:GetUserDataTable() | |
| 618 local option = user.option | |
| 619 local options = user.options | |
| 620 local path = user.path | |
| 621 local info = new() | |
| 622 | |
| 623 local func | |
| 624 local group = options | |
| 625 local funcname = GetFuncName(option) | |
| 626 local handler | |
| 627 local confirm | |
| 628 local validate | |
| 629 --build the info table containing the path | |
| 630 -- pick up functions while traversing the tree | |
| 631 if group[funcname] ~= nil then | |
| 632 func = group[funcname] | |
| 633 end | |
| 634 handler = group.handler or handler | |
| 635 confirm = group.confirm | |
| 636 validate = group.validate | |
| 637 for i = 1, #path do | |
| 638 local v = path[i] | |
| 639 group = GetSubOption(group, v) | |
| 640 info[i] = v | |
| 641 if group[funcname] ~= nil then | |
| 642 func = group[funcname] | |
| 643 end | |
| 644 handler = group.handler or handler | |
| 645 if group.confirm ~= nil then | |
| 646 confirm = group.confirm | |
| 647 end | |
| 648 if group.validate ~= nil then | |
| 649 validate = group.validate | |
| 650 end | |
| 651 end | |
| 652 | |
| 653 info.options = options | |
| 654 info.appName = user.appName | |
| 655 info.arg = option.arg | |
| 656 info.handler = handler | |
| 657 info.option = option | |
| 658 info.type = option.type | |
| 659 info.uiType = "dialog" | |
| 660 info.uiName = MAJOR | |
| 661 | |
| 662 local name | |
| 663 if type(option.name) == "function" then | |
| 664 name = option.name(info) | |
| 665 elseif type(option.name) == "string" then | |
| 666 name = option.name | |
| 667 else | |
| 668 name = "" | |
| 669 end | |
| 670 local usage = option.usage | |
| 671 local pattern = option.pattern | |
| 672 | |
| 673 local validated = true | |
| 674 | |
| 675 if option.type == "input" then | |
| 676 if type(pattern)=="string" then | |
| 677 if not strmatch(..., pattern) then | |
| 678 validated = false | |
| 679 end | |
| 680 end | |
| 681 end | |
| 682 | |
| 683 local success | |
| 684 if validated and option.type ~= "execute" then | |
| 685 if type(validate) == "string" then | |
| 686 if handler and handler[validate] then | |
| 687 success, validated = safecall(handler[validate], handler, info, ...) | |
| 688 if not success then validated = false end | |
| 689 else | |
| 690 error(format("Method %s doesn't exist in handler for type execute", validate)) | |
| 691 end | |
| 692 elseif type(validate) == "function" then | |
| 693 success, validated = safecall(validate, info, ...) | |
| 694 if not success then validated = false end | |
| 695 end | |
| 696 end | |
| 697 | |
| 698 local rootframe = user.rootframe | |
| 699 if type(validated) == "string" then | |
| 700 --validate function returned a message to display | |
| 701 if rootframe.SetStatusText then | |
| 702 rootframe:SetStatusText(validated) | |
| 703 else | |
| 704 -- TODO: do something else. | |
| 705 end | |
| 706 PlaySound("igPlayerInviteDecline") | |
| 707 del(info) | |
| 708 return true | |
| 709 elseif not validated then | |
| 710 --validate returned false | |
| 711 if rootframe.SetStatusText then | |
| 712 if usage then | |
| 713 rootframe:SetStatusText(name..": "..usage) | |
| 714 else | |
| 715 if pattern then | |
| 716 rootframe:SetStatusText(name..": Expected "..pattern) | |
| 717 else | |
| 718 rootframe:SetStatusText(name..": Invalid Value") | |
| 719 end | |
| 720 end | |
| 721 else | |
| 722 -- TODO: do something else | |
| 723 end | |
| 724 PlaySound("igPlayerInviteDecline") | |
| 725 del(info) | |
| 726 return true | |
| 727 else | |
| 728 | |
| 729 local confirmText = option.confirmText | |
| 730 --call confirm func/method | |
| 731 if type(confirm) == "string" then | |
| 732 if handler and handler[confirm] then | |
| 733 success, confirm = safecall(handler[confirm], handler, info, ...) | |
| 734 if success and type(confirm) == "string" then | |
| 735 confirmText = confirm | |
| 736 confirm = true | |
| 737 elseif not success then | |
| 738 confirm = false | |
| 739 end | |
| 740 else | |
| 741 error(format("Method %s doesn't exist in handler for type confirm", confirm)) | |
| 742 end | |
| 743 elseif type(confirm) == "function" then | |
| 744 success, confirm = safecall(confirm, info, ...) | |
| 745 if success and type(confirm) == "string" then | |
| 746 confirmText = confirm | |
| 747 confirm = true | |
| 748 elseif not success then | |
| 749 confirm = false | |
| 750 end | |
| 751 end | |
| 752 | |
| 753 --confirm if needed | |
| 754 if type(confirm) == "boolean" then | |
| 755 if confirm then | |
| 756 if not confirmText then | |
| 757 local name, desc = option.name, option.desc | |
| 758 if type(name) == "function" then | |
| 759 name = name(info) | |
| 760 end | |
| 761 if type(desc) == "function" then | |
| 762 desc = desc(info) | |
| 763 end | |
| 764 confirmText = name | |
| 765 if desc then | |
| 766 confirmText = confirmText.." - "..desc | |
| 767 end | |
| 768 end | |
| 769 | |
| 770 local iscustom = user.rootframe:GetUserData("iscustom") | |
| 771 local rootframe | |
| 772 | |
| 773 if iscustom then | |
| 774 rootframe = user.rootframe | |
| 775 end | |
| 776 local basepath = user.rootframe:GetUserData("basepath") | |
| 777 if type(func) == "string" then | |
| 778 if handler and handler[func] then | |
| 779 confirmPopup(user.appName, rootframe, basepath, info, confirmText, handler[func], handler, info, ...) | |
| 780 else | |
| 781 error(format("Method %s doesn't exist in handler for type func", func)) | |
| 782 end | |
| 783 elseif type(func) == "function" then | |
| 784 confirmPopup(user.appName, rootframe, basepath, info, confirmText, func, info, ...) | |
| 785 end | |
| 786 --func will be called and info deleted when the confirm dialog is responded to | |
| 787 return | |
| 788 end | |
| 789 end | |
| 790 | |
| 791 --call the function | |
| 792 if type(func) == "string" then | |
| 793 if handler and handler[func] then | |
| 794 safecall(handler[func],handler, info, ...) | |
| 795 else | |
| 796 error(format("Method %s doesn't exist in handler for type func", func)) | |
| 797 end | |
| 798 elseif type(func) == "function" then | |
| 799 safecall(func,info, ...) | |
| 800 end | |
| 801 | |
| 802 | |
| 803 | |
| 804 local iscustom = user.rootframe:GetUserData("iscustom") | |
| 805 local basepath = user.rootframe:GetUserData("basepath") or emptyTbl | |
| 806 --full refresh of the frame, some controls dont cause this on all events | |
| 807 if option.type == "color" then | |
| 808 if event == "OnValueConfirmed" then | |
| 809 | |
| 810 if iscustom then | |
| 811 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
| 812 else | |
| 813 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
| 814 end | |
| 815 end | |
| 816 elseif option.type == "range" then | |
| 817 if event == "OnMouseUp" then | |
| 818 if iscustom then | |
| 819 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
| 820 else | |
| 821 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
| 822 end | |
| 823 end | |
| 824 --multiselects don't cause a refresh on 'OnValueChanged' only 'OnClosed' | |
| 825 elseif option.type == "multiselect" then | |
| 826 user.valuechanged = true | |
| 827 else | |
| 828 if iscustom then | |
| 829 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
| 830 else | |
| 831 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
| 832 end | |
| 833 end | |
| 834 | |
| 835 end | |
| 836 del(info) | |
| 837 end | |
| 838 | |
| 839 local function ActivateSlider(widget, event, value) | |
| 840 local option = widget:GetUserData("option") | |
| 841 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 | |
| 842 if min then | |
| 843 if step then | |
| 844 value = math_floor((value - min) / step + 0.5) * step + min | |
| 845 end | |
| 846 value = math_max(value, min) | |
| 847 end | |
| 848 if max then | |
| 849 value = math_min(value, max) | |
| 850 end | |
| 851 ActivateControl(widget,event,value) | |
| 852 end | |
| 853 | |
| 854 --called from a checkbox that is part of an internally created multiselect group | |
| 855 --this type is safe to refresh on activation of one control | |
| 856 local function ActivateMultiControl(widget, event, ...) | |
| 857 ActivateControl(widget, event, widget:GetUserData("value"), ...) | |
| 858 local user = widget:GetUserDataTable() | |
| 859 local iscustom = user.rootframe:GetUserData("iscustom") | |
| 860 local basepath = user.rootframe:GetUserData("basepath") or emptyTbl | |
| 861 if iscustom then | |
| 862 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
| 863 else | |
| 864 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
| 865 end | |
| 866 end | |
| 867 | |
| 868 local function MultiControlOnClosed(widget, event, ...) | |
| 869 local user = widget:GetUserDataTable() | |
| 870 if user.valuechanged then | |
| 871 local iscustom = user.rootframe:GetUserData("iscustom") | |
| 872 local basepath = user.rootframe:GetUserData("basepath") or emptyTbl | |
| 873 if iscustom then | |
| 874 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
| 875 else | |
| 876 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
| 877 end | |
| 878 end | |
| 879 end | |
| 880 | |
| 881 local function FrameOnClose(widget, event) | |
| 882 local appName = widget:GetUserData("appName") | |
| 883 AceConfigDialog.OpenFrames[appName] = nil | |
| 884 gui:Release(widget) | |
| 885 end | |
| 886 | |
| 887 local function CheckOptionHidden(option, options, path, appName) | |
| 888 --check for a specific boolean option | |
| 889 local hidden = pickfirstset(option.dialogHidden,option.guiHidden) | |
| 890 if hidden ~= nil then | |
| 891 return hidden | |
| 892 end | |
| 893 | |
| 894 return GetOptionsMemberValue("hidden", option, options, path, appName) | |
| 895 end | |
| 896 | |
| 897 local function CheckOptionDisabled(option, options, path, appName) | |
| 898 --check for a specific boolean option | |
| 899 local disabled = pickfirstset(option.dialogDisabled,option.guiDisabled) | |
| 900 if disabled ~= nil then | |
| 901 return disabled | |
| 902 end | |
| 903 | |
| 904 return GetOptionsMemberValue("disabled", option, options, path, appName) | |
| 905 end | |
| 906 --[[ | |
| 907 local function BuildTabs(group, options, path, appName) | |
| 908 local tabs = new() | |
| 909 local text = new() | |
| 910 local keySort = new() | |
| 911 local opts = new() | |
| 912 | |
| 913 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
| 914 | |
| 915 for i = 1, #keySort do | |
| 916 local k = keySort[i] | |
| 917 local v = opts[k] | |
| 918 if v.type == "group" then | |
| 919 path[#path+1] = k | |
| 920 local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
| 921 local hidden = CheckOptionHidden(v, options, path, appName) | |
| 922 if not inline and not hidden then | |
| 923 tinsert(tabs, k) | |
| 924 text[k] = GetOptionsMemberValue("name", v, options, path, appName) | |
| 925 end | |
| 926 path[#path] = nil | |
| 927 end | |
| 928 end | |
| 929 | |
| 930 del(keySort) | |
| 931 del(opts) | |
| 932 | |
| 933 return tabs, text | |
| 934 end | |
| 935 ]] | |
| 936 local function BuildSelect(group, options, path, appName) | |
| 937 local groups = new() | |
| 938 local order = new() | |
| 939 local keySort = new() | |
| 940 local opts = new() | |
| 941 | |
| 942 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
| 943 | |
| 944 for i = 1, #keySort do | |
| 945 local k = keySort[i] | |
| 946 local v = opts[k] | |
| 947 if v.type == "group" then | |
| 948 path[#path+1] = k | |
| 949 local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
| 950 local hidden = CheckOptionHidden(v, options, path, appName) | |
| 951 if not inline and not hidden then | |
| 952 groups[k] = GetOptionsMemberValue("name", v, options, path, appName) | |
| 953 tinsert(order, k) | |
| 954 end | |
| 955 path[#path] = nil | |
| 956 end | |
| 957 end | |
| 958 | |
| 959 del(opts) | |
| 960 del(keySort) | |
| 961 | |
| 962 return groups, order | |
| 963 end | |
| 964 | |
| 965 local function BuildSubGroups(group, tree, options, path, appName) | |
| 966 local keySort = new() | |
| 967 local opts = new() | |
| 968 | |
| 969 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
| 970 | |
| 971 for i = 1, #keySort do | |
| 972 local k = keySort[i] | |
| 973 local v = opts[k] | |
| 974 if v.type == "group" then | |
| 975 path[#path+1] = k | |
| 976 local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
| 977 local hidden = CheckOptionHidden(v, options, path, appName) | |
| 978 if not inline and not hidden then | |
| 979 local entry = new() | |
| 980 entry.value = k | |
| 981 entry.text = GetOptionsMemberValue("name", v, options, path, appName) | |
| 982 entry.icon = GetOptionsMemberValue("icon", v, options, path, appName) | |
| 983 entry.iconCoords = GetOptionsMemberValue("iconCoords", v, options, path, appName) | |
| 984 entry.disabled = CheckOptionDisabled(v, options, path, appName) | |
| 985 if not tree.children then tree.children = new() end | |
| 986 tinsert(tree.children,entry) | |
| 987 if (v.childGroups or "tree") == "tree" then | |
| 988 BuildSubGroups(v,entry, options, path, appName) | |
| 989 end | |
| 990 end | |
| 991 path[#path] = nil | |
| 992 end | |
| 993 end | |
| 994 | |
| 995 del(keySort) | |
| 996 del(opts) | |
| 997 end | |
| 998 | |
| 999 local function BuildGroups(group, options, path, appName, recurse) | |
| 1000 local tree = new() | |
| 1001 local keySort = new() | |
| 1002 local opts = new() | |
| 1003 | |
| 1004 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
| 1005 | |
| 1006 for i = 1, #keySort do | |
| 1007 local k = keySort[i] | |
| 1008 local v = opts[k] | |
| 1009 if v.type == "group" then | |
| 1010 path[#path+1] = k | |
| 1011 local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
| 1012 local hidden = CheckOptionHidden(v, options, path, appName) | |
| 1013 if not inline and not hidden then | |
| 1014 local entry = new() | |
| 1015 entry.value = k | |
| 1016 entry.text = GetOptionsMemberValue("name", v, options, path, appName) | |
| 1017 entry.icon = GetOptionsMemberValue("icon", v, options, path, appName) | |
| 1018 entry.disabled = CheckOptionDisabled(v, options, path, appName) | |
| 1019 tinsert(tree,entry) | |
| 1020 if recurse and (v.childGroups or "tree") == "tree" then | |
| 1021 BuildSubGroups(v,entry, options, path, appName) | |
| 1022 end | |
| 1023 end | |
| 1024 path[#path] = nil | |
| 1025 end | |
| 1026 end | |
| 1027 del(keySort) | |
| 1028 del(opts) | |
| 1029 return tree | |
| 1030 end | |
| 1031 | |
| 1032 local function InjectInfo(control, options, option, path, rootframe, appName) | |
| 1033 local user = control:GetUserDataTable() | |
| 1034 for i = 1, #path do | |
| 1035 user[i] = path[i] | |
| 1036 end | |
| 1037 user.rootframe = rootframe | |
| 1038 user.option = option | |
| 1039 user.options = options | |
| 1040 user.path = copy(path) | |
| 1041 user.appName = appName | |
| 1042 control:SetCallback("OnRelease", CleanUserData) | |
| 1043 control:SetCallback("OnLeave", OptionOnMouseLeave) | |
| 1044 control:SetCallback("OnEnter", OptionOnMouseOver) | |
| 1045 end | |
| 1046 | |
| 1047 | |
| 1048 --[[ | |
| 1049 options - root of the options table being fed | |
| 1050 container - widget that controls will be placed in | |
| 1051 rootframe - Frame object the options are in | |
| 1052 path - table with the keys to get to the group being fed | |
| 1053 --]] | |
| 1054 | |
| 1055 local function FeedOptions(appName, options,container,rootframe,path,group,inline) | |
| 1056 local keySort = new() | |
| 1057 local opts = new() | |
| 1058 | |
| 1059 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
| 1060 | |
| 1061 for i = 1, #keySort do | |
| 1062 local k = keySort[i] | |
| 1063 local v = opts[k] | |
| 1064 tinsert(path, k) | |
| 1065 local hidden = CheckOptionHidden(v, options, path, appName) | |
| 1066 local name = GetOptionsMemberValue("name", v, options, path, appName) | |
| 1067 if not hidden then | |
| 1068 if v.type == "group" then | |
| 1069 if inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then | |
| 1070 --Inline group | |
| 1071 local GroupContainer | |
| 1072 if name and name ~= "" then | |
| 1073 GroupContainer = gui:Create("InlineGroup") | |
| 1074 GroupContainer:SetTitle(name or "") | |
| 1075 else | |
| 1076 GroupContainer = gui:Create("SimpleGroup") | |
| 1077 end | |
| 1078 | |
| 1079 GroupContainer.width = "fill" | |
| 1080 GroupContainer:SetLayout("flow") | |
| 1081 container:AddChild(GroupContainer) | |
| 1082 FeedOptions(appName,options,GroupContainer,rootframe,path,v,true) | |
| 1083 end | |
| 1084 else | |
| 1085 --Control to feed | |
| 1086 local control | |
| 1087 | |
| 1088 local name = GetOptionsMemberValue("name", v, options, path, appName) | |
| 1089 | |
| 1090 if v.type == "execute" then | |
| 1091 | |
| 1092 local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName) | |
| 1093 local image, width, height = GetOptionsMemberValue("image",v, options, path, appName) | |
| 1094 | |
| 1095 if type(image) == "string" then | |
| 1096 control = gui:Create("Icon") | |
| 1097 if not width then | |
| 1098 width = GetOptionsMemberValue("imageWidth",v, options, path, appName) | |
| 1099 end | |
| 1100 if not height then | |
| 1101 height = GetOptionsMemberValue("imageHeight",v, options, path, appName) | |
| 1102 end | |
| 1103 if type(imageCoords) == "table" then | |
| 1104 control:SetImage(image, unpack(imageCoords)) | |
| 1105 else | |
| 1106 control:SetImage(image) | |
| 1107 end | |
| 1108 if type(width) ~= "number" then | |
| 1109 width = 32 | |
| 1110 end | |
| 1111 if type(height) ~= "number" then | |
| 1112 height = 32 | |
| 1113 end | |
| 1114 control:SetImageSize(width, height) | |
| 1115 control:SetLabel(name) | |
| 1116 else | |
| 1117 control = gui:Create("Button") | |
| 1118 control:SetText(name) | |
| 1119 end | |
| 1120 control:SetCallback("OnClick",ActivateControl) | |
| 1121 | |
| 1122 elseif v.type == "input" then | |
| 1123 local controlType = v.dialogControl or v.control or (v.multiline and "MultiLineEditBox") or "EditBox" | |
| 1124 control = gui:Create(controlType) | |
| 1125 if not control then | |
| 1126 geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) | |
| 1127 control = gui:Create(v.multiline and "MultiLineEditBox" or "EditBox") | |
| 1128 end | |
| 1129 | |
| 1130 if v.multiline and control.SetNumLines then | |
| 1131 control:SetNumLines(tonumber(v.multiline) or 4) | |
| 1132 end | |
| 1133 control:SetLabel(name) | |
| 1134 control:SetCallback("OnEnterPressed",ActivateControl) | |
| 1135 local text = GetOptionsMemberValue("get",v, options, path, appName) | |
| 1136 if type(text) ~= "string" then | |
| 1137 text = "" | |
| 1138 end | |
| 1139 control:SetText(text) | |
| 1140 | |
| 1141 elseif v.type == "toggle" then | |
| 1142 control = gui:Create("CheckBox") | |
| 1143 control:SetLabel(name) | |
| 1144 control:SetTriState(v.tristate) | |
| 1145 local value = GetOptionsMemberValue("get",v, options, path, appName) | |
| 1146 control:SetValue(value) | |
| 1147 control:SetCallback("OnValueChanged",ActivateControl) | |
| 1148 | |
| 1149 if v.descStyle == "inline" then | |
| 1150 local desc = GetOptionsMemberValue("desc", v, options, path, appName) | |
| 1151 control:SetDescription(desc) | |
| 1152 end | |
| 1153 | |
| 1154 local image = GetOptionsMemberValue("image", v, options, path, appName) | |
| 1155 local imageCoords = GetOptionsMemberValue("imageCoords", v, options, path, appName) | |
| 1156 | |
| 1157 if type(image) == "string" then | |
| 1158 if type(imageCoords) == "table" then | |
| 1159 control:SetImage(image, unpack(imageCoords)) | |
| 1160 else | |
| 1161 control:SetImage(image) | |
| 1162 end | |
| 1163 end | |
| 1164 elseif v.type == "range" then | |
| 1165 control = gui:Create("Slider") | |
| 1166 control:SetLabel(name) | |
| 1167 control:SetSliderValues(v.softMin or v.min or 0, v.softMax or v.max or 100, v.bigStep or v.step or 0) | |
| 1168 control:SetIsPercent(v.isPercent) | |
| 1169 local value = GetOptionsMemberValue("get",v, options, path, appName) | |
| 1170 if type(value) ~= "number" then | |
| 1171 value = 0 | |
| 1172 end | |
| 1173 control:SetValue(value) | |
| 1174 control:SetCallback("OnValueChanged",ActivateSlider) | |
| 1175 control:SetCallback("OnMouseUp",ActivateSlider) | |
| 1176 | |
| 1177 elseif v.type == "select" then | |
| 1178 local values = GetOptionsMemberValue("values", v, options, path, appName) | |
| 1179 if v.style == "radio" then | |
| 1180 local disabled = CheckOptionDisabled(v, options, path, appName) | |
| 1181 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
| 1182 control = gui:Create("InlineGroup") | |
| 1183 control:SetLayout("Flow") | |
| 1184 control:SetTitle(name) | |
| 1185 control.width = "fill" | |
| 1186 | |
| 1187 control:PauseLayout() | |
| 1188 local optionValue = GetOptionsMemberValue("get",v, options, path, appName) | |
| 1189 local t = {} | |
| 1190 for value, text in pairs(values) do | |
| 1191 t[#t+1]=value | |
| 1192 end | |
| 1193 tsort(t) | |
| 1194 for k, value in ipairs(t) do | |
| 1195 local text = values[value] | |
| 1196 local radio = gui:Create("CheckBox") | |
| 1197 radio:SetLabel(text) | |
| 1198 radio:SetUserData("value", value) | |
| 1199 radio:SetUserData("text", text) | |
| 1200 radio:SetDisabled(disabled) | |
| 1201 radio:SetType("radio") | |
| 1202 radio:SetValue(optionValue == value) | |
| 1203 radio:SetCallback("OnValueChanged", ActivateMultiControl) | |
| 1204 InjectInfo(radio, options, v, path, rootframe, appName) | |
| 1205 control:AddChild(radio) | |
| 1206 if width == "double" then | |
| 1207 radio:SetWidth(width_multiplier * 2) | |
| 1208 elseif width == "half" then | |
| 1209 radio:SetWidth(width_multiplier / 2) | |
| 1210 elseif width == "full" then | |
| 1211 radio.width = "fill" | |
| 1212 else | |
| 1213 radio:SetWidth(width_multiplier) | |
| 1214 end | |
| 1215 end | |
| 1216 control:ResumeLayout() | |
| 1217 control:DoLayout() | |
| 1218 else | |
| 1219 local controlType = v.dialogControl or v.control or "Dropdown" | |
| 1220 control = gui:Create(controlType) | |
| 1221 if not control then | |
| 1222 geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) | |
| 1223 control = gui:Create("Dropdown") | |
| 1224 end | |
| 1225 local itemType = v.itemControl | |
| 1226 if itemType and not gui:GetWidgetVersion(itemType) then | |
| 1227 geterrorhandler()(("Invalid Custom Item Type - %s"):format(tostring(itemType))) | |
| 1228 itemType = nil | |
| 1229 end | |
| 1230 control:SetLabel(name) | |
| 1231 control:SetList(values, nil, itemType) | |
| 1232 local value = GetOptionsMemberValue("get",v, options, path, appName) | |
| 1233 if not values[value] then | |
| 1234 value = nil | |
| 1235 end | |
| 1236 control:SetValue(value) | |
| 1237 control:SetCallback("OnValueChanged", ActivateControl) | |
| 1238 end | |
| 1239 | |
| 1240 elseif v.type == "multiselect" then | |
| 1241 local values = GetOptionsMemberValue("values", v, options, path, appName) | |
| 1242 local disabled = CheckOptionDisabled(v, options, path, appName) | |
| 1243 | |
| 1244 local controlType = v.dialogControl or v.control | |
| 1245 | |
| 1246 local valuesort = new() | |
| 1247 if values then | |
| 1248 for value, text in pairs(values) do | |
| 1249 tinsert(valuesort, value) | |
| 1250 end | |
| 1251 end | |
| 1252 tsort(valuesort) | |
| 1253 | |
| 1254 if controlType then | |
| 1255 control = gui:Create(controlType) | |
| 1256 if not control then | |
| 1257 geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) | |
| 1258 end | |
| 1259 end | |
| 1260 if control then | |
| 1261 control:SetMultiselect(true) | |
| 1262 control:SetLabel(name) | |
| 1263 control:SetList(values) | |
| 1264 control:SetDisabled(disabled) | |
| 1265 control:SetCallback("OnValueChanged",ActivateControl) | |
| 1266 control:SetCallback("OnClosed", MultiControlOnClosed) | |
| 1267 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
| 1268 if width == "double" then | |
| 1269 control:SetWidth(width_multiplier * 2) | |
| 1270 elseif width == "half" then | |
| 1271 control:SetWidth(width_multiplier / 2) | |
| 1272 elseif width == "full" then | |
| 1273 control.width = "fill" | |
| 1274 else | |
| 1275 control:SetWidth(width_multiplier) | |
| 1276 end | |
| 1277 --check:SetTriState(v.tristate) | |
| 1278 for i = 1, #valuesort do | |
| 1279 local key = valuesort[i] | |
| 1280 local value = GetOptionsMemberValue("get",v, options, path, appName, key) | |
| 1281 control:SetItemValue(key,value) | |
| 1282 end | |
| 1283 else | |
| 1284 control = gui:Create("InlineGroup") | |
| 1285 control:SetLayout("Flow") | |
| 1286 control:SetTitle(name) | |
| 1287 control.width = "fill" | |
| 1288 | |
| 1289 control:PauseLayout() | |
| 1290 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
| 1291 for i = 1, #valuesort do | |
| 1292 local value = valuesort[i] | |
| 1293 local text = values[value] | |
| 1294 local check = gui:Create("CheckBox") | |
| 1295 check:SetLabel(text) | |
| 1296 check:SetUserData("value", value) | |
| 1297 check:SetUserData("text", text) | |
| 1298 check:SetDisabled(disabled) | |
| 1299 check:SetTriState(v.tristate) | |
| 1300 check:SetValue(GetOptionsMemberValue("get",v, options, path, appName, value)) | |
| 1301 check:SetCallback("OnValueChanged",ActivateMultiControl) | |
| 1302 InjectInfo(check, options, v, path, rootframe, appName) | |
| 1303 control:AddChild(check) | |
| 1304 if width == "double" then | |
| 1305 check:SetWidth(width_multiplier * 2) | |
| 1306 elseif width == "half" then | |
| 1307 check:SetWidth(width_multiplier / 2) | |
| 1308 elseif width == "full" then | |
| 1309 check.width = "fill" | |
| 1310 else | |
| 1311 check:SetWidth(width_multiplier) | |
| 1312 end | |
| 1313 end | |
| 1314 control:ResumeLayout() | |
| 1315 control:DoLayout() | |
| 1316 | |
| 1317 | |
| 1318 end | |
| 1319 | |
| 1320 del(valuesort) | |
| 1321 | |
| 1322 elseif v.type == "color" then | |
| 1323 control = gui:Create("ColorPicker") | |
| 1324 control:SetLabel(name) | |
| 1325 control:SetHasAlpha(GetOptionsMemberValue("hasAlpha",v, options, path, appName)) | |
| 1326 control:SetColor(GetOptionsMemberValue("get",v, options, path, appName)) | |
| 1327 control:SetCallback("OnValueChanged",ActivateControl) | |
| 1328 control:SetCallback("OnValueConfirmed",ActivateControl) | |
| 1329 | |
| 1330 elseif v.type == "keybinding" then | |
| 1331 control = gui:Create("Keybinding") | |
| 1332 control:SetLabel(name) | |
| 1333 control:SetKey(GetOptionsMemberValue("get",v, options, path, appName)) | |
| 1334 control:SetCallback("OnKeyChanged",ActivateControl) | |
| 1335 | |
| 1336 elseif v.type == "header" then | |
| 1337 control = gui:Create("Heading") | |
| 1338 control:SetText(name) | |
| 1339 control.width = "fill" | |
| 1340 | |
| 1341 elseif v.type == "description" then | |
| 1342 control = gui:Create("Label") | |
| 1343 control:SetText(name) | |
| 1344 | |
| 1345 local fontSize = GetOptionsMemberValue("fontSize",v, options, path, appName) | |
| 1346 if fontSize == "medium" then | |
| 1347 control:SetFontObject(GameFontHighlight) | |
| 1348 elseif fontSize == "large" then | |
| 1349 control:SetFontObject(GameFontHighlightLarge) | |
| 1350 else -- small or invalid | |
| 1351 control:SetFontObject(GameFontHighlightSmall) | |
| 1352 end | |
| 1353 | |
| 1354 local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName) | |
| 1355 local image, width, height = GetOptionsMemberValue("image",v, options, path, appName) | |
| 1356 | |
| 1357 if type(image) == "string" then | |
| 1358 if not width then | |
| 1359 width = GetOptionsMemberValue("imageWidth",v, options, path, appName) | |
| 1360 end | |
| 1361 if not height then | |
| 1362 height = GetOptionsMemberValue("imageHeight",v, options, path, appName) | |
| 1363 end | |
| 1364 if type(imageCoords) == "table" then | |
| 1365 control:SetImage(image, unpack(imageCoords)) | |
| 1366 else | |
| 1367 control:SetImage(image) | |
| 1368 end | |
| 1369 if type(width) ~= "number" then | |
| 1370 width = 32 | |
| 1371 end | |
| 1372 if type(height) ~= "number" then | |
| 1373 height = 32 | |
| 1374 end | |
| 1375 control:SetImageSize(width, height) | |
| 1376 end | |
| 1377 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
| 1378 control.width = not width and "fill" | |
| 1379 end | |
| 1380 | |
| 1381 --Common Init | |
| 1382 if control then | |
| 1383 if control.width ~= "fill" then | |
| 1384 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
| 1385 if width == "double" then | |
| 1386 control:SetWidth(width_multiplier * 2) | |
| 1387 elseif width == "half" then | |
| 1388 control:SetWidth(width_multiplier / 2) | |
| 1389 elseif width == "full" then | |
| 1390 control.width = "fill" | |
| 1391 else | |
| 1392 control:SetWidth(width_multiplier) | |
| 1393 end | |
| 1394 end | |
| 1395 if control.SetDisabled then | |
| 1396 local disabled = CheckOptionDisabled(v, options, path, appName) | |
| 1397 control:SetDisabled(disabled) | |
| 1398 end | |
| 1399 | |
| 1400 InjectInfo(control, options, v, path, rootframe, appName) | |
| 1401 container:AddChild(control) | |
| 1402 end | |
| 1403 | |
| 1404 end | |
| 1405 end | |
| 1406 tremove(path) | |
| 1407 end | |
| 1408 container:ResumeLayout() | |
| 1409 container:DoLayout() | |
| 1410 del(keySort) | |
| 1411 del(opts) | |
| 1412 end | |
| 1413 | |
| 1414 local function BuildPath(path, ...) | |
| 1415 for i = 1, select("#",...) do | |
| 1416 tinsert(path, (select(i,...))) | |
| 1417 end | |
| 1418 end | |
| 1419 | |
| 1420 | |
| 1421 local function TreeOnButtonEnter(widget, event, uniquevalue, button) | |
| 1422 local user = widget:GetUserDataTable() | |
| 1423 if not user then return end | |
| 1424 local options = user.options | |
| 1425 local option = user.option | |
| 1426 local path = user.path | |
| 1427 local appName = user.appName | |
| 1428 | |
| 1429 local feedpath = new() | |
| 1430 for i = 1, #path do | |
| 1431 feedpath[i] = path[i] | |
| 1432 end | |
| 1433 | |
| 1434 BuildPath(feedpath, ("\001"):split(uniquevalue)) | |
| 1435 local group = options | |
| 1436 for i = 1, #feedpath do | |
| 1437 if not group then return end | |
| 1438 group = GetSubOption(group, feedpath[i]) | |
| 1439 end | |
| 1440 | |
| 1441 local name = GetOptionsMemberValue("name", group, options, feedpath, appName) | |
| 1442 local desc = GetOptionsMemberValue("desc", group, options, feedpath, appName) | |
| 1443 | |
| 1444 GameTooltip:SetOwner(button, "ANCHOR_NONE") | |
| 1445 if widget.type == "TabGroup" then | |
| 1446 GameTooltip:SetPoint("BOTTOM",button,"TOP") | |
| 1447 else | |
| 1448 GameTooltip:SetPoint("LEFT",button,"RIGHT") | |
| 1449 end | |
| 1450 | |
| 1451 GameTooltip:SetText(name, 1, .82, 0, 1) | |
| 1452 | |
| 1453 if type(desc) == "string" then | |
| 1454 GameTooltip:AddLine(desc, 1, 1, 1, 1) | |
| 1455 end | |
| 1456 | |
| 1457 GameTooltip:Show() | |
| 1458 end | |
| 1459 | |
| 1460 local function TreeOnButtonLeave(widget, event, value, button) | |
| 1461 GameTooltip:Hide() | |
| 1462 end | |
| 1463 | |
| 1464 | |
| 1465 local function GroupExists(appName, options, path, uniquevalue) | |
| 1466 if not uniquevalue then return false end | |
| 1467 | |
| 1468 local feedpath = new() | |
| 1469 local temppath = new() | |
| 1470 for i = 1, #path do | |
| 1471 feedpath[i] = path[i] | |
| 1472 end | |
| 1473 | |
| 1474 BuildPath(feedpath, ("\001"):split(uniquevalue)) | |
| 1475 | |
| 1476 local group = options | |
| 1477 for i = 1, #feedpath do | |
| 1478 local v = feedpath[i] | |
| 1479 temppath[i] = v | |
| 1480 group = GetSubOption(group, v) | |
| 1481 | |
| 1482 if not group or group.type ~= "group" or CheckOptionHidden(group, options, temppath, appName) then | |
| 1483 del(feedpath) | |
| 1484 del(temppath) | |
| 1485 return false | |
| 1486 end | |
| 1487 end | |
| 1488 del(feedpath) | |
| 1489 del(temppath) | |
| 1490 return true | |
| 1491 end | |
| 1492 | |
| 1493 local function GroupSelected(widget, event, uniquevalue) | |
| 1494 | |
| 1495 local user = widget:GetUserDataTable() | |
| 1496 | |
| 1497 local options = user.options | |
| 1498 local option = user.option | |
| 1499 local path = user.path | |
| 1500 local rootframe = user.rootframe | |
| 1501 | |
| 1502 local feedpath = new() | |
| 1503 for i = 1, #path do | |
| 1504 feedpath[i] = path[i] | |
| 1505 end | |
| 1506 | |
| 1507 BuildPath(feedpath, ("\001"):split(uniquevalue)) | |
| 1508 local group = options | |
| 1509 for i = 1, #feedpath do | |
| 1510 group = GetSubOption(group, feedpath[i]) | |
| 1511 end | |
| 1512 widget:ReleaseChildren() | |
| 1513 AceConfigDialog:FeedGroup(user.appName,options,widget,rootframe,feedpath) | |
| 1514 | |
| 1515 del(feedpath) | |
| 1516 end | |
| 1517 | |
| 1518 | |
| 1519 | |
| 1520 --[[ | |
| 1521 -- INTERNAL -- | |
| 1522 This function will feed one group, and any inline child groups into the given container | |
| 1523 Select Groups will only have the selection control (tree, tabs, dropdown) fed in | |
| 1524 and have a group selected, this event will trigger the feeding of child groups | |
| 1525 | |
| 1526 Rules: | |
| 1527 If the group is Inline, FeedOptions | |
| 1528 If the group has no child groups, FeedOptions | |
| 1529 | |
| 1530 If the group is a tab or select group, FeedOptions then add the Group Control | |
| 1531 If the group is a tree group FeedOptions then | |
| 1532 its parent isnt a tree group: then add the tree control containing this and all child tree groups | |
| 1533 if its parent is a tree group, its already a node on a tree | |
| 1534 --]] | |
| 1535 | |
| 1536 function AceConfigDialog:FeedGroup(appName,options,container,rootframe,path, isRoot) | |
| 1537 local group = options | |
| 1538 --follow the path to get to the curent group | |
| 1539 local inline | |
| 1540 local grouptype, parenttype = options.childGroups, "none" | |
| 1541 | |
| 1542 | |
| 1543 for i = 1, #path do | |
| 1544 local v = path[i] | |
| 1545 group = GetSubOption(group, v) | |
| 1546 inline = inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
| 1547 parenttype = grouptype | |
| 1548 grouptype = group.childGroups | |
| 1549 end | |
| 1550 | |
| 1551 if not parenttype then | |
| 1552 parenttype = "tree" | |
| 1553 end | |
| 1554 | |
| 1555 --check if the group has child groups | |
| 1556 local hasChildGroups | |
| 1557 for k, v in pairs(group.args) do | |
| 1558 if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) and not CheckOptionHidden(v, options, path, appName) then | |
| 1559 hasChildGroups = true | |
| 1560 end | |
| 1561 end | |
| 1562 if group.plugins then | |
| 1563 for plugin, t in pairs(group.plugins) do | |
| 1564 for k, v in pairs(t) do | |
| 1565 if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) and not CheckOptionHidden(v, options, path, appName) then | |
| 1566 hasChildGroups = true | |
| 1567 end | |
| 1568 end | |
| 1569 end | |
| 1570 end | |
| 1571 | |
| 1572 container:SetLayout("flow") | |
| 1573 local scroll | |
| 1574 | |
| 1575 --Add a scrollframe if we are not going to add a group control, this is the inverse of the conditions for that later on | |
| 1576 if (not (hasChildGroups and not inline)) or (grouptype ~= "tab" and grouptype ~= "select" and (parenttype == "tree" and not isRoot)) then | |
| 1577 if container.type ~= "InlineGroup" and container.type ~= "SimpleGroup" then | |
| 1578 scroll = gui:Create("ScrollFrame") | |
| 1579 scroll:SetLayout("flow") | |
| 1580 scroll.width = "fill" | |
| 1581 scroll.height = "fill" | |
| 1582 container:SetLayout("fill") | |
| 1583 container:AddChild(scroll) | |
| 1584 container = scroll | |
| 1585 end | |
| 1586 end | |
| 1587 | |
| 1588 FeedOptions(appName,options,container,rootframe,path,group,nil) | |
| 1589 | |
| 1590 if scroll then | |
| 1591 container:PerformLayout() | |
| 1592 local status = self:GetStatusTable(appName, path) | |
| 1593 if not status.scroll then | |
| 1594 status.scroll = {} | |
| 1595 end | |
| 1596 scroll:SetStatusTable(status.scroll) | |
| 1597 end | |
| 1598 | |
| 1599 if hasChildGroups and not inline then | |
| 1600 local name = GetOptionsMemberValue("name", group, options, path, appName) | |
| 1601 if grouptype == "tab" then | |
| 1602 | |
| 1603 local tab = gui:Create("TabGroup") | |
| 1604 InjectInfo(tab, options, group, path, rootframe, appName) | |
| 1605 tab:SetCallback("OnGroupSelected", GroupSelected) | |
| 1606 tab:SetCallback("OnTabEnter", TreeOnButtonEnter) | |
| 1607 tab:SetCallback("OnTabLeave", TreeOnButtonLeave) | |
| 1608 | |
| 1609 local status = AceConfigDialog:GetStatusTable(appName, path) | |
| 1610 if not status.groups then | |
| 1611 status.groups = {} | |
| 1612 end | |
| 1613 tab:SetStatusTable(status.groups) | |
| 1614 tab.width = "fill" | |
| 1615 tab.height = "fill" | |
| 1616 | |
| 1617 local tabs = BuildGroups(group, options, path, appName) | |
| 1618 tab:SetTabs(tabs) | |
| 1619 tab:SetUserData("tablist", tabs) | |
| 1620 | |
| 1621 for i = 1, #tabs do | |
| 1622 local entry = tabs[i] | |
| 1623 if not entry.disabled then | |
| 1624 tab:SelectTab((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value) | |
| 1625 break | |
| 1626 end | |
| 1627 end | |
| 1628 | |
| 1629 container:AddChild(tab) | |
| 1630 | |
| 1631 elseif grouptype == "select" then | |
| 1632 | |
| 1633 local select = gui:Create("DropdownGroup") | |
| 1634 select:SetTitle(name) | |
| 1635 InjectInfo(select, options, group, path, rootframe, appName) | |
| 1636 select:SetCallback("OnGroupSelected", GroupSelected) | |
| 1637 local status = AceConfigDialog:GetStatusTable(appName, path) | |
| 1638 if not status.groups then | |
| 1639 status.groups = {} | |
| 1640 end | |
| 1641 select:SetStatusTable(status.groups) | |
| 1642 local grouplist, orderlist = BuildSelect(group, options, path, appName) | |
| 1643 select:SetGroupList(grouplist, orderlist) | |
| 1644 select:SetUserData("grouplist", grouplist) | |
| 1645 select:SetUserData("orderlist", orderlist) | |
| 1646 | |
| 1647 local firstgroup = orderlist[1] | |
| 1648 if firstgroup then | |
| 1649 select:SetGroup((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or firstgroup) | |
| 1650 end | |
| 1651 | |
| 1652 select.width = "fill" | |
| 1653 select.height = "fill" | |
| 1654 | |
| 1655 container:AddChild(select) | |
| 1656 | |
| 1657 --assume tree group by default | |
| 1658 --if parenttype is tree then this group is already a node on that tree | |
| 1659 elseif (parenttype ~= "tree") or isRoot then | |
| 1660 local tree = gui:Create("TreeGroup") | |
| 1661 InjectInfo(tree, options, group, path, rootframe, appName) | |
| 1662 tree:EnableButtonTooltips(false) | |
| 1663 | |
| 1664 tree.width = "fill" | |
| 1665 tree.height = "fill" | |
| 1666 | |
| 1667 tree:SetCallback("OnGroupSelected", GroupSelected) | |
| 1668 tree:SetCallback("OnButtonEnter", TreeOnButtonEnter) | |
| 1669 tree:SetCallback("OnButtonLeave", TreeOnButtonLeave) | |
| 1670 | |
| 1671 local status = AceConfigDialog:GetStatusTable(appName, path) | |
| 1672 if not status.groups then | |
| 1673 status.groups = {} | |
| 1674 end | |
| 1675 local treedefinition = BuildGroups(group, options, path, appName, true) | |
| 1676 tree:SetStatusTable(status.groups) | |
| 1677 | |
| 1678 tree:SetTree(treedefinition) | |
| 1679 tree:SetUserData("tree",treedefinition) | |
| 1680 | |
| 1681 for i = 1, #treedefinition do | |
| 1682 local entry = treedefinition[i] | |
| 1683 if not entry.disabled then | |
| 1684 tree:SelectByValue((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value) | |
| 1685 break | |
| 1686 end | |
| 1687 end | |
| 1688 | |
| 1689 container:AddChild(tree) | |
| 1690 end | |
| 1691 end | |
| 1692 end | |
| 1693 | |
| 1694 local old_CloseSpecialWindows | |
| 1695 | |
| 1696 | |
| 1697 local function RefreshOnUpdate(this) | |
| 1698 for appName in pairs(this.closing) do | |
| 1699 if AceConfigDialog.OpenFrames[appName] then | |
| 1700 AceConfigDialog.OpenFrames[appName]:Hide() | |
| 1701 end | |
| 1702 if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then | |
| 1703 for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do | |
| 1704 if not widget:IsVisible() then | |
| 1705 widget:ReleaseChildren() | |
| 1706 end | |
| 1707 end | |
| 1708 end | |
| 1709 this.closing[appName] = nil | |
| 1710 end | |
| 1711 | |
| 1712 if this.closeAll then | |
| 1713 for k, v in pairs(AceConfigDialog.OpenFrames) do | |
| 1714 if not this.closeAllOverride[k] then | |
| 1715 v:Hide() | |
| 1716 end | |
| 1717 end | |
| 1718 this.closeAll = nil | |
| 1719 wipe(this.closeAllOverride) | |
| 1720 end | |
| 1721 | |
| 1722 for appName in pairs(this.apps) do | |
| 1723 if AceConfigDialog.OpenFrames[appName] then | |
| 1724 local user = AceConfigDialog.OpenFrames[appName]:GetUserDataTable() | |
| 1725 AceConfigDialog:Open(appName, unpack(user.basepath or emptyTbl)) | |
| 1726 end | |
| 1727 if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then | |
| 1728 for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do | |
| 1729 local user = widget:GetUserDataTable() | |
| 1730 if widget:IsVisible() then | |
| 1731 AceConfigDialog:Open(widget:GetUserData("appName"), widget, unpack(user.basepath or emptyTbl)) | |
| 1732 end | |
| 1733 end | |
| 1734 end | |
| 1735 this.apps[appName] = nil | |
| 1736 end | |
| 1737 this:SetScript("OnUpdate", nil) | |
| 1738 end | |
| 1739 | |
| 1740 -- Upgrade the OnUpdate script as well, if needed. | |
| 1741 if AceConfigDialog.frame:GetScript("OnUpdate") then | |
| 1742 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
| 1743 end | |
| 1744 | |
| 1745 --- Close all open options windows | |
| 1746 function AceConfigDialog:CloseAll() | |
| 1747 AceConfigDialog.frame.closeAll = true | |
| 1748 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
| 1749 if next(self.OpenFrames) then | |
| 1750 return true | |
| 1751 end | |
| 1752 end | |
| 1753 | |
| 1754 --- Close a specific options window. | |
| 1755 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
| 1756 function AceConfigDialog:Close(appName) | |
| 1757 if self.OpenFrames[appName] then | |
| 1758 AceConfigDialog.frame.closing[appName] = true | |
| 1759 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
| 1760 return true | |
| 1761 end | |
| 1762 end | |
| 1763 | |
| 1764 -- Internal -- Called by AceConfigRegistry | |
| 1765 function AceConfigDialog:ConfigTableChanged(event, appName) | |
| 1766 AceConfigDialog.frame.apps[appName] = true | |
| 1767 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
| 1768 end | |
| 1769 | |
| 1770 reg.RegisterCallback(AceConfigDialog, "ConfigTableChange", "ConfigTableChanged") | |
| 1771 | |
| 1772 --- Sets the default size of the options window for a specific application. | |
| 1773 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
| 1774 -- @param width The default width | |
| 1775 -- @param height The default height | |
| 1776 function AceConfigDialog:SetDefaultSize(appName, width, height) | |
| 1777 local status = AceConfigDialog:GetStatusTable(appName) | |
| 1778 if type(width) == "number" and type(height) == "number" then | |
| 1779 status.width = width | |
| 1780 status.height = height | |
| 1781 end | |
| 1782 end | |
| 1783 | |
| 1784 --- Open an option window at the specified path (if any). | |
| 1785 -- This function can optionally feed the group into a pre-created container | |
| 1786 -- instead of creating a new container frame. | |
| 1787 -- @paramsig appName [, container][, ...] | |
| 1788 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
| 1789 -- @param container An optional container frame to feed the options into | |
| 1790 -- @param ... The path to open after creating the options window (see `:SelectGroup` for details) | |
| 1791 function AceConfigDialog:Open(appName, container, ...) | |
| 1792 if not old_CloseSpecialWindows then | |
| 1793 old_CloseSpecialWindows = CloseSpecialWindows | |
| 1794 CloseSpecialWindows = function() | |
| 1795 local found = old_CloseSpecialWindows() | |
| 1796 return self:CloseAll() or found | |
| 1797 end | |
| 1798 end | |
| 1799 local app = reg:GetOptionsTable(appName) | |
| 1800 if not app then | |
| 1801 error(("%s isn't registed with AceConfigRegistry, unable to open config"):format(appName), 2) | |
| 1802 end | |
| 1803 local options = app("dialog", MAJOR) | |
| 1804 | |
| 1805 local f | |
| 1806 | |
| 1807 local path = new() | |
| 1808 local name = GetOptionsMemberValue("name", options, options, path, appName) | |
| 1809 | |
| 1810 --If an optional path is specified add it to the path table before feeding the options | |
| 1811 --as container is optional as well it may contain the first element of the path | |
| 1812 if type(container) == "string" then | |
| 1813 tinsert(path, container) | |
| 1814 container = nil | |
| 1815 end | |
| 1816 for n = 1, select("#",...) do | |
| 1817 tinsert(path, (select(n, ...))) | |
| 1818 end | |
| 1819 | |
| 1820 --if a container is given feed into that | |
| 1821 if container then | |
| 1822 f = container | |
| 1823 f:ReleaseChildren() | |
| 1824 f:SetUserData("appName", appName) | |
| 1825 f:SetUserData("iscustom", true) | |
| 1826 if #path > 0 then | |
| 1827 f:SetUserData("basepath", copy(path)) | |
| 1828 end | |
| 1829 local status = AceConfigDialog:GetStatusTable(appName) | |
| 1830 if not status.width then | |
| 1831 status.width = 700 | |
| 1832 end | |
| 1833 if not status.height then | |
| 1834 status.height = 500 | |
| 1835 end | |
| 1836 if f.SetStatusTable then | |
| 1837 f:SetStatusTable(status) | |
| 1838 end | |
| 1839 if f.SetTitle then | |
| 1840 f:SetTitle(name or "") | |
| 1841 end | |
| 1842 else | |
| 1843 if not self.OpenFrames[appName] then | |
| 1844 f = gui:Create("Frame") | |
| 1845 self.OpenFrames[appName] = f | |
| 1846 else | |
| 1847 f = self.OpenFrames[appName] | |
| 1848 end | |
| 1849 f:ReleaseChildren() | |
| 1850 f:SetCallback("OnClose", FrameOnClose) | |
| 1851 f:SetUserData("appName", appName) | |
| 1852 if #path > 0 then | |
| 1853 f:SetUserData("basepath", copy(path)) | |
| 1854 end | |
| 1855 f:SetTitle(name or "") | |
| 1856 local status = AceConfigDialog:GetStatusTable(appName) | |
| 1857 f:SetStatusTable(status) | |
| 1858 end | |
| 1859 | |
| 1860 self:FeedGroup(appName,options,f,f,path,true) | |
| 1861 if f.Show then | |
| 1862 f:Show() | |
| 1863 end | |
| 1864 del(path) | |
| 1865 | |
| 1866 if AceConfigDialog.frame.closeAll then | |
| 1867 -- close all is set, but thats not good, since we're just opening here, so force it | |
| 1868 AceConfigDialog.frame.closeAllOverride[appName] = true | |
| 1869 end | |
| 1870 end | |
| 1871 | |
| 1872 -- convert pre-39 BlizOptions structure to the new format | |
| 1873 if oldminor and oldminor < 39 and AceConfigDialog.BlizOptions then | |
| 1874 local old = AceConfigDialog.BlizOptions | |
| 1875 local new = {} | |
| 1876 for key, widget in pairs(old) do | |
| 1877 local appName = widget:GetUserData("appName") | |
| 1878 if not new[appName] then new[appName] = {} end | |
| 1879 new[appName][key] = widget | |
| 1880 end | |
| 1881 AceConfigDialog.BlizOptions = new | |
| 1882 else | |
| 1883 AceConfigDialog.BlizOptions = AceConfigDialog.BlizOptions or {} | |
| 1884 end | |
| 1885 | |
| 1886 local function FeedToBlizPanel(widget, event) | |
| 1887 local path = widget:GetUserData("path") | |
| 1888 AceConfigDialog:Open(widget:GetUserData("appName"), widget, unpack(path or emptyTbl)) | |
| 1889 end | |
| 1890 | |
| 1891 local function ClearBlizPanel(widget, event) | |
| 1892 local appName = widget:GetUserData("appName") | |
| 1893 AceConfigDialog.frame.closing[appName] = true | |
| 1894 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
| 1895 end | |
| 1896 | |
| 1897 --- Add an option table into the Blizzard Interface Options panel. | |
| 1898 -- You can optionally supply a descriptive name to use and a parent frame to use, | |
| 1899 -- as well as a path in the options table.\\ | |
| 1900 -- If no name is specified, the appName will be used instead. | |
| 1901 -- | |
| 1902 -- If you specify a proper `parent` (by name), the interface options will generate a | |
| 1903 -- tree layout. Note that only one level of children is supported, so the parent always | |
| 1904 -- has to be a head-level note. | |
| 1905 -- | |
| 1906 -- This function returns a reference to the container frame registered with the Interface | |
| 1907 -- Options. You can use this reference to open the options with the API function | |
| 1908 -- `InterfaceOptionsFrame_OpenToCategory`. | |
| 1909 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
| 1910 -- @param name A descriptive name to display in the options tree (defaults to appName) | |
| 1911 -- @param parent The parent to use in the interface options tree. | |
| 1912 -- @param ... The path in the options table to feed into the interface options panel. | |
| 1913 -- @return The reference to the frame registered into the Interface Options. | |
| 1914 function AceConfigDialog:AddToBlizOptions(appName, name, parent, ...) | |
| 1915 local BlizOptions = AceConfigDialog.BlizOptions | |
| 1916 | |
| 1917 local key = appName | |
| 1918 for n = 1, select("#", ...) do | |
| 1919 key = key.."\001"..select(n, ...) | |
| 1920 end | |
| 1921 | |
| 1922 if not BlizOptions[appName] then | |
| 1923 BlizOptions[appName] = {} | |
| 1924 end | |
| 1925 | |
| 1926 if not BlizOptions[appName][key] then | |
| 1927 local group = gui:Create("BlizOptionsGroup") | |
| 1928 BlizOptions[appName][key] = group | |
| 1929 group:SetName(name or appName, parent) | |
| 1930 | |
| 1931 group:SetTitle(name or appName) | |
| 1932 group:SetUserData("appName", appName) | |
| 1933 if select("#", ...) > 0 then | |
| 1934 local path = {} | |
| 1935 for n = 1, select("#",...) do | |
| 1936 tinsert(path, (select(n, ...))) | |
| 1937 end | |
| 1938 group:SetUserData("path", path) | |
| 1939 end | |
| 1940 group:SetCallback("OnShow", FeedToBlizPanel) | |
| 1941 group:SetCallback("OnHide", ClearBlizPanel) | |
| 1942 InterfaceOptions_AddCategory(group.frame) | |
| 1943 return group.frame | |
| 1944 else | |
| 1945 error(("%s has already been added to the Blizzard Options Window with the given path"):format(appName), 2) | |
| 1946 end | |
| 1947 end |
