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