Version 0.2.2
- added Options UI under Interface Options - store settings in profiles - added more translationsmaster
parent
32a692279a
commit
db3db16594
@ -0,0 +1,332 @@
|
|||||||
|
local AddonName, AddonTable = ...
|
||||||
|
local Grichelde = LibStub("AceAddon-3.0"):GetAddon(AddonName)
|
||||||
|
|
||||||
|
Grichelde.options = {
|
||||||
|
name = Grichelde.L.AddonName,
|
||||||
|
type = "group",
|
||||||
|
cmdHidden = true,
|
||||||
|
childGroups = "tab",
|
||||||
|
args = {
|
||||||
|
enabled = {
|
||||||
|
order = 0,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Enabled_Name,
|
||||||
|
desc = Grichelde.L.Options_Enabled_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end,
|
||||||
|
},
|
||||||
|
|
||||||
|
channels = {
|
||||||
|
order = 2,
|
||||||
|
type = "group",
|
||||||
|
name = Grichelde.L.Options_Channels_Group_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_Group_Desc,
|
||||||
|
args = {
|
||||||
|
say = {
|
||||||
|
order = 0,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Channels_ChannelSay_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_ChannelSay_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
emote = {
|
||||||
|
order = 1,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Channels_ChannelEmote_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_ChannelEmote_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
yell = {
|
||||||
|
order = 2,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Channels_ChannelYell_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_ChannelYell_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
party = {
|
||||||
|
order = 3,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Channels_ChannelParty_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_ChannelParty_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
partyLeader = {
|
||||||
|
order = 4,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Channels_ChannelPartyLeader_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_ChannelPartyLeader_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
guild = {
|
||||||
|
order = 5,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Channels_ChannelGuild_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_ChannelGuild_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
officer = {
|
||||||
|
order = 6,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Channels_ChannelOfficer_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_ChannelOfficer_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
raid = {
|
||||||
|
order = 7,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Channels_ChannelRaid_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_ChannelRaid_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
raidLeader = {
|
||||||
|
order = 8,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Channels_ChannelRaidLeader_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_ChannelRaidLeader_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
instance = {
|
||||||
|
order = 9,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Channels_ChannelInstance_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_ChannelInstance_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
battleground = {
|
||||||
|
order = 10,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Channels_ChannelBattleground_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_ChannelBattleground_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
whisper = {
|
||||||
|
order = 11,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Channels_ChannelWhisper_Name,
|
||||||
|
desc = Grichelde.L.Options_Channels_ChannelWhisper_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
replacements = {
|
||||||
|
order = 3,
|
||||||
|
type = "group",
|
||||||
|
name = Grichelde.L.Options_Replacements_Group_Name,
|
||||||
|
desc = Grichelde.L.Options_Replacements_Group_Desc,
|
||||||
|
childGroups = "tree",
|
||||||
|
args = {
|
||||||
|
searchText = {
|
||||||
|
order = 0,
|
||||||
|
type = "input",
|
||||||
|
name = Grichelde.L.Options_Replacements_SearchText_Name,
|
||||||
|
desc = Grichelde.L.Options_Replacements_SearchText_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
replacetext = {
|
||||||
|
order = 1,
|
||||||
|
type = "input",
|
||||||
|
name = Grichelde.L.Options_Replacements_ReplaceText_Name,
|
||||||
|
desc = Grichelde.L.Options_Replacements_ReplaceText_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
},
|
||||||
|
caseSensitive = {
|
||||||
|
order = 2,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Replacements_CaseSensitive_Name,
|
||||||
|
desc = Grichelde.L.Options_Replacements_CaseSensitive_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end },
|
||||||
|
consolidate = {
|
||||||
|
order = 3,
|
||||||
|
type = "toggle",
|
||||||
|
name = Grichelde.L.Options_Replacements_Consolidate_Name,
|
||||||
|
desc = Grichelde.L.Options_Replacements_Consolidate_Desc,
|
||||||
|
set = function(info, val) Grichelde.enabled = val end,
|
||||||
|
get = function(info) return Grichelde.enabled end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local Grichelde_DB_Defaults = {
|
||||||
|
profile = {
|
||||||
|
enabled = true,
|
||||||
|
channels = {
|
||||||
|
"SAY", "EMOTE", "YELL", "PARTY", "GUILD"
|
||||||
|
},
|
||||||
|
replacements = {
|
||||||
|
["**"] = {
|
||||||
|
left = "",
|
||||||
|
right = "",
|
||||||
|
caseSensitive = false,
|
||||||
|
consolidate = true,
|
||||||
|
},
|
||||||
|
replacement = {
|
||||||
|
left = "s",
|
||||||
|
right = "ch",
|
||||||
|
caseSensitive = false,
|
||||||
|
consolidate = true,
|
||||||
|
},
|
||||||
|
replacement = {
|
||||||
|
left = "t",
|
||||||
|
right = "ck",
|
||||||
|
caseSensitive = false,
|
||||||
|
consolidate = true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Grichelde:LoadDatabase()
|
||||||
|
-- Called when the addon is loaded
|
||||||
|
self.DB = LibStub("AceDB-3.0"):New(AddonName .."DB", Grichelde_DB_Defaults, true)
|
||||||
|
self.DB.RegisterCallback(self, "OnProfileChanged", "RefreshConfig")
|
||||||
|
self.DB.RegisterCallback(self, "OnProfileCopied", "RefreshConfig")
|
||||||
|
self.DB.RegisterCallback(self, "OnProfileReset", "RefreshConfig")
|
||||||
|
|
||||||
|
local activeProfile = self.DB:GetCurrentProfile()
|
||||||
|
self:Print("current profile " .. activeProfile)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:SetupOptions()
|
||||||
|
-- add DB-backed profiles to UI options
|
||||||
|
self.options.args.profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.DB)
|
||||||
|
-- self.options.args.profile.order = 1
|
||||||
|
|
||||||
|
-- Adding options to blizzard frame
|
||||||
|
self.Config = LibStub("AceConfig-3.0"):RegisterOptionsTable(AddonName, self.options)
|
||||||
|
self.Dialog = LibStub("AceConfigDialog-3.0"):AddToBlizOptions(AddonName, self.L.AddonName)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:SetupSlashCommands()
|
||||||
|
local function HandleSlashCommand(input)
|
||||||
|
-- Show the GUI if no input is supplied, otherwise handle the chat input.
|
||||||
|
if not input or input:trim() == "" then
|
||||||
|
LibStub("AceConfigDialog-3.0"):Open(AddonName)
|
||||||
|
else
|
||||||
|
-- handle slash ourselves
|
||||||
|
self:Print("Handle slash command: " .. input)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self:RegisterChatCommand("grichelde", HandleSlashCommand)
|
||||||
|
self:RegisterChatCommand("gri", HandleSlashCommand)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:RefreshConfig(name)
|
||||||
|
self:Print("Refreshed: " .. name)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
-- profile actions
|
||||||
|
function Grichelde:SaveProfile(name)
|
||||||
|
local toCopy = self.db:GetCurrentProfile()
|
||||||
|
if name and name ~= toCopy then
|
||||||
|
self.db:SetProfile(name)
|
||||||
|
self.db:CopyProfile(toCopy)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:SetProfile(name)
|
||||||
|
local profile = self:MatchProfile(name)
|
||||||
|
if profile and profile ~= self.db:GetCurrentProfile() then
|
||||||
|
self.db:SetProfile(profile)
|
||||||
|
else
|
||||||
|
self:Printf(L.Options_Profile_Invalid, name or "null")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:DeleteProfile(name)
|
||||||
|
local profile = self:MatchProfile(name)
|
||||||
|
if profile and profile ~= self.db:GetCurrentProfile() then
|
||||||
|
self.db:DeleteProfile(profile)
|
||||||
|
else
|
||||||
|
self:Print(L.Options_Profile_DeleteError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:CopyProfile(name)
|
||||||
|
if name and name ~= self.db:GetCurrentProfile() then
|
||||||
|
self.db:CopyProfile(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:ResetProfile()
|
||||||
|
self.db:ResetProfile()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:ListProfiles()
|
||||||
|
self:Print(L.Options_Profile_Available)
|
||||||
|
|
||||||
|
local current = self.db:GetCurrentProfile()
|
||||||
|
for _, k in ipairs(self.db:GetProfiles()) do
|
||||||
|
if k == current then
|
||||||
|
print(" - " .. k, 1, 1, 0)
|
||||||
|
else
|
||||||
|
print(" - " .. k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:MatchProfile(name)
|
||||||
|
name = name:lower()
|
||||||
|
|
||||||
|
local nameRealm = name .. " - " .. GetRealmName():lower()
|
||||||
|
local match
|
||||||
|
|
||||||
|
for _, k in ipairs(self.db:GetProfiles()) do
|
||||||
|
local key = k:lower()
|
||||||
|
if key == name then
|
||||||
|
return k
|
||||||
|
elseif key == nameRealm then
|
||||||
|
match = k
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return match
|
||||||
|
end
|
||||||
|
|
||||||
|
-- profile events
|
||||||
|
function Grichelde:OnNewProfile(msg, db, name)
|
||||||
|
self:Printf(L.Options_Profile_Created, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:OnProfileDeleted(msg, db, name)
|
||||||
|
self:Printf(L.Options_Profile_Deleted, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:OnProfileChanged(msg, db, name)
|
||||||
|
self:Printf(L.Options_Profile_Loaded, name)
|
||||||
|
self:Load()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:OnProfileCopied(msg, db, name)
|
||||||
|
self:Printf(L.Options_Profile_Copied, name)
|
||||||
|
self:Reload()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:OnProfileReset(msg, db)
|
||||||
|
self:Printf(L.Options_Profile_Reset, db:GetCurrentProfile())
|
||||||
|
self:Reload()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grichelde:OnProfileShutdown(msg, db, name)
|
||||||
|
self:Unload()
|
||||||
|
end
|
||||||
|
]]--
|
||||||
|
|
@ -0,0 +1,58 @@
|
|||||||
|
--- AceConfig-3.0 wrapper library.
|
||||||
|
-- Provides an API to register an options table with the config registry,
|
||||||
|
-- as well as associate it with a slash command.
|
||||||
|
-- @class file
|
||||||
|
-- @name AceConfig-3.0
|
||||||
|
-- @release $Id: AceConfig-3.0.lua 1161 2017-08-12 14:30:16Z funkydude $
|
||||||
|
|
||||||
|
--[[
|
||||||
|
AceConfig-3.0
|
||||||
|
|
||||||
|
Very light wrapper library that combines all the AceConfig subcomponents into one more easily used whole.
|
||||||
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
local cfgreg = LibStub("AceConfigRegistry-3.0")
|
||||||
|
local cfgcmd = LibStub("AceConfigCmd-3.0")
|
||||||
|
|
||||||
|
local MAJOR, MINOR = "AceConfig-3.0", 3
|
||||||
|
local AceConfig = LibStub:NewLibrary(MAJOR, MINOR)
|
||||||
|
|
||||||
|
if not AceConfig then return end
|
||||||
|
|
||||||
|
--TODO: local cfgdlg = LibStub("AceConfigDialog-3.0", true)
|
||||||
|
--TODO: local cfgdrp = LibStub("AceConfigDropdown-3.0", true)
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
--- 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). http://www.wowace.com/addons/ace3/pages/ace-config-3-0-options-tables/
|
||||||
|
-- @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
|
||||||
|
|
||||||
|
if slashcmd then
|
||||||
|
if type(slashcmd) == "table" then
|
||||||
|
for _,cmd in pairs(slashcmd) do
|
||||||
|
cfgcmd:CreateChatCommand(cmd, appName)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cfgcmd:CreateChatCommand(slashcmd, appName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,8 @@
|
|||||||
|
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||||
|
..\FrameXML\UI.xsd">
|
||||||
|
<Include file="AceConfigRegistry-3.0\AceConfigRegistry-3.0.xml"/>
|
||||||
|
<Include file="AceConfigCmd-3.0\AceConfigCmd-3.0.xml"/>
|
||||||
|
<Include file="AceConfigDialog-3.0\AceConfigDialog-3.0.xml"/>
|
||||||
|
<!--<Include file="AceConfigDropdown-3.0\AceConfigDropdown-3.0.xml"/>-->
|
||||||
|
<Script file="AceConfig-3.0.lua"/>
|
||||||
|
</Ui>
|
@ -0,0 +1,794 @@
|
|||||||
|
--- 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 1161 2017-08-12 14:30:16Z funkydude $
|
||||||
|
|
||||||
|
--[[
|
||||||
|
AceConfigCmd-3.0
|
||||||
|
|
||||||
|
Handles commandline optionstable access
|
||||||
|
|
||||||
|
REQUIRES: AceConsole-3.0 for command registration (loaded on demand)
|
||||||
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
-- TODO: plugin args
|
||||||
|
|
||||||
|
local cfgreg = LibStub("AceConfigRegistry-3.0")
|
||||||
|
|
||||||
|
local MAJOR, MINOR = "AceConfigCmd-3.0", 14
|
||||||
|
local AceConfigCmd = LibStub:NewLibrary(MAJOR, MINOR)
|
||||||
|
|
||||||
|
if not AceConfigCmd then return end
|
||||||
|
|
||||||
|
AceConfigCmd.commands = AceConfigCmd.commands or {}
|
||||||
|
local commands = AceConfigCmd.commands
|
||||||
|
|
||||||
|
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
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local function print(msg)
|
||||||
|
(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(...)
|
||||||
|
for i=1,select("#",...) do
|
||||||
|
if select(i,...)~=nil then
|
||||||
|
return select(i,...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- err() - produce real error() regarding malformed options tables etc
|
||||||
|
|
||||||
|
local function err(info,inputpos,msg )
|
||||||
|
local cmdstr=" "..strsub(info.input, 1, inputpos-1)
|
||||||
|
error(MAJOR..": /" ..info[0] ..cmdstr ..": "..(msg or "malformed options table"), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- usererr() - produce chatframe message regarding bad slash syntax etc
|
||||||
|
|
||||||
|
local function usererr(info,inputpos,msg )
|
||||||
|
local cmdstr=strsub(info.input, 1, inputpos-1);
|
||||||
|
print("/" ..info[0] .. " "..cmdstr ..": "..(msg or "malformed options table"))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- callmethod() - call a given named method (e.g. "get", "set") with given arguments
|
||||||
|
|
||||||
|
local function callmethod(info, inputpos, tab, methodtype, ...)
|
||||||
|
local method = info[methodtype]
|
||||||
|
if not method then
|
||||||
|
err(info, inputpos, "'"..methodtype.."': not set")
|
||||||
|
end
|
||||||
|
|
||||||
|
info.arg = tab.arg
|
||||||
|
info.option = tab
|
||||||
|
info.type = tab.type
|
||||||
|
|
||||||
|
if type(method)=="function" then
|
||||||
|
return method(info, ...)
|
||||||
|
elseif type(method)=="string" then
|
||||||
|
if type(info.handler[method])~="function" then
|
||||||
|
err(info, inputpos, "'"..methodtype.."': '"..method.."' is not a member function of "..tostring(info.handler))
|
||||||
|
end
|
||||||
|
return info.handler[method](info.handler, info, ...)
|
||||||
|
else
|
||||||
|
assert(false) -- type should have already been checked on read
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- callfunction() - call a given named function (e.g. "name", "desc") with given arguments
|
||||||
|
|
||||||
|
local function callfunction(info, tab, methodtype, ...)
|
||||||
|
local method = tab[methodtype]
|
||||||
|
|
||||||
|
info.arg = tab.arg
|
||||||
|
info.option = tab
|
||||||
|
info.type = tab.type
|
||||||
|
|
||||||
|
if type(method)=="function" then
|
||||||
|
return method(info, ...)
|
||||||
|
else
|
||||||
|
assert(false) -- type should have already been checked on read
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- do_final() - do the final step (set/execute) along with validation and confirmation
|
||||||
|
|
||||||
|
local function do_final(info, inputpos, tab, methodtype, ...)
|
||||||
|
if info.validate then
|
||||||
|
local res = callmethod(info,inputpos,tab,"validate",...)
|
||||||
|
if type(res)=="string" then
|
||||||
|
usererr(info, inputpos, "'"..strsub(info.input, inputpos).."' - "..res)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- console ignores .confirm
|
||||||
|
|
||||||
|
callmethod(info,inputpos,tab,methodtype, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- getparam() - used by handle() to retreive and store "handler", "get", "set", etc
|
||||||
|
|
||||||
|
local function getparam(info, inputpos, tab, depth, paramname, types, errormsg)
|
||||||
|
local old,oldat = info[paramname], info[paramname.."_at"]
|
||||||
|
local val=tab[paramname]
|
||||||
|
if val~=nil then
|
||||||
|
if val==false then
|
||||||
|
val=nil
|
||||||
|
elseif not types[type(val)] then
|
||||||
|
err(info, inputpos, "'" .. paramname.. "' - "..errormsg)
|
||||||
|
end
|
||||||
|
info[paramname] = val
|
||||||
|
info[paramname.."_at"] = depth
|
||||||
|
end
|
||||||
|
return old,oldat
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- iterateargs(tab) - custom iterator that iterates both t.args and t.plugins.*
|
||||||
|
local dummytable={}
|
||||||
|
|
||||||
|
local function iterateargs(tab)
|
||||||
|
if not tab.plugins then
|
||||||
|
return pairs(tab.args)
|
||||||
|
end
|
||||||
|
|
||||||
|
local argtabkey,argtab=next(tab.plugins)
|
||||||
|
local v
|
||||||
|
|
||||||
|
return function(_, k)
|
||||||
|
while argtab do
|
||||||
|
k,v = next(argtab, k)
|
||||||
|
if k then return k,v end
|
||||||
|
if argtab==tab.args then
|
||||||
|
argtab=nil
|
||||||
|
else
|
||||||
|
argtabkey,argtab = next(tab.plugins, argtabkey)
|
||||||
|
if not argtabkey then
|
||||||
|
argtab=tab.args
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
local sortTbl = {} -- [1..n]=name
|
||||||
|
local refTbl = {} -- [name]=tableref
|
||||||
|
|
||||||
|
for k,v in iterateargs(tab) do
|
||||||
|
if not refTbl[k] then -- a plugin overriding something in .args
|
||||||
|
tinsert(sortTbl, k)
|
||||||
|
refTbl[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
info.order = o1
|
||||||
|
info[#info+1] = one
|
||||||
|
o1 = callmethod(info, inputpos, refTbl[one], "order")
|
||||||
|
info[#info] = nil
|
||||||
|
info.order = nil
|
||||||
|
end
|
||||||
|
if type(o2) == "function" or type(o1) == "string" then
|
||||||
|
info.order = o2
|
||||||
|
info[#info+1] = two
|
||||||
|
o2 = callmethod(info, inputpos, refTbl[two], "order")
|
||||||
|
info[#info] = nil
|
||||||
|
info.order = nil
|
||||||
|
end
|
||||||
|
if o1<0 and o2<0 then return o1<o2 end
|
||||||
|
if o2<0 then return true end
|
||||||
|
if o1<0 then return false end
|
||||||
|
if o1==o2 then return tostring(one)<tostring(two) end -- compare names
|
||||||
|
return o1<o2
|
||||||
|
end)
|
||||||
|
|
||||||
|
for i = 1, #sortTbl do
|
||||||
|
local k = sortTbl[i]
|
||||||
|
local v = refTbl[k]
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function keybindingValidateFunc(text)
|
||||||
|
if text == nil or text == "NONE" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
text = text:upper()
|
||||||
|
local shift, ctrl, alt
|
||||||
|
local modifier
|
||||||
|
while true do
|
||||||
|
if text == "-" then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
modifier, text = strsplit('-', text, 2)
|
||||||
|
if text then
|
||||||
|
if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if modifier == "SHIFT" then
|
||||||
|
if shift then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
shift = true
|
||||||
|
end
|
||||||
|
if modifier == "CTRL" then
|
||||||
|
if ctrl then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
ctrl = true
|
||||||
|
end
|
||||||
|
if modifier == "ALT" then
|
||||||
|
if alt then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
alt = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
text = modifier
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if text == "" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if not text:find("^F%d+$") and text ~= "CAPSLOCK" and text:len() ~= 1 and (text:byte() < 128 or text:len() > 4) and not _G["KEY_" .. text] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local s = text
|
||||||
|
if shift then
|
||||||
|
s = "SHIFT-" .. s
|
||||||
|
end
|
||||||
|
if ctrl then
|
||||||
|
s = "CTRL-" .. s
|
||||||
|
end
|
||||||
|
if alt then
|
||||||
|
s = "ALT-" .. s
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 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)
|
||||||
|
|
||||||
|
local function handle(info, inputpos, tab, depth, retfalse)
|
||||||
|
|
||||||
|
if not(type(tab)=="table" and type(tab.type)=="string") then err(info,inputpos) end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
-- Grab hold of handler,set,get,func,etc if set (and remember old ones)
|
||||||
|
-- Note that we do NOT validate if method names are correct at this stage,
|
||||||
|
-- the handler may change before they're actually used!
|
||||||
|
|
||||||
|
local oldhandler,oldhandler_at = getparam(info,inputpos,tab,depth,"handler",handlertypes,handlermsg)
|
||||||
|
local oldset,oldset_at = getparam(info,inputpos,tab,depth,"set",functypes,funcmsg)
|
||||||
|
local oldget,oldget_at = getparam(info,inputpos,tab,depth,"get",functypes,funcmsg)
|
||||||
|
local oldfunc,oldfunc_at = getparam(info,inputpos,tab,depth,"func",functypes,funcmsg)
|
||||||
|
local oldvalidate,oldvalidate_at = getparam(info,inputpos,tab,depth,"validate",functypes,funcmsg)
|
||||||
|
--local oldconfirm,oldconfirm_at = getparam(info,inputpos,tab,depth,"confirm",functypes,funcmsg)
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
-- Act according to .type of this table
|
||||||
|
|
||||||
|
if tab.type=="group" then
|
||||||
|
------------ group --------------------------------------------
|
||||||
|
|
||||||
|
if type(tab.args)~="table" then err(info, inputpos) end
|
||||||
|
if tab.plugins and type(tab.plugins)~="table" then err(info,inputpos) end
|
||||||
|
|
||||||
|
-- grab next arg from input
|
||||||
|
local _,nextpos,arg = (info.input):find(" *([^ ]+) *", inputpos)
|
||||||
|
if not arg then
|
||||||
|
showhelp(info, inputpos, tab, depth)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
nextpos=nextpos+1
|
||||||
|
|
||||||
|
-- loop .args and try to find a key with a matching name
|
||||||
|
for k,v in iterateargs(tab) do
|
||||||
|
if not(type(k)=="string" and type(v)=="table" and type(v.type)=="string") then err(info,inputpos, "options table child '"..tostring(k).."' is malformed") end
|
||||||
|
|
||||||
|
-- is this child an inline group? if so, traverse into it
|
||||||
|
if v.type=="group" and pickfirstset(v.cmdInline, v.inline, false) then
|
||||||
|
info[depth+1] = k
|
||||||
|
if handle(info, inputpos, v, depth+1, true)==false then
|
||||||
|
info[depth+1] = nil
|
||||||
|
-- wasn't found in there, but that's ok, we just keep looking down here
|
||||||
|
else
|
||||||
|
return -- done, name was found in inline group
|
||||||
|
end
|
||||||
|
-- matching name and not a inline group
|
||||||
|
elseif strlower(arg)==strlower(k:gsub(" ", "_")) then
|
||||||
|
info[depth+1] = k
|
||||||
|
return handle(info,nextpos,v,depth+1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- no match
|
||||||
|
if retfalse then
|
||||||
|
-- restore old infotable members and return false to indicate failure
|
||||||
|
info.handler,info.handler_at = oldhandler,oldhandler_at
|
||||||
|
info.set,info.set_at = oldset,oldset_at
|
||||||
|
info.get,info.get_at = oldget,oldget_at
|
||||||
|
info.func,info.func_at = oldfunc,oldfunc_at
|
||||||
|
info.validate,info.validate_at = oldvalidate,oldvalidate_at
|
||||||
|
--info.confirm,info.confirm_at = oldconfirm,oldconfirm_at
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- couldn't find the command, display error
|
||||||
|
usererr(info, inputpos, "'"..arg.."' - " .. L["unknown argument"])
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local str = strsub(info.input,inputpos);
|
||||||
|
|
||||||
|
if tab.type=="execute" then
|
||||||
|
------------ execute --------------------------------------------
|
||||||
|
do_final(info, inputpos, tab, "func")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
elseif tab.type=="input" then
|
||||||
|
------------ input --------------------------------------------
|
||||||
|
|
||||||
|
local res = true
|
||||||
|
if tab.pattern then
|
||||||
|
if not(type(tab.pattern)=="string") then err(info, inputpos, "'pattern' - expected a string") end
|
||||||
|
if not strmatch(str, tab.pattern) then
|
||||||
|
usererr(info, inputpos, "'"..str.."' - " .. L["invalid input"])
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
do_final(info, inputpos, tab, "set", str)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
elseif tab.type=="toggle" then
|
||||||
|
------------ toggle --------------------------------------------
|
||||||
|
local b
|
||||||
|
local str = strtrim(strlower(str))
|
||||||
|
if str=="" then
|
||||||
|
b = callmethod(info, inputpos, tab, "get")
|
||||||
|
|
||||||
|
if tab.tristate then
|
||||||
|
--cycle in true, nil, false order
|
||||||
|
if b then
|
||||||
|
b = nil
|
||||||
|
elseif b == nil then
|
||||||
|
b = false
|
||||||
|
else
|
||||||
|
b = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
b = not b
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif str==L["on"] then
|
||||||
|
b = true
|
||||||
|
elseif str==L["off"] then
|
||||||
|
b = false
|
||||||
|
elseif tab.tristate and str==L["default"] then
|
||||||
|
b = nil
|
||||||
|
else
|
||||||
|
if tab.tristate then
|
||||||
|
usererr(info, inputpos, format(L["'%s' - expected 'on', 'off' or 'default', or no argument to toggle."], str))
|
||||||
|
else
|
||||||
|
usererr(info, inputpos, format(L["'%s' - expected 'on' or 'off', or no argument to toggle."], str))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
do_final(info, inputpos, tab, "set", b)
|
||||||
|
|
||||||
|
|
||||||
|
elseif tab.type=="range" then
|
||||||
|
------------ range --------------------------------------------
|
||||||
|
local val = tonumber(str)
|
||||||
|
if not val then
|
||||||
|
usererr(info, inputpos, "'"..str.."' - "..L["expected number"])
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if type(info.step)=="number" then
|
||||||
|
val = val- (val % info.step)
|
||||||
|
end
|
||||||
|
if type(info.min)=="number" and val<info.min then
|
||||||
|
usererr(info, inputpos, val.." - "..format(L["must be equal to or higher than %s"], tostring(info.min)) )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if type(info.max)=="number" and val>info.max then
|
||||||
|
usererr(info, inputpos, val.." - "..format(L["must be equal to or lower than %s"], tostring(info.max)) )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
do_final(info, inputpos, tab, "set", val)
|
||||||
|
|
||||||
|
|
||||||
|
elseif tab.type=="select" then
|
||||||
|
------------ select ------------------------------------
|
||||||
|
local str = strtrim(strlower(str))
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
if strlower(k)==str then
|
||||||
|
str = k -- overwrite with key (in case of case mismatches)
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not ok then
|
||||||
|
usererr(info, inputpos, "'"..str.."' - "..L["unknown selection"])
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
do_final(info, inputpos, tab, "set", str)
|
||||||
|
|
||||||
|
elseif tab.type=="multiselect" then
|
||||||
|
------------ multiselect -------------------------------------------
|
||||||
|
local str = strtrim(strlower(str))
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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 str:gmatch("[^ ]+") do
|
||||||
|
--parse option=on etc
|
||||||
|
local opt, val = v:match('(.+)=(.+)')
|
||||||
|
--get option if toggling
|
||||||
|
if not opt then
|
||||||
|
opt = v
|
||||||
|
end
|
||||||
|
|
||||||
|
--check that the opt is valid
|
||||||
|
local ok
|
||||||
|
for k,v in pairs(values) do
|
||||||
|
if strlower(k)==opt then
|
||||||
|
opt = k -- overwrite with key (in case of case mismatches)
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not ok then
|
||||||
|
usererr(info, inputpos, "'"..opt.."' - "..L["unknown selection"])
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--check that if val was supplied it is valid
|
||||||
|
if val then
|
||||||
|
if val == L["on"] or val == L["off"] or (tab.tristate and val == L["default"]) then
|
||||||
|
--val is valid insert it
|
||||||
|
sels[opt] = val
|
||||||
|
else
|
||||||
|
if tab.tristate then
|
||||||
|
usererr(info, inputpos, format(L["'%s' '%s' - expected 'on', 'off' or 'default', or no argument to toggle."], v, val))
|
||||||
|
else
|
||||||
|
usererr(info, inputpos, format(L["'%s' '%s' - expected 'on' or 'off', or no argument to toggle."], v, val))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- no val supplied, toggle
|
||||||
|
sels[opt] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for opt, val in pairs(sels) do
|
||||||
|
local newval
|
||||||
|
|
||||||
|
if (val == true) then
|
||||||
|
--toggle the option
|
||||||
|
local b = callmethod(info, inputpos, tab, "get", opt)
|
||||||
|
|
||||||
|
if tab.tristate then
|
||||||
|
--cycle in true, nil, false order
|
||||||
|
if b then
|
||||||
|
b = nil
|
||||||
|
elseif b == nil then
|
||||||
|
b = false
|
||||||
|
else
|
||||||
|
b = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
b = not b
|
||||||
|
end
|
||||||
|
newval = b
|
||||||
|
else
|
||||||
|
--set the option as specified
|
||||||
|
if val==L["on"] then
|
||||||
|
newval = true
|
||||||
|
elseif val==L["off"] then
|
||||||
|
newval = false
|
||||||
|
elseif val==L["default"] then
|
||||||
|
newval = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
do_final(info, inputpos, tab, "set", opt, newval)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
elseif tab.type=="color" then
|
||||||
|
------------ color --------------------------------------------
|
||||||
|
local str = strtrim(strlower(str))
|
||||||
|
if str == "" then
|
||||||
|
--TODO: Show current value
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local r, g, b, a
|
||||||
|
|
||||||
|
local hasAlpha = tab.hasAlpha
|
||||||
|
if type(hasAlpha) == "function" or type(hasAlpha) == "string" then
|
||||||
|
info.hasAlpha = hasAlpha
|
||||||
|
hasAlpha = callmethod(info, inputpos, tab, 'hasAlpha')
|
||||||
|
info.hasAlpha = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if hasAlpha then
|
||||||
|
if str:len() == 8 and str:find("^%x*$") then
|
||||||
|
--parse a hex string
|
||||||
|
r,g,b,a = tonumber(str:sub(1, 2), 16) / 255, tonumber(str:sub(3, 4), 16) / 255, tonumber(str:sub(5, 6), 16) / 255, tonumber(str:sub(7, 8), 16) / 255
|
||||||
|
else
|
||||||
|
--parse seperate values
|
||||||
|
r,g,b,a = str:match("^([%d%.]+) ([%d%.]+) ([%d%.]+) ([%d%.]+)$")
|
||||||
|
r,g,b,a = tonumber(r), tonumber(g), tonumber(b), tonumber(a)
|
||||||
|
end
|
||||||
|
if not (r and g and b and a) then
|
||||||
|
usererr(info, inputpos, format(L["'%s' - expected 'RRGGBBAA' or 'r g b a'."], str))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if r >= 0.0 and r <= 1.0 and g >= 0.0 and g <= 1.0 and b >= 0.0 and b <= 1.0 and a >= 0.0 and a <= 1.0 then
|
||||||
|
--values are valid
|
||||||
|
elseif r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255 and a >= 0 and a <= 255 then
|
||||||
|
--values are valid 0..255, convert to 0..1
|
||||||
|
r = r / 255
|
||||||
|
g = g / 255
|
||||||
|
b = b / 255
|
||||||
|
a = a / 255
|
||||||
|
else
|
||||||
|
--values are invalid
|
||||||
|
usererr(info, inputpos, format(L["'%s' - values must all be either in the range 0..1 or 0..255."], str))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
a = 1.0
|
||||||
|
if str:len() == 6 and str:find("^%x*$") then
|
||||||
|
--parse a hex string
|
||||||
|
r,g,b = tonumber(str:sub(1, 2), 16) / 255, tonumber(str:sub(3, 4), 16) / 255, tonumber(str:sub(5, 6), 16) / 255
|
||||||
|
else
|
||||||
|
--parse seperate values
|
||||||
|
r,g,b = str:match("^([%d%.]+) ([%d%.]+) ([%d%.]+)$")
|
||||||
|
r,g,b = tonumber(r), tonumber(g), tonumber(b)
|
||||||
|
end
|
||||||
|
if not (r and g and b) then
|
||||||
|
usererr(info, inputpos, format(L["'%s' - expected 'RRGGBB' or 'r g b'."], str))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if r >= 0.0 and r <= 1.0 and g >= 0.0 and g <= 1.0 and b >= 0.0 and b <= 1.0 then
|
||||||
|
--values are valid
|
||||||
|
elseif r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255 then
|
||||||
|
--values are valid 0..255, convert to 0..1
|
||||||
|
r = r / 255
|
||||||
|
g = g / 255
|
||||||
|
b = b / 255
|
||||||
|
else
|
||||||
|
--values are invalid
|
||||||
|
usererr(info, inputpos, format(L["'%s' - values must all be either in the range 0-1 or 0-255."], str))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
do_final(info, inputpos, tab, "set", r,g,b,a)
|
||||||
|
|
||||||
|
elseif tab.type=="keybinding" then
|
||||||
|
------------ keybinding --------------------------------------------
|
||||||
|
local str = strtrim(strlower(str))
|
||||||
|
if str == "" then
|
||||||
|
--TODO: Show current value
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local value = keybindingValidateFunc(str:upper())
|
||||||
|
if value == false then
|
||||||
|
usererr(info, inputpos, format(L["'%s' - Invalid Keybinding."], str))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
do_final(info, inputpos, tab, "set", value)
|
||||||
|
|
||||||
|
elseif tab.type=="description" then
|
||||||
|
------------ description --------------------
|
||||||
|
-- ignore description, GUI config only
|
||||||
|
else
|
||||||
|
err(info, inputpos, "unknown options table item type '"..tostring(tab.type).."'")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 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")
|
||||||
|
--
|
||||||
|
-- -- 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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
local options = assert( optgetter("cmd", MAJOR) )
|
||||||
|
|
||||||
|
local info = { -- Don't try to recycle this, it gets handed off to callbacks and whatnot
|
||||||
|
[0] = slashcmd,
|
||||||
|
appName = appName,
|
||||||
|
options = options,
|
||||||
|
input = input,
|
||||||
|
self = self,
|
||||||
|
handler = self,
|
||||||
|
uiType = "cmd",
|
||||||
|
uiName = MAJOR,
|
||||||
|
}
|
||||||
|
|
||||||
|
handle(info, 1, options, 0) -- (info, inputpos, table, depth)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Utility function to create a slash command handler.
|
||||||
|
-- Also registers tab completion with AceTab
|
||||||
|
-- @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)
|
||||||
|
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
|
||||||
|
|
||||||
|
--- 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
|
@ -0,0 +1,4 @@
|
|||||||
|
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||||
|
..\FrameXML\UI.xsd">
|
||||||
|
<Script file="AceConfigCmd-3.0.lua"/>
|
||||||
|
</Ui>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,4 @@
|
|||||||
|
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||||
|
..\FrameXML\UI.xsd">
|
||||||
|
<Script file="AceConfigDialog-3.0.lua"/>
|
||||||
|
</Ui>
|
@ -0,0 +1,350 @@
|
|||||||
|
--- 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 1169 2018-02-27 16:18:28Z nevcairiel $
|
||||||
|
local CallbackHandler = LibStub("CallbackHandler-1.0")
|
||||||
|
|
||||||
|
local MAJOR, MINOR = "AceConfigRegistry-3.0", 18
|
||||||
|
local AceConfigRegistry = LibStub:NewLibrary(MAJOR, MINOR)
|
||||||
|
|
||||||
|
if not AceConfigRegistry then return end
|
||||||
|
|
||||||
|
AceConfigRegistry.tables = AceConfigRegistry.tables or {}
|
||||||
|
|
||||||
|
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 optstringnumberfunc={["nil"]=true,["string"]=true,["number"]=true,["function"]=true, _="string, number 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 optstringnumber={["nil"]=true,["string"]=true,["number"]=true, _="string 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=optstringnumberfunc,
|
||||||
|
iconCoords=optmethodtable,
|
||||||
|
handler=opttable,
|
||||||
|
get=optmethodfalse,
|
||||||
|
set=optmethodfalse,
|
||||||
|
func=optmethodfalse,
|
||||||
|
arg={["*"]=true},
|
||||||
|
width=optstringnumber,
|
||||||
|
}
|
||||||
|
|
||||||
|
local typedkeys={
|
||||||
|
header={},
|
||||||
|
description={
|
||||||
|
image=optstringnumberfunc,
|
||||||
|
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=optstringnumberfunc,
|
||||||
|
imageCoords=optmethodtable,
|
||||||
|
imageHeight=optnumber,
|
||||||
|
imageWidth=optnumber,
|
||||||
|
},
|
||||||
|
input={
|
||||||
|
pattern=optstring,
|
||||||
|
usage=optstring,
|
||||||
|
control=optstring,
|
||||||
|
dialogControl=optstring,
|
||||||
|
dropdownControl=optstring,
|
||||||
|
multiline=optboolnumber,
|
||||||
|
},
|
||||||
|
toggle={
|
||||||
|
tristate=optbool,
|
||||||
|
image=optstringnumberfunc,
|
||||||
|
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,
|
||||||
|
itemControl=optstring,
|
||||||
|
},
|
||||||
|
multiselect={
|
||||||
|
values=ismethodtable,
|
||||||
|
style=optstring,
|
||||||
|
tristate=optbool,
|
||||||
|
control=optstring,
|
||||||
|
dialogControl=optstring,
|
||||||
|
dropdownControl=optstring,
|
||||||
|
},
|
||||||
|
color={
|
||||||
|
hasAlpha=optmethodbool,
|
||||||
|
},
|
||||||
|
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.
|
||||||
|
-- @param skipValidation Skip options table validation (primarily useful for extremely huge options, with a noticeable slowdown)
|
||||||
|
function AceConfigRegistry:RegisterOptionsTable(appName, options, skipValidation)
|
||||||
|
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] and not skipValidation 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] and not skipValidation 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
|
@ -0,0 +1,4 @@
|
|||||||
|
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||||
|
..\FrameXML\UI.xsd">
|
||||||
|
<Script file="AceConfigRegistry-3.0.lua"/>
|
||||||
|
</Ui>
|
@ -0,0 +1,746 @@
|
|||||||
|
--- **AceDB-3.0** manages the SavedVariables of your addon.
|
||||||
|
-- It offers profile management, smart defaults and namespaces for modules.\\
|
||||||
|
-- Data can be saved in different data-types, depending on its intended usage.
|
||||||
|
-- The most common data-type is the `profile` type, which allows the user to choose
|
||||||
|
-- the active profile, and manage the profiles of all of his characters.\\
|
||||||
|
-- The following data types are available:
|
||||||
|
-- * **char** Character-specific data. Every character has its own database.
|
||||||
|
-- * **realm** Realm-specific data. All of the players characters on the same realm share this database.
|
||||||
|
-- * **class** Class-specific data. All of the players characters of the same class share this database.
|
||||||
|
-- * **race** Race-specific data. All of the players characters of the same race share this database.
|
||||||
|
-- * **faction** Faction-specific data. All of the players characters of the same faction share this database.
|
||||||
|
-- * **factionrealm** Faction and realm specific data. All of the players characters on the same realm and of the same faction share this database.
|
||||||
|
-- * **locale** Locale specific data, based on the locale of the players game client.
|
||||||
|
-- * **global** Global Data. All characters on the same account share this database.
|
||||||
|
-- * **profile** Profile-specific data. All characters using the same profile share this database. The user can control which profile should be used.
|
||||||
|
--
|
||||||
|
-- Creating a new Database using the `:New` function will return a new DBObject. A database will inherit all functions
|
||||||
|
-- of the DBObjectLib listed here. \\
|
||||||
|
-- If you create a new namespaced child-database (`:RegisterNamespace`), you'll get a DBObject as well, but note
|
||||||
|
-- that the child-databases cannot individually change their profile, and are linked to their parents profile - and because of that,
|
||||||
|
-- the profile related APIs are not available. Only `:RegisterDefaults` and `:ResetProfile` are available on child-databases.
|
||||||
|
--
|
||||||
|
-- For more details on how to use AceDB-3.0, see the [[AceDB-3.0 Tutorial]].
|
||||||
|
--
|
||||||
|
-- You may also be interested in [[libdualspec-1-0|LibDualSpec-1.0]] to do profile switching automatically when switching specs.
|
||||||
|
--
|
||||||
|
-- @usage
|
||||||
|
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("DBExample")
|
||||||
|
--
|
||||||
|
-- -- declare defaults to be used in the DB
|
||||||
|
-- local defaults = {
|
||||||
|
-- profile = {
|
||||||
|
-- setting = true,
|
||||||
|
-- }
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
-- function MyAddon:OnInitialize()
|
||||||
|
-- -- Assuming the .toc says ## SavedVariables: MyAddonDB
|
||||||
|
-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB", defaults, true)
|
||||||
|
-- end
|
||||||
|
-- @class file
|
||||||
|
-- @name AceDB-3.0.lua
|
||||||
|
-- @release $Id: AceDB-3.0.lua 1142 2016-07-11 08:36:19Z nevcairiel $
|
||||||
|
local ACEDB_MAJOR, ACEDB_MINOR = "AceDB-3.0", 26
|
||||||
|
local AceDB, oldminor = LibStub:NewLibrary(ACEDB_MAJOR, ACEDB_MINOR)
|
||||||
|
|
||||||
|
if not AceDB then return end -- No upgrade needed
|
||||||
|
|
||||||
|
-- Lua APIs
|
||||||
|
local type, pairs, next, error = type, pairs, next, error
|
||||||
|
local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
AceDB.db_registry = AceDB.db_registry or {}
|
||||||
|
AceDB.frame = AceDB.frame or CreateFrame("Frame")
|
||||||
|
|
||||||
|
local CallbackHandler
|
||||||
|
local CallbackDummy = { Fire = function() end }
|
||||||
|
|
||||||
|
local DBObjectLib = {}
|
||||||
|
|
||||||
|
--[[-------------------------------------------------------------------------
|
||||||
|
AceDB Utility Functions
|
||||||
|
---------------------------------------------------------------------------]]
|
||||||
|
|
||||||
|
-- Simple shallow copy for copying defaults
|
||||||
|
local function copyTable(src, dest)
|
||||||
|
if type(dest) ~= "table" then dest = {} end
|
||||||
|
if type(src) == "table" then
|
||||||
|
for k,v in pairs(src) do
|
||||||
|
if type(v) == "table" then
|
||||||
|
-- try to index the key first so that the metatable creates the defaults, if set, and use that table
|
||||||
|
v = copyTable(v, dest[k])
|
||||||
|
end
|
||||||
|
dest[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return dest
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Called to add defaults to a section of the database
|
||||||
|
--
|
||||||
|
-- When a ["*"] default section is indexed with a new key, a table is returned
|
||||||
|
-- and set in the host table. These tables must be cleaned up by removeDefaults
|
||||||
|
-- in order to ensure we don't write empty default tables.
|
||||||
|
local function copyDefaults(dest, src)
|
||||||
|
-- this happens if some value in the SV overwrites our default value with a non-table
|
||||||
|
--if type(dest) ~= "table" then return end
|
||||||
|
for k, v in pairs(src) do
|
||||||
|
if k == "*" or k == "**" then
|
||||||
|
if type(v) == "table" then
|
||||||
|
-- This is a metatable used for table defaults
|
||||||
|
local mt = {
|
||||||
|
-- This handles the lookup and creation of new subtables
|
||||||
|
__index = function(t,k)
|
||||||
|
if k == nil then return nil end
|
||||||
|
local tbl = {}
|
||||||
|
copyDefaults(tbl, v)
|
||||||
|
rawset(t, k, tbl)
|
||||||
|
return tbl
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
setmetatable(dest, mt)
|
||||||
|
-- handle already existing tables in the SV
|
||||||
|
for dk, dv in pairs(dest) do
|
||||||
|
if not rawget(src, dk) and type(dv) == "table" then
|
||||||
|
copyDefaults(dv, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Values are not tables, so this is just a simple return
|
||||||
|
local mt = {__index = function(t,k) return k~=nil and v or nil end}
|
||||||
|
setmetatable(dest, mt)
|
||||||
|
end
|
||||||
|
elseif type(v) == "table" then
|
||||||
|
if not rawget(dest, k) then rawset(dest, k, {}) end
|
||||||
|
if type(dest[k]) == "table" then
|
||||||
|
copyDefaults(dest[k], v)
|
||||||
|
if src['**'] then
|
||||||
|
copyDefaults(dest[k], src['**'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if rawget(dest, k) == nil then
|
||||||
|
rawset(dest, k, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Called to remove all defaults in the default table from the database
|
||||||
|
local function removeDefaults(db, defaults, blocker)
|
||||||
|
-- remove all metatables from the db, so we don't accidentally create new sub-tables through them
|
||||||
|
setmetatable(db, nil)
|
||||||
|
-- loop through the defaults and remove their content
|
||||||
|
for k,v in pairs(defaults) do
|
||||||
|
if k == "*" or k == "**" then
|
||||||
|
if type(v) == "table" then
|
||||||
|
-- Loop through all the actual k,v pairs and remove
|
||||||
|
for key, value in pairs(db) do
|
||||||
|
if type(value) == "table" then
|
||||||
|
-- if the key was not explicitly specified in the defaults table, just strip everything from * and ** tables
|
||||||
|
if defaults[key] == nil and (not blocker or blocker[key] == nil) then
|
||||||
|
removeDefaults(value, v)
|
||||||
|
-- if the table is empty afterwards, remove it
|
||||||
|
if next(value) == nil then
|
||||||
|
db[key] = nil
|
||||||
|
end
|
||||||
|
-- if it was specified, only strip ** content, but block values which were set in the key table
|
||||||
|
elseif k == "**" then
|
||||||
|
removeDefaults(value, v, defaults[key])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif k == "*" then
|
||||||
|
-- check for non-table default
|
||||||
|
for key, value in pairs(db) do
|
||||||
|
if defaults[key] == nil and v == value then
|
||||||
|
db[key] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif type(v) == "table" and type(db[k]) == "table" then
|
||||||
|
-- if a blocker was set, dive into it, to allow multi-level defaults
|
||||||
|
removeDefaults(db[k], v, blocker and blocker[k])
|
||||||
|
if next(db[k]) == nil then
|
||||||
|
db[k] = nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- check if the current value matches the default, and that its not blocked by another defaults table
|
||||||
|
if db[k] == defaults[k] and (not blocker or blocker[k] == nil) then
|
||||||
|
db[k] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- This is called when a table section is first accessed, to set up the defaults
|
||||||
|
local function initSection(db, section, svstore, key, defaults)
|
||||||
|
local sv = rawget(db, "sv")
|
||||||
|
|
||||||
|
local tableCreated
|
||||||
|
if not sv[svstore] then sv[svstore] = {} end
|
||||||
|
if not sv[svstore][key] then
|
||||||
|
sv[svstore][key] = {}
|
||||||
|
tableCreated = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local tbl = sv[svstore][key]
|
||||||
|
|
||||||
|
if defaults then
|
||||||
|
copyDefaults(tbl, defaults)
|
||||||
|
end
|
||||||
|
rawset(db, section, tbl)
|
||||||
|
|
||||||
|
return tableCreated, tbl
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Metatable to handle the dynamic creation of sections and copying of sections.
|
||||||
|
local dbmt = {
|
||||||
|
__index = function(t, section)
|
||||||
|
local keys = rawget(t, "keys")
|
||||||
|
local key = keys[section]
|
||||||
|
if key then
|
||||||
|
local defaultTbl = rawget(t, "defaults")
|
||||||
|
local defaults = defaultTbl and defaultTbl[section]
|
||||||
|
|
||||||
|
if section == "profile" then
|
||||||
|
local new = initSection(t, section, "profiles", key, defaults)
|
||||||
|
if new then
|
||||||
|
-- Callback: OnNewProfile, database, newProfileKey
|
||||||
|
t.callbacks:Fire("OnNewProfile", t, key)
|
||||||
|
end
|
||||||
|
elseif section == "profiles" then
|
||||||
|
local sv = rawget(t, "sv")
|
||||||
|
if not sv.profiles then sv.profiles = {} end
|
||||||
|
rawset(t, "profiles", sv.profiles)
|
||||||
|
elseif section == "global" then
|
||||||
|
local sv = rawget(t, "sv")
|
||||||
|
if not sv.global then sv.global = {} end
|
||||||
|
if defaults then
|
||||||
|
copyDefaults(sv.global, defaults)
|
||||||
|
end
|
||||||
|
rawset(t, section, sv.global)
|
||||||
|
else
|
||||||
|
initSection(t, section, section, key, defaults)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return rawget(t, section)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
local function validateDefaults(defaults, keyTbl, offset)
|
||||||
|
if not defaults then return end
|
||||||
|
offset = offset or 0
|
||||||
|
for k in pairs(defaults) do
|
||||||
|
if not keyTbl[k] or k == "profiles" then
|
||||||
|
error(("Usage: AceDBObject:RegisterDefaults(defaults): '%s' is not a valid datatype."):format(k), 3 + offset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local preserve_keys = {
|
||||||
|
["callbacks"] = true,
|
||||||
|
["RegisterCallback"] = true,
|
||||||
|
["UnregisterCallback"] = true,
|
||||||
|
["UnregisterAllCallbacks"] = true,
|
||||||
|
["children"] = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
local realmKey = GetRealmName()
|
||||||
|
local charKey = UnitName("player") .. " - " .. realmKey
|
||||||
|
local _, classKey = UnitClass("player")
|
||||||
|
local _, raceKey = UnitRace("player")
|
||||||
|
local factionKey = UnitFactionGroup("player")
|
||||||
|
local factionrealmKey = factionKey .. " - " .. realmKey
|
||||||
|
local localeKey = GetLocale():lower()
|
||||||
|
|
||||||
|
local regionTable = { "US", "KR", "EU", "TW", "CN" }
|
||||||
|
local regionKey = regionTable[GetCurrentRegion()]
|
||||||
|
local factionrealmregionKey = factionrealmKey .. " - " .. regionKey
|
||||||
|
|
||||||
|
-- Actual database initialization function
|
||||||
|
local function initdb(sv, defaults, defaultProfile, olddb, parent)
|
||||||
|
-- Generate the database keys for each section
|
||||||
|
|
||||||
|
-- map "true" to our "Default" profile
|
||||||
|
if defaultProfile == true then defaultProfile = "Default" end
|
||||||
|
|
||||||
|
local profileKey
|
||||||
|
if not parent then
|
||||||
|
-- Make a container for profile keys
|
||||||
|
if not sv.profileKeys then sv.profileKeys = {} end
|
||||||
|
|
||||||
|
-- Try to get the profile selected from the char db
|
||||||
|
profileKey = sv.profileKeys[charKey] or defaultProfile or charKey
|
||||||
|
|
||||||
|
-- save the selected profile for later
|
||||||
|
sv.profileKeys[charKey] = profileKey
|
||||||
|
else
|
||||||
|
-- Use the profile of the parents DB
|
||||||
|
profileKey = parent.keys.profile or defaultProfile or charKey
|
||||||
|
|
||||||
|
-- clear the profileKeys in the DB, namespaces don't need to store them
|
||||||
|
sv.profileKeys = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- This table contains keys that enable the dynamic creation
|
||||||
|
-- of each section of the table. The 'global' and 'profiles'
|
||||||
|
-- have a key of true, since they are handled in a special case
|
||||||
|
local keyTbl= {
|
||||||
|
["char"] = charKey,
|
||||||
|
["realm"] = realmKey,
|
||||||
|
["class"] = classKey,
|
||||||
|
["race"] = raceKey,
|
||||||
|
["faction"] = factionKey,
|
||||||
|
["factionrealm"] = factionrealmKey,
|
||||||
|
["factionrealmregion"] = factionrealmregionKey,
|
||||||
|
["profile"] = profileKey,
|
||||||
|
["locale"] = localeKey,
|
||||||
|
["global"] = true,
|
||||||
|
["profiles"] = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
validateDefaults(defaults, keyTbl, 1)
|
||||||
|
|
||||||
|
-- This allows us to use this function to reset an entire database
|
||||||
|
-- Clear out the old database
|
||||||
|
if olddb then
|
||||||
|
for k,v in pairs(olddb) do if not preserve_keys[k] then olddb[k] = nil end end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Give this database the metatable so it initializes dynamically
|
||||||
|
local db = setmetatable(olddb or {}, dbmt)
|
||||||
|
|
||||||
|
if not rawget(db, "callbacks") then
|
||||||
|
-- try to load CallbackHandler-1.0 if it loaded after our library
|
||||||
|
if not CallbackHandler then CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0", true) end
|
||||||
|
db.callbacks = CallbackHandler and CallbackHandler:New(db) or CallbackDummy
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Copy methods locally into the database object, to avoid hitting
|
||||||
|
-- the metatable when calling methods
|
||||||
|
|
||||||
|
if not parent then
|
||||||
|
for name, func in pairs(DBObjectLib) do
|
||||||
|
db[name] = func
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- hack this one in
|
||||||
|
db.RegisterDefaults = DBObjectLib.RegisterDefaults
|
||||||
|
db.ResetProfile = DBObjectLib.ResetProfile
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set some properties in the database object
|
||||||
|
db.profiles = sv.profiles
|
||||||
|
db.keys = keyTbl
|
||||||
|
db.sv = sv
|
||||||
|
--db.sv_name = name
|
||||||
|
db.defaults = defaults
|
||||||
|
db.parent = parent
|
||||||
|
|
||||||
|
-- store the DB in the registry
|
||||||
|
AceDB.db_registry[db] = true
|
||||||
|
|
||||||
|
return db
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle PLAYER_LOGOUT
|
||||||
|
-- strip all defaults from all databases
|
||||||
|
-- and cleans up empty sections
|
||||||
|
local function logoutHandler(frame, event)
|
||||||
|
if event == "PLAYER_LOGOUT" then
|
||||||
|
for db in pairs(AceDB.db_registry) do
|
||||||
|
db.callbacks:Fire("OnDatabaseShutdown", db)
|
||||||
|
db:RegisterDefaults(nil)
|
||||||
|
|
||||||
|
-- cleanup sections that are empty without defaults
|
||||||
|
local sv = rawget(db, "sv")
|
||||||
|
for section in pairs(db.keys) do
|
||||||
|
if rawget(sv, section) then
|
||||||
|
-- global is special, all other sections have sub-entrys
|
||||||
|
-- also don't delete empty profiles on main dbs, only on namespaces
|
||||||
|
if section ~= "global" and (section ~= "profiles" or rawget(db, "parent")) then
|
||||||
|
for key in pairs(sv[section]) do
|
||||||
|
if not next(sv[section][key]) then
|
||||||
|
sv[section][key] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not next(sv[section]) then
|
||||||
|
sv[section] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
AceDB.frame:RegisterEvent("PLAYER_LOGOUT")
|
||||||
|
AceDB.frame:SetScript("OnEvent", logoutHandler)
|
||||||
|
|
||||||
|
|
||||||
|
--[[-------------------------------------------------------------------------
|
||||||
|
AceDB Object Method Definitions
|
||||||
|
---------------------------------------------------------------------------]]
|
||||||
|
|
||||||
|
--- Sets the defaults table for the given database object by clearing any
|
||||||
|
-- that are currently set, and then setting the new defaults.
|
||||||
|
-- @param defaults A table of defaults for this database
|
||||||
|
function DBObjectLib:RegisterDefaults(defaults)
|
||||||
|
if defaults and type(defaults) ~= "table" then
|
||||||
|
error("Usage: AceDBObject:RegisterDefaults(defaults): 'defaults' - table or nil expected.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
validateDefaults(defaults, self.keys)
|
||||||
|
|
||||||
|
-- Remove any currently set defaults
|
||||||
|
if self.defaults then
|
||||||
|
for section,key in pairs(self.keys) do
|
||||||
|
if self.defaults[section] and rawget(self, section) then
|
||||||
|
removeDefaults(self[section], self.defaults[section])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set the DBObject.defaults table
|
||||||
|
self.defaults = defaults
|
||||||
|
|
||||||
|
-- Copy in any defaults, only touching those sections already created
|
||||||
|
if defaults then
|
||||||
|
for section,key in pairs(self.keys) do
|
||||||
|
if defaults[section] and rawget(self, section) then
|
||||||
|
copyDefaults(self[section], defaults[section])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Changes the profile of the database and all of it's namespaces to the
|
||||||
|
-- supplied named profile
|
||||||
|
-- @param name The name of the profile to set as the current profile
|
||||||
|
function DBObjectLib:SetProfile(name)
|
||||||
|
if type(name) ~= "string" then
|
||||||
|
error("Usage: AceDBObject:SetProfile(name): 'name' - string expected.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- changing to the same profile, dont do anything
|
||||||
|
if name == self.keys.profile then return end
|
||||||
|
|
||||||
|
local oldProfile = self.profile
|
||||||
|
local defaults = self.defaults and self.defaults.profile
|
||||||
|
|
||||||
|
-- Callback: OnProfileShutdown, database
|
||||||
|
self.callbacks:Fire("OnProfileShutdown", self)
|
||||||
|
|
||||||
|
if oldProfile and defaults then
|
||||||
|
-- Remove the defaults from the old profile
|
||||||
|
removeDefaults(oldProfile, defaults)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.profile = nil
|
||||||
|
self.keys["profile"] = name
|
||||||
|
|
||||||
|
-- if the storage exists, save the new profile
|
||||||
|
-- this won't exist on namespaces.
|
||||||
|
if self.sv.profileKeys then
|
||||||
|
self.sv.profileKeys[charKey] = name
|
||||||
|
end
|
||||||
|
|
||||||
|
-- populate to child namespaces
|
||||||
|
if self.children then
|
||||||
|
for _, db in pairs(self.children) do
|
||||||
|
DBObjectLib.SetProfile(db, name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Callback: OnProfileChanged, database, newProfileKey
|
||||||
|
self.callbacks:Fire("OnProfileChanged", self, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns a table with the names of the existing profiles in the database.
|
||||||
|
-- You can optionally supply a table to re-use for this purpose.
|
||||||
|
-- @param tbl A table to store the profile names in (optional)
|
||||||
|
function DBObjectLib:GetProfiles(tbl)
|
||||||
|
if tbl and type(tbl) ~= "table" then
|
||||||
|
error("Usage: AceDBObject:GetProfiles(tbl): 'tbl' - table or nil expected.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clear the container table
|
||||||
|
if tbl then
|
||||||
|
for k,v in pairs(tbl) do tbl[k] = nil end
|
||||||
|
else
|
||||||
|
tbl = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local curProfile = self.keys.profile
|
||||||
|
|
||||||
|
local i = 0
|
||||||
|
for profileKey in pairs(self.profiles) do
|
||||||
|
i = i + 1
|
||||||
|
tbl[i] = profileKey
|
||||||
|
if curProfile and profileKey == curProfile then curProfile = nil end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add the current profile, if it hasn't been created yet
|
||||||
|
if curProfile then
|
||||||
|
i = i + 1
|
||||||
|
tbl[i] = curProfile
|
||||||
|
end
|
||||||
|
|
||||||
|
return tbl, i
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the current profile name used by the database
|
||||||
|
function DBObjectLib:GetCurrentProfile()
|
||||||
|
return self.keys.profile
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Deletes a named profile. This profile must not be the active profile.
|
||||||
|
-- @param name The name of the profile to be deleted
|
||||||
|
-- @param silent If true, do not raise an error when the profile does not exist
|
||||||
|
function DBObjectLib:DeleteProfile(name, silent)
|
||||||
|
if type(name) ~= "string" then
|
||||||
|
error("Usage: AceDBObject:DeleteProfile(name): 'name' - string expected.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.keys.profile == name then
|
||||||
|
error("Cannot delete the active profile in an AceDBObject.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not rawget(self.profiles, name) and not silent then
|
||||||
|
error("Cannot delete profile '" .. name .. "'. It does not exist.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.profiles[name] = nil
|
||||||
|
|
||||||
|
-- populate to child namespaces
|
||||||
|
if self.children then
|
||||||
|
for _, db in pairs(self.children) do
|
||||||
|
DBObjectLib.DeleteProfile(db, name, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- switch all characters that use this profile back to the default
|
||||||
|
if self.sv.profileKeys then
|
||||||
|
for key, profile in pairs(self.sv.profileKeys) do
|
||||||
|
if profile == name then
|
||||||
|
self.sv.profileKeys[key] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Callback: OnProfileDeleted, database, profileKey
|
||||||
|
self.callbacks:Fire("OnProfileDeleted", self, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Copies a named profile into the current profile, overwriting any conflicting
|
||||||
|
-- settings.
|
||||||
|
-- @param name The name of the profile to be copied into the current profile
|
||||||
|
-- @param silent If true, do not raise an error when the profile does not exist
|
||||||
|
function DBObjectLib:CopyProfile(name, silent)
|
||||||
|
if type(name) ~= "string" then
|
||||||
|
error("Usage: AceDBObject:CopyProfile(name): 'name' - string expected.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if name == self.keys.profile then
|
||||||
|
error("Cannot have the same source and destination profiles.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not rawget(self.profiles, name) and not silent then
|
||||||
|
error("Cannot copy profile '" .. name .. "'. It does not exist.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reset the profile before copying
|
||||||
|
DBObjectLib.ResetProfile(self, nil, true)
|
||||||
|
|
||||||
|
local profile = self.profile
|
||||||
|
local source = self.profiles[name]
|
||||||
|
|
||||||
|
copyTable(source, profile)
|
||||||
|
|
||||||
|
-- populate to child namespaces
|
||||||
|
if self.children then
|
||||||
|
for _, db in pairs(self.children) do
|
||||||
|
DBObjectLib.CopyProfile(db, name, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Callback: OnProfileCopied, database, sourceProfileKey
|
||||||
|
self.callbacks:Fire("OnProfileCopied", self, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Resets the current profile to the default values (if specified).
|
||||||
|
-- @param noChildren if set to true, the reset will not be populated to the child namespaces of this DB object
|
||||||
|
-- @param noCallbacks if set to true, won't fire the OnProfileReset callback
|
||||||
|
function DBObjectLib:ResetProfile(noChildren, noCallbacks)
|
||||||
|
local profile = self.profile
|
||||||
|
|
||||||
|
for k,v in pairs(profile) do
|
||||||
|
profile[k] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local defaults = self.defaults and self.defaults.profile
|
||||||
|
if defaults then
|
||||||
|
copyDefaults(profile, defaults)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- populate to child namespaces
|
||||||
|
if self.children and not noChildren then
|
||||||
|
for _, db in pairs(self.children) do
|
||||||
|
DBObjectLib.ResetProfile(db, nil, noCallbacks)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Callback: OnProfileReset, database
|
||||||
|
if not noCallbacks then
|
||||||
|
self.callbacks:Fire("OnProfileReset", self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Resets the entire database, using the string defaultProfile as the new default
|
||||||
|
-- profile.
|
||||||
|
-- @param defaultProfile The profile name to use as the default
|
||||||
|
function DBObjectLib:ResetDB(defaultProfile)
|
||||||
|
if defaultProfile and type(defaultProfile) ~= "string" then
|
||||||
|
error("Usage: AceDBObject:ResetDB(defaultProfile): 'defaultProfile' - string or nil expected.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local sv = self.sv
|
||||||
|
for k,v in pairs(sv) do
|
||||||
|
sv[k] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local parent = self.parent
|
||||||
|
|
||||||
|
initdb(sv, self.defaults, defaultProfile, self)
|
||||||
|
|
||||||
|
-- fix the child namespaces
|
||||||
|
if self.children then
|
||||||
|
if not sv.namespaces then sv.namespaces = {} end
|
||||||
|
for name, db in pairs(self.children) do
|
||||||
|
if not sv.namespaces[name] then sv.namespaces[name] = {} end
|
||||||
|
initdb(sv.namespaces[name], db.defaults, self.keys.profile, db, self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Callback: OnDatabaseReset, database
|
||||||
|
self.callbacks:Fire("OnDatabaseReset", self)
|
||||||
|
-- Callback: OnProfileChanged, database, profileKey
|
||||||
|
self.callbacks:Fire("OnProfileChanged", self, self.keys["profile"])
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Creates a new database namespace, directly tied to the database. This
|
||||||
|
-- is a full scale database in it's own rights other than the fact that
|
||||||
|
-- it cannot control its profile individually
|
||||||
|
-- @param name The name of the new namespace
|
||||||
|
-- @param defaults A table of values to use as defaults
|
||||||
|
function DBObjectLib:RegisterNamespace(name, defaults)
|
||||||
|
if type(name) ~= "string" then
|
||||||
|
error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - string expected.", 2)
|
||||||
|
end
|
||||||
|
if defaults and type(defaults) ~= "table" then
|
||||||
|
error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'defaults' - table or nil expected.", 2)
|
||||||
|
end
|
||||||
|
if self.children and self.children[name] then
|
||||||
|
error ("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - a namespace with that name already exists.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local sv = self.sv
|
||||||
|
if not sv.namespaces then sv.namespaces = {} end
|
||||||
|
if not sv.namespaces[name] then
|
||||||
|
sv.namespaces[name] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local newDB = initdb(sv.namespaces[name], defaults, self.keys.profile, nil, self)
|
||||||
|
|
||||||
|
if not self.children then self.children = {} end
|
||||||
|
self.children[name] = newDB
|
||||||
|
return newDB
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns an already existing namespace from the database object.
|
||||||
|
-- @param name The name of the new namespace
|
||||||
|
-- @param silent if true, the addon is optional, silently return nil if its not found
|
||||||
|
-- @usage
|
||||||
|
-- local namespace = self.db:GetNamespace('namespace')
|
||||||
|
-- @return the namespace object if found
|
||||||
|
function DBObjectLib:GetNamespace(name, silent)
|
||||||
|
if type(name) ~= "string" then
|
||||||
|
error("Usage: AceDBObject:GetNamespace(name): 'name' - string expected.", 2)
|
||||||
|
end
|
||||||
|
if not silent and not (self.children and self.children[name]) then
|
||||||
|
error ("Usage: AceDBObject:GetNamespace(name): 'name' - namespace does not exist.", 2)
|
||||||
|
end
|
||||||
|
if not self.children then self.children = {} end
|
||||||
|
return self.children[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[-------------------------------------------------------------------------
|
||||||
|
AceDB Exposed Methods
|
||||||
|
---------------------------------------------------------------------------]]
|
||||||
|
|
||||||
|
--- Creates a new database object that can be used to handle database settings and profiles.
|
||||||
|
-- By default, an empty DB is created, using a character specific profile.
|
||||||
|
--
|
||||||
|
-- You can override the default profile used by passing any profile name as the third argument,
|
||||||
|
-- or by passing //true// as the third argument to use a globally shared profile called "Default".
|
||||||
|
--
|
||||||
|
-- Note that there is no token replacement in the default profile name, passing a defaultProfile as "char"
|
||||||
|
-- will use a profile named "char", and not a character-specific profile.
|
||||||
|
-- @param tbl The name of variable, or table to use for the database
|
||||||
|
-- @param defaults A table of database defaults
|
||||||
|
-- @param defaultProfile The name of the default profile. If not set, a character specific profile will be used as the default.
|
||||||
|
-- You can also pass //true// to use a shared global profile called "Default".
|
||||||
|
-- @usage
|
||||||
|
-- -- Create an empty DB using a character-specific default profile.
|
||||||
|
-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB")
|
||||||
|
-- @usage
|
||||||
|
-- -- Create a DB using defaults and using a shared default profile
|
||||||
|
-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB", defaults, true)
|
||||||
|
function AceDB:New(tbl, defaults, defaultProfile)
|
||||||
|
if type(tbl) == "string" then
|
||||||
|
local name = tbl
|
||||||
|
tbl = _G[name]
|
||||||
|
if not tbl then
|
||||||
|
tbl = {}
|
||||||
|
_G[name] = tbl
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(tbl) ~= "table" then
|
||||||
|
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'tbl' - table expected.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if defaults and type(defaults) ~= "table" then
|
||||||
|
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaults' - table expected.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if defaultProfile and type(defaultProfile) ~= "string" and defaultProfile ~= true then
|
||||||
|
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaultProfile' - string or true expected.", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
return initdb(tbl, defaults, defaultProfile)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- upgrade existing databases
|
||||||
|
for db in pairs(AceDB.db_registry) do
|
||||||
|
if not db.parent then
|
||||||
|
for name,func in pairs(DBObjectLib) do
|
||||||
|
db[name] = func
|
||||||
|
end
|
||||||
|
else
|
||||||
|
db.RegisterDefaults = DBObjectLib.RegisterDefaults
|
||||||
|
db.ResetProfile = DBObjectLib.ResetProfile
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,4 @@
|
|||||||
|
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||||
|
..\FrameXML\UI.xsd">
|
||||||
|
<Script file="AceDB-3.0.lua"/>
|
||||||
|
</Ui>
|
@ -0,0 +1,4 @@
|
|||||||
|
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||||
|
..\FrameXML\UI.xsd">
|
||||||
|
<Script file="AceDBOptions-3.0.lua"/>
|
||||||
|
</Ui>
|
Loading…
Reference in New Issue