Zerotorescue@0: --- AceConfigDialog-3.0 generates AceGUI-3.0 based windows based on option tables. Zerotorescue@0: -- @class file Zerotorescue@0: -- @name AceConfigDialog-3.0 Zerotorescue@0: -- @release $Id: AceConfigDialog-3.0.lua 958 2010-07-03 10:22:29Z nevcairiel $ Zerotorescue@0: Zerotorescue@0: local LibStub = LibStub Zerotorescue@0: local MAJOR, MINOR = "AceConfigDialog-3.0", 49 Zerotorescue@0: local AceConfigDialog, oldminor = LibStub:NewLibrary(MAJOR, MINOR) Zerotorescue@0: Zerotorescue@0: if not AceConfigDialog then return end Zerotorescue@0: Zerotorescue@0: AceConfigDialog.OpenFrames = AceConfigDialog.OpenFrames or {} Zerotorescue@0: AceConfigDialog.Status = AceConfigDialog.Status or {} Zerotorescue@0: AceConfigDialog.frame = AceConfigDialog.frame or CreateFrame("Frame") Zerotorescue@0: Zerotorescue@0: AceConfigDialog.frame.apps = AceConfigDialog.frame.apps or {} Zerotorescue@0: AceConfigDialog.frame.closing = AceConfigDialog.frame.closing or {} Zerotorescue@0: AceConfigDialog.frame.closeAllOverride = AceConfigDialog.frame.closeAllOverride or {} Zerotorescue@0: Zerotorescue@0: local gui = LibStub("AceGUI-3.0") Zerotorescue@0: local reg = LibStub("AceConfigRegistry-3.0") Zerotorescue@0: Zerotorescue@0: -- Lua APIs Zerotorescue@0: local tconcat, tinsert, tsort, tremove = table.concat, table.insert, table.sort, table.remove Zerotorescue@0: local strmatch, format = string.match, string.format Zerotorescue@0: local assert, loadstring, error = assert, loadstring, error Zerotorescue@0: local pairs, next, select, type, unpack, wipe = pairs, next, select, type, unpack, wipe Zerotorescue@0: local rawset, tostring, tonumber = rawset, tostring, tonumber Zerotorescue@0: local math_min, math_max, math_floor = math.min, math.max, math.floor Zerotorescue@0: Zerotorescue@0: -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded Zerotorescue@0: -- List them here for Mikk's FindGlobals script Zerotorescue@0: -- GLOBALS: NORMAL_FONT_COLOR, GameTooltip, StaticPopupDialogs, ACCEPT, CANCEL, StaticPopup_Show Zerotorescue@0: -- GLOBALS: PlaySound, GameFontHighlight, GameFontHighlightSmall, GameFontHighlightLarge Zerotorescue@0: -- GLOBALS: CloseSpecialWindows, InterfaceOptions_AddCategory, geterrorhandler Zerotorescue@0: Zerotorescue@0: local emptyTbl = {} Zerotorescue@0: Zerotorescue@0: --[[ Zerotorescue@0: xpcall safecall implementation Zerotorescue@0: ]] Zerotorescue@0: local xpcall = xpcall Zerotorescue@0: Zerotorescue@0: local function errorhandler(err) Zerotorescue@0: return geterrorhandler()(err) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function CreateDispatcher(argCount) Zerotorescue@0: local code = [[ Zerotorescue@0: local xpcall, eh = ... Zerotorescue@0: local method, ARGS Zerotorescue@0: local function call() return method(ARGS) end Zerotorescue@0: Zerotorescue@0: local function dispatch(func, ...) Zerotorescue@0: method = func Zerotorescue@0: if not method then return end Zerotorescue@0: ARGS = ... Zerotorescue@0: return xpcall(call, eh) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: return dispatch Zerotorescue@0: ]] Zerotorescue@0: Zerotorescue@0: local ARGS = {} Zerotorescue@0: for i = 1, argCount do ARGS[i] = "arg"..i end Zerotorescue@0: code = code:gsub("ARGS", tconcat(ARGS, ", ")) Zerotorescue@0: return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local Dispatchers = setmetatable({}, {__index=function(self, argCount) Zerotorescue@0: local dispatcher = CreateDispatcher(argCount) Zerotorescue@0: rawset(self, argCount, dispatcher) Zerotorescue@0: return dispatcher Zerotorescue@0: end}) Zerotorescue@0: Dispatchers[0] = function(func) Zerotorescue@0: return xpcall(func, errorhandler) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function safecall(func, ...) Zerotorescue@0: return Dispatchers[select("#", ...)](func, ...) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local width_multiplier = 170 Zerotorescue@0: Zerotorescue@0: --[[ Zerotorescue@0: Group Types Zerotorescue@0: Tree - All Descendant Groups will all become nodes on the tree, direct child options will appear above the tree Zerotorescue@0: - Descendant Groups with inline=true and thier children will not become nodes Zerotorescue@0: Zerotorescue@0: Tab - Direct Child Groups will become tabs, direct child options will appear above the tab control Zerotorescue@0: - Grandchild groups will default to inline unless specified otherwise Zerotorescue@0: Zerotorescue@0: Select- Same as Tab but with entries in a dropdown rather than tabs Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: Inline Groups Zerotorescue@0: - Will not become nodes of a select group, they will be effectivly part of thier parent group seperated by a border Zerotorescue@0: - If declared on a direct child of a root node of a select group, they will appear above the group container control Zerotorescue@0: - When a group is displayed inline, all descendants will also be inline members of the group Zerotorescue@0: Zerotorescue@0: ]] Zerotorescue@0: Zerotorescue@0: -- Recycling functions Zerotorescue@0: local new, del, copy Zerotorescue@0: --newcount, delcount,createdcount,cached = 0,0,0 Zerotorescue@0: do Zerotorescue@0: local pool = setmetatable({},{__mode="k"}) Zerotorescue@0: function new() Zerotorescue@0: --newcount = newcount + 1 Zerotorescue@0: local t = next(pool) Zerotorescue@0: if t then Zerotorescue@0: pool[t] = nil Zerotorescue@0: return t Zerotorescue@0: else Zerotorescue@0: --createdcount = createdcount + 1 Zerotorescue@0: return {} Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: function copy(t) Zerotorescue@0: local c = new() Zerotorescue@0: for k, v in pairs(t) do Zerotorescue@0: c[k] = v Zerotorescue@0: end Zerotorescue@0: return c Zerotorescue@0: end Zerotorescue@0: function del(t) Zerotorescue@0: --delcount = delcount + 1 Zerotorescue@0: for k in pairs(t) do Zerotorescue@0: t[k] = nil Zerotorescue@0: end Zerotorescue@0: pool[t] = true Zerotorescue@0: end Zerotorescue@0: -- function cached() Zerotorescue@0: -- local n = 0 Zerotorescue@0: -- for k in pairs(pool) do Zerotorescue@0: -- n = n + 1 Zerotorescue@0: -- end Zerotorescue@0: -- return n Zerotorescue@0: -- end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- picks the first non-nil value and returns it Zerotorescue@0: local function pickfirstset(...) Zerotorescue@0: for i=1,select("#",...) do Zerotorescue@0: if select(i,...)~=nil then Zerotorescue@0: return select(i,...) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --gets an option from a given group, checking plugins Zerotorescue@0: local function GetSubOption(group, key) Zerotorescue@0: if group.plugins then Zerotorescue@0: for plugin, t in pairs(group.plugins) do Zerotorescue@0: if t[key] then Zerotorescue@0: return t[key] Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: return group.args[key] Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --Option member type definitions, used to decide how to access it Zerotorescue@0: Zerotorescue@0: --Is the member Inherited from parent options Zerotorescue@0: local isInherited = { Zerotorescue@0: set = true, Zerotorescue@0: get = true, Zerotorescue@0: func = true, Zerotorescue@0: confirm = true, Zerotorescue@0: validate = true, Zerotorescue@0: disabled = true, Zerotorescue@0: hidden = true Zerotorescue@0: } Zerotorescue@0: Zerotorescue@0: --Does a string type mean a literal value, instead of the default of a method of the handler Zerotorescue@0: local stringIsLiteral = { Zerotorescue@0: name = true, Zerotorescue@0: desc = true, Zerotorescue@0: icon = true, Zerotorescue@0: usage = true, Zerotorescue@0: width = true, Zerotorescue@0: image = true, Zerotorescue@0: fontSize = true, Zerotorescue@0: } Zerotorescue@0: Zerotorescue@0: --Is Never a function or method Zerotorescue@0: local allIsLiteral = { Zerotorescue@0: type = true, Zerotorescue@0: descStyle = true, Zerotorescue@0: imageWidth = true, Zerotorescue@0: imageHeight = true, Zerotorescue@0: } Zerotorescue@0: Zerotorescue@0: --gets the value for a member that could be a function Zerotorescue@0: --function refs are called with an info arg Zerotorescue@0: --every other type is returned Zerotorescue@0: local function GetOptionsMemberValue(membername, option, options, path, appName, ...) Zerotorescue@0: --get definition for the member Zerotorescue@0: local inherits = isInherited[membername] Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: --get the member of the option, traversing the tree if it can be inherited Zerotorescue@0: local member Zerotorescue@0: Zerotorescue@0: if inherits then Zerotorescue@0: local group = options Zerotorescue@0: if group[membername] ~= nil then Zerotorescue@0: member = group[membername] Zerotorescue@0: end Zerotorescue@0: for i = 1, #path do Zerotorescue@0: group = GetSubOption(group, path[i]) Zerotorescue@0: if group[membername] ~= nil then Zerotorescue@0: member = group[membername] Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: else Zerotorescue@0: member = option[membername] Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --check if we need to call a functon, or if we have a literal value Zerotorescue@0: if ( not allIsLiteral[membername] ) and ( type(member) == "function" or ((not stringIsLiteral[membername]) and type(member) == "string") ) then Zerotorescue@0: --We have a function to call Zerotorescue@0: local info = new() Zerotorescue@0: --traverse the options table, picking up the handler and filling the info with the path Zerotorescue@0: local handler Zerotorescue@0: local group = options Zerotorescue@0: handler = group.handler or handler Zerotorescue@0: Zerotorescue@0: for i = 1, #path do Zerotorescue@0: group = GetSubOption(group, path[i]) Zerotorescue@0: info[i] = path[i] Zerotorescue@0: handler = group.handler or handler Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: info.options = options Zerotorescue@0: info.appName = appName Zerotorescue@0: info[0] = appName Zerotorescue@0: info.arg = option.arg Zerotorescue@0: info.handler = handler Zerotorescue@0: info.option = option Zerotorescue@0: info.type = option.type Zerotorescue@0: info.uiType = "dialog" Zerotorescue@0: info.uiName = MAJOR Zerotorescue@0: Zerotorescue@0: local a, b, c ,d Zerotorescue@0: --using 4 returns for the get of a color type, increase if a type needs more Zerotorescue@0: if type(member) == "function" then Zerotorescue@0: --Call the function Zerotorescue@0: a,b,c,d = member(info, ...) Zerotorescue@0: else Zerotorescue@0: --Call the method Zerotorescue@0: if handler and handler[member] then Zerotorescue@0: a,b,c,d = handler[member](handler, info, ...) Zerotorescue@0: else Zerotorescue@0: error(format("Method %s doesn't exist in handler for type %s", member, membername)) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: del(info) Zerotorescue@0: return a,b,c,d Zerotorescue@0: else Zerotorescue@0: --The value isnt a function to call, return it Zerotorescue@0: return member Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --[[calls an options function that could be inherited, method name or function ref Zerotorescue@0: local function CallOptionsFunction(funcname ,option, options, path, appName, ...) Zerotorescue@0: local info = new() Zerotorescue@0: Zerotorescue@0: local func Zerotorescue@0: local group = options Zerotorescue@0: local handler Zerotorescue@0: Zerotorescue@0: --build the info table containing the path Zerotorescue@0: -- pick up functions while traversing the tree Zerotorescue@0: if group[funcname] ~= nil then Zerotorescue@0: func = group[funcname] Zerotorescue@0: end Zerotorescue@0: handler = group.handler or handler Zerotorescue@0: Zerotorescue@0: for i, v in ipairs(path) do Zerotorescue@0: group = GetSubOption(group, v) Zerotorescue@0: info[i] = v Zerotorescue@0: if group[funcname] ~= nil then Zerotorescue@0: func = group[funcname] Zerotorescue@0: end Zerotorescue@0: handler = group.handler or handler Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: info.options = options Zerotorescue@0: info[0] = appName Zerotorescue@0: info.arg = option.arg Zerotorescue@0: Zerotorescue@0: local a, b, c ,d Zerotorescue@0: if type(func) == "string" then Zerotorescue@0: if handler and handler[func] then Zerotorescue@0: a,b,c,d = handler[func](handler, info, ...) Zerotorescue@0: else Zerotorescue@0: error(string.format("Method %s doesn't exist in handler for type func", func)) Zerotorescue@0: end Zerotorescue@0: elseif type(func) == "function" then Zerotorescue@0: a,b,c,d = func(info, ...) Zerotorescue@0: end Zerotorescue@0: del(info) Zerotorescue@0: return a,b,c,d Zerotorescue@0: end Zerotorescue@0: --]] Zerotorescue@0: Zerotorescue@0: --tables to hold orders and names for options being sorted, will be created with new() Zerotorescue@0: --prevents needing to call functions repeatedly while sorting Zerotorescue@0: local tempOrders Zerotorescue@0: local tempNames Zerotorescue@0: Zerotorescue@0: local function compareOptions(a,b) Zerotorescue@0: if not a then Zerotorescue@0: return true Zerotorescue@0: end Zerotorescue@0: if not b then Zerotorescue@0: return false Zerotorescue@0: end Zerotorescue@0: local OrderA, OrderB = tempOrders[a] or 100, tempOrders[b] or 100 Zerotorescue@0: if OrderA == OrderB then Zerotorescue@0: local NameA = (type(tempNames[a] == "string") and tempNames[a]) or "" Zerotorescue@0: local NameB = (type(tempNames[b] == "string") and tempNames[b]) or "" Zerotorescue@0: return NameA:upper() < NameB:upper() Zerotorescue@0: end Zerotorescue@0: if OrderA < 0 then Zerotorescue@0: if OrderB > 0 then Zerotorescue@0: return false Zerotorescue@0: end Zerotorescue@0: else Zerotorescue@0: if OrderB < 0 then Zerotorescue@0: return true Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: return OrderA < OrderB Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: --builds 2 tables out of an options group Zerotorescue@0: -- keySort, sorted keys Zerotorescue@0: -- opts, combined options from .plugins and args Zerotorescue@0: local function BuildSortedOptionsTable(group, keySort, opts, options, path, appName) Zerotorescue@0: tempOrders = new() Zerotorescue@0: tempNames = new() Zerotorescue@0: Zerotorescue@0: if group.plugins then Zerotorescue@0: for plugin, t in pairs(group.plugins) do Zerotorescue@0: for k, v in pairs(t) do Zerotorescue@0: if not opts[k] then Zerotorescue@0: tinsert(keySort, k) Zerotorescue@0: opts[k] = v Zerotorescue@0: Zerotorescue@0: path[#path+1] = k Zerotorescue@0: tempOrders[k] = GetOptionsMemberValue("order", v, options, path, appName) Zerotorescue@0: tempNames[k] = GetOptionsMemberValue("name", v, options, path, appName) Zerotorescue@0: path[#path] = nil Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: for k, v in pairs(group.args) do Zerotorescue@0: if not opts[k] then Zerotorescue@0: tinsert(keySort, k) Zerotorescue@0: opts[k] = v Zerotorescue@0: Zerotorescue@0: path[#path+1] = k Zerotorescue@0: tempOrders[k] = GetOptionsMemberValue("order", v, options, path, appName) Zerotorescue@0: tempNames[k] = GetOptionsMemberValue("name", v, options, path, appName) Zerotorescue@0: path[#path] = nil Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: tsort(keySort, compareOptions) Zerotorescue@0: Zerotorescue@0: del(tempOrders) Zerotorescue@0: del(tempNames) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function DelTree(tree) Zerotorescue@0: if tree.children then Zerotorescue@0: local childs = tree.children Zerotorescue@0: for i = 1, #childs do Zerotorescue@0: DelTree(childs[i]) Zerotorescue@0: del(childs[i]) Zerotorescue@0: end Zerotorescue@0: del(childs) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function CleanUserData(widget, event) Zerotorescue@0: Zerotorescue@0: local user = widget:GetUserDataTable() Zerotorescue@0: Zerotorescue@0: if user.path then Zerotorescue@0: del(user.path) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if widget.type == "TreeGroup" then Zerotorescue@0: local tree = user.tree Zerotorescue@0: widget:SetTree(nil) Zerotorescue@0: if tree then Zerotorescue@0: for i = 1, #tree do Zerotorescue@0: DelTree(tree[i]) Zerotorescue@0: del(tree[i]) Zerotorescue@0: end Zerotorescue@0: del(tree) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if widget.type == "TabGroup" then Zerotorescue@0: widget:SetTabs(nil) Zerotorescue@0: if user.tablist then Zerotorescue@0: del(user.tablist) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if widget.type == "DropdownGroup" then Zerotorescue@0: widget:SetGroupList(nil) Zerotorescue@0: if user.grouplist then Zerotorescue@0: del(user.grouplist) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- - Gets a status table for the given appname and options path. Zerotorescue@0: -- @param appName The application name as given to `:RegisterOptionsTable()` Zerotorescue@0: -- @param path The path to the options (a table with all group keys) Zerotorescue@0: -- @return Zerotorescue@0: function AceConfigDialog:GetStatusTable(appName, path) Zerotorescue@0: local status = self.Status Zerotorescue@0: Zerotorescue@0: if not status[appName] then Zerotorescue@0: status[appName] = {} Zerotorescue@0: status[appName].status = {} Zerotorescue@0: status[appName].children = {} Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: status = status[appName] Zerotorescue@0: Zerotorescue@0: if path then Zerotorescue@0: for i = 1, #path do Zerotorescue@0: local v = path[i] Zerotorescue@0: if not status.children[v] then Zerotorescue@0: status.children[v] = {} Zerotorescue@0: status.children[v].status = {} Zerotorescue@0: status.children[v].children = {} Zerotorescue@0: end Zerotorescue@0: status = status.children[v] Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: return status.status Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --- Selects the specified path in the options window. Zerotorescue@0: -- The path specified has to match the keys of the groups in the table. Zerotorescue@0: -- @param appName The application name as given to `:RegisterOptionsTable()` Zerotorescue@0: -- @param ... The path to the key that should be selected Zerotorescue@0: function AceConfigDialog:SelectGroup(appName, ...) Zerotorescue@0: local path = new() Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: local app = reg:GetOptionsTable(appName) Zerotorescue@0: if not app then Zerotorescue@0: error(("%s isn't registed with AceConfigRegistry, unable to open config"):format(appName), 2) Zerotorescue@0: end Zerotorescue@0: local options = app("dialog", MAJOR) Zerotorescue@0: local group = options Zerotorescue@0: local status = self:GetStatusTable(appName, path) Zerotorescue@0: if not status.groups then Zerotorescue@0: status.groups = {} Zerotorescue@0: end Zerotorescue@0: status = status.groups Zerotorescue@0: local treevalue Zerotorescue@0: local treestatus Zerotorescue@0: Zerotorescue@0: for n = 1, select("#",...) do Zerotorescue@0: local key = select(n, ...) Zerotorescue@0: Zerotorescue@0: if group.childGroups == "tab" or group.childGroups == "select" then Zerotorescue@0: --if this is a tab or select group, select the group Zerotorescue@0: status.selected = key Zerotorescue@0: --children of this group are no longer extra levels of a tree Zerotorescue@0: treevalue = nil Zerotorescue@0: else Zerotorescue@0: --tree group by default Zerotorescue@0: if treevalue then Zerotorescue@0: --this is an extra level of a tree group, build a uniquevalue for it Zerotorescue@0: treevalue = treevalue.."\001"..key Zerotorescue@0: else Zerotorescue@0: --this is the top level of a tree group, the uniquevalue is the same as the key Zerotorescue@0: treevalue = key Zerotorescue@0: if not status.groups then Zerotorescue@0: status.groups = {} Zerotorescue@0: end Zerotorescue@0: --save this trees status table for any extra levels or groups Zerotorescue@0: treestatus = status Zerotorescue@0: end Zerotorescue@0: --make sure that the tree entry is open, and select it. Zerotorescue@0: --the selected group will be overwritten if a child is the final target but still needs to be open Zerotorescue@0: treestatus.selected = treevalue Zerotorescue@0: treestatus.groups[treevalue] = true Zerotorescue@0: Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --move to the next group in the path Zerotorescue@0: group = GetSubOption(group, key) Zerotorescue@0: if not group then Zerotorescue@0: break Zerotorescue@0: end Zerotorescue@0: tinsert(path, key) Zerotorescue@0: status = self:GetStatusTable(appName, path) Zerotorescue@0: if not status.groups then Zerotorescue@0: status.groups = {} Zerotorescue@0: end Zerotorescue@0: status = status.groups Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: del(path) Zerotorescue@0: reg:NotifyChange(appName) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function OptionOnMouseOver(widget, event) Zerotorescue@0: --show a tooltip/set the status bar to the desc text Zerotorescue@0: local user = widget:GetUserDataTable() Zerotorescue@0: local opt = user.option Zerotorescue@0: local options = user.options Zerotorescue@0: local path = user.path Zerotorescue@0: local appName = user.appName Zerotorescue@0: Zerotorescue@0: GameTooltip:SetOwner(widget.frame, "ANCHOR_TOPRIGHT") Zerotorescue@0: local name = GetOptionsMemberValue("name", opt, options, path, appName) Zerotorescue@0: local desc = GetOptionsMemberValue("desc", opt, options, path, appName) Zerotorescue@0: local usage = GetOptionsMemberValue("usage", opt, options, path, appName) Zerotorescue@0: local descStyle = opt.descStyle Zerotorescue@0: Zerotorescue@0: if descStyle and descStyle ~= "tooltip" then return end Zerotorescue@0: Zerotorescue@0: GameTooltip:SetText(name, 1, .82, 0, 1) Zerotorescue@0: Zerotorescue@0: if opt.type == "multiselect" then Zerotorescue@0: GameTooltip:AddLine(user.text,0.5, 0.5, 0.8, 1) Zerotorescue@0: end Zerotorescue@0: if type(desc) == "string" then Zerotorescue@0: GameTooltip:AddLine(desc, 1, 1, 1, 1) Zerotorescue@0: end Zerotorescue@0: if type(usage) == "string" then Zerotorescue@0: GameTooltip:AddLine("Usage: "..usage, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: GameTooltip:Show() Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function OptionOnMouseLeave(widget, event) Zerotorescue@0: GameTooltip:Hide() Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function GetFuncName(option) Zerotorescue@0: local type = option.type Zerotorescue@0: if type == "execute" then Zerotorescue@0: return "func" Zerotorescue@0: else Zerotorescue@0: return "set" Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: local function confirmPopup(appName, rootframe, basepath, info, message, func, ...) Zerotorescue@0: if not StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] then Zerotorescue@0: StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] = {} Zerotorescue@0: end Zerotorescue@0: local t = StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] Zerotorescue@0: for k in pairs(t) do Zerotorescue@0: t[k] = nil Zerotorescue@0: end Zerotorescue@0: t.text = message Zerotorescue@0: t.button1 = ACCEPT Zerotorescue@0: t.button2 = CANCEL Zerotorescue@0: local dialog, oldstrata Zerotorescue@0: t.OnAccept = function() Zerotorescue@0: safecall(func, unpack(t)) Zerotorescue@0: if dialog and oldstrata then Zerotorescue@0: dialog:SetFrameStrata(oldstrata) Zerotorescue@0: end Zerotorescue@0: AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl)) Zerotorescue@0: del(info) Zerotorescue@0: end Zerotorescue@0: t.OnCancel = function() Zerotorescue@0: if dialog and oldstrata then Zerotorescue@0: dialog:SetFrameStrata(oldstrata) Zerotorescue@0: end Zerotorescue@0: AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl)) Zerotorescue@0: del(info) Zerotorescue@0: end Zerotorescue@0: for i = 1, select("#", ...) do Zerotorescue@0: t[i] = select(i, ...) or false Zerotorescue@0: end Zerotorescue@0: t.timeout = 0 Zerotorescue@0: t.whileDead = 1 Zerotorescue@0: t.hideOnEscape = 1 Zerotorescue@0: Zerotorescue@0: dialog = StaticPopup_Show("ACECONFIGDIALOG30_CONFIRM_DIALOG") Zerotorescue@0: if dialog then Zerotorescue@0: oldstrata = dialog:GetFrameStrata() Zerotorescue@0: dialog:SetFrameStrata("TOOLTIP") Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function ActivateControl(widget, event, ...) Zerotorescue@0: --This function will call the set / execute handler for the widget Zerotorescue@0: --widget:GetUserDataTable() contains the needed info Zerotorescue@0: local user = widget:GetUserDataTable() Zerotorescue@0: local option = user.option Zerotorescue@0: local options = user.options Zerotorescue@0: local path = user.path Zerotorescue@0: local info = new() Zerotorescue@0: Zerotorescue@0: local func Zerotorescue@0: local group = options Zerotorescue@0: local funcname = GetFuncName(option) Zerotorescue@0: local handler Zerotorescue@0: local confirm Zerotorescue@0: local validate Zerotorescue@0: --build the info table containing the path Zerotorescue@0: -- pick up functions while traversing the tree Zerotorescue@0: if group[funcname] ~= nil then Zerotorescue@0: func = group[funcname] Zerotorescue@0: end Zerotorescue@0: handler = group.handler or handler Zerotorescue@0: confirm = group.confirm Zerotorescue@0: validate = group.validate Zerotorescue@0: for i = 1, #path do Zerotorescue@0: local v = path[i] Zerotorescue@0: group = GetSubOption(group, v) Zerotorescue@0: info[i] = v Zerotorescue@0: if group[funcname] ~= nil then Zerotorescue@0: func = group[funcname] Zerotorescue@0: end Zerotorescue@0: handler = group.handler or handler Zerotorescue@0: if group.confirm ~= nil then Zerotorescue@0: confirm = group.confirm Zerotorescue@0: end Zerotorescue@0: if group.validate ~= nil then Zerotorescue@0: validate = group.validate Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: info.options = options Zerotorescue@0: info.appName = user.appName Zerotorescue@0: info.arg = option.arg Zerotorescue@0: info.handler = handler Zerotorescue@0: info.option = option Zerotorescue@0: info.type = option.type Zerotorescue@0: info.uiType = "dialog" Zerotorescue@0: info.uiName = MAJOR Zerotorescue@0: Zerotorescue@0: local name Zerotorescue@0: if type(option.name) == "function" then Zerotorescue@0: name = option.name(info) Zerotorescue@0: elseif type(option.name) == "string" then Zerotorescue@0: name = option.name Zerotorescue@0: else Zerotorescue@0: name = "" Zerotorescue@0: end Zerotorescue@0: local usage = option.usage Zerotorescue@0: local pattern = option.pattern Zerotorescue@0: Zerotorescue@0: local validated = true Zerotorescue@0: Zerotorescue@0: if option.type == "input" then Zerotorescue@0: if type(pattern)=="string" then Zerotorescue@0: if not strmatch(..., pattern) then Zerotorescue@0: validated = false Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local success Zerotorescue@0: if validated and option.type ~= "execute" then Zerotorescue@0: if type(validate) == "string" then Zerotorescue@0: if handler and handler[validate] then Zerotorescue@0: success, validated = safecall(handler[validate], handler, info, ...) Zerotorescue@0: if not success then validated = false end Zerotorescue@0: else Zerotorescue@0: error(format("Method %s doesn't exist in handler for type execute", validate)) Zerotorescue@0: end Zerotorescue@0: elseif type(validate) == "function" then Zerotorescue@0: success, validated = safecall(validate, info, ...) Zerotorescue@0: if not success then validated = false end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local rootframe = user.rootframe Zerotorescue@0: if type(validated) == "string" then Zerotorescue@0: --validate function returned a message to display Zerotorescue@0: if rootframe.SetStatusText then Zerotorescue@0: rootframe:SetStatusText(validated) Zerotorescue@0: else Zerotorescue@0: -- TODO: do something else. Zerotorescue@0: end Zerotorescue@0: PlaySound("igPlayerInviteDecline") Zerotorescue@0: del(info) Zerotorescue@0: return true Zerotorescue@0: elseif not validated then Zerotorescue@0: --validate returned false Zerotorescue@0: if rootframe.SetStatusText then Zerotorescue@0: if usage then Zerotorescue@0: rootframe:SetStatusText(name..": "..usage) Zerotorescue@0: else Zerotorescue@0: if pattern then Zerotorescue@0: rootframe:SetStatusText(name..": Expected "..pattern) Zerotorescue@0: else Zerotorescue@0: rootframe:SetStatusText(name..": Invalid Value") Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: else Zerotorescue@0: -- TODO: do something else Zerotorescue@0: end Zerotorescue@0: PlaySound("igPlayerInviteDecline") Zerotorescue@0: del(info) Zerotorescue@0: return true Zerotorescue@0: else Zerotorescue@0: Zerotorescue@0: local confirmText = option.confirmText Zerotorescue@0: --call confirm func/method Zerotorescue@0: if type(confirm) == "string" then Zerotorescue@0: if handler and handler[confirm] then Zerotorescue@0: success, confirm = safecall(handler[confirm], handler, info, ...) Zerotorescue@0: if success and type(confirm) == "string" then Zerotorescue@0: confirmText = confirm Zerotorescue@0: confirm = true Zerotorescue@0: elseif not success then Zerotorescue@0: confirm = false Zerotorescue@0: end Zerotorescue@0: else Zerotorescue@0: error(format("Method %s doesn't exist in handler for type confirm", confirm)) Zerotorescue@0: end Zerotorescue@0: elseif type(confirm) == "function" then Zerotorescue@0: success, confirm = safecall(confirm, info, ...) Zerotorescue@0: if success and type(confirm) == "string" then Zerotorescue@0: confirmText = confirm Zerotorescue@0: confirm = true Zerotorescue@0: elseif not success then Zerotorescue@0: confirm = false Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --confirm if needed Zerotorescue@0: if type(confirm) == "boolean" then Zerotorescue@0: if confirm then Zerotorescue@0: if not confirmText then Zerotorescue@0: local name, desc = option.name, option.desc Zerotorescue@0: if type(name) == "function" then Zerotorescue@0: name = name(info) Zerotorescue@0: end Zerotorescue@0: if type(desc) == "function" then Zerotorescue@0: desc = desc(info) Zerotorescue@0: end Zerotorescue@0: confirmText = name Zerotorescue@0: if desc then Zerotorescue@0: confirmText = confirmText.." - "..desc Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local iscustom = user.rootframe:GetUserData("iscustom") Zerotorescue@0: local rootframe Zerotorescue@0: Zerotorescue@0: if iscustom then Zerotorescue@0: rootframe = user.rootframe Zerotorescue@0: end Zerotorescue@0: local basepath = user.rootframe:GetUserData("basepath") Zerotorescue@0: if type(func) == "string" then Zerotorescue@0: if handler and handler[func] then Zerotorescue@0: confirmPopup(user.appName, rootframe, basepath, info, confirmText, handler[func], handler, info, ...) Zerotorescue@0: else Zerotorescue@0: error(format("Method %s doesn't exist in handler for type func", func)) Zerotorescue@0: end Zerotorescue@0: elseif type(func) == "function" then Zerotorescue@0: confirmPopup(user.appName, rootframe, basepath, info, confirmText, func, info, ...) Zerotorescue@0: end Zerotorescue@0: --func will be called and info deleted when the confirm dialog is responded to Zerotorescue@0: return Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --call the function Zerotorescue@0: if type(func) == "string" then Zerotorescue@0: if handler and handler[func] then Zerotorescue@0: safecall(handler[func],handler, info, ...) Zerotorescue@0: else Zerotorescue@0: error(format("Method %s doesn't exist in handler for type func", func)) Zerotorescue@0: end Zerotorescue@0: elseif type(func) == "function" then Zerotorescue@0: safecall(func,info, ...) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: local iscustom = user.rootframe:GetUserData("iscustom") Zerotorescue@0: local basepath = user.rootframe:GetUserData("basepath") or emptyTbl Zerotorescue@0: --full refresh of the frame, some controls dont cause this on all events Zerotorescue@0: if option.type == "color" then Zerotorescue@0: if event == "OnValueConfirmed" then Zerotorescue@0: Zerotorescue@0: if iscustom then Zerotorescue@0: AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) Zerotorescue@0: else Zerotorescue@0: AceConfigDialog:Open(user.appName, unpack(basepath)) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: elseif option.type == "range" then Zerotorescue@0: if event == "OnMouseUp" then Zerotorescue@0: if iscustom then Zerotorescue@0: AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) Zerotorescue@0: else Zerotorescue@0: AceConfigDialog:Open(user.appName, unpack(basepath)) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: --multiselects don't cause a refresh on 'OnValueChanged' only 'OnClosed' Zerotorescue@0: elseif option.type == "multiselect" then Zerotorescue@0: user.valuechanged = true Zerotorescue@0: else Zerotorescue@0: if iscustom then Zerotorescue@0: AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) Zerotorescue@0: else Zerotorescue@0: AceConfigDialog:Open(user.appName, unpack(basepath)) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: end Zerotorescue@0: del(info) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function ActivateSlider(widget, event, value) Zerotorescue@0: local option = widget:GetUserData("option") Zerotorescue@0: 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 Zerotorescue@0: if min then Zerotorescue@0: if step then Zerotorescue@0: value = math_floor((value - min) / step + 0.5) * step + min Zerotorescue@0: end Zerotorescue@0: value = math_max(value, min) Zerotorescue@0: end Zerotorescue@0: if max then Zerotorescue@0: value = math_min(value, max) Zerotorescue@0: end Zerotorescue@0: ActivateControl(widget,event,value) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --called from a checkbox that is part of an internally created multiselect group Zerotorescue@0: --this type is safe to refresh on activation of one control Zerotorescue@0: local function ActivateMultiControl(widget, event, ...) Zerotorescue@0: ActivateControl(widget, event, widget:GetUserData("value"), ...) Zerotorescue@0: local user = widget:GetUserDataTable() Zerotorescue@0: local iscustom = user.rootframe:GetUserData("iscustom") Zerotorescue@0: local basepath = user.rootframe:GetUserData("basepath") or emptyTbl Zerotorescue@0: if iscustom then Zerotorescue@0: AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) Zerotorescue@0: else Zerotorescue@0: AceConfigDialog:Open(user.appName, unpack(basepath)) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function MultiControlOnClosed(widget, event, ...) Zerotorescue@0: local user = widget:GetUserDataTable() Zerotorescue@0: if user.valuechanged then Zerotorescue@0: local iscustom = user.rootframe:GetUserData("iscustom") Zerotorescue@0: local basepath = user.rootframe:GetUserData("basepath") or emptyTbl Zerotorescue@0: if iscustom then Zerotorescue@0: AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) Zerotorescue@0: else Zerotorescue@0: AceConfigDialog:Open(user.appName, unpack(basepath)) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function FrameOnClose(widget, event) Zerotorescue@0: local appName = widget:GetUserData("appName") Zerotorescue@0: AceConfigDialog.OpenFrames[appName] = nil Zerotorescue@0: gui:Release(widget) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function CheckOptionHidden(option, options, path, appName) Zerotorescue@0: --check for a specific boolean option Zerotorescue@0: local hidden = pickfirstset(option.dialogHidden,option.guiHidden) Zerotorescue@0: if hidden ~= nil then Zerotorescue@0: return hidden Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: return GetOptionsMemberValue("hidden", option, options, path, appName) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function CheckOptionDisabled(option, options, path, appName) Zerotorescue@0: --check for a specific boolean option Zerotorescue@0: local disabled = pickfirstset(option.dialogDisabled,option.guiDisabled) Zerotorescue@0: if disabled ~= nil then Zerotorescue@0: return disabled Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: return GetOptionsMemberValue("disabled", option, options, path, appName) Zerotorescue@0: end Zerotorescue@0: --[[ Zerotorescue@0: local function BuildTabs(group, options, path, appName) Zerotorescue@0: local tabs = new() Zerotorescue@0: local text = new() Zerotorescue@0: local keySort = new() Zerotorescue@0: local opts = new() Zerotorescue@0: Zerotorescue@0: BuildSortedOptionsTable(group, keySort, opts, options, path, appName) Zerotorescue@0: Zerotorescue@0: for i = 1, #keySort do Zerotorescue@0: local k = keySort[i] Zerotorescue@0: local v = opts[k] Zerotorescue@0: if v.type == "group" then Zerotorescue@0: path[#path+1] = k Zerotorescue@0: local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) Zerotorescue@0: local hidden = CheckOptionHidden(v, options, path, appName) Zerotorescue@0: if not inline and not hidden then Zerotorescue@0: tinsert(tabs, k) Zerotorescue@0: text[k] = GetOptionsMemberValue("name", v, options, path, appName) Zerotorescue@0: end Zerotorescue@0: path[#path] = nil Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: del(keySort) Zerotorescue@0: del(opts) Zerotorescue@0: Zerotorescue@0: return tabs, text Zerotorescue@0: end Zerotorescue@0: ]] Zerotorescue@0: local function BuildSelect(group, options, path, appName) Zerotorescue@0: local groups = new() Zerotorescue@0: local keySort = new() Zerotorescue@0: local opts = new() Zerotorescue@0: Zerotorescue@0: BuildSortedOptionsTable(group, keySort, opts, options, path, appName) Zerotorescue@0: Zerotorescue@0: for i = 1, #keySort do Zerotorescue@0: local k = keySort[i] Zerotorescue@0: local v = opts[k] Zerotorescue@0: if v.type == "group" then Zerotorescue@0: path[#path+1] = k Zerotorescue@0: local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) Zerotorescue@0: local hidden = CheckOptionHidden(v, options, path, appName) Zerotorescue@0: if not inline and not hidden then Zerotorescue@0: groups[k] = GetOptionsMemberValue("name", v, options, path, appName) Zerotorescue@0: end Zerotorescue@0: path[#path] = nil Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: del(keySort) Zerotorescue@0: del(opts) Zerotorescue@0: Zerotorescue@0: return groups Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function BuildSubGroups(group, tree, options, path, appName) Zerotorescue@0: local keySort = new() Zerotorescue@0: local opts = new() Zerotorescue@0: Zerotorescue@0: BuildSortedOptionsTable(group, keySort, opts, options, path, appName) Zerotorescue@0: Zerotorescue@0: for i = 1, #keySort do Zerotorescue@0: local k = keySort[i] Zerotorescue@0: local v = opts[k] Zerotorescue@0: if v.type == "group" then Zerotorescue@0: path[#path+1] = k Zerotorescue@0: local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) Zerotorescue@0: local hidden = CheckOptionHidden(v, options, path, appName) Zerotorescue@0: if not inline and not hidden then Zerotorescue@0: local entry = new() Zerotorescue@0: entry.value = k Zerotorescue@0: entry.text = GetOptionsMemberValue("name", v, options, path, appName) Zerotorescue@0: entry.icon = GetOptionsMemberValue("icon", v, options, path, appName) Zerotorescue@0: entry.iconCoords = GetOptionsMemberValue("iconCoords", v, options, path, appName) Zerotorescue@0: entry.disabled = CheckOptionDisabled(v, options, path, appName) Zerotorescue@0: if not tree.children then tree.children = new() end Zerotorescue@0: tinsert(tree.children,entry) Zerotorescue@0: if (v.childGroups or "tree") == "tree" then Zerotorescue@0: BuildSubGroups(v,entry, options, path, appName) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: path[#path] = nil Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: del(keySort) Zerotorescue@0: del(opts) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function BuildGroups(group, options, path, appName, recurse) Zerotorescue@0: local tree = new() Zerotorescue@0: local keySort = new() Zerotorescue@0: local opts = new() Zerotorescue@0: Zerotorescue@0: BuildSortedOptionsTable(group, keySort, opts, options, path, appName) Zerotorescue@0: Zerotorescue@0: for i = 1, #keySort do Zerotorescue@0: local k = keySort[i] Zerotorescue@0: local v = opts[k] Zerotorescue@0: if v.type == "group" then Zerotorescue@0: path[#path+1] = k Zerotorescue@0: local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) Zerotorescue@0: local hidden = CheckOptionHidden(v, options, path, appName) Zerotorescue@0: if not inline and not hidden then Zerotorescue@0: local entry = new() Zerotorescue@0: entry.value = k Zerotorescue@0: entry.text = GetOptionsMemberValue("name", v, options, path, appName) Zerotorescue@0: entry.icon = GetOptionsMemberValue("icon", v, options, path, appName) Zerotorescue@0: entry.disabled = CheckOptionDisabled(v, options, path, appName) Zerotorescue@0: tinsert(tree,entry) Zerotorescue@0: if recurse and (v.childGroups or "tree") == "tree" then Zerotorescue@0: BuildSubGroups(v,entry, options, path, appName) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: path[#path] = nil Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: del(keySort) Zerotorescue@0: del(opts) Zerotorescue@0: return tree Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function InjectInfo(control, options, option, path, rootframe, appName) Zerotorescue@0: local user = control:GetUserDataTable() Zerotorescue@0: for i = 1, #path do Zerotorescue@0: user[i] = path[i] Zerotorescue@0: end Zerotorescue@0: user.rootframe = rootframe Zerotorescue@0: user.option = option Zerotorescue@0: user.options = options Zerotorescue@0: user.path = copy(path) Zerotorescue@0: user.appName = appName Zerotorescue@0: control:SetCallback("OnRelease", CleanUserData) Zerotorescue@0: control:SetCallback("OnLeave", OptionOnMouseLeave) Zerotorescue@0: control:SetCallback("OnEnter", OptionOnMouseOver) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: --[[ Zerotorescue@0: options - root of the options table being fed Zerotorescue@0: container - widget that controls will be placed in Zerotorescue@0: rootframe - Frame object the options are in Zerotorescue@0: path - table with the keys to get to the group being fed Zerotorescue@0: --]] Zerotorescue@0: Zerotorescue@0: local function FeedOptions(appName, options,container,rootframe,path,group,inline) Zerotorescue@0: local keySort = new() Zerotorescue@0: local opts = new() Zerotorescue@0: Zerotorescue@0: BuildSortedOptionsTable(group, keySort, opts, options, path, appName) Zerotorescue@0: Zerotorescue@0: for i = 1, #keySort do Zerotorescue@0: local k = keySort[i] Zerotorescue@0: local v = opts[k] Zerotorescue@0: tinsert(path, k) Zerotorescue@0: local hidden = CheckOptionHidden(v, options, path, appName) Zerotorescue@0: local name = GetOptionsMemberValue("name", v, options, path, appName) Zerotorescue@0: if not hidden then Zerotorescue@0: if v.type == "group" then Zerotorescue@0: if inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then Zerotorescue@0: --Inline group Zerotorescue@0: local GroupContainer Zerotorescue@0: if name and name ~= "" then Zerotorescue@0: GroupContainer = gui:Create("InlineGroup") Zerotorescue@0: GroupContainer:SetTitle(name or "") Zerotorescue@0: else Zerotorescue@0: GroupContainer = gui:Create("SimpleGroup") Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: GroupContainer.width = "fill" Zerotorescue@0: GroupContainer:SetLayout("flow") Zerotorescue@0: container:AddChild(GroupContainer) Zerotorescue@0: FeedOptions(appName,options,GroupContainer,rootframe,path,v,true) Zerotorescue@0: end Zerotorescue@0: else Zerotorescue@0: --Control to feed Zerotorescue@0: local control Zerotorescue@0: Zerotorescue@0: local name = GetOptionsMemberValue("name", v, options, path, appName) Zerotorescue@0: Zerotorescue@0: if v.type == "execute" then Zerotorescue@0: Zerotorescue@0: local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName) Zerotorescue@0: local image, width, height = GetOptionsMemberValue("image",v, options, path, appName) Zerotorescue@0: Zerotorescue@0: if type(image) == "string" then Zerotorescue@0: control = gui:Create("Icon") Zerotorescue@0: if not width then Zerotorescue@0: width = GetOptionsMemberValue("imageWidth",v, options, path, appName) Zerotorescue@0: end Zerotorescue@0: if not height then Zerotorescue@0: height = GetOptionsMemberValue("imageHeight",v, options, path, appName) Zerotorescue@0: end Zerotorescue@0: if type(imageCoords) == "table" then Zerotorescue@0: control:SetImage(image, unpack(imageCoords)) Zerotorescue@0: else Zerotorescue@0: control:SetImage(image) Zerotorescue@0: end Zerotorescue@0: if type(width) ~= "number" then Zerotorescue@0: width = 32 Zerotorescue@0: end Zerotorescue@0: if type(height) ~= "number" then Zerotorescue@0: height = 32 Zerotorescue@0: end Zerotorescue@0: control:SetImageSize(width, height) Zerotorescue@0: control:SetLabel(name) Zerotorescue@0: else Zerotorescue@0: control = gui:Create("Button") Zerotorescue@0: control:SetText(name) Zerotorescue@0: end Zerotorescue@0: control:SetCallback("OnClick",ActivateControl) Zerotorescue@0: Zerotorescue@0: elseif v.type == "input" then Zerotorescue@0: local controlType = v.dialogControl or v.control or (v.multiline and "MultiLineEditBox") or "EditBox" Zerotorescue@0: control = gui:Create(controlType) Zerotorescue@0: if not control then Zerotorescue@0: geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) Zerotorescue@0: control = gui:Create(v.multiline and "MultiLineEditBox" or "EditBox") Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if v.multiline and control.SetNumLines then Zerotorescue@0: control:SetNumLines(tonumber(v.multiline) or 4) Zerotorescue@0: end Zerotorescue@0: control:SetLabel(name) Zerotorescue@0: control:SetCallback("OnEnterPressed",ActivateControl) Zerotorescue@0: local text = GetOptionsMemberValue("get",v, options, path, appName) Zerotorescue@0: if type(text) ~= "string" then Zerotorescue@0: text = "" Zerotorescue@0: end Zerotorescue@0: control:SetText(text) Zerotorescue@0: Zerotorescue@0: elseif v.type == "toggle" then Zerotorescue@0: control = gui:Create("CheckBox") Zerotorescue@0: control:SetLabel(name) Zerotorescue@0: control:SetTriState(v.tristate) Zerotorescue@0: local value = GetOptionsMemberValue("get",v, options, path, appName) Zerotorescue@0: control:SetValue(value) Zerotorescue@0: control:SetCallback("OnValueChanged",ActivateControl) Zerotorescue@0: Zerotorescue@0: if v.descStyle == "inline" then Zerotorescue@0: local desc = GetOptionsMemberValue("desc", v, options, path, appName) Zerotorescue@0: control:SetDescription(desc) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local image = GetOptionsMemberValue("image", v, options, path, appName) Zerotorescue@0: local imageCoords = GetOptionsMemberValue("imageCoords", v, options, path, appName) Zerotorescue@0: Zerotorescue@0: if type(image) == "string" then Zerotorescue@0: if type(imageCoords) == "table" then Zerotorescue@0: control:SetImage(image, unpack(imageCoords)) Zerotorescue@0: else Zerotorescue@0: control:SetImage(image) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: elseif v.type == "range" then Zerotorescue@0: control = gui:Create("Slider") Zerotorescue@0: control:SetLabel(name) Zerotorescue@0: control:SetSliderValues(v.softMin or v.min or 0, v.softMax or v.max or 100, v.bigStep or v.step or 0) Zerotorescue@0: control:SetIsPercent(v.isPercent) Zerotorescue@0: local value = GetOptionsMemberValue("get",v, options, path, appName) Zerotorescue@0: if type(value) ~= "number" then Zerotorescue@0: value = 0 Zerotorescue@0: end Zerotorescue@0: control:SetValue(value) Zerotorescue@0: control:SetCallback("OnValueChanged",ActivateSlider) Zerotorescue@0: control:SetCallback("OnMouseUp",ActivateSlider) Zerotorescue@0: Zerotorescue@0: elseif v.type == "select" then Zerotorescue@0: local values = GetOptionsMemberValue("values", v, options, path, appName) Zerotorescue@0: local controlType = v.dialogControl or v.control or "Dropdown" Zerotorescue@0: control = gui:Create(controlType) Zerotorescue@0: if not control then Zerotorescue@0: geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) Zerotorescue@0: control = gui:Create("Dropdown") Zerotorescue@0: end Zerotorescue@0: control:SetLabel(name) Zerotorescue@0: control:SetList(values) Zerotorescue@0: local value = GetOptionsMemberValue("get",v, options, path, appName) Zerotorescue@0: if not values[value] then Zerotorescue@0: value = nil Zerotorescue@0: end Zerotorescue@0: control:SetValue(value) Zerotorescue@0: control:SetCallback("OnValueChanged",ActivateControl) Zerotorescue@0: Zerotorescue@0: elseif v.type == "multiselect" then Zerotorescue@0: local values = GetOptionsMemberValue("values", v, options, path, appName) Zerotorescue@0: local disabled = CheckOptionDisabled(v, options, path, appName) Zerotorescue@0: Zerotorescue@0: local controlType = v.dialogControl or v.control Zerotorescue@0: Zerotorescue@0: local valuesort = new() Zerotorescue@0: if values then Zerotorescue@0: for value, text in pairs(values) do Zerotorescue@0: tinsert(valuesort, value) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: tsort(valuesort) Zerotorescue@0: Zerotorescue@0: if controlType then Zerotorescue@0: control = gui:Create(controlType) Zerotorescue@0: if not control then Zerotorescue@0: geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: if control then Zerotorescue@0: control:SetMultiselect(true) Zerotorescue@0: control:SetLabel(name) Zerotorescue@0: control:SetList(values) Zerotorescue@0: control:SetDisabled(disabled) Zerotorescue@0: control:SetCallback("OnValueChanged",ActivateControl) Zerotorescue@0: control:SetCallback("OnClosed", MultiControlOnClosed) Zerotorescue@0: local width = GetOptionsMemberValue("width",v,options,path,appName) Zerotorescue@0: if width == "double" then Zerotorescue@0: control:SetWidth(width_multiplier * 2) Zerotorescue@0: elseif width == "half" then Zerotorescue@0: control:SetWidth(width_multiplier / 2) Zerotorescue@0: elseif width == "full" then Zerotorescue@0: control.width = "fill" Zerotorescue@0: else Zerotorescue@0: control:SetWidth(width_multiplier) Zerotorescue@0: end Zerotorescue@0: --check:SetTriState(v.tristate) Zerotorescue@0: for i = 1, #valuesort do Zerotorescue@0: local key = valuesort[i] Zerotorescue@0: local value = GetOptionsMemberValue("get",v, options, path, appName, key) Zerotorescue@0: control:SetItemValue(key,value) Zerotorescue@0: end Zerotorescue@0: else Zerotorescue@0: control = gui:Create("InlineGroup") Zerotorescue@0: control:SetLayout("Flow") Zerotorescue@0: control:SetTitle(name) Zerotorescue@0: control.width = "fill" Zerotorescue@0: Zerotorescue@0: control:PauseLayout() Zerotorescue@0: local width = GetOptionsMemberValue("width",v,options,path,appName) Zerotorescue@0: for i = 1, #valuesort do Zerotorescue@0: local value = valuesort[i] Zerotorescue@0: local text = values[value] Zerotorescue@0: local check = gui:Create("CheckBox") Zerotorescue@0: check:SetLabel(text) Zerotorescue@0: check:SetUserData("value", value) Zerotorescue@0: check:SetUserData("text", text) Zerotorescue@0: check:SetDisabled(disabled) Zerotorescue@0: check:SetTriState(v.tristate) Zerotorescue@0: check:SetValue(GetOptionsMemberValue("get",v, options, path, appName, value)) Zerotorescue@0: check:SetCallback("OnValueChanged",ActivateMultiControl) Zerotorescue@0: InjectInfo(check, options, v, path, rootframe, appName) Zerotorescue@0: control:AddChild(check) Zerotorescue@0: if width == "double" then Zerotorescue@0: check:SetWidth(width_multiplier * 2) Zerotorescue@0: elseif width == "half" then Zerotorescue@0: check:SetWidth(width_multiplier / 2) Zerotorescue@0: elseif width == "full" then Zerotorescue@0: check.width = "fill" Zerotorescue@0: else Zerotorescue@0: check:SetWidth(width_multiplier) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: control:ResumeLayout() Zerotorescue@0: control:DoLayout() Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: del(valuesort) Zerotorescue@0: Zerotorescue@0: elseif v.type == "color" then Zerotorescue@0: control = gui:Create("ColorPicker") Zerotorescue@0: control:SetLabel(name) Zerotorescue@0: control:SetHasAlpha(v.hasAlpha) Zerotorescue@0: control:SetColor(GetOptionsMemberValue("get",v, options, path, appName)) Zerotorescue@0: control:SetCallback("OnValueChanged",ActivateControl) Zerotorescue@0: control:SetCallback("OnValueConfirmed",ActivateControl) Zerotorescue@0: Zerotorescue@0: elseif v.type == "keybinding" then Zerotorescue@0: control = gui:Create("Keybinding") Zerotorescue@0: control:SetLabel(name) Zerotorescue@0: control:SetKey(GetOptionsMemberValue("get",v, options, path, appName)) Zerotorescue@0: control:SetCallback("OnKeyChanged",ActivateControl) Zerotorescue@0: Zerotorescue@0: elseif v.type == "header" then Zerotorescue@0: control = gui:Create("Heading") Zerotorescue@0: control:SetText(name) Zerotorescue@0: control.width = "fill" Zerotorescue@0: Zerotorescue@0: elseif v.type == "description" then Zerotorescue@0: control = gui:Create("Label") Zerotorescue@0: control:SetText(name) Zerotorescue@0: Zerotorescue@0: local fontSize = GetOptionsMemberValue("fontSize",v, options, path, appName) Zerotorescue@0: if fontSize == "medium" then Zerotorescue@0: control:SetFontObject(GameFontHighlight) Zerotorescue@0: elseif fontSize == "large" then Zerotorescue@0: control:SetFontObject(GameFontHighlightLarge) Zerotorescue@0: else -- small or invalid Zerotorescue@0: control:SetFontObject(GameFontHighlightSmall) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName) Zerotorescue@0: local image, width, height = GetOptionsMemberValue("image",v, options, path, appName) Zerotorescue@0: Zerotorescue@0: if type(image) == "string" then Zerotorescue@0: if not width then Zerotorescue@0: width = GetOptionsMemberValue("imageWidth",v, options, path, appName) Zerotorescue@0: end Zerotorescue@0: if not height then Zerotorescue@0: height = GetOptionsMemberValue("imageHeight",v, options, path, appName) Zerotorescue@0: end Zerotorescue@0: if type(imageCoords) == "table" then Zerotorescue@0: control:SetImage(image, unpack(imageCoords)) Zerotorescue@0: else Zerotorescue@0: control:SetImage(image) Zerotorescue@0: end Zerotorescue@0: if type(width) ~= "number" then Zerotorescue@0: width = 32 Zerotorescue@0: end Zerotorescue@0: if type(height) ~= "number" then Zerotorescue@0: height = 32 Zerotorescue@0: end Zerotorescue@0: control:SetImageSize(width, height) Zerotorescue@0: end Zerotorescue@0: local width = GetOptionsMemberValue("width",v,options,path,appName) Zerotorescue@0: control.width = not width and "fill" Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --Common Init Zerotorescue@0: if control then Zerotorescue@0: if control.width ~= "fill" then Zerotorescue@0: local width = GetOptionsMemberValue("width",v,options,path,appName) Zerotorescue@0: if width == "double" then Zerotorescue@0: control:SetWidth(width_multiplier * 2) Zerotorescue@0: elseif width == "half" then Zerotorescue@0: control:SetWidth(width_multiplier / 2) Zerotorescue@0: elseif width == "full" then Zerotorescue@0: control.width = "fill" Zerotorescue@0: else Zerotorescue@0: control:SetWidth(width_multiplier) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: if control.SetDisabled then Zerotorescue@0: local disabled = CheckOptionDisabled(v, options, path, appName) Zerotorescue@0: control:SetDisabled(disabled) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: InjectInfo(control, options, v, path, rootframe, appName) Zerotorescue@0: container:AddChild(control) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: tremove(path) Zerotorescue@0: end Zerotorescue@0: container:ResumeLayout() Zerotorescue@0: container:DoLayout() Zerotorescue@0: del(keySort) Zerotorescue@0: del(opts) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function BuildPath(path, ...) Zerotorescue@0: for i = 1, select("#",...) do Zerotorescue@0: tinsert(path, (select(i,...))) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: local function TreeOnButtonEnter(widget, event, uniquevalue, button) Zerotorescue@0: local user = widget:GetUserDataTable() Zerotorescue@0: if not user then return end Zerotorescue@0: local options = user.options Zerotorescue@0: local option = user.option Zerotorescue@0: local path = user.path Zerotorescue@0: local appName = user.appName Zerotorescue@0: Zerotorescue@0: local feedpath = new() Zerotorescue@0: for i = 1, #path do Zerotorescue@0: feedpath[i] = path[i] Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: BuildPath(feedpath, ("\001"):split(uniquevalue)) Zerotorescue@0: local group = options Zerotorescue@0: for i = 1, #feedpath do Zerotorescue@0: if not group then return end Zerotorescue@0: group = GetSubOption(group, feedpath[i]) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local name = GetOptionsMemberValue("name", group, options, feedpath, appName) Zerotorescue@0: local desc = GetOptionsMemberValue("desc", group, options, feedpath, appName) Zerotorescue@0: Zerotorescue@0: GameTooltip:SetOwner(button, "ANCHOR_NONE") Zerotorescue@0: if widget.type == "TabGroup" then Zerotorescue@0: GameTooltip:SetPoint("BOTTOM",button,"TOP") Zerotorescue@0: else Zerotorescue@0: GameTooltip:SetPoint("LEFT",button,"RIGHT") Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: GameTooltip:SetText(name, 1, .82, 0, 1) Zerotorescue@0: Zerotorescue@0: if type(desc) == "string" then Zerotorescue@0: GameTooltip:AddLine(desc, 1, 1, 1, 1) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: GameTooltip:Show() Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function TreeOnButtonLeave(widget, event, value, button) Zerotorescue@0: GameTooltip:Hide() Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: local function GroupExists(appName, options, path, uniquevalue) Zerotorescue@0: if not uniquevalue then return false end Zerotorescue@0: Zerotorescue@0: local feedpath = new() Zerotorescue@0: local temppath = new() Zerotorescue@0: for i = 1, #path do Zerotorescue@0: feedpath[i] = path[i] Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: BuildPath(feedpath, ("\001"):split(uniquevalue)) Zerotorescue@0: Zerotorescue@0: local group = options Zerotorescue@0: for i = 1, #feedpath do Zerotorescue@0: local v = feedpath[i] Zerotorescue@0: temppath[i] = v Zerotorescue@0: group = GetSubOption(group, v) Zerotorescue@0: Zerotorescue@0: if not group or group.type ~= "group" or CheckOptionHidden(group, options, temppath, appName) then Zerotorescue@0: del(feedpath) Zerotorescue@0: del(temppath) Zerotorescue@0: return false Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: del(feedpath) Zerotorescue@0: del(temppath) Zerotorescue@0: return true Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function GroupSelected(widget, event, uniquevalue) Zerotorescue@0: Zerotorescue@0: local user = widget:GetUserDataTable() Zerotorescue@0: Zerotorescue@0: local options = user.options Zerotorescue@0: local option = user.option Zerotorescue@0: local path = user.path Zerotorescue@0: local rootframe = user.rootframe Zerotorescue@0: Zerotorescue@0: local feedpath = new() Zerotorescue@0: for i = 1, #path do Zerotorescue@0: feedpath[i] = path[i] Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: BuildPath(feedpath, ("\001"):split(uniquevalue)) Zerotorescue@0: local group = options Zerotorescue@0: for i = 1, #feedpath do Zerotorescue@0: group = GetSubOption(group, feedpath[i]) Zerotorescue@0: end Zerotorescue@0: widget:ReleaseChildren() Zerotorescue@0: AceConfigDialog:FeedGroup(user.appName,options,widget,rootframe,feedpath) Zerotorescue@0: Zerotorescue@0: del(feedpath) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: --[[ Zerotorescue@0: -- INTERNAL -- Zerotorescue@0: This function will feed one group, and any inline child groups into the given container Zerotorescue@0: Select Groups will only have the selection control (tree, tabs, dropdown) fed in Zerotorescue@0: and have a group selected, this event will trigger the feeding of child groups Zerotorescue@0: Zerotorescue@0: Rules: Zerotorescue@0: If the group is Inline, FeedOptions Zerotorescue@0: If the group has no child groups, FeedOptions Zerotorescue@0: Zerotorescue@0: If the group is a tab or select group, FeedOptions then add the Group Control Zerotorescue@0: If the group is a tree group FeedOptions then Zerotorescue@0: its parent isnt a tree group: then add the tree control containing this and all child tree groups Zerotorescue@0: if its parent is a tree group, its already a node on a tree Zerotorescue@0: --]] Zerotorescue@0: Zerotorescue@0: function AceConfigDialog:FeedGroup(appName,options,container,rootframe,path, isRoot) Zerotorescue@0: local group = options Zerotorescue@0: --follow the path to get to the curent group Zerotorescue@0: local inline Zerotorescue@0: local grouptype, parenttype = options.childGroups, "none" Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: for i = 1, #path do Zerotorescue@0: local v = path[i] Zerotorescue@0: group = GetSubOption(group, v) Zerotorescue@0: inline = inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) Zerotorescue@0: parenttype = grouptype Zerotorescue@0: grouptype = group.childGroups Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if not parenttype then Zerotorescue@0: parenttype = "tree" Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --check if the group has child groups Zerotorescue@0: local hasChildGroups Zerotorescue@0: for k, v in pairs(group.args) do Zerotorescue@0: if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) and not CheckOptionHidden(v, options, path, appName) then Zerotorescue@0: hasChildGroups = true Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: if group.plugins then Zerotorescue@0: for plugin, t in pairs(group.plugins) do Zerotorescue@0: for k, v in pairs(t) do Zerotorescue@0: if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) and not CheckOptionHidden(v, options, path, appName) then Zerotorescue@0: hasChildGroups = true Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: container:SetLayout("flow") Zerotorescue@0: local scroll Zerotorescue@0: Zerotorescue@0: --Add a scrollframe if we are not going to add a group control, this is the inverse of the conditions for that later on Zerotorescue@0: if (not (hasChildGroups and not inline)) or (grouptype ~= "tab" and grouptype ~= "select" and (parenttype == "tree" and not isRoot)) then Zerotorescue@0: if container.type ~= "InlineGroup" and container.type ~= "SimpleGroup" then Zerotorescue@0: scroll = gui:Create("ScrollFrame") Zerotorescue@0: scroll:SetLayout("flow") Zerotorescue@0: scroll.width = "fill" Zerotorescue@0: scroll.height = "fill" Zerotorescue@0: container:SetLayout("fill") Zerotorescue@0: container:AddChild(scroll) Zerotorescue@0: container = scroll Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: FeedOptions(appName,options,container,rootframe,path,group,nil) Zerotorescue@0: Zerotorescue@0: if scroll then Zerotorescue@0: container:PerformLayout() Zerotorescue@0: local status = self:GetStatusTable(appName, path) Zerotorescue@0: if not status.scroll then Zerotorescue@0: status.scroll = {} Zerotorescue@0: end Zerotorescue@0: scroll:SetStatusTable(status.scroll) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if hasChildGroups and not inline then Zerotorescue@0: local name = GetOptionsMemberValue("name", group, options, path, appName) Zerotorescue@0: if grouptype == "tab" then Zerotorescue@0: Zerotorescue@0: local tab = gui:Create("TabGroup") Zerotorescue@0: InjectInfo(tab, options, group, path, rootframe, appName) Zerotorescue@0: tab:SetCallback("OnGroupSelected", GroupSelected) Zerotorescue@0: tab:SetCallback("OnTabEnter", TreeOnButtonEnter) Zerotorescue@0: tab:SetCallback("OnTabLeave", TreeOnButtonLeave) Zerotorescue@0: Zerotorescue@0: local status = AceConfigDialog:GetStatusTable(appName, path) Zerotorescue@0: if not status.groups then Zerotorescue@0: status.groups = {} Zerotorescue@0: end Zerotorescue@0: tab:SetStatusTable(status.groups) Zerotorescue@0: tab.width = "fill" Zerotorescue@0: tab.height = "fill" Zerotorescue@0: Zerotorescue@0: local tabs = BuildGroups(group, options, path, appName) Zerotorescue@0: tab:SetTabs(tabs) Zerotorescue@0: tab:SetUserData("tablist", tabs) Zerotorescue@0: Zerotorescue@0: for i = 1, #tabs do Zerotorescue@0: local entry = tabs[i] Zerotorescue@0: if not entry.disabled then Zerotorescue@0: tab:SelectTab((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value) Zerotorescue@0: break Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: container:AddChild(tab) Zerotorescue@0: Zerotorescue@0: elseif grouptype == "select" then Zerotorescue@0: Zerotorescue@0: local select = gui:Create("DropdownGroup") Zerotorescue@0: select:SetTitle(name) Zerotorescue@0: InjectInfo(select, options, group, path, rootframe, appName) Zerotorescue@0: select:SetCallback("OnGroupSelected", GroupSelected) Zerotorescue@0: local status = AceConfigDialog:GetStatusTable(appName, path) Zerotorescue@0: if not status.groups then Zerotorescue@0: status.groups = {} Zerotorescue@0: end Zerotorescue@0: select:SetStatusTable(status.groups) Zerotorescue@0: local grouplist = BuildSelect(group, options, path, appName) Zerotorescue@0: select:SetGroupList(grouplist) Zerotorescue@0: select:SetUserData("grouplist", grouplist) Zerotorescue@0: local firstgroup Zerotorescue@0: for k, v in pairs(grouplist) do Zerotorescue@0: if not firstgroup or k < firstgroup then Zerotorescue@0: firstgroup = k Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if firstgroup then Zerotorescue@0: select:SetGroup((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or firstgroup) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: select.width = "fill" Zerotorescue@0: select.height = "fill" Zerotorescue@0: Zerotorescue@0: container:AddChild(select) Zerotorescue@0: Zerotorescue@0: --assume tree group by default Zerotorescue@0: --if parenttype is tree then this group is already a node on that tree Zerotorescue@0: elseif (parenttype ~= "tree") or isRoot then Zerotorescue@0: local tree = gui:Create("TreeGroup") Zerotorescue@0: InjectInfo(tree, options, group, path, rootframe, appName) Zerotorescue@0: tree:EnableButtonTooltips(false) Zerotorescue@0: Zerotorescue@0: tree.width = "fill" Zerotorescue@0: tree.height = "fill" Zerotorescue@0: Zerotorescue@0: tree:SetCallback("OnGroupSelected", GroupSelected) Zerotorescue@0: tree:SetCallback("OnButtonEnter", TreeOnButtonEnter) Zerotorescue@0: tree:SetCallback("OnButtonLeave", TreeOnButtonLeave) Zerotorescue@0: Zerotorescue@0: local status = AceConfigDialog:GetStatusTable(appName, path) Zerotorescue@0: if not status.groups then Zerotorescue@0: status.groups = {} Zerotorescue@0: end Zerotorescue@0: local treedefinition = BuildGroups(group, options, path, appName, true) Zerotorescue@0: tree:SetStatusTable(status.groups) Zerotorescue@0: Zerotorescue@0: tree:SetTree(treedefinition) Zerotorescue@0: tree:SetUserData("tree",treedefinition) Zerotorescue@0: Zerotorescue@0: for i = 1, #treedefinition do Zerotorescue@0: local entry = treedefinition[i] Zerotorescue@0: if not entry.disabled then Zerotorescue@0: tree:SelectByValue((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value) Zerotorescue@0: break Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: container:AddChild(tree) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local old_CloseSpecialWindows Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: local function RefreshOnUpdate(this) Zerotorescue@0: for appName in pairs(this.closing) do Zerotorescue@0: if AceConfigDialog.OpenFrames[appName] then Zerotorescue@0: AceConfigDialog.OpenFrames[appName]:Hide() Zerotorescue@0: end Zerotorescue@0: if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then Zerotorescue@0: for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do Zerotorescue@0: if not widget:IsVisible() then Zerotorescue@0: widget:ReleaseChildren() Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: this.closing[appName] = nil Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if this.closeAll then Zerotorescue@0: for k, v in pairs(AceConfigDialog.OpenFrames) do Zerotorescue@0: if not this.closeAllOverride[k] then Zerotorescue@0: v:Hide() Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: this.closeAll = nil Zerotorescue@0: wipe(this.closeAllOverride) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: for appName in pairs(this.apps) do Zerotorescue@0: if AceConfigDialog.OpenFrames[appName] then Zerotorescue@0: local user = AceConfigDialog.OpenFrames[appName]:GetUserDataTable() Zerotorescue@0: AceConfigDialog:Open(appName, unpack(user.basepath or emptyTbl)) Zerotorescue@0: end Zerotorescue@0: if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then Zerotorescue@0: for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do Zerotorescue@0: local user = widget:GetUserDataTable() Zerotorescue@0: if widget:IsVisible() then Zerotorescue@0: AceConfigDialog:Open(widget:GetUserData("appName"), widget, unpack(user.basepath or emptyTbl)) Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: this.apps[appName] = nil Zerotorescue@0: end Zerotorescue@0: this:SetScript("OnUpdate", nil) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- Upgrade the OnUpdate script as well, if needed. Zerotorescue@0: if AceConfigDialog.frame:GetScript("OnUpdate") then Zerotorescue@0: AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --- Close all open options windows Zerotorescue@0: function AceConfigDialog:CloseAll() Zerotorescue@0: AceConfigDialog.frame.closeAll = true Zerotorescue@0: AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) Zerotorescue@0: if next(self.OpenFrames) then Zerotorescue@0: return true Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --- Close a specific options window. Zerotorescue@0: -- @param appName The application name as given to `:RegisterOptionsTable()` Zerotorescue@0: function AceConfigDialog:Close(appName) Zerotorescue@0: if self.OpenFrames[appName] then Zerotorescue@0: AceConfigDialog.frame.closing[appName] = true Zerotorescue@0: AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) Zerotorescue@0: return true Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- Internal -- Called by AceConfigRegistry Zerotorescue@0: function AceConfigDialog:ConfigTableChanged(event, appName) Zerotorescue@0: AceConfigDialog.frame.apps[appName] = true Zerotorescue@0: AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: reg.RegisterCallback(AceConfigDialog, "ConfigTableChange", "ConfigTableChanged") Zerotorescue@0: Zerotorescue@0: --- Sets the default size of the options window for a specific application. Zerotorescue@0: -- @param appName The application name as given to `:RegisterOptionsTable()` Zerotorescue@0: -- @param width The default width Zerotorescue@0: -- @param height The default height Zerotorescue@0: function AceConfigDialog:SetDefaultSize(appName, width, height) Zerotorescue@0: local status = AceConfigDialog:GetStatusTable(appName) Zerotorescue@0: if type(width) == "number" and type(height) == "number" then Zerotorescue@0: status.width = width Zerotorescue@0: status.height = height Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --- Open an option window at the specified path (if any). Zerotorescue@0: -- This function can optionally feed the group into a pre-created container Zerotorescue@0: -- instead of creating a new container frame. Zerotorescue@0: -- @paramsig appName [, container][, ...] Zerotorescue@0: -- @param appName The application name as given to `:RegisterOptionsTable()` Zerotorescue@0: -- @param container An optional container frame to feed the options into Zerotorescue@0: -- @param ... The path to open after creating the options window (see `:SelectGroup` for details) Zerotorescue@0: function AceConfigDialog:Open(appName, container, ...) Zerotorescue@0: if not old_CloseSpecialWindows then Zerotorescue@0: old_CloseSpecialWindows = CloseSpecialWindows Zerotorescue@0: CloseSpecialWindows = function() Zerotorescue@0: local found = old_CloseSpecialWindows() Zerotorescue@0: return self:CloseAll() or found Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: local app = reg:GetOptionsTable(appName) Zerotorescue@0: if not app then Zerotorescue@0: error(("%s isn't registed with AceConfigRegistry, unable to open config"):format(appName), 2) Zerotorescue@0: end Zerotorescue@0: local options = app("dialog", MAJOR) Zerotorescue@0: Zerotorescue@0: local f Zerotorescue@0: Zerotorescue@0: local path = new() Zerotorescue@0: local name = GetOptionsMemberValue("name", options, options, path, appName) Zerotorescue@0: Zerotorescue@0: --If an optional path is specified add it to the path table before feeding the options Zerotorescue@0: --as container is optional as well it may contain the first element of the path Zerotorescue@0: if type(container) == "string" then Zerotorescue@0: tinsert(path, container) Zerotorescue@0: container = nil Zerotorescue@0: end Zerotorescue@0: for n = 1, select("#",...) do Zerotorescue@0: tinsert(path, (select(n, ...))) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --if a container is given feed into that Zerotorescue@0: if container then Zerotorescue@0: f = container Zerotorescue@0: f:ReleaseChildren() Zerotorescue@0: f:SetUserData("appName", appName) Zerotorescue@0: f:SetUserData("iscustom", true) Zerotorescue@0: if #path > 0 then Zerotorescue@0: f:SetUserData("basepath", copy(path)) Zerotorescue@0: end Zerotorescue@0: local status = AceConfigDialog:GetStatusTable(appName) Zerotorescue@0: if not status.width then Zerotorescue@0: status.width = 700 Zerotorescue@0: end Zerotorescue@0: if not status.height then Zerotorescue@0: status.height = 500 Zerotorescue@0: end Zerotorescue@0: if f.SetStatusTable then Zerotorescue@0: f:SetStatusTable(status) Zerotorescue@0: end Zerotorescue@0: if f.SetTitle then Zerotorescue@0: f:SetTitle(name or "") Zerotorescue@0: end Zerotorescue@0: else Zerotorescue@0: if not self.OpenFrames[appName] then Zerotorescue@0: f = gui:Create("Frame") Zerotorescue@0: self.OpenFrames[appName] = f Zerotorescue@0: else Zerotorescue@0: f = self.OpenFrames[appName] Zerotorescue@0: end Zerotorescue@0: f:ReleaseChildren() Zerotorescue@0: f:SetCallback("OnClose", FrameOnClose) Zerotorescue@0: f:SetUserData("appName", appName) Zerotorescue@0: if #path > 0 then Zerotorescue@0: f:SetUserData("basepath", copy(path)) Zerotorescue@0: end Zerotorescue@0: f:SetTitle(name or "") Zerotorescue@0: local status = AceConfigDialog:GetStatusTable(appName) Zerotorescue@0: f:SetStatusTable(status) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: self:FeedGroup(appName,options,f,f,path,true) Zerotorescue@0: if f.Show then Zerotorescue@0: f:Show() Zerotorescue@0: end Zerotorescue@0: del(path) Zerotorescue@0: Zerotorescue@0: if AceConfigDialog.frame.closeAll then Zerotorescue@0: -- close all is set, but thats not good, since we're just opening here, so force it Zerotorescue@0: AceConfigDialog.frame.closeAllOverride[appName] = true Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- convert pre-39 BlizOptions structure to the new format Zerotorescue@0: if oldminor and oldminor < 39 and AceConfigDialog.BlizOptions then Zerotorescue@0: local old = AceConfigDialog.BlizOptions Zerotorescue@0: local new = {} Zerotorescue@0: for key, widget in pairs(old) do Zerotorescue@0: local appName = widget:GetUserData("appName") Zerotorescue@0: if not new[appName] then new[appName] = {} end Zerotorescue@0: new[appName][key] = widget Zerotorescue@0: end Zerotorescue@0: AceConfigDialog.BlizOptions = new Zerotorescue@0: else Zerotorescue@0: AceConfigDialog.BlizOptions = AceConfigDialog.BlizOptions or {} Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function FeedToBlizPanel(widget, event) Zerotorescue@0: local path = widget:GetUserData("path") Zerotorescue@0: AceConfigDialog:Open(widget:GetUserData("appName"), widget, unpack(path or emptyTbl)) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function ClearBlizPanel(widget, event) Zerotorescue@0: local appName = widget:GetUserData("appName") Zerotorescue@0: AceConfigDialog.frame.closing[appName] = true Zerotorescue@0: AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: --- Add an option table into the Blizzard Interface Options panel. Zerotorescue@0: -- You can optionally supply a descriptive name to use and a parent frame to use, Zerotorescue@0: -- as well as a path in the options table.\\ Zerotorescue@0: -- If no name is specified, the appName will be used instead. Zerotorescue@0: -- Zerotorescue@0: -- If you specify a proper `parent` (by name), the interface options will generate a Zerotorescue@0: -- tree layout. Note that only one level of children is supported, so the parent always Zerotorescue@0: -- has to be a head-level note. Zerotorescue@0: -- Zerotorescue@0: -- This function returns a reference to the container frame registered with the Interface Zerotorescue@0: -- Options. You can use this reference to open the options with the API function Zerotorescue@0: -- `InterfaceOptionsFrame_OpenToCategory`. Zerotorescue@0: -- @param appName The application name as given to `:RegisterOptionsTable()` Zerotorescue@0: -- @param name A descriptive name to display in the options tree (defaults to appName) Zerotorescue@0: -- @param parent The parent to use in the interface options tree. Zerotorescue@0: -- @param ... The path in the options table to feed into the interface options panel. Zerotorescue@0: -- @return The reference to the frame registered into the Interface Options. Zerotorescue@0: function AceConfigDialog:AddToBlizOptions(appName, name, parent, ...) Zerotorescue@0: local BlizOptions = AceConfigDialog.BlizOptions Zerotorescue@0: Zerotorescue@0: local key = appName Zerotorescue@0: for n = 1, select("#", ...) do Zerotorescue@0: key = key.."\001"..select(n, ...) Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if not BlizOptions[appName] then Zerotorescue@0: BlizOptions[appName] = {} Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if not BlizOptions[appName][key] then Zerotorescue@0: local group = gui:Create("BlizOptionsGroup") Zerotorescue@0: BlizOptions[appName][key] = group Zerotorescue@0: group:SetName(name or appName, parent) Zerotorescue@0: Zerotorescue@0: group:SetTitle(name or appName) Zerotorescue@0: group:SetUserData("appName", appName) Zerotorescue@0: if select("#", ...) > 0 then Zerotorescue@0: local path = {} Zerotorescue@0: for n = 1, select("#",...) do Zerotorescue@0: tinsert(path, (select(n, ...))) Zerotorescue@0: end Zerotorescue@0: group:SetUserData("path", path) Zerotorescue@0: end Zerotorescue@0: group:SetCallback("OnShow", FeedToBlizPanel) Zerotorescue@0: group:SetCallback("OnHide", ClearBlizPanel) Zerotorescue@0: InterfaceOptions_AddCategory(group.frame) Zerotorescue@0: return group.frame Zerotorescue@0: else Zerotorescue@0: error(("%s has already been added to the Blizzard Options Window with the given path"):format(appName), 2) Zerotorescue@0: end Zerotorescue@0: end