view Libs/AceConfig-3.0/AceConfigRegistry-3.0/AceConfigRegistry-3.0.lua @ 6:f10c8a083d2a

The ALPHA help request popup should pop when the addon is enabled for every 15th time. I really would like some data to. The timer to start opening mail when you open the mailbox will now use the initial mail opening delay.
author Zerotorescue
date Wed, 08 Sep 2010 00:48:37 +0200
parents 823e33465b6e
children
line wrap: on
line source
--- 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 921 2010-05-09 15:49:14Z nevcairiel $
local MAJOR, MINOR = "AceConfigRegistry-3.0", 12
local AceConfigRegistry = LibStub:NewLibrary(MAJOR, MINOR)

if not AceConfigRegistry then return end

AceConfigRegistry.tables = AceConfigRegistry.tables or {}

local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")

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:


AceConfigRegistry.validated = {
	-- list of options table names ran through :ValidateOptionsTable automatically. 
	-- CLEARED ON PURPOSE, since newer versions may have newer validators
	cmd = {},
	dropdown = {},
	dialog = {},
}



local function err(msg, errlvl, ...)
	local t = {}
	for i=select("#",...),1,-1 do
		tinsert(t, (select(i, ...)))
	end
	error(MAJOR..":ValidateOptionsTable(): "..tconcat(t,".")..msg, errlvl+2)
end


local isstring={["string"]=true, _="string"}
local isstringfunc={["string"]=true,["function"]=true, _="string or funcref"}
local istable={["table"]=true,   _="table"}
local ismethodtable={["table"]=true,["string"]=true,["function"]=true,   _="methodname, funcref or table"}
local optstring={["nil"]=true,["string"]=true, _="string"}
local optstringfunc={["nil"]=true,["string"]=true,["function"]=true, _="string or funcref"}
local optnumber={["nil"]=true,["number"]=true, _="number"}
local optmethod={["nil"]=true,["string"]=true,["function"]=true, _="methodname or funcref"}
local optmethodfalse={["nil"]=true,["string"]=true,["function"]=true,["boolean"]={[false]=true},  _="methodname, funcref or false"}
local optmethodnumber={["nil"]=true,["string"]=true,["function"]=true,["number"]=true,  _="methodname, funcref or number"}
local optmethodtable={["nil"]=true,["string"]=true,["function"]=true,["table"]=true,  _="methodname, funcref or table"}
local optmethodbool={["nil"]=true,["string"]=true,["function"]=true,["boolean"]=true,  _="methodname, funcref or boolean"}
local opttable={["nil"]=true,["table"]=true,  _="table"}
local optbool={["nil"]=true,["boolean"]=true,  _="boolean"}
local optboolnumber={["nil"]=true,["boolean"]=true,["number"]=true,  _="boolean or number"}

local basekeys={
	type=isstring,
	name=isstringfunc,
	desc=optstringfunc,
	descStyle=optstring,
	order=optmethodnumber,
	validate=optmethodfalse,
	confirm=optmethodbool,
	confirmText=optstring,
	disabled=optmethodbool,
	hidden=optmethodbool,
		guiHidden=optmethodbool,
		dialogHidden=optmethodbool,
		dropdownHidden=optmethodbool,
	cmdHidden=optmethodbool,
	icon=optstringfunc,
	iconCoords=optmethodtable,
	handler=opttable,
	get=optmethodfalse,
	set=optmethodfalse,
	func=optmethodfalse,
	arg={["*"]=true},
	width=optstring,
}

local typedkeys={
	header={},
	description={
		image=optstringfunc,
		imageCoords=optmethodtable,
		imageHeight=optnumber,
		imageWidth=optnumber,
		fontSize=optstringfunc,
	},
	group={
		args=istable,
		plugins=opttable,
		inline=optbool,
			cmdInline=optbool,
			guiInline=optbool,
			dropdownInline=optbool,
			dialogInline=optbool,
		childGroups=optstring,
	},
	execute={
		image=optstringfunc,
		imageCoords=optmethodtable,
		imageHeight=optnumber,
		imageWidth=optnumber,
	},
	input={
		pattern=optstring,
		usage=optstring,
		control=optstring,
		dialogControl=optstring,
		dropdownControl=optstring,
		multiline=optboolnumber,
	},
	toggle={
		tristate=optbool,
		image=optstringfunc,
		imageCoords=optmethodtable,
	},
	tristate={
	},
	range={
		min=optnumber,
		softMin=optnumber,
		max=optnumber,
		softMax=optnumber,
		step=optnumber,
		bigStep=optnumber,
		isPercent=optbool,
	},
	select={
		values=ismethodtable,
		style={
			["nil"]=true, 
			["string"]={dropdown=true,radio=true}, 
			_="string: 'dropdown' or 'radio'"
		},
		control=optstring,
		dialogControl=optstring,
		dropdownControl=optstring,
	},
	multiselect={
		values=ismethodtable,
		style=optstring,
		tristate=optbool,
		control=optstring,
		dialogControl=optstring,
		dropdownControl=optstring,
	},
	color={
		hasAlpha=optbool,
	},
	keybinding={
		-- TODO
	},
}

