/trunk/libs/AceConfig-3.0
-- as well as associate it with a slash command. |
-- @class file |
-- @name AceConfig-3.0 |
-- @release $Id: AceConfig-3.0.lua 710 2008-12-19 10:14:39Z nevcairiel $ |
-- @release $Id: AceConfig-3.0.lua 877 2009-11-02 15:56:50Z nevcairiel $ |
--[[ |
AceConfig-3.0 |
Very light wrapper library that combines all the AceConfig subcomponents into one more easily used whole. |
Also automatically adds "config", "enable" and "disable" commands to options table as appropriate. |
]] |
local MAJOR, MINOR = "AceConfig-3.0", 2 |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
local AceConfig = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
if not AceConfig then return end |
local cfgreg = LibStub("AceConfigRegistry-3.0") |
local cfgcmd = LibStub("AceConfigCmd-3.0") |
local cfgdlg = LibStub("AceConfigDialog-3.0") |
--TODO: local cfgdrp = LibStub("AceConfigDropdown-3.0") |
-- Lua APIs |
local pcall, error, type, pairs = pcall, error, type, pairs |
--------------------------------------------------------------------- |
-- ------------------------------------------------------------------- |
-- :RegisterOptionsTable(appName, options, slashcmd, persist) |
-- |
-- - appName - (string) application name |
-- - options - table or function ref, see AceConfigRegistry |
-- - slashcmd - slash command (string) or table with commands, or nil to NOT create a slash command |
function lib:RegisterOptionsTable(appName, options, slashcmd) |
--- Register a option table with the AceConfig registry. |
-- You can supply a slash command (or a table of slash commands) to register with AceConfigCmd directly. |
-- @paramsig appName, options [, slashcmd] |
-- @param appName The application name for the config table. |
-- @param options The option table (or a function to generate one on demand) |
-- @param slashcmd A slash command to register for the option table, or a table of slash commands. |
-- @usage |
-- local AceConfig = LibStub("AceConfig-3.0") |
-- AceConfig:RegisterOptionsTable("MyAddon", myOptions, {"/myslash", "/my"}) |
function AceConfig:RegisterOptionsTable(appName, options, slashcmd) |
local ok,msg = pcall(cfgreg.RegisterOptionsTable, self, appName, options) |
if not ok then error(msg, 2) end |
--- AceConfigDialog-3.0 generates AceGUI-3.0 based windows based on option tables. |
-- @class file |
-- @name AceConfigDialog-3.0 |
-- @release $Id: AceConfigDialog-3.0.lua 736 2009-02-14 11:13:43Z nevcairiel $ |
-- @release $Id: AceConfigDialog-3.0.lua 902 2009-12-12 14:56:14Z nevcairiel $ |
local LibStub = LibStub |
local MAJOR, MINOR = "AceConfigDialog-3.0", 26 |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
local MAJOR, MINOR = "AceConfigDialog-3.0", 43 |
local AceConfigDialog, oldminor = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
if not AceConfigDialog then return end |
lib.OpenFrames = lib.OpenFrames or {} |
lib.Status = lib.Status or {} |
lib.frame = lib.frame or CreateFrame("Frame") |
AceConfigDialog.OpenFrames = AceConfigDialog.OpenFrames or {} |
AceConfigDialog.Status = AceConfigDialog.Status or {} |
AceConfigDialog.frame = AceConfigDialog.frame or CreateFrame("Frame") |
lib.frame.apps = lib.frame.apps or {} |
lib.frame.closing = lib.frame.closing or {} |
AceConfigDialog.frame.apps = AceConfigDialog.frame.apps or {} |
AceConfigDialog.frame.closing = AceConfigDialog.frame.closing or {} |
local gui = LibStub("AceGUI-3.0") |
local reg = LibStub("AceConfigRegistry-3.0") |
local select = select |
local pairs = pairs |
local type = type |
local assert = assert |
local tinsert = tinsert |
local tremove = tremove |
local error = error |
local table = table |
local unpack = unpack |
local string = string |
local next = next |
local math = math |
local _ |
-- Lua APIs |
local tconcat, tinsert, tsort, tremove = table.concat, table.insert, table.sort, table.remove |
local strmatch, format = string.match, string.format |
local assert, loadstring, error = assert, loadstring, error |
local pairs, next, select, type, unpack = pairs, next, select, type, unpack |
local rawset, tostring = rawset, tostring |
local math_min, math_max, math_floor = math.min, math.max, math.floor |
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded |
-- List them here for Mikk's FindGlobals script |
-- GLOBALS: NORMAL_FONT_COLOR, GameTooltip, StaticPopupDialogs, ACCEPT, CANCEL, StaticPopup_Show |
-- GLOBALS: PlaySound, GameFontHighlight, GameFontHighlightSmall, GameFontHighlightLarge |
-- GLOBALS: CloseSpecialWindows, InterfaceOptions_AddCategory, geterrorhandler |
local emptyTbl = {} |
--[[ |
xpcall safecall implementation |
]] |
local ARGS = {} |
for i = 1, argCount do ARGS[i] = "arg"..i end |
code = code:gsub("ARGS", table.concat(ARGS, ", ")) |
code = code:gsub("ARGS", tconcat(ARGS, ", ")) |
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) |
end |
usage = true, |
width = true, |
image = true, |
fontSize = true, |
} |
--Is Never a function or method |
local allIsLiteral = { |
type = true, |
descStyle = true, |
imageWidth = true, |
imageHeight = true, |
} |
if handler and handler[member] then |
a,b,c,d = handler[member](handler, info, ...) |
else |
error(string.format("Method %s doesn't exist in handler for type %s", member, membername)) |
error(format("Method %s doesn't exist in handler for type %s", member, membername)) |
end |
end |
del(info) |
end |
end |
table.sort(keySort, compareOptions) |
tsort(keySort, compareOptions) |
del(tempOrders) |
del(tempNames) |
end |
end |
--[[ |
Gets a status table for the given appname and options path |
]] |
function lib:GetStatusTable(appName, path) |
-- - Gets a status table for the given appname and options path. |
-- @param appName The application name as given to `:RegisterOptionsTable()` |
-- @param path The path to the options (a table with all group keys) |
-- @return |
function AceConfigDialog:GetStatusTable(appName, path) |
local status = self.Status |
if not status[appName] then |
return status.status |
end |
--[[ |
Sets the given path to be selected |
]] |
function lib:SelectGroup(appName, ...) |
--- Selects the specified path in the options window. |
-- The path specified has to match the keys of the groups in the table. |
-- @param appName The application name as given to `:RegisterOptionsTable()` |
-- @param ... The path to the key that should be selected |
function AceConfigDialog:SelectGroup(appName, ...) |
local path = new() |
local name = GetOptionsMemberValue("name", opt, options, path, appName) |
local desc = GetOptionsMemberValue("desc", opt, options, path, appName) |
local usage = GetOptionsMemberValue("usage", opt, options, path, appName) |
local descStyle = opt.descStyle |
if descStyle and descStyle ~= "tooltip" then return end |
GameTooltip:SetText(name, 1, .82, 0, 1) |
if opt.type == 'multiselect' then |
if dialog and oldstrata then |
dialog:SetFrameStrata(oldstrata) |
end |
lib:Open(appName, rootframe, basepath and unpack(basepath)) |
AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl)) |
del(info) |
end |
t.OnCancel = function() |
if dialog and oldstrata then |
dialog:SetFrameStrata(oldstrata) |
end |
lib:Open(appName, rootframe, basepath and unpack(basepath)) |
AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl)) |
del(info) |
end |
for i = 1, select('#', ...) do |
success, validated = safecall(handler[validate], handler, info, ...) |
if not success then validated = false end |
else |
error(string.format("Method %s doesn't exist in handler for type execute", validate)) |
error(format("Method %s doesn't exist in handler for type execute", validate)) |
end |
elseif type(validate) == "function" then |
success, validated = safecall(validate, info, ...) |
--validate function returned a message to display |
if rootframe.SetStatusText then |
rootframe:SetStatusText(validated) |
else |
-- TODO: do something else. |
end |
PlaySound("igPlayerInviteDecline") |
del(info) |
rootframe:SetStatusText(name..": Invalid Value") |
end |
end |
else |
-- TODO: do something else |
end |
PlaySound("igPlayerInviteDecline") |
del(info) |
confirm = false |
end |
else |
error(string.format("Method %s doesn't exist in handler for type confirm", confirm)) |
error(format("Method %s doesn't exist in handler for type confirm", confirm)) |
end |
elseif type(confirm) == "function" then |
success, confirm = safecall(confirm, info, ...) |
if handler and handler[func] then |
confirmPopup(user.appName, rootframe, basepath, info, confirmText, handler[func], handler, info, ...) |
else |
error(string.format("Method %s doesn't exist in handler for type func", func)) |
error(format("Method %s doesn't exist in handler for type func", func)) |
end |
elseif type(func) == "function" then |
confirmPopup(user.appName, rootframe, basepath, info, confirmText, func, info, ...) |
if handler and handler[func] then |
safecall(handler[func],handler, info, ...) |
else |
error(string.format("Method %s doesn't exist in handler for type func", func)) |
error(format("Method %s doesn't exist in handler for type func", func)) |
end |
elseif type(func) == "function" then |
safecall(func,info, ...) |
local iscustom = user.rootframe:GetUserData('iscustom') |
local basepath = user.rootframe:GetUserData('basepath') |
local basepath = user.rootframe:GetUserData('basepath') or emptyTbl |
--full refresh of the frame, some controls dont cause this on all events |
if option.type == "color" then |
if event == "OnValueConfirmed" then |
if iscustom then |
lib:Open(user.appName, user.rootframe, basepath and unpack(basepath)) |
AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) |
else |
lib:Open(user.appName, basepath and unpack(basepath)) |
AceConfigDialog:Open(user.appName, unpack(basepath)) |
end |
end |
elseif option.type == "range" then |
if event == "OnMouseUp" then |
if iscustom then |
lib:Open(user.appName, user.rootframe, basepath and unpack(basepath)) |
AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) |
else |
lib:Open(user.appName, basepath and unpack(basepath)) |
AceConfigDialog:Open(user.appName, unpack(basepath)) |
end |
end |
--multiselects don't cause a refresh on 'OnValueChanged' only 'OnClosed' |
user.valuechanged = true |
else |
if iscustom then |
lib:Open(user.appName, user.rootframe, basepath and unpack(basepath)) |
AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) |
else |
lib:Open(user.appName, basepath and unpack(basepath)) |
AceConfigDialog:Open(user.appName, unpack(basepath)) |
end |
end |
local option = widget:GetUserData('option') |
local min, max, step = option.min or 0, option.max or 100, option.step |
if step then |
value = math.floor((value - min) / step + 0.5) * step + min |
value = math_floor((value - min) / step + 0.5) * step + min |
else |
value = math.max(math.min(value,max),min) |
value = math_max(math_min(value,max),min) |
end |
ActivateControl(widget,event,value) |
end |
ActivateControl(widget, event, widget:GetUserData('value'), ...) |
local user = widget:GetUserDataTable() |
local iscustom = user.rootframe:GetUserData('iscustom') |
local basepath = user.rootframe:GetUserData('basepath') |
local basepath = user.rootframe:GetUserData('basepath') or emptyTbl |
if iscustom then |
lib:Open(user.appName, user.rootframe, basepath and unpack(basepath)) |
AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) |
else |
lib:Open(user.appName, basepath and unpack(basepath)) |
AceConfigDialog:Open(user.appName, unpack(basepath)) |
end |
end |
local user = widget:GetUserDataTable() |
if user.valuechanged then |
local iscustom = user.rootframe:GetUserData('iscustom') |
local basepath = user.rootframe:GetUserData('basepath') |
local basepath = user.rootframe:GetUserData('basepath') or emptyTbl |
if iscustom then |
lib:Open(user.appName, user.rootframe, basepath and unpack(basepath)) |
AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) |
else |
lib:Open(user.appName, basepath and unpack(basepath)) |
AceConfigDialog:Open(user.appName, unpack(basepath)) |
end |
end |
end |
local function FrameOnClose(widget, event) |
local appName = widget:GetUserData('appName') |
lib.OpenFrames[appName] = nil |
AceConfigDialog.OpenFrames[appName] = nil |
gui:Release(widget) |
end |
local entry = new() |
entry.value = k |
entry.text = GetOptionsMemberValue("name", v, options, path, appName) |
entry.icon = GetOptionsMemberValue("icon", v, options, path, appName) |
entry.disabled = CheckOptionDisabled(v, options, path, appName) |
if not tree.children then tree.children = new() end |
tinsert(tree.children,entry) |
local entry = new() |
entry.value = k |
entry.text = GetOptionsMemberValue("name", v, options, path, appName) |
entry.icon = GetOptionsMemberValue("icon", v, options, path, appName) |
entry.disabled = CheckOptionDisabled(v, options, path, appName) |
tinsert(tree,entry) |
if recurse and (v.childGroups or "tree") == "tree" then |
local name = GetOptionsMemberValue("name", v, options, path, appName) |
if v.type == "execute" then |
control = gui:Create("Button") |
control:SetText(name) |
local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName) |
local image, width, height = GetOptionsMemberValue("image",v, options, path, appName) |
if type(image) == 'string' then |
control = gui:Create("Icon") |
if not width then |
width = GetOptionsMemberValue("imageWidth",v, options, path, appName) |
end |
if not height then |
height = GetOptionsMemberValue("imageHeight",v, options, path, appName) |
end |
if type(imageCoords) == 'table' then |
control:SetImage(image, unpack(imageCoords)) |
else |
control:SetImage(image) |
end |
if type(width) ~= "number" then |
width = 32 |
end |
if type(height) ~= "number" then |
height = 32 |
end |
control:SetImageSize(width, height) |
control:SetLabel(name) |
else |
control = gui:Create("Button") |
control:SetText(name) |
end |
control:SetCallback("OnClick",ActivateControl) |
elseif v.type == "input" then |
local controlType = v.dialogControl or v.control or (v.multiline and "MultiLineEditBox") or "EditBox" |
control = gui:Create(controlType) |
if not control then |
error(("Invalid Custom Control Type - %s"):format(tostring(controlType))) |
geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) |
control = gui:Create(v.multiline and "MultiLineEditBox" or "EditBox") |
end |
if v.multiline then |
local value = GetOptionsMemberValue("get",v, options, path, appName) |
control:SetValue(value) |
control:SetCallback("OnValueChanged",ActivateControl) |
if v.descStyle == "inline" then |
local desc = GetOptionsMemberValue("desc", v, options, path, appName) |
control:SetDescription(desc) |
end |
local image = GetOptionsMemberValue("image", v, options, path, appName) |
local imageCoords = GetOptionsMemberValue("imageCoords", v, options, path, appName) |
if type(image) == 'string' then |
if type(imageCoords) == 'table' then |
control:SetImage(image, unpack(imageCoords)) |
else |
control:SetImage(image) |
end |
end |
elseif v.type == "range" then |
control = gui:Create("Slider") |
control:SetLabel(name) |
local controlType = v.dialogControl or v.control or "Dropdown" |
control = gui:Create(controlType) |
if not control then |
error(("Invalid Custom Control Type - %s"):format(tostring(controlType))) |
geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) |
control = gui:Create("Dropdown") |
end |
control:SetLabel(name) |
control:SetList(values) |
tinsert(valuesort, value) |
end |
end |
table.sort(valuesort) |
tsort(valuesort) |
if controlType then |
control = gui:Create(controlType) |
if not control then |
error(("Invalid Custom Control Type - %s"):format(tostring(controlType))) |
geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) |
end |
end |
if control then |
control:SetMultiselect(true) |
control:SetLabel(name) |
control:SetList(values) |
elseif v.type == "description" then |
control = gui:Create("Label") |
control:SetText(name) |
local fontSize = GetOptionsMemberValue("fontSize",v, options, path, appName) |
if fontSize == "medium" then |
control:SetFontObject(GameFontHighlight) |
elseif fontSize == "large" then |
control:SetFontObject(GameFontHighlightLarge) |
else -- small or invalid |
control:SetFontObject(GameFontHighlightSmall) |
end |
local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName) |
local image, width, height = GetOptionsMemberValue("image",v, options, path, appName) |
feedpath[i] = path[i] |
end |
BuildPath(feedpath, string.split("\001", uniquevalue)) |
BuildPath(feedpath, ("\001"):split(uniquevalue)) |
local group = options |
for i = 1, #feedpath do |
if not group then return end |
GameTooltip:SetOwner(button, "ANCHOR_NONE") |
if widget.type == "TabGroup" then |
GameTooltip:SetPoint("BOTTOM",button,"TOP") |
GameTooltip:SetPoint("BOTTOM",button,"TOP") |
else |
GameTooltip:SetPoint("LEFT",button,"RIGHT") |
GameTooltip:SetPoint("LEFT",button,"RIGHT") |
end |
GameTooltip:SetText(name, 1, .82, 0, 1) |
feedpath[i] = path[i] |
end |
BuildPath(feedpath, string.split("\001", uniquevalue)) |
BuildPath(feedpath, ("\001"):split(uniquevalue)) |
local group = options |
for i = 1, #feedpath do |
feedpath[i] = path[i] |
end |
BuildPath(feedpath, string.split("\001", uniquevalue)) |
BuildPath(feedpath, ("\001"):split(uniquevalue)) |
local group = options |
for i = 1, #feedpath do |
group = GetSubOption(group, feedpath[i]) |
end |
widget:ReleaseChildren() |
lib:FeedGroup(user.appName,options,widget,rootframe,feedpath) |
AceConfigDialog:FeedGroup(user.appName,options,widget,rootframe,feedpath) |
del(feedpath) |
end |
--[[ |
-- INTERNAL -- |
This function will feed one group, and any inline child groups into the given container |
Select Groups will only have the selection control (tree, tabs, dropdown) fed in |
and have a group selected, this event will trigger the feeding of child groups |
if its parent is a tree group, its already a node on a tree |
--]] |
function lib:FeedGroup(appName,options,container,rootframe,path, isRoot) |
function AceConfigDialog:FeedGroup(appName,options,container,rootframe,path, isRoot) |
local group = options |
--follow the path to get to the curent group |
local inline |
--check if the group has child groups |
local hasChildGroups |
for k, v in pairs(group.args) do |
if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then |
if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) and not CheckOptionHidden(v, options, path, appName) then |
hasChildGroups = true |
end |
end |
if group.plugins then |
for plugin, t in pairs(group.plugins) do |
for k, v in pairs(t) do |
if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then |
if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) and not CheckOptionHidden(v, options, path, appName) then |
hasChildGroups = true |
end |
end |
--Add a scrollframe if we are not going to add a group control, this is the inverse of the conditions for that later on |
if (not (hasChildGroups and not inline)) or (grouptype ~= "tab" and grouptype ~= "select" and (parenttype == "tree" and not isRoot)) then |
if container.type ~= "InlineGroup" then |
if container.type ~= "InlineGroup" and container.type ~= "SimpleGroup" then |
scroll = gui:Create("ScrollFrame") |
scroll:SetLayout("flow") |
scroll.width = "fill" |
end |
if hasChildGroups and not inline then |
local name = GetOptionsMemberValue("name", group, options, path, appName) |
if grouptype == "tab" then |
local tab = gui:Create("TabGroup") |
tab:SetCallback("OnTabEnter", TreeOnButtonEnter) |
tab:SetCallback("OnTabLeave", TreeOnButtonLeave) |
local status = lib:GetStatusTable(appName, path) |
local status = AceConfigDialog:GetStatusTable(appName, path) |
if not status.groups then |
status.groups = {} |
end |
elseif grouptype == "select" then |
local select = gui:Create("DropdownGroup") |
select:SetTitle(name) |
InjectInfo(select, options, group, path, rootframe, appName) |
select:SetCallback("OnGroupSelected", GroupSelected) |
local status = lib:GetStatusTable(appName, path) |
local status = AceConfigDialog:GetStatusTable(appName, path) |
if not status.groups then |
status.groups = {} |
end |
end |
if firstgroup then |
select:SetGroup( (GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or firstgroup) |
select:SetGroup((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or firstgroup) |
end |
select.width = "fill" |
tree:SetCallback("OnButtonEnter", TreeOnButtonEnter) |
tree:SetCallback("OnButtonLeave", TreeOnButtonLeave) |
local status = lib:GetStatusTable(appName, path) |
local status = AceConfigDialog:GetStatusTable(appName, path) |
if not status.groups then |
status.groups = {} |
end |
local function RefreshOnUpdate(this) |
for appName in pairs(this.closing) do |
if lib.OpenFrames[appName] then |
lib.OpenFrames[appName]:Hide() |
if AceConfigDialog.OpenFrames[appName] then |
AceConfigDialog.OpenFrames[appName]:Hide() |
end |
if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then |
for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do |
if not widget:IsVisible() then |
widget:ReleaseChildren() |
end |
end |
end |
this.closing[appName] = nil |
end |
if this.closeAll then |
for k, v in pairs(lib.OpenFrames) do |
for k, v in pairs(AceConfigDialog.OpenFrames) do |
v:Hide() |
end |
this.closeAll = nil |
end |
for appName in pairs(this.apps) do |
if lib.OpenFrames[appName] then |
local user = lib.OpenFrames[appName]:GetUserDataTable() |
lib:Open(appName, user.basepath and unpack(user.basepath)) |
if AceConfigDialog.OpenFrames[appName] then |
local user = AceConfigDialog.OpenFrames[appName]:GetUserDataTable() |
AceConfigDialog:Open(appName, unpack(user.basepath or emptyTbl)) |
end |
if lib.BlizOptions and lib.BlizOptions[appName] then |
local widget = lib.BlizOptions[appName] |
local user = widget:GetUserDataTable() |
if widget:IsVisible() then |
lib:Open(widget:GetUserData('appName'), widget, user.basepath and unpack(user.basepath)) |
if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then |
for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do |
local user = widget:GetUserDataTable() |
if widget:IsVisible() then |
AceConfigDialog:Open(widget:GetUserData('appName'), widget, unpack(user.basepath or emptyTbl)) |
end |
end |
end |
this.apps[appName] = nil |
this:SetScript("OnUpdate", nil) |
end |
function lib:CloseAll() |
lib.frame.closeAll = true |
lib.frame:SetScript("OnUpdate", RefreshOnUpdate) |
-- Upgrade the OnUpdate script as well, if needed. |
if AceConfigDialog.frame:GetScript("OnUpdate") then |
AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) |
end |
--- Close all open options windows |
function AceConfigDialog:CloseAll() |
AceConfigDialog.frame.closeAll = true |
AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) |
if next(self.OpenFrames) then |
return true |
end |
end |
function lib:Close(appName) |
--- Close a specific options window. |
-- @param appName The application name as given to `:RegisterOptionsTable()` |
function AceConfigDialog:Close(appName) |
if self.OpenFrames[appName] then |
lib.frame.closing[appName] = true |
lib.frame:SetScript("OnUpdate", RefreshOnUpdate) |
AceConfigDialog.frame.closing[appName] = true |
AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) |
return true |
end |
end |
function lib:ConfigTableChanged(event, appName) |
lib.frame.apps[appName] = true |
lib.frame:SetScript("OnUpdate", RefreshOnUpdate) |
-- Internal -- Called by AceConfigRegistry |
function AceConfigDialog:ConfigTableChanged(event, appName) |
AceConfigDialog.frame.apps[appName] = true |
AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) |
end |
reg.RegisterCallback(lib, "ConfigTableChange", "ConfigTableChanged") |
reg.RegisterCallback(AceConfigDialog, "ConfigTableChange", "ConfigTableChanged") |
function lib:SetDefaultSize(appName, width, height) |
local status = lib:GetStatusTable(appName) |
--- Sets the default size of the options window for a specific application. |
-- @param appName The application name as given to `:RegisterOptionsTable()` |
-- @param width The default width |
-- @param height The default height |
function AceConfigDialog:SetDefaultSize(appName, width, height) |
local status = AceConfigDialog:GetStatusTable(appName) |
if type(width) == "number" and type(height) == "number" then |
status.width = width |
status.height = height |
end |
end |
-- :Open(appName, [container], [path ...]) |
function lib:Open(appName, container, ...) |
--- Open an option window at the specified path (if any). |
-- This function can optionally feed the group into a pre-created container |
-- instead of creating a new container frame. |
-- @paramsig appName [, container][, ...] |
-- @param appName The application name as given to `:RegisterOptionsTable()` |
-- @param container An optional container frame to feed the options into |
-- @param ... The path to open after creating the options window (see `:SelectGroup` for details) |
function AceConfigDialog:Open(appName, container, ...) |
if not old_CloseSpecialWindows then |
old_CloseSpecialWindows = CloseSpecialWindows |
CloseSpecialWindows = function() |
if #path > 0 then |
f:SetUserData('basepath', copy(path)) |
end |
local status = lib:GetStatusTable(appName) |
local status = AceConfigDialog:GetStatusTable(appName) |
if not status.width then |
status.width = 700 |
end |
if f.SetStatusTable then |
f:SetStatusTable(status) |
end |
if f.SetTitle then |
f:SetTitle(name or "") |
end |
else |
if not self.OpenFrames[appName] then |
f = gui:Create("Frame") |
f:SetUserData('basepath', copy(path)) |
end |
f:SetTitle(name or "") |
local status = lib:GetStatusTable(appName) |
local status = AceConfigDialog:GetStatusTable(appName) |
f:SetStatusTable(status) |
end |
del(path) |
end |
lib.BlizOptions = lib.BlizOptions or {} |
-- convert pre-39 BlizOptions structure to the new format |
if oldminor and oldminor < 39 and AceConfigDialog.BlizOptions then |
local old = AceConfigDialog.BlizOptions |
local new = {} |
for key, widget in pairs(old) do |
local appName = widget:GetUserData('appName') |
if not new[appName] then new[appName] = {} end |
new[appName][key] = widget |
end |
AceConfigDialog.BlizOptions = new |
else |
AceConfigDialog.BlizOptions = AceConfigDialog.BlizOptions or {} |
end |
local function FeedToBlizPanel(widget, event) |
local path = widget:GetUserData('path') |
lib:Open(widget:GetUserData('appName'), widget, path and unpack(path)) |
AceConfigDialog:Open(widget:GetUserData('appName'), widget, unpack(path or emptyTbl)) |
end |
local function ClearBlizPanel(widget, event) |
widget:ReleaseChildren() |
local appName = widget:GetUserData('appName') |
AceConfigDialog.frame.closing[appName] = true |
AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) |
end |
function lib:AddToBlizOptions(appName, name, parent, ...) |
local BlizOptions = lib.BlizOptions |
--- Add an option table into the Blizzard Interface Options panel. |
-- You can optionally supply a descriptive name to use and a parent frame to use, |
-- as well as a path in the options table.\\ |
-- If no name is specified, the appName will be used instead. |
-- |
-- If you specify a proper `parent` (by name), the interface options will generate a |
-- tree layout. Note that only one level of children is supported, so the parent always |
-- has to be a head-level note. |
-- |
-- This function returns a reference to the container frame registered with the Interface |
-- Options. You can use this reference to open the options with the API function |
-- `InterfaceOptionsFrame_OpenToCategory`. |
-- @param appName The application name as given to `:RegisterOptionsTable()` |
-- @param name A descriptive name to display in the options tree (defaults to appName) |
-- @param parent The parent to use in the interface options tree. |
-- @param ... The path in the options table to feed into the interface options panel. |
-- @return The reference to the frame registered into the Interface Options. |
function AceConfigDialog:AddToBlizOptions(appName, name, parent, ...) |
local BlizOptions = AceConfigDialog.BlizOptions |
local key = appName |
for n = 1, select('#', ...) do |
key = key..'\001'..select(n, ...) |
end |
if not BlizOptions[key] then |
if not BlizOptions[appName] then |
BlizOptions[appName] = {} |
end |
if not BlizOptions[appName][key] then |
local group = gui:Create("BlizOptionsGroup") |
BlizOptions[key] = group |
BlizOptions[appName][key] = group |
group:SetName(name or appName, parent) |
group:SetTitle(name or appName) |
--- AceConfigCmd-3.0 handles access to optionstable through the "command line" interface via the ChatFrames. |
--- AceConfigCmd-3.0 handles access to an options table through the "command line" interface via the ChatFrames. |
-- @class file |
-- @name AceConfigCmd-3.0 |
-- @release $Id: AceConfigCmd-3.0.lua 733 2009-02-03 22:24:44Z nevcairiel $ |
-- @release $Id: AceConfigCmd-3.0.lua 904 2009-12-13 11:56:37Z nevcairiel $ |
--[[ |
AceConfigCmd-3.0 |
]] |
-- TODO: handle disabled / hidden |
-- TODO: implement handlers for all types |
-- TODO: plugin args |
local MAJOR, MINOR = "AceConfigCmd-3.0", 7 |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
local MAJOR, MINOR = "AceConfigCmd-3.0", 12 |
local AceConfigCmd = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
if not AceConfigCmd then return end |
lib.commands = lib.commands or {} |
local commands = lib.commands |
AceConfigCmd.commands = AceConfigCmd.commands or {} |
local commands = AceConfigCmd.commands |
local cfgreg = LibStub("AceConfigRegistry-3.0") |
local AceConsole -- LoD |
local AceConsoleName = "AceConsole-3.0" |
-- Lua APIs |
local strsub, strsplit, strlower, strmatch, strtrim = string.sub, string.split, string.lower, string.match, string.trim |
local format, tonumber, tostring = string.format, tonumber, tostring |
local tsort, tinsert = table.sort, table.insert |
local select, pairs, next, type = select, pairs, next, type |
local error, assert = error, assert |
-- WoW APIs |
local _G = _G |
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded |
-- List them here for Mikk's FindGlobals script |
-- GLOBALS: LibStub, SELECTED_CHAT_FRAME, DEFAULT_CHAT_FRAME |
local L = setmetatable({}, { -- TODO: replace with proper locale |
__index = function(self,k) return k end |
}) |
(SELECTED_CHAT_FRAME or DEFAULT_CHAT_FRAME):AddMessage(msg) |
end |
-- constants used by getparam() calls below |
local handlertypes = {["table"]=true} |
local handlermsg = "expected a table" |
local functypes = {["function"]=true, ["string"]=true} |
local funcmsg = "expected function or member name" |
-- pickfirstset() - picks the first non-nil value and returns it |
local function pickfirstset(...) |
end |
end |
local function showhelp(info, inputpos, tab, noHead) |
local function checkhidden(info, inputpos, tab) |
if tab.cmdHidden~=nil then |
return tab.cmdHidden |
end |
local hidden = tab.hidden |
if type(hidden) == "function" or type(hidden) == "string" then |
info.hidden = hidden |
hidden = callmethod(info, inputpos, tab, 'hidden') |
info.hidden = nil |
end |
return hidden |
end |
local function showhelp(info, inputpos, tab, depth, noHead) |
if not noHead then |
print("|cff33ff99"..info.appName.."|r: Arguments to |cffffff78/"..info[0].."|r "..strsub(info.input,1,inputpos-1)..":") |
end |
for k,v in iterateargs(tab) do |
if not refTbl[k] then -- a plugin overriding something in .args |
table.insert(sortTbl, k) |
tinsert(sortTbl, k) |
refTbl[k] = v |
end |
end |
table.sort(sortTbl, function(one, two) |
tsort(sortTbl, function(one, two) |
local o1 = refTbl[one].order or 100 |
local o2 = refTbl[two].order or 100 |
if type(o1) == "function" or type(o1) == "string" then |
return o1<o2 |
end) |
for _,k in ipairs(sortTbl) do |
for i = 1, #sortTbl do |
local k = sortTbl[i] |
local v = refTbl[k] |
if not pickfirstset(v.cmdHidden, v.hidden, false) then |
-- recursively show all inline groups |
local name, desc = v.name, v.desc |
if type(name) == "function" then |
name = callfunction(info, v, 'name') |
if not checkhidden(info, inputpos, v) then |
if v.type ~= "description" and v.type ~= "header" then |
-- recursively show all inline groups |
local name, desc = v.name, v.desc |
if type(name) == "function" then |
name = callfunction(info, v, 'name') |
end |
if type(desc) == "function" then |
desc = callfunction(info, v, 'desc') |
end |
if v.type == "group" and pickfirstset(v.cmdInline, v.inline, false) then |
print(" "..(desc or name)..":") |
local oldhandler,oldhandler_at = getparam(info, inputpos, v, depth, "handler", handlertypes, handlermsg) |
showhelp(info, inputpos, v, depth, true) |
info.handler,info.handler_at = oldhandler,oldhandler_at |
else |
local key = k:gsub(" ", "_") |
print(" |cffffff78"..key.."|r - "..(desc or name or "")) |
end |
end |
if type(desc) == "function" then |
desc = callfunction(info, v, 'desc') |
end |
if v.type == "group" and pickfirstset(v.cmdInline, v.inline, false) then |
print(" "..(desc or name)..":") |
showhelp(info, inputpos, v, true) |
elseif v.type ~= "description" and v.type ~= "header" then |
print(" |cffffff78"..k.."|r - "..(desc or name or "")) |
end |
end |
end |
end |
return s |
end |
-- constants used by getparam() calls below |
local handlertypes = {["table"]=true} |
local handlermsg = "expected a table" |
local functypes = {["function"]=true, ["string"]=true} |
local funcmsg = "expected function or member name" |
-- handle() - selfrecursing function that processes input->optiontable |
-- - depth - starts at 0 |
-- - retfalse - return false rather than produce error if a match is not found (used by inlined groups) |
if tab.plugins and type(tab.plugins)~="table" then err(info,inputpos) end |
-- grab next arg from input |
local _,nextpos,arg = string.find(info.input, " *([^ ]+) *", inputpos) |
local _,nextpos,arg = (info.input):find(" *([^ ]+) *", inputpos) |
if not arg then |
showhelp(info, inputpos, tab) |
showhelp(info, inputpos, tab, depth) |
return |
end |
nextpos=nextpos+1 |
return -- done, name was found in inline group |
end |
-- matching name and not a inline group |
elseif strlower(arg)==strlower(k) then |
elseif strlower(arg)==strlower(k:gsub(" ", "_")) then |
info[depth+1] = k |
return handle(info,nextpos,v,depth+1) |
end |
elseif tab.type=="select" then |
------------ select ------------------------------------ |
local str = strtrim(strlower(str)) |
if str == "" then |
--TODO: Show current selection and possible values |
return |
end |
local values = tab.values |
if type(values) == "function" or type(values) == "string" then |
values = callmethod(info, inputpos, tab, "values") |
info.values = nil |
end |
if str == "" then |
local b = callmethod(info, inputpos, tab, "get") |
local fmt = "|cffffff78- [%s]|r %s" |
local fmt_sel = "|cffffff78- [%s]|r %s |cffff0000*|r" |
print(L["Options for |cffffff78"..info[#info].."|r:"]) |
for k, v in pairs(values) do |
if b == k then |
print(fmt_sel:format(k, v)) |
else |
print(fmt:format(k, v)) |
end |
end |
return |
end |
local ok |
for k,v in pairs(values) do |
elseif tab.type=="multiselect" then |
------------ multiselect ------------------------------------------- |
local str = strtrim(strlower(str)) |
if str == "" then |
--TODO: Show current values |
return |
end |
local values = tab.values |
if type(values) == "function" or type(values) == "string" then |
info.values = values |
values = callmethod(info, inputpos, tab, "values") |
info.values = nil |
end |
end |
if str == "" then |
local fmt = "|cffffff78- [%s]|r %s" |
local fmt_sel = "|cffffff78- [%s]|r %s |cffff0000*|r" |
print(L["Options for |cffffff78"..info[#info].."|r (multiple possible):"]) |
for k, v in pairs(values) do |
if callmethod(info, inputpos, tab, "get", k) then |
print(fmt_sel:format(k, v)) |
else |
print(fmt:format(k, v)) |
end |
end |
return |
end |
--build a table of the selections, checking that they exist |
--parse for =on =off =default in the process |
--table will be key = true for options that should toggle, key = [on|off|default] for options to be set |
local sels = {} |
for v in string.gmatch(str, "[^ ]+") do |
for v in str:gmatch("[^ ]+") do |
--parse option=on etc |
local opt, val = string.match(v,'(.+)=(.+)') |
local opt, val = v:match('(.+)=(.+)') |
--get option if toggling |
if not opt then |
opt = v |
end |
end |
----------------------------------------------------------------------- |
-- HandleCommand(slashcmd, appName, input) |
-- |
-- Call this from a chat command handler to parse the command input as operations on an aceoptions table |
--- Handle the chat command. |
-- This is usually called from a chat command handler to parse the command input as operations on an aceoptions table.\\ |
-- AceConfigCmd uses this function internally when a slash command is registered with `:CreateChatCommand` |
-- @param slashcmd The slash command WITHOUT leading slash (only used for error output) |
-- @param appName The application name as given to `:RegisterOptionsTable()` |
-- @param input The commandline input (as given by the WoW handler, i.e. without the command itself) |
-- @usage |
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceConsole-3.0") |
-- -- Use AceConsole-3.0 to register a Chat Command |
-- MyAddon:RegisterChatCommand("mychat", "ChatCommand") |
-- |
-- slashcmd (string) - the slash command WITHOUT leading slash (only used for error output) |
-- appName (string) - the application name as given to AceConfigRegistry:RegisterOptionsTable() |
-- input (string) -- the commandline input (as given by the WoW handler, i.e. without the command itself) |
-- -- Show the GUI if no input is supplied, otherwise handle the chat input. |
-- function MyAddon:ChatCommand(input) |
-- -- Assuming "MyOptions" is the appName of a valid options table |
-- if not input or input:trim() == "" then |
-- LibStub("AceConfigDialog-3.0"):Open("MyOptions") |
-- else |
-- LibStub("AceConfigCmd-3.0").HandleCommand(MyAddon, "mychat", "MyOptions", input) |
-- end |
-- end |
function AceConfigCmd:HandleCommand(slashcmd, appName, input) |
function lib:HandleCommand(slashcmd, appName, input) |
local optgetter = cfgreg:GetOptionsTable(appName) |
if not optgetter then |
error([[Usage: HandleCommand("slashcmd", "appName", "input"): 'appName' - no options table "]]..tostring(appName)..[[" has been registered]], 2) |
handle(info, 1, options, 0) -- (info, inputpos, table, depth) |
end |
----------------------------------------------------------------------- |
-- CreateChatCommand(slashcmd, appName) |
-- |
-- Utility function to create a slash command handler. |
--- Utility function to create a slash command handler. |
-- Also registers tab completion with AceTab |
-- |
-- slashcmd (string) - the slash command WITHOUT leading slash (only used for error output) |
-- appName (string) - the application name as given to AceConfigRegistry:RegisterOptionsTable() |
function lib:CreateChatCommand(slashcmd, appName) |
-- @param slashcmd The slash command WITHOUT leading slash (only used for error output) |
-- @param appName The application name as given to `:RegisterOptionsTable()` |
function AceConfigCmd:CreateChatCommand(slashcmd, appName) |
if not AceConsole then |
AceConsole = LibStub(AceConsoleName) |
end |
if AceConsole.RegisterChatCommand(self, slashcmd, function(input) |
lib.HandleCommand(self, slashcmd, appName, input) -- upgradable |
AceConfigCmd.HandleCommand(self, slashcmd, appName, input) -- upgradable |
end, |
true) then -- succesfully registered so lets get the command -> app table in |
commands[slashcmd] = appName |
end |
end |
-- GetChatCommandOptions(slashcmd) |
-- |
-- Utility function that returns the options table that belongs to a slashcommand |
-- mainly used by AceTab |
function lib:GetChatCommandOptions(slashcmd) |
--- Utility function that returns the options table that belongs to a slashcommand. |
-- Designed to be used for the AceTab interface. |
-- @param slashcmd The slash command WITHOUT leading slash (only used for error output) |
-- @return The options table associated with the slash command (or nil if the slash command was not registered) |
function AceConfigCmd:GetChatCommandOptions(slashcmd) |
return commands[slashcmd] |
end |
--- AceConfigRegistry-3.0 handles central registration of options tables in use by addons and modules. |
-- Options tables can be registered as raw tables, or as function refs that return a table.<br> |
-- These functions receive two arguments: "uiType" and "uiName". <br> |
-- Valid "uiTypes": "cmd", "dropdown", "dialog". This is verified by the library at call time. <br> |
-- The "uiName" field is expected to contain the full name of the calling addon, including version, e.g. "FooBar-1.0". This is verified by the library at call time.<br> |
-- :IterateOptionsTables() and :GetOptionsTable() always return a function reference that the requesting config handling addon must call with the above arguments. |
--- AceConfigRegistry-3.0 handles central registration of options tables in use by addons and modules.\\ |
-- Options tables can be registered as raw tables, OR as function refs that return a table.\\ |
-- Such functions receive three arguments: "uiType", "uiName", "appName". \\ |
-- * Valid **uiTypes**: "cmd", "dropdown", "dialog". This is verified by the library at call time. \\ |
-- * The **uiName** field is expected to contain the full name of the calling addon, including version, e.g. "FooBar-1.0". This is verified by the library at call time.\\ |
-- * The **appName** field is the options table name as given at registration time \\ |
-- |
-- :IterateOptionsTables() (and :GetOptionsTable() if only given one argument) return a function reference that the requesting config handling addon must call with valid "uiType", "uiName". |
-- @class file |
-- @name AceConfigRegistry-3.0 |
-- @release $Id: AceConfigRegistry-3.0.lua 710 2008-12-19 10:14:39Z nevcairiel $ |
local MAJOR, MINOR = "AceConfigRegistry-3.0", 6 |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
-- @release $Id: AceConfigRegistry-3.0.lua 890 2009-12-06 12:50:05Z nevcairiel $ |
local MAJOR, MINOR = "AceConfigRegistry-3.0", 11 |
local AceConfigRegistry = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
if not AceConfigRegistry then return end |
lib.tables = lib.tables or {} |
AceConfigRegistry.tables = AceConfigRegistry.tables or {} |
local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0") |
if not lib.callbacks then |
lib.callbacks = CallbackHandler:New(lib) |
if not AceConfigRegistry.callbacks then |
AceConfigRegistry.callbacks = CallbackHandler:New(AceConfigRegistry) |
end |
-- Lua APIs |
local tinsert, tconcat = table.insert, table.concat |
local strfind, strmatch = string.find, string.match |
local type, tostring, select, pairs = type, tostring, select, pairs |
local error, assert = error, assert |
----------------------------------------------------------------------- |
-- Validating options table consistency: |
lib.validated = { |
AceConfigRegistry.validated = { |
-- list of options table names ran through :ValidateOptionsTable automatically. |
-- CLEARED ON PURPOSE, since newer versions may have newer validators |
cmd = {}, |
for i=select("#",...),1,-1 do |
tinsert(t, (select(i, ...))) |
end |
error(MAJOR..":ValidateOptionsTable(): "..table.concat(t,".")..msg, errlvl+2) |
error(MAJOR..":ValidateOptionsTable(): "..tconcat(t,".")..msg, errlvl+2) |
end |
type=isstring, |
name=isstringfunc, |
desc=optstringfunc, |
descStyle=optstring, |
order=optmethodnumber, |
validate=optmethodfalse, |
confirm=optmethodbool, |
imageCoords=optmethodtable, |
imageHeight=optnumber, |
imageWidth=optnumber, |
fontSize=optstringfunc, |
}, |
group={ |
args=istable, |
childGroups=optstring, |
}, |
execute={ |
-- func={ |
-- ["function"]=true, |
-- ["string"]=true, |
-- _="methodname or funcref" |
-- }, |
image=optstringfunc, |
imageCoords=optmethodtable, |
imageHeight=optnumber, |
imageWidth=optnumber, |
}, |
input={ |
pattern=optstring, |
}, |
toggle={ |
tristate=optbool, |
image=optstringfunc, |
imageCoords=optmethodtable, |
}, |
tristate={ |
}, |
if type(k)~="string" then |
err("["..tostring(k).."] - key is not a string", errlvl,...) |
end |
if strfind(k, "[%c \127]") then |
err("["..tostring(k).."] - key name contained spaces (or control characters)", errlvl,...) |
if strfind(k, "[%c\127]") then |
err("["..tostring(k).."] - key name contained control characters", errlvl,...) |
end |
end |
end |
end |
--------------------------------------------------------------------- |
-- :ValidateOptionsTable(options,name,errlvl) |
-- - options - the table |
-- - name - (string) name of table, used in error reports |
-- - errlvl - (optional number) error level offset, default 0 |
-- |
-- Validates basic structure and integrity of an options table |
-- Does NOT verify that get/set etc actually exist, since they can be defined at any depth |
function lib:ValidateOptionsTable(options,name,errlvl) |
--- Validates basic structure and integrity of an options table \\ |
-- Does NOT verify that get/set etc actually exist, since they can be defined at any depth |
-- @param options The table to be validated |
-- @param name The name of the table to be validated (shown in any error message) |
-- @param errlvl (optional number) error level offset, default 0 (=errors point to the function calling :ValidateOptionsTable) |
function AceConfigRegistry:ValidateOptionsTable(options,name,errlvl) |
errlvl=(errlvl or 0)+1 |
name = name or "Optionstable" |
if not options.name then |
validate(options,errlvl,name) |
end |
------------------------------ |
-- :NotifyChange(appName) |
-- - appName - string identifying the addon |
-- |
-- Fires a ConfigTableChange callback for those listening in on it, allowing config GUIs to refresh |
------------------------------ |
function lib:NotifyChange(appName) |
if not lib.tables[appName] then return end |
lib.callbacks:Fire("ConfigTableChange", appName) |
--- Fires a "ConfigTableChange" callback for those listening in on it, allowing config GUIs to refresh. |
-- You should call this function if your options table changed from any outside event, like a game event |
-- or a timer. |
-- @param appName The application name as given to `:RegisterOptionsTable()` |
function AceConfigRegistry:NotifyChange(appName) |
if not AceConfigRegistry.tables[appName] then return end |
AceConfigRegistry.callbacks:Fire("ConfigTableChange", appName) |
end |
--------------------------------------------------------------------- |
-- ------------------------------------------------------------------- |
-- Registering and retreiving options tables: |
end |
end |
--------------------------------------------------------------------- |
-- :RegisterOptionsTable(appName, options) |
-- - appName - string identifying the addon |
-- - options - table or function reference |
function lib:RegisterOptionsTable(appName, options) |
--- Register an options table with the config registry. |
-- @param appName The application name as given to `:RegisterOptionsTable()` |
-- @param options The options table, OR a function reference that generates it on demand. \\ |
-- See the top of the page for info on arguments passed to such functions. |
function AceConfigRegistry:RegisterOptionsTable(appName, options) |
if type(options)=="table" then |
if options.type~="group" then -- quick sanity checker |
error(MAJOR..": RegisterOptionsTable(appName, options): 'options' - missing type='group' member in root group", 2) |
end |
lib.tables[appName] = function(uiType, uiName, errlvl) |
AceConfigRegistry.tables[appName] = function(uiType, uiName, errlvl) |
errlvl=(errlvl or 0)+1 |
validateGetterArgs(uiType, uiName, errlvl) |
if not lib.validated[uiType][appName] then |
lib:ValidateOptionsTable(options, appName, errlvl) -- upgradable |
lib.validated[uiType][appName] = true |
if not AceConfigRegistry.validated[uiType][appName] then |
AceConfigRegistry:ValidateOptionsTable(options, appName, errlvl) -- upgradable |
AceConfigRegistry.validated[uiType][appName] = true |
end |
return options |
end |
elseif type(options)=="function" then |
lib.tables[appName] = function(uiType, uiName, errlvl) |
AceConfigRegistry.tables[appName] = function(uiType, uiName, errlvl) |
errlvl=(errlvl or 0)+1 |
validateGetterArgs(uiType, uiName, errlvl) |
local tab = assert(options(uiType, uiName)) |
if not lib.validated[uiType][appName] then |
lib:ValidateOptionsTable(tab, appName, errlvl) -- upgradable |
lib.validated[uiType][appName] = true |
local tab = assert(options(uiType, uiName, appName)) |
if not AceConfigRegistry.validated[uiType][appName] then |
AceConfigRegistry:ValidateOptionsTable(tab, appName, errlvl) -- upgradable |
AceConfigRegistry.validated[uiType][appName] = true |
end |
return tab |
end |
end |
end |
--- Returns an iterator of ["appName"]=funcref pairs |
function AceConfigRegistry:IterateOptionsTables() |
return pairs(AceConfigRegistry.tables) |
end |
--------------------------------------------------------------------- |
-- :IterateOptionsTables() |
-- |
-- Returns an iterator of ["appName"]=funcref pairs |
function lib:IterateOptionsTables() |
return pairs(lib.tables) |
end |
--------------------------------------------------------------------- |
-- :GetOptionsTable(appName) |
-- - appName - which addon to retreive the options table of |
-- Optional: |
-- - uiType - "cmd", "dropdown", "dialog" |
-- - uiName - e.g. "MyLib-1.0" |
-- |
--- Query the registry for a specific options table. |
-- If only appName is given, a function is returned which you |
-- can call with (uiType,uiName) to get the table. |
-- can call with (uiType,uiName) to get the table.\\ |
-- If uiType&uiName are given, the table is returned. |
function lib:GetOptionsTable(appName, uiType, uiName) |
local f = lib.tables[appName] |
-- @param appName The application name as given to `:RegisterOptionsTable()` |
-- @param uiType The type of UI to get the table for, one of "cmd", "dropdown", "dialog" |
-- @param uiName The name of the library/addon querying for the table, e.g. "MyLib-1.0" |
function AceConfigRegistry:GetOptionsTable(appName, uiType, uiName) |
local f = AceConfigRegistry.tables[appName] |
if not f then |
return nil |
end |