local function validateKey(k,errlvl,...)
	errlvl=(errlvl or 0)+1
	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 control characters", errlvl,...)
	end
end

local function validateVal(v, oktypes, errlvl,...)
	errlvl=(errlvl or 0)+1
	local isok=oktypes[type(v)] or oktypes["*"]

	if not isok then
		err(": expected a "..oktypes._..", got '"..tostring(v).."'", errlvl,...)
	end
	if type(isok)=="table" then		-- isok was a table containing specific values to be tested for!
		if not isok[v] then
			err(": did not expect "..type(v).." value '"..tostring(v).."'", errlvl,...)
		end
	end
end

local function validate(options,errlvl,...)
	errlvl=(errlvl or 0)+1
	-- basic consistency
	if type(options)~="table" then
		err(": expected a table, got a "..type(options), errlvl,...)
	end
	if type(options.type)~="string" then
		err(".type: expected a string, got a "..type(options.type), errlvl,...)
	end
	
	-- get type and 'typedkeys' member
	local tk = typedkeys[options.type]
	if not tk then
		err(".type: unknown type '"..options.type.."'", errlvl,...)
	end
	
	-- make sure that all options[] are known parameters
	for k,v in pairs(options) do
		if not (tk[k] or basekeys[k]) then
			err(": unknown parameter", errlvl,tostring(k),...)
		end
	end

	-- verify that required params are there, and that everything is the right type
	for k,oktypes in pairs(basekeys) do
		validateVal(options[k], oktypes, errlvl,k,...)
	end
	for k,oktypes in pairs(tk) do
		validateVal(options[k], oktypes, errlvl,k,...)
	end

	-- extra logic for groups
	if options.type=="group" then
		for k,v in pairs(options.args) do
			validateKey(k,errlvl,"args",...)
			validate(v, errlvl,k,"args",...)
		end
		if options.plugins then
			for plugname,plugin in pairs(options.plugins) do
				if type(plugin)~="table" then
					err(": expected a table, got '"..tostring(plugin).."'", errlvl,tostring(plugname),"plugins",...)
				end
				for k,v in pairs(plugin) do
					validateKey(k,errlvl,tostring(plugname),"plugins",...)
					validate(v, errlvl,k,tostring(plugname),"plugins",...)
				end
			end
		end
	end
end


--- 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
		options.name=name	-- bit of a hack, the root level doesn't really need a .name :-/
	end
	validate(options,errlvl,name)
end

--- 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:


-- validateGetterArgs: helper function for :GetOptionsTable (or, rather, the getter functions returned by it)

local function validateGetterArgs(uiType, uiName, errlvl)
	errlvl=(errlvl or 0)+2
	if uiType~="cmd" and uiType~="dropdown" and uiType~="dialog" then
		error(MAJOR..": Requesting options table: 'uiType' - invalid configuration UI type, expected 'cmd', 'dropdown' or 'dialog'", errlvl)
	end
	if not strmatch(uiName, "[A-Za-z]%-[0-9]") then	-- Expecting e.g. "MyLib-1.2"
		error(MAJOR..": Requesting options table: 'uiName' - badly formatted or missing version number. Expected e.g. 'MyLib-1.2'", errlvl)
	end
end

--- 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
		AceConfigRegistry.tables[appName] = function(uiType, uiName, errlvl)
			errlvl=(errlvl or 0)+1
			validateGetterArgs(uiType, uiName, errlvl)
			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
		AceConfigRegistry.tables[appName] = function(uiType, uiName, errlvl)
			errlvl=(errlvl or 0)+1
			validateGetterArgs(uiType, uiName, errlvl)
			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
	else
		error(MAJOR..": RegisterOptionsTable(appName, options): 'options' - expected table or function reference", 2)
	end
end

--- Returns an iterator of ["appName"]=funcref pairs
function AceConfigRegistry:IterateOptionsTables()
	return pairs(AceConfigRegistry.tables)
end




--- 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.\\
-- If uiType&uiName are given, the table is returned.
-- @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
	
	if uiType then
		return f(uiType,uiName,1)	-- get the table for us
	else
		return f	-- return the function
	end
end