Mercurial > wow > reaction
diff lib/AceDB-2.0/AceDB-2.0.lua @ 22:1b9323256a1b
Merging in 1.0 dev tree
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Fri, 07 Mar 2008 22:10:55 +0000 |
parents | libs/AceDB-2.0/AceDB-2.0.lua@c11ca1d8ed91 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/AceDB-2.0/AceDB-2.0.lua Fri Mar 07 22:10:55 2008 +0000 @@ -0,0 +1,2187 @@ +--[[ +Name: AceDB-2.0 +Revision: $Rev: 46764 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceDB-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceDB-2.0 +Description: Mixin to allow for fast, clean, and featureful saved variable + access. +Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0 +License: LGPL v2.1 +]] + +local MAJOR_VERSION = "AceDB-2.0" +local MINOR_VERSION = "$Revision: 46764 $" + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end + +local function safecall(func,...) + local success, err = pcall(func,...) + if not success then geterrorhandler()(err) end +end + +local ACTIVE, ENABLED, STATE, TOGGLE_ACTIVE, MAP_ACTIVESUSPENDED, SET_PROFILE, SET_PROFILE_USAGE, PROFILE, PLAYER_OF_REALM, CHOOSE_PROFILE_DESC, CHOOSE_PROFILE_GUI, COPY_PROFILE_DESC, COPY_PROFILE_GUI, OTHER_PROFILE_DESC, OTHER_PROFILE_GUI, OTHER_PROFILE_USAGE, RESET_PROFILE, RESET_PROFILE_DESC, CHARACTER_COLON, REALM_COLON, CLASS_COLON, DEFAULT, ALTERNATIVE + +-- Move these into "enUS" when they've been translated in all other locales +local DELETE_PROFILE = "Delete" +local DELETE_PROFILE_DESC = "Deletes a profile. Note that no check is made whether this profile is in use by other characters or not." +local DELETE_PROFILE_USAGE = "<profile name>" + +if GetLocale() == "deDE" then + ACTIVE = "Aktiv" + ENABLED = "Aktiviert" + STATE = "Status" + TOGGLE_ACTIVE = "Stoppt/Aktiviert dieses Addon." + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Aktiv|r", [false] = "|cffff0000Gestoppt|r" } + SET_PROFILE = "Setzt das Profil f\195\188r dieses Addon." + SET_PROFILE_USAGE = "{Charakter || Klasse || Realm || <Profilname>}" + PROFILE = "Profil" + PLAYER_OF_REALM = "%s von %s" + CHOOSE_PROFILE_DESC = "W\195\164hle ein Profil." + CHOOSE_PROFILE_GUI = "W\195\164hle" + COPY_PROFILE_DESC = "Kopiert Einstellungen von einem anderem Profil." + COPY_PROFILE_GUI = "Kopiere von" + OTHER_PROFILE_DESC = "W\195\164hle ein anderes Profil." + OTHER_PROFILE_GUI = "Anderes" + OTHER_PROFILE_USAGE = "<Profilname>" + RESET_PROFILE = "Reset profile" -- fix + RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix + + CHARACTER_COLON = "Charakter: " + REALM_COLON = "Realm: " + CLASS_COLON = "Klasse: " + + DEFAULT = "Default" -- fix + ALTERNATIVE = "Alternative" -- fix +elseif GetLocale() == "frFR" then + ACTIVE = "Actif" + ENABLED = "Activ\195\169" + STATE = "Etat" + TOGGLE_ACTIVE = "Suspend/active cet addon." + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Actif|r", [false] = "|cffff0000Suspendu|r" } + SET_PROFILE = "S\195\169lectionne le profil pour cet addon." + SET_PROFILE_USAGE = "{perso || classe || royaume || <nom de profil>}" + PROFILE = "Profil" + PLAYER_OF_REALM = "%s de %s" + CHOOSE_PROFILE_DESC = "Choisissez un profil." + CHOOSE_PROFILE_GUI = "Choix" + COPY_PROFILE_DESC = "Copier les param\195\168tres d'un autre profil." + COPY_PROFILE_GUI = "Copier \195\160 partir de" + OTHER_PROFILE_DESC = "Choisissez un autre profil." + OTHER_PROFILE_GUI = "Autre" + OTHER_PROFILE_USAGE = "<nom de profil>" + RESET_PROFILE = "Reset profile" -- fix + RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix + + CHARACTER_COLON = "Personnage: " + REALM_COLON = "Royaume: " + CLASS_COLON = "Classe: " + + DEFAULT = "Default" -- fix + ALTERNATIVE = "Alternative" -- fix +elseif GetLocale() == "koKR" then + DELETE_PROFILE = "삭제" + DELETE_PROFILE_DESC = "프로필을 삭제합니다." + DELETE_PROFILE_USAGE = "<프로필명>" + + ACTIVE = "사용" + ENABLED = "사용" + STATE = "상태" + TOGGLE_ACTIVE = "이 애드온 중지/다시 시작" + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00사용|r", [false] = "|cffff0000중지|r" } + SET_PROFILE = "이 애드온에 프로필 설정" + SET_PROFILE_USAGE = "{캐릭터명 || 직업 || 서버명 || <프로필명>}" + PROFILE = "프로필" + PLAYER_OF_REALM = "%s (%s 서버)" + CHOOSE_PROFILE_DESC = "프로필을 선택합니다." + CHOOSE_PROFILE_GUI = "선택" + COPY_PROFILE_DESC = "다른 프로필 설정을 복사합니다." + COPY_PROFILE_GUI = "복사" + OTHER_PROFILE_DESC = "다른 프로필을 선택합니다." + OTHER_PROFILE_GUI = "기타" + OTHER_PROFILE_USAGE = "<프로필명>" + RESET_PROFILE = "프로필 초기화" + RESET_PROFILE_DESC = "모든 세팅에서 현재 프로필을 초기화 합니다." + + CHARACTER_COLON = "캐릭터: " + REALM_COLON = "서버: " + CLASS_COLON = "직업: " + + DEFAULT = "기본값" + ALTERNATIVE = "대체" +elseif GetLocale() == "zhTW" then + DELETE_PROFILE = "刪除" + DELETE_PROFILE_DESC = "刪除記錄檔。注意,有可能別的角色也使用這個記錄檔。" + DELETE_PROFILE_USAGE = "<記錄檔名稱>" + + ACTIVE = "啟動" + ENABLED = "啟用" + STATE = "狀態" + TOGGLE_ACTIVE = "暫停/繼續使用這個插件。" + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00啟動|r", [false] = "|cffff0000已暫停|r" } + SET_PROFILE = "設定這插件的記錄檔。" + SET_PROFILE_USAGE = "{角色 || 職業 || 伺服器 || <記錄檔名稱>}" + PROFILE = "記錄檔" + PLAYER_OF_REALM = "%s - %s" + CHOOSE_PROFILE_DESC = "選擇一個記錄檔。" + CHOOSE_PROFILE_GUI = "選擇" + COPY_PROFILE_DESC = "由其他記錄檔複製設定。" + COPY_PROFILE_GUI = "複製自" + OTHER_PROFILE_DESC = "選擇其他記錄檔。" + OTHER_PROFILE_GUI = "其他" + OTHER_PROFILE_USAGE = "<記錄檔名稱>" + RESET_PROFILE = "重設記錄檔" + RESET_PROFILE_DESC = "清除目前的記錄檔上的所有設定。" + + CHARACTER_COLON = "角色: " + REALM_COLON = "伺服器: " + CLASS_COLON = "職業: " + + DEFAULT = "預設" + ALTERNATIVE = "替代" +elseif GetLocale() == "zhCN" then + ACTIVE = "\230\156\137\230\149\136" + ENABLED = "\229\144\175\231\148\168" + STATE = "\231\138\182\230\128\129" + TOGGLE_ACTIVE = "\230\154\130\229\129\156/\230\129\162\229\164\141 \230\173\164\230\143\146\228\187\182." + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00\230\156\137\230\149\136|r", [false] = "|cffff0000\230\154\130\229\129\156|r" } + SET_PROFILE = "\232\174\190\231\189\174\233\133\141\231\189\174\230\150\135\228\187\182\228\184\186\232\191\153\230\143\146\228\187\182." + SET_PROFILE_USAGE = "{\229\173\151\231\172\166 || \233\128\137\228\187\182\231\177\187 || \229\159\159 || <\233\133\141\231\189\174\230\150\135\228\187\182\229\144\141\229\173\151>}" + PROFILE = "\233\133\141\231\189\174\230\150\135\228\187\182" + PLAYER_OF_REALM = "%s \231\154\132 %s" + CHOOSE_PROFILE_DESC = "\233\128\137\230\139\169\233\133\141\231\189\174\230\150\135\228\187\182." + CHOOSE_PROFILE_GUI = "\233\128\137\230\139\169" + COPY_PROFILE_DESC = "\229\164\141\229\136\182\232\174\190\231\189\174\228\187\142\229\143\166\228\184\128\228\184\170\233\133\141\231\189\174\230\150\135\228\187\182." + COPY_PROFILE_GUI = "\229\164\141\229\136\182\228\187\142" + OTHER_PROFILE_DESC = "\233\128\137\230\139\169\229\143\166\228\184\128\228\184\170\233\133\141\231\189\174\230\150\135\228\187\182." + OTHER_PROFILE_GUI = "\229\133\182\228\187\150" + OTHER_PROFILE_USAGE = "<\233\133\141\231\189\174\230\150\135\228\187\182\229\144\141\229\173\151>" + RESET_PROFILE = "Reset profile" -- fix + RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix + + CHARACTER_COLON = "\229\173\151\231\172\166: " + REALM_COLON = "\229\159\159: " + CLASS_COLON = "\233\128\137\228\187\182\231\177\187: " + + DEFAULT = "Default" -- fix + ALTERNATIVE = "Alternative" -- fix +elseif GetLocale() == "esES" then + ACTIVE = "Activo" + ENABLED = "Activado" + STATE = "Estado" + TOGGLE_ACTIVE = "Parar/Continuar este accesorio" + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Activo|r", [false] = "|cffff0000Parado|r" } + SET_PROFILE = "Selecciona el perfil para este accesorio." + SET_PROFILE_USAGE = "{perso || clase || reino || <nombre del perfil>}" + PROFILE = "Perfil" + PLAYER_OF_REALM = "%s de %s" + CHOOSE_PROFILE_DESC = "Elige un perfil." + CHOOSE_PROFILE_GUI = "Elige" + COPY_PROFILE_DESC = "Copiar de un perfil a otro" + COPY_PROFILE_GUI = "Copiar desde" + OTHER_PROFILE_DESC = "Elige otro perfil." + OTHER_PROFILE_GUI = "Otro" + OTHER_PROFILE_USAGE = "<nombre del perfil>" + RESET_PROFILE = "Reset profile" -- fix + RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix + + CHARACTER_COLON = "Personaje: " + REALM_COLON = "Reino: " + CLASS_COLON = "Clase: " + + DEFAULT = "Por defecto" + ALTERNATIVE = "Alternativo" +else -- enUS + ACTIVE = "Active" + ENABLED = "Enabled" + STATE = "State" + TOGGLE_ACTIVE = "Suspend/resume this addon." + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Active|r", [false] = "|cffff0000Suspended|r" } + SET_PROFILE = "Set profile for this addon." + SET_PROFILE_USAGE = "{char || class || realm || <profile name>}" + PROFILE = "Profile" + PLAYER_OF_REALM = "%s of %s" + CHOOSE_PROFILE_DESC = "Choose a profile." + CHOOSE_PROFILE_GUI = "Choose" + COPY_PROFILE_DESC = "Copy settings from another profile." + COPY_PROFILE_GUI = "Copy from" + OTHER_PROFILE_DESC = "Choose another profile." + OTHER_PROFILE_GUI = "Other" + OTHER_PROFILE_USAGE = "<profile name>" + RESET_PROFILE = "Reset profile" + RESET_PROFILE_DESC = "Clear all settings of the current profile." + + CHARACTER_COLON = "Character: " + REALM_COLON = "Realm: " + CLASS_COLON = "Class: " + + DEFAULT = "Default" + ALTERNATIVE = "Alternative" +end +local convertFromOldCharID +do + local matchStr = "^" .. PLAYER_OF_REALM:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1"):gsub("%%s", "(.+)") .. "$" + function convertFromOldCharID(str) + local player, realm = str:match(matchStr) + if not player then + return str + end + return player .. " - " .. realm + end +end + +local AceOO = AceLibrary("AceOO-2.0") +local AceEvent +local Mixin = AceOO.Mixin +local AceDB = Mixin { + "RegisterDB", + "RegisterDefaults", + "ResetDB", + "SetProfile", + "GetProfile", + "CopyProfileFrom", + "DeleteProfile", + "ToggleActive", + "IsActive", + "AcquireDBNamespace", + } +local Dewdrop = AceLibrary:HasInstance("Dewdrop-2.0") and AceLibrary("Dewdrop-2.0") + +local _G = getfenv(0) + +local function inheritDefaults(t, defaults) + if not defaults then + return t + end + for k,v in pairs(defaults) do + if k == "*" or k == "**" then + local v = v + if type(v) == "table" then + setmetatable(t, { + __index = function(self, key) + if key == nil then + return nil + end + self[key] = {} + inheritDefaults(self[key], v) + return self[key] + end + } ) + else + setmetatable(t, { + __index = function(self, key) + if key == nil then + return nil + end + self[key] = v + return self[key] + end + } ) + end + for key in pairs(t) do + if (defaults[key] == nil or key == k) and type(t[key]) == "table" then + inheritDefaults(t[key], v) + end + end + else + if type(v) == "table" then + if type(rawget(t, k)) ~= "table" then + t[k] = {} + end + inheritDefaults(t[k], v) + if defaults["**"] then + inheritDefaults(t[k], defaults["**"]) + end + elseif rawget(t, k) == nil then + t[k] = v + end + end + end + return t +end + +local _,race = UnitRace("player") +local faction +if race == "Orc" or race == "Scourge" or race == "Troll" or race == "Tauren" or race == "BloodElf" then + faction = FACTION_HORDE +else + faction = FACTION_ALLIANCE +end +local server = GetRealmName():trim() +local charID = UnitName("player") .. " - " .. server +local realmID = server .. " - " .. faction +local classID = UnitClass("player") + +AceDB.CHAR_ID = charID +AceDB.REALM_ID = realmID +AceDB.CLASS_ID = classID + +AceDB.FACTION = faction +AceDB.REALM = server +AceDB.NAME = UnitName("player") + +local new, del +do + local list = setmetatable({}, {__mode="k"}) + function new() + local t = next(list) + if t then + list[t] = nil + return t + else + return {} + end + end + + function del(t) + setmetatable(t, nil) + for k in pairs(t) do + t[k] = nil + end + list[t] = true + end +end + +local caseInsensitive_mt = { + __index = function(self, key) + if type(key) ~= "string" then + return nil + end + local lowerKey = key:lower() + for k,v in pairs(self) do + if k:lower() == lowerKey then + return self[k] + end + end + end, + __newindex = function(self, key, value) + if type(key) ~= "string" then + return error("table index is nil", 2) + end + local lowerKey = key:lower() + for k in pairs(self) do + if k:lower() == lowerKey then + rawset(self, k, nil) + rawset(self, key, value) + return + end + end + rawset(self, key, value) + end +} + +local db_mt = { __index = function(db, key) + if key == "char" then + if db.charName then + if type(_G[db.charName]) ~= "table" then + _G[db.charName] = {} + end + if type(_G[db.charName].global) ~= "table" then + _G[db.charName].global = {} + end + rawset(db, 'char', _G[db.charName].global) + else + if type(db.raw.chars) ~= "table" then + db.raw.chars = {} + end + local id = charID + if type(db.raw.chars[id]) ~= "table" then + db.raw.chars[id] = {} + end + rawset(db, 'char', db.raw.chars[id]) + end + if db.defaults and db.defaults.char then + inheritDefaults(db.char, db.defaults.char) + end + return db.char + elseif key == "realm" then + if type(db.raw.realms) ~= "table" then + db.raw.realms = {} + end + local id = realmID + if type(db.raw.realms[id]) ~= "table" then + db.raw.realms[id] = {} + end + rawset(db, 'realm', db.raw.realms[id]) + if db.defaults and db.defaults.realm then + inheritDefaults(db.realm, db.defaults.realm) + end + return db.realm + elseif key == "server" then + if type(db.raw.servers) ~= "table" then + db.raw.servers = {} + end + local id = server + if type(db.raw.servers[id]) ~= "table" then + db.raw.servers[id] = {} + end + rawset(db, 'server', db.raw.servers[id]) + if db.defaults and db.defaults.server then + inheritDefaults(db.server, db.defaults.server) + end + return db.server + elseif key == "account" then + if type(db.raw.account) ~= "table" then + db.raw.account = {} + end + rawset(db, 'account', db.raw.account) + if db.defaults and db.defaults.account then + inheritDefaults(db.account, db.defaults.account) + end + return db.account + elseif key == "faction" then + if type(db.raw.factions) ~= "table" then + db.raw.factions = {} + end + local id = faction + if type(db.raw.factions[id]) ~= "table" then + db.raw.factions[id] = {} + end + rawset(db, 'faction', db.raw.factions[id]) + if db.defaults and db.defaults.faction then + inheritDefaults(db.faction, db.defaults.faction) + end + return db.faction + elseif key == "class" then + if type(db.raw.classes) ~= "table" then + db.raw.classes = {} + end + local id = classID + if type(db.raw.classes[id]) ~= "table" then + db.raw.classes[id] = {} + end + rawset(db, 'class', db.raw.classes[id]) + if db.defaults and db.defaults.class then + inheritDefaults(db.class, db.defaults.class) + end + return db.class + elseif key == "profile" then + if type(db.raw.profiles) ~= "table" then + db.raw.profiles = setmetatable({}, caseInsensitive_mt) + else + setmetatable(db.raw.profiles, caseInsensitive_mt) + end + local id = db.raw.currentProfile[charID] + if id == "char" then + id = "char/" .. charID + elseif id == "class" then + id = "class/" .. classID + elseif id == "realm" then + id = "realm/" .. realmID + end + if type(db.raw.profiles[id]) ~= "table" then + db.raw.profiles[id] = {} + end + rawset(db, 'profile', db.raw.profiles[id]) + if db.defaults and db.defaults.profile then + inheritDefaults(db.profile, db.defaults.profile) + end + return db.profile + elseif key == "raw" or key == "defaults" or key == "name" or key == "charName" or key == "namespaces" then + return nil + end + error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2) +end, __newindex = function(db, key, value) + error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2) +end } + +local function RecalculateAceDBCopyFromList(target) + local db = target.db + local t = target['acedb-profile-copylist'] + for k,v in pairs(t) do + t[k] = nil + end + local _,currentProfile = AceDB.GetProfile(target) + if db and db.raw then + if db.raw.profiles then + for k in pairs(db.raw.profiles) do + if currentProfile ~= k then + if k:find("^char/") then + local name = k:sub(6) + local player, realm = name:match("^(.*) %- (.*)$") + if player then + name = PLAYER_OF_REALM:format(player, realm) + end + t[k] = CHARACTER_COLON .. name + elseif k:find("^realm/") then + local name = k:sub(7) + t[k] = REALM_COLON .. name + elseif k:find("^class/") then + local name = k:sub(7) + t[k] = CLASS_COLON .. name + else + t[k] = k + end + end + end + end + if db.raw.namespaces then + for _,n in pairs(db.raw.namespaces) do + if n.profiles then + for k in pairs(n.profiles) do + if currentProfile ~= k then + if k:find('^char/') then + local name = k:sub(6) + local player, realm = name:match("^(.*) %- (.*)$") + if player then + name = PLAYER_OF_REALM:format(player, realm) + end + t[k] = CHARACTER_COLON .. name + elseif k:find('^realm/') then + local name = k:sub(7) + t[k] = REALM_COLON .. name + elseif k:find('^class/') then + local name = k:sub(7) + t[k] = CLASS_COLON .. name + else + t[k] = k + end + end + end + end + end + end + end + if t.Default then + t.Default = DEFAULT + end + if t.Alternative then + t.Alternative = ALTERNATIVE + end +end + +local function RecalculateAceDBProfileList(target) + local t = target['acedb-profile-list'] + for k,v in pairs(t) do + t[k] = nil + end + t.char = CHARACTER_COLON .. PLAYER_OF_REALM:format(UnitName("player"), server) + t.realm = REALM_COLON .. realmID + t.class = CLASS_COLON .. classID + t.Default = DEFAULT + local db = target.db + if db and db.raw then + if db.raw.profiles then + for k in pairs(db.raw.profiles) do + if not k:find("^char/") and not k:find("^realm/") and not k:find("^class/") then + t[k] = k + end + end + end + if db.raw.namespaces then + for _,n in pairs(db.raw.namespaces) do + if n.profiles then + for k in pairs(n.profiles) do + if not k:find("^char/") and not k:find("^realm/") and not k:find("^class/") then + t[k] = k + end + end + end + end + end + local curr = db.raw.currentProfile and db.raw.currentProfile[charID] + if curr and not t[curr] then + t[curr] = curr + end + end + if t.Alternative then + t.Alternative = ALTERNATIVE + end +end + +local CrawlForSerialization +local CrawlForDeserialization + +local function SerializeObject(o) + local t = { o:Serialize() } + CrawlForSerialization(t) + t[0] = o.class:GetLibraryVersion() + return t +end + +local function DeserializeObject(t) + CrawlForDeserialization(t) + local className = t[0] + t[0] = nil + return AceLibrary(className):Deserialize(unpack(t)) +end + +local function IsSerializable(t) + return AceOO.inherits(t, AceOO.Class) and t.class and type(t.class.Deserialize) == "function" and type(t.Serialize) == "function" and type(t.class.GetLibraryVersion) == "function" +end + +function CrawlForSerialization(t) + local tmp = new() + for k,v in pairs(t) do + tmp[k] = v + end + for k,v in pairs(tmp) do + if type(v) == "table" and type(rawget(v, 0)) ~= "userdata" then + if IsSerializable(v) then + v = SerializeObject(v) + t[k] = v + else + CrawlForSerialization(v) + end + end + if type(k) == "table" and type(rawget(k, 0)) ~= "userdata" then + if IsSerializable(k) then + t[k] = nil + t[SerializeObject(k)] = v + else + CrawlForSerialization(k) + end + end + tmp[k] = nil + k = nil + end + tmp = del(tmp) +end + +local function IsDeserializable(t) + return type(rawget(t, 0)) == "string" and AceLibrary:HasInstance(rawget(t, 0)) +end + +function CrawlForDeserialization(t) + local tmp = new() + for k,v in pairs(t) do + tmp[k] = v + end + for k,v in pairs(tmp) do + if type(v) == "table" then + if IsDeserializable(v) then + t[k] = DeserializeObject(v) + del(v) + v = t[k] + elseif type(rawget(v, 0)) ~= "userdata" then + CrawlForDeserialization(v) + end + end + if type(k) == "table" then + if IsDeserializable(k) then + t[k] = nil + t[DeserializeObject(k)] = v + del(k) + elseif type(rawget(k, 0)) ~= "userdata" then + CrawlForDeserialization(k) + end + end + tmp[k] = nil + k = nil + end + tmp = del(tmp) +end + +local namespace_mt = { __index = function(namespace, key) + local db = namespace.db + local name = namespace.name + if key == "char" then + if db.charName then + if type(_G[db.charName]) ~= "table" then + _G[db.charName] = {} + end + if type(_G[db.charName].namespaces) ~= "table" then + _G[db.charName].namespaces = {} + end + if type(_G[db.charName].namespaces[name]) ~= "table" then + _G[db.charName].namespaces[name] = {} + end + rawset(namespace, 'char', _G[db.charName].namespaces[name]) + else + if type(db.raw.namespaces) ~= "table" then + db.raw.namespaces = {} + end + if type(db.raw.namespaces[name]) ~= "table" then + db.raw.namespaces[name] = {} + end + if type(db.raw.namespaces[name].chars) ~= "table" then + db.raw.namespaces[name].chars = {} + end + local id = charID + if type(db.raw.namespaces[name].chars[id]) ~= "table" then + db.raw.namespaces[name].chars[id] = {} + end + rawset(namespace, 'char', db.raw.namespaces[name].chars[id]) + end + if namespace.defaults and namespace.defaults.char then + inheritDefaults(namespace.char, namespace.defaults.char) + end + return namespace.char + elseif key == "realm" then + if type(db.raw.namespaces) ~= "table" then + db.raw.namespaces = {} + end + if type(db.raw.namespaces[name]) ~= "table" then + db.raw.namespaces[name] = {} + end + if type(db.raw.namespaces[name].realms) ~= "table" then + db.raw.namespaces[name].realms = {} + end + local id = realmID + if type(db.raw.namespaces[name].realms[id]) ~= "table" then + db.raw.namespaces[name].realms[id] = {} + end + rawset(namespace, 'realm', db.raw.namespaces[name].realms[id]) + if namespace.defaults and namespace.defaults.realm then + inheritDefaults(namespace.realm, namespace.defaults.realm) + end + return namespace.realm + elseif key == "server" then + if type(db.raw.namespaces) ~= "table" then + db.raw.namespaces = {} + end + if type(db.raw.namespaces[name]) ~= "table" then + db.raw.namespaces[name] = {} + end + if type(db.raw.namespaces[name].servers) ~= "table" then + db.raw.namespaces[name].servers = {} + end + local id = server + if type(db.raw.namespaces[name].servers[id]) ~= "table" then + db.raw.namespaces[name].servers[id] = {} + end + rawset(namespace, 'server', db.raw.namespaces[name].servers[id]) + if namespace.defaults and namespace.defaults.server then + inheritDefaults(namespace.server, namespace.defaults.server) + end + return namespace.server + elseif key == "account" then + if type(db.raw.namespaces) ~= "table" then + db.raw.namespaces = {} + end + if type(db.raw.namespaces[name]) ~= "table" then + db.raw.namespaces[name] = {} + end + if type(db.raw.namespaces[name].account) ~= "table" then + db.raw.namespaces[name].account = {} + end + rawset(namespace, 'account', db.raw.namespaces[name].account) + if namespace.defaults and namespace.defaults.account then + inheritDefaults(namespace.account, namespace.defaults.account) + end + return namespace.account + elseif key == "faction" then + if type(db.raw.namespaces) ~= "table" then + db.raw.namespaces = {} + end + if type(db.raw.namespaces[name]) ~= "table" then + db.raw.namespaces[name] = {} + end + if type(db.raw.namespaces[name].factions) ~= "table" then + db.raw.namespaces[name].factions = {} + end + local id = faction + if type(db.raw.namespaces[name].factions[id]) ~= "table" then + db.raw.namespaces[name].factions[id] = {} + end + rawset(namespace, 'faction', db.raw.namespaces[name].factions[id]) + if namespace.defaults and namespace.defaults.faction then + inheritDefaults(namespace.faction, namespace.defaults.faction) + end + return namespace.faction + elseif key == "class" then + if type(db.raw.namespaces) ~= "table" then + db.raw.namespaces = {} + end + if type(db.raw.namespaces[name]) ~= "table" then + db.raw.namespaces[name] = {} + end + if type(db.raw.namespaces[name].classes) ~= "table" then + db.raw.namespaces[name].classes = {} + end + local id = classID + if type(db.raw.namespaces[name].classes[id]) ~= "table" then + db.raw.namespaces[name].classes[id] = {} + end + rawset(namespace, 'class', db.raw.namespaces[name].classes[id]) + if namespace.defaults and namespace.defaults.class then + inheritDefaults(namespace.class, namespace.defaults.class) + end + return namespace.class + elseif key == "profile" then + if type(db.raw.namespaces) ~= "table" then + db.raw.namespaces = {} + end + if type(db.raw.namespaces[name]) ~= "table" then + db.raw.namespaces[name] = {} + end + if type(db.raw.namespaces[name].profiles) ~= "table" then + db.raw.namespaces[name].profiles = setmetatable({}, caseInsensitive_mt) + else + setmetatable(db.raw.namespaces[name].profiles, caseInsensitive_mt) + end + local id = db.raw.currentProfile[charID] + if id == "char" then + id = "char/" .. charID + elseif id == "class" then + id = "class/" .. classID + elseif id == "realm" then + id = "realm/" .. realmID + end + if type(db.raw.namespaces[name].profiles[id]) ~= "table" then + db.raw.namespaces[name].profiles[id] = {} + end + rawset(namespace, 'profile', db.raw.namespaces[name].profiles[id]) + if namespace.defaults and namespace.defaults.profile then + inheritDefaults(namespace.profile, namespace.defaults.profile) + end + return namespace.profile + elseif key == "defaults" or key == "name" or key == "db" then + return nil + end + error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2) +end, __newindex = function(db, key, value) + error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2) +end } + +local tmp = {} +function AceDB:InitializeDB(addonName) + local db = self.db + + if not db then + if addonName then + AceDB.addonsLoaded[addonName] = true + end + return + end + + if db.raw then + -- someone manually initialized + return + end + + if type(_G[db.name]) ~= "table" then + _G[db.name] = {} + else + CrawlForDeserialization(_G[db.name]) + end + if db.charName then + if type(_G[db.charName]) ~= "table" then + _G[db.charName] = {} + else + CrawlForDeserialization(_G[db.charName]) + end + end + rawset(db, 'raw', _G[db.name]) + if not db.raw.currentProfile then + db.raw.currentProfile = {} + else + for k,v in pairs(db.raw.currentProfile) do + tmp[convertFromOldCharID(k)] = v + db.raw.currentProfile[k] = nil + end + for k,v in pairs(tmp) do + db.raw.currentProfile[k] = v + tmp[k] = nil + end + end + if not db.raw.currentProfile[charID] then + db.raw.currentProfile[charID] = AceDB.registry[self] or "Default" + end + if db.raw.profiles then + for k,v in pairs(db.raw.profiles) do + local new_k = k + if k:find("^char/") then + new_k = "char/" .. convertFromOldCharID(k:sub(6)) + end + tmp[new_k] = v + db.raw.profiles[k] = nil + end + for k,v in pairs(tmp) do + db.raw.profiles[k] = v + tmp[k] = nil + end + end + if db.raw.disabledModules then -- AceModuleCore-2.0 + for k,v in pairs(db.raw.disabledModules) do + local new_k = k + if k:find("^char/") then + new_k = "char/" .. convertFromOldCharID(k:sub(6)) + end + tmp[new_k] = v + db.raw.disabledModules[k] = nil + end + for k,v in pairs(tmp) do + db.raw.disabledModules[k] = v + tmp[k] = nil + end + end + if db.raw.chars then + for k,v in pairs(db.raw.chars) do + tmp[convertFromOldCharID(k)] = v + db.raw.chars[k] = nil + end + for k,v in pairs(tmp) do + db.raw.chars[k] = v + tmp[k] = nil + end + end + if db.raw.namespaces then + for l,u in pairs(db.raw.namespaces) do + if u.chars then + for k,v in pairs(u.chars) do + tmp[convertFromOldCharID(k)] = v + u.chars[k] = nil + end + for k,v in pairs(tmp) do + u.chars[k] = v + tmp[k] = nil + end + end + end + end + if db.raw.disabled then + setmetatable(db.raw.disabled, caseInsensitive_mt) + end + if self['acedb-profile-copylist'] then + RecalculateAceDBCopyFromList(self) + end + if self['acedb-profile-list'] then + RecalculateAceDBProfileList(self) + end + setmetatable(db, db_mt) +end + +function AceDB:OnEmbedInitialize(target, name) + if name then + self:ADDON_LOADED(name) + end + self.InitializeDB(target, name) +end + +function AceDB:RegisterDB(name, charName, defaultProfile) + AceDB:argCheck(name, 2, "string") + AceDB:argCheck(charName, 3, "string", "nil") + AceDB:argCheck(defaultProfile, 4, "string", "nil") + if self.db then + AceDB:error("Cannot call \"RegisterDB\" if self.db is set.") + end + local stack = debugstack() + local addonName = stack:gsub(".-\n.-\\AddOns\\(.-)\\.*", "%1") + self.db = { + name = name, + charName = charName + } + AceDB.registry[self] = defaultProfile or "Default" + if AceDB.addonsLoaded[addonName] then + AceDB.InitializeDB(self, addonName) + else + AceDB.addonsToBeInitialized[self] = addonName + end +end + +function AceDB:RegisterDefaults(kind, defaults, a3) + local name + if a3 then + name, kind, defaults = kind, defaults, a3 + AceDB:argCheck(name, 2, "string") + AceDB:argCheck(kind, 3, "string") + AceDB:argCheck(defaults, 4, "table") + else + AceDB:argCheck(kind, 2, "string") + AceDB:argCheck(defaults, 3, "table") + end + if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" and kind ~= "faction" and kind ~= "server" then + AceDB:error("Bad argument #%d to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", \"realm\", \"server\", or \"faction\" expected, got %q)", a3 and 3 or 2, kind) + end + if type(self.db) ~= "table" or type(self.db.name) ~= "string" then + AceDB:error("Cannot call \"RegisterDefaults\" unless \"RegisterDB\" has been previously called.") + end + local db + if name then + local namespace = self:AcquireDBNamespace(name) + if namespace.defaults and namespace.defaults[kind] then + AceDB:error("\"RegisterDefaults\" has already been called for %q::%q.", name, kind) + end + db = namespace + else + if self.db.defaults and self.db.defaults[kind] then + AceDB:error("\"RegisterDefaults\" has already been called for %q.", kind) + end + db = self.db + end + if not db.defaults then + rawset(db, 'defaults', {}) + end + db.defaults[kind] = defaults + if rawget(db, kind) then + inheritDefaults(db[kind], defaults) + end +end + +function AceDB:ResetDB(kind, a2) + local name + if a2 then + name, kind = kind, a2 + AceDB:argCheck(name, 2, "nil", "string") + AceDB:argCheck(kind, 3, "nil", "string") + else + AceDB:argCheck(kind, 2, "nil", "string") + if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" and kind ~= "faction" and kind ~= "server" then + name, kind = kind, nil + end + end + if not self.db or not self.db.raw then + AceDB:error("Cannot call \"ResetDB\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") + end + local db = self.db + if not kind then + if not name then + if db.charName then + _G[db.charName] = nil + end + _G[db.name] = nil + rawset(db, 'raw', nil) + AceDB.InitializeDB(self) + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'account', nil) + rawset(v, 'char', nil) + rawset(v, 'class', nil) + rawset(v, 'profile', nil) + rawset(v, 'realm', nil) + rawset(v, 'server', nil) + rawset(v, 'faction', nil) + end + end + else + if db.raw.namespaces then + db.raw.namespaces[name] = nil + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'account', nil) + rawset(v, 'char', nil) + rawset(v, 'class', nil) + rawset(v, 'profile', nil) + rawset(v, 'realm', nil) + rawset(v, 'server', nil) + rawset(v, 'faction', nil) + end + end + end + elseif kind == "account" then + if name then + db.raw.account = nil + rawset(db, 'account', nil) + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + v.account = nil + end + end + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'account', nil) + end + end + else + if db.raw.namespaces and db.raw.namespaces[name] then + db.raw.namespaces[name].account = nil + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'account', nil) + end + end + end + elseif kind == "char" then + if name then + if db.charName then + _G[db.charName] = nil + else + if db.raw.chars then + db.raw.chars[charID] = nil + end + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + if v.chars then + v.chars[charID] = nil + end + end + end + end + rawset(db, 'char', nil) + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'char', nil) + end + end + else + if db.charName then + local x = _G[db.charName] + if x.namespaces then + x.namespaces[name] = nil + end + else + if db.raw.namespaces then + local v = db.namespaces[name] + if v and v.chars then + v.chars[charID] = nil + end + end + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'char', nil) + end + end + end + elseif kind == "realm" then + if not name then + if db.raw.realms then + db.raw.realms[realmID] = nil + end + rawset(db, 'realm', nil) + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + if v.realms then + v.realms[realmID] = nil + end + end + end + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'realm', nil) + end + end + else + if db.raw.namespaces then + local v = db.raw.namespaces[name] + if v and v.realms then + v.realms[realmID] = nil + end + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'realm', nil) + end + end + end + elseif kind == "server" then + if not name then + if db.raw.servers then + db.raw.servers[server] = nil + end + rawset(db, 'server', nil) + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + if v.servers then + v.servers[server] = nil + end + end + end + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'server', nil) + end + end + else + if db.raw.namespaces then + local v = db.raw.namespaces[name] + if v and v.servers then + v.servers[server] = nil + end + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'server', nil) + end + end + end + elseif kind == "faction" then + if not name then + if db.raw.factions then + db.raw.factions[faction] = nil + end + rawset(db, 'faction', nil) + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + if v.factions then + v.factions[faction] = nil + end + end + end + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'faction', nil) + end + end + else + if db.raw.namespaces then + local v = db.raw.namespaces[name] + if v and v.factions then + v.factions[faction] = nil + end + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'faction', nil) + end + end + end + elseif kind == "class" then + if not name then + if db.raw.realms then + db.raw.realms[classID] = nil + end + rawset(db, 'class', nil) + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + if v.classes then + v.classes[classID] = nil + end + end + end + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'class', nil) + end + end + else + if db.raw.namespaces then + local v = db.raw.namespaces[name] + if v and v.classes then + v.classes[classID] = nil + end + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'class', nil) + end + end + end + elseif kind == "profile" then + local id = db.raw.currentProfile and db.raw.currentProfile[charID] or AceDB.registry[self] or "Default" + if id == "char" then + id = "char/" .. charID + elseif id == "class" then + id = "class/" .. classID + elseif id == "realm" then + id = "realm/" .. realmID + end + + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedProfileDisable) == "function" then + safecall(mixin.OnEmbedProfileDisable, mixin, self, id) + end + end + end + current = current.super + end + if type(self.OnProfileDisable) == "function" then + safecall(self.OnProfileDisable, self, id) + end + local active = self:IsActive() + + if not name then + if db.raw.profiles then + db.raw.profiles[id] = nil + end + rawset(db, 'profile', nil) + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + if v.profiles then + v.profiles[id] = nil + end + end + end + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'profile', nil) + end + end + else + if db.raw.namespaces then + local v = db.raw.namespaces[name] + if v and v.profiles then + v.profiles[id] = nil + end + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'profile', nil) + end + end + end + + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedProfileEnable) == "function" then + safecall(mixin.OnEmbedProfileEnable, mixin, self, id) + end + end + end + current = current.super + end + if type(self.OnProfileEnable) == "function" then + safecall(self.OnProfileEnable, self, id) + end + local newactive = self:IsActive() + if active ~= newactive then + if newactive then + local first = nil + if AceOO.inherits(self, "AceAddon-2.0") then + local AceAddon = AceLibrary("AceAddon-2.0") + if not AceAddon.addonsStarted[self] then + return + end + if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[self] then + AceAddon.addonsEnabled[self] = true + first = true + end + end + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable, mixin, self, first) + end + end + end + current = current.super + end + if type(self.OnEnable) == "function" then + safecall(self.OnEnable, self, first) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", self, first) + end + else + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedDisable) == "function" then + safecall(mixin.OnEmbedDisable, mixin, self) + end + end + end + current = current.super + end + if type(self.OnDisable) == "function" then + safecall(self.OnDisable, self) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonDisabled", self) + end + end + end + else + return -- skip event + end + if AceEvent then + AceEvent:TriggerEvent("AceDB20_ResetDB", self, self.db.name, kind) + end +end + +local function cleanDefaults(t, defaults, blocker) + if defaults then + for k,v in pairs(t) do + if (not blocker or (blocker[k] == nil and blocker['*'] == nil and blocker['**'] == nil)) and (defaults[k] ~= nil or defaults['*'] ~= nil or defaults['**'] ~= nil) then + local u = defaults[k] + if u == nil then + u = defaults['*'] + if u == nil then + u = defaults['**'] + end + end + if v == u then + t[k] = nil + elseif type(v) == "table" and type(u) == "table" then + if cleanDefaults(v, u) then + t[k] = nil + else + local w = defaults['**'] + if w ~= u then + if cleanDefaults(v, w, u) then + t[k] = nil + end + end + end + end + end + end + end + return t and next(t) == nil +end + +function AceDB:GetProfile() + if not self.db or not self.db.raw then + return nil + end + if not self.db.raw.currentProfile then + self.db.raw.currentProfile = {} + end + if not self.db.raw.currentProfile[charID] then + self.db.raw.currentProfile[charID] = AceDB.registry[self] or "Default" + end + local profile = self.db.raw.currentProfile[charID] + if profile == "char" then + return "char", "char/" .. charID + elseif profile == "class" then + return "class", "class/" .. classID + elseif profile == "realm" then + return "realm", "realm/" .. realmID + end + return profile, profile +end + +local function copyTable(to, from) + setmetatable(to, nil) + for k,v in pairs(from) do + if type(k) == "table" then + k = copyTable({}, k) + end + if type(v) == "table" then + v = copyTable({}, v) + end + to[k] = v + end + setmetatable(to, from) + return to +end + +function AceDB:SetProfile(name) + AceDB:argCheck(name, 2, "string") + if not self.db or not self.db.raw then + AceDB:error("Cannot call \"SetProfile\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") + end + local db = self.db + local lowerName = name:lower() + if lowerName:find("^char/") or lowerName:find("^realm/") or lowerName:find("^class/") then + if lowerName:find("^char/") then + name = "char" + else + name = lowerName:sub(1, 5) + end + lowerName = name:lower() + end + local oldName = db.raw.currentProfile[charID] + if oldName:lower() == name:lower() then + return + end + local oldProfileData = db.profile + local realName = name + if lowerName == "char" then + realName = name .. "/" .. charID + elseif lowerName == "realm" then + realName = name .. "/" .. realmID + elseif lowerName == "class" then + realName = name .. "/" .. classID + end + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedProfileDisable) == "function" then + safecall(mixin.OnEmbedProfileDisable, mixin, self, realName) + end + end + end + current = current.super + end + if type(self.OnProfileDisable) == "function" then + safecall(self.OnProfileDisable, self, realName) + end + local active = self:IsActive() + db.raw.currentProfile[charID] = name + rawset(db, 'profile', nil) + if db.namespaces then + for k,v in pairs(db.namespaces) do + rawset(v, 'profile', nil) + end + end + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedProfileEnable) == "function" then + safecall(mixin.OnEmbedProfileEnable, mixin, self, oldName, oldProfileData) + end + end + end + current = current.super + end + if type(self.OnProfileEnable) == "function" then + safecall(self.OnProfileEnable, self, oldName, oldProfileData) + end + if cleanDefaults(oldProfileData, db.defaults and db.defaults.profile) then + db.raw.profiles[oldName] = nil + if not next(db.raw.profiles) then + db.raw.profiles = nil + end + end + local newactive = self:IsActive() + if active ~= newactive then + local first = nil + if AceOO.inherits(self, "AceAddon-2.0") then + local AceAddon = AceLibrary("AceAddon-2.0") + if not AceAddon.addonsStarted[self] then + return + end + if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[self] then + first = true + end + end + if newactive then + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable, mixin, self, first) + end + end + end + current = current.super + end + if type(self.OnEnable) == "function" then + safecall(self.OnEnable, self, first) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", self, first) + end + else + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedDisable) == "function" then + safecall(mixin.OnEmbedDisable, mixin, self) + end + end + end + current = current.super + end + if type(self.OnDisable) == "function" then + safecall(self.OnDisable, self) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonDisabled", self) + end + end + end + if self['acedb-profile-list'] then + RecalculateAceDBProfileList(self) + end + if self['acedb-profile-copylist'] then + RecalculateAceDBCopyFromList(self) + end + if Dewdrop then + Dewdrop:Refresh() + end +end + +function AceDB:CopyProfileFrom(copyFrom) + AceDB:argCheck(copyFrom, 2, "string") + if not self.db or not self.db.raw then + AceDB:error("Cannot call \"CopyProfileFrom\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") + end + local db = self.db + local lowerCopyFrom = copyFrom:lower() + if not db.raw.profiles or not db.raw.profiles[copyFrom] then + local good = false + if db.raw.namespaces then + for _,n in pairs(db.raw.namespaces) do + if n.profiles and n.profiles[copyFrom] then + good = true + break + end + end + end + if not good then + AceDB:error("Cannot copy from profile %q, it does not exist.", copyFrom) + end + end + local currentProfile = db.raw.currentProfile[charID] + if currentProfile:lower() == lowerCopyFrom then + AceDB:error("Cannot copy from profile %q, it is currently in use.", copyFrom) + end + local oldProfileData = db.profile + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedProfileDisable) == "function" then + safecall(mixin.OnEmbedProfileDisable, mixin, self, currentProfile) + end + end + end + current = current.super + end + if type(self.OnProfileDisable) == "function" then + safecall(self.OnProfileDisable, self, realName) + end + local active = self:IsActive() + for k,v in pairs(db.profile) do + db.profile[k] = nil + end + if db.raw.profiles[copyFrom] then + copyTable(db.profile, db.raw.profiles[copyFrom]) + end + inheritDefaults(db.profile, db.defaults and db.defaults.profile) + if db.namespaces then + for l,u in pairs(db.namespaces) do + for k,v in pairs(u.profile) do + u.profile[k] = nil + end + if db.raw.namespaces[l].profiles[copyFrom] then + copyTable(u.profile, db.raw.namespaces[l].profiles[copyFrom]) + end + inheritDefaults(u.profile, u.defaults and u.defaults.profile) + end + end + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedProfileEnable) == "function" then + safecall(mixin.OnEmbedProfileEnable, mixin, self, copyFrom, oldProfileData, copyFrom) + end + end + end + current = current.super + end + if type(self.OnProfileEnable) == "function" then + safecall(self.OnProfileEnable, self, copyFrom, oldProfileData, copyFrom) + end + local newactive = self:IsActive() + if active ~= newactive then + if AceOO.inherits(self, "AceAddon-2.0") then + local AceAddon = AceLibrary("AceAddon-2.0") + if not AceAddon.addonsStarted[self] then + return + end + end + if newactive then + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable, mixin, self) + end + end + end + current = current.super + end + if type(self.OnEnable) == "function" then + safecall(self.OnEnable, self) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", self) + end + else + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedDisable) == "function" then + safecall(mixin.OnEmbedDisable, mixin, self) + end + end + end + current = current.super + end + if type(self.OnDisable) == "function" then + safecall(self.OnDisable, self) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonDisabled", self) + end + end + end + if self['acedb-profile-list'] then + RecalculateAceDBProfileList(self) + end + if self['acedb-profile-copylist'] then + RecalculateAceDBCopyFromList(self) + end + if Dewdrop then + Dewdrop:Refresh() + end +end + +function AceDB:DeleteProfile(profile, noconfirm) + AceDB:argCheck(profile , 2, "string") + if not self.db or not self.db.raw then + AceDB:error("Cannot call \"DeleteProfile\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") + end + local db = self.db + + local currentProfile = db.raw.currentProfile[charID] + if currentProfile:lower() == profile:lower() then + AceDB:error("Cannot delete profile %q, it is currently in use.", profile) + end + + if not (noconfirm or IsShiftKeyDown()) then + if not StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] then + StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] = {} + end + local t = StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] + t.text = format("%s: %s?", DELETE_PROFILE, profile) + t.button1 = DELETE_PROFILE + t.button2 = CANCEL or "Cancel" + t.OnAccept = function() + self:DeleteProfile(profile, true) + end + t.timeout = 0 + t.whileDead = 1 + t.hideOnEscape = 1 + + StaticPopup_Show("ACEDB20_CONFIRM_DELETE_DIALOG") + return; + end + + local good = false + if db.raw.profiles and db.raw.profiles[profile] then + good = true; + db.raw.profiles[profile] = nil; + end + + if db.raw.namespaces then + for _,n in pairs(db.raw.namespaces) do + if n.profiles and n.profiles[profile] then + n.profiles[profile] = nil; + good = true + end + end + end + + if not good then + AceDB:error("Cannot delete profile %q, it does not exist.", profile) + end + + if self['acedb-profile-list'] then + RecalculateAceDBProfileList(self) + end + if self['acedb-profile-copylist'] then + RecalculateAceDBCopyFromList(self) + end + + if Dewdrop then + Dewdrop:Refresh() + end +end + +function AceDB:IsActive() + return not self.db or not self.db.raw or not self.db.raw.disabled or not self.db.raw.disabled[self.db.raw.currentProfile[charID]] +end + +function AceDB:ToggleActive(state) + AceDB:argCheck(state, 2, "boolean", "nil") + if not self.db or not self.db.raw then + AceDB:error("Cannot call \"ToggleActive\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") + end + local db = self.db + if not db.raw.disabled then + db.raw.disabled = setmetatable({}, caseInsensitive_mt) + end + local profile = db.raw.currentProfile[charID] + local disable + if state == nil then + disable = not db.raw.disabled[profile] + else + disable = not state + if disable == db.raw.disabled[profile] then + return + end + end + db.raw.disabled[profile] = disable or nil + if AceOO.inherits(self, "AceAddon-2.0") then + local AceAddon = AceLibrary("AceAddon-2.0") + if not AceAddon.addonsStarted[self] then + return + end + end + if not disable then + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable, mixin, self) + end + end + end + current = current.super + end + if type(self.OnEnable) == "function" then + safecall(self.OnEnable, self) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", self) + end + else + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedDisable) == "function" then + safecall(mixin.OnEmbedDisable, mixin, self) + end + end + end + current = current.super + end + if type(self.OnDisable) == "function" then + safecall(self.OnDisable, self) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonDisabled", self) + end + end + return not disable +end + +function AceDB:embed(target) + self.super.embed(self, target) + if not AceEvent then + AceDB:error(MAJOR_VERSION .. " requires AceEvent-2.0") + end +end + +function AceDB:ADDON_LOADED(name) + AceDB.addonsLoaded[name] = true + for addon, addonName in pairs(AceDB.addonsToBeInitialized) do + if name == addonName then + AceDB.InitializeDB(addon, name) + AceDB.addonsToBeInitialized[addon] = nil + end + end +end + +function AceDB:PLAYER_LOGOUT() + for addon, defaultProfile in pairs(AceDB.registry) do + local db = addon.db + if db then + if type(addon.OnDatabaseCleanup) == "function" then + safecall(addon.OnDatabaseCleanup, addon) + end + setmetatable(db, nil) + CrawlForSerialization(db.raw) + if type(_G[db.charName]) == "table" then + CrawlForSerialization(_G[db.charName]) + end + if db.char and cleanDefaults(db.char, db.defaults and db.defaults.char) then + if db.charName and _G[db.charName] and _G[db.charName].global == db.char then + _G[db.charName].global = nil + if not next(_G[db.charName]) then + _G[db.charName] = nil + end + else + if db.raw.chars then + db.raw.chars[charID] = nil + if not next(db.raw.chars) then + db.raw.chars = nil + end + end + end + end + if db.realm and cleanDefaults(db.realm, db.defaults and db.defaults.realm) then + if db.raw.realms then + db.raw.realms[realmID] = nil + if not next(db.raw.realms) then + db.raw.realms = nil + end + end + end + if db.server and cleanDefaults(db.server, db.defaults and db.defaults.server) then + if db.raw.servers then + db.raw.servers[server] = nil + if not next(db.raw.servers) then + db.raw.servers = nil + end + end + end + if db.faction and cleanDefaults(db.faction, db.defaults and db.defaults.faction) then + if db.raw.factions then + db.raw.factions[faction] = nil + if not next(db.raw.factions) then + db.raw.factions = nil + end + end + end + if db.class and cleanDefaults(db.class, db.defaults and db.defaults.class) then + if db.raw.classes then + db.raw.classes[classID] = nil + if not next(db.raw.classes) then + db.raw.classes = nil + end + end + end + if db.account and cleanDefaults(db.account, db.defaults and db.defaults.account) then + db.raw.account = nil + end + if db.profile and cleanDefaults(db.profile, db.defaults and db.defaults.profile) then + if db.raw.profiles then + db.raw.profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or defaultProfile or "Default"] = nil + if not next(db.raw.profiles) then + db.raw.profiles = nil + end + end + end + if db.namespaces and db.raw.namespaces then + for name,v in pairs(db.namespaces) do + if db.raw.namespaces[name] then + setmetatable(v, nil) + if v.char and cleanDefaults(v.char, v.defaults and v.defaults.char) then + if db.charName and _G[db.charName] and _G[db.charName].namespaces and _G[db.charName].namespaces[name] == v then + _G[db.charName].namespaces[name] = nil + if not next(_G[db.charName].namespaces) then + _G[db.charName].namespaces = nil + if not next(_G[db.charName]) then + _G[db.charName] = nil + end + end + else + if db.raw.namespaces[name].chars then + db.raw.namespaces[name].chars[charID] = nil + if not next(db.raw.namespaces[name].chars) then + db.raw.namespaces[name].chars = nil + end + end + end + end + if v.realm and cleanDefaults(v.realm, v.defaults and v.defaults.realm) then + if db.raw.namespaces[name].realms then + db.raw.namespaces[name].realms[realmID] = nil + if not next(db.raw.namespaces[name].realms) then + db.raw.namespaces[name].realms = nil + end + end + end + if v.server and cleanDefaults(v.server, v.defaults and v.defaults.server) then + if db.raw.namespaces[name].servers then + db.raw.namespaces[name].servers[server] = nil + if not next(db.raw.namespaces[name].servers) then + db.raw.namespaces[name].servers = nil + end + end + end + if v.faction and cleanDefaults(v.faction, v.defaults and v.defaults.faction) then + if db.raw.namespaces[name].factions then + db.raw.namespaces[name].factions[faction] = nil + if not next(db.raw.namespaces[name].factions) then + db.raw.namespaces[name].factions = nil + end + end + end + if v.class and cleanDefaults(v.class, v.defaults and v.defaults.class) then + if db.raw.namespaces[name].classes then + db.raw.namespaces[name].classes[classID] = nil + if not next(db.raw.namespaces[name].classes) then + db.raw.namespaces[name].classes = nil + end + end + end + if v.account and cleanDefaults(v.account, v.defaults and v.defaults.account) then + db.raw.namespaces[name].account = nil + end + if v.profile and cleanDefaults(v.profile, v.defaults and v.defaults.profile) then + if db.raw.namespaces[name].profiles then + db.raw.namespaces[name].profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or defaultProfile or "Default"] = nil + if not next(db.raw.namespaces[name].profiles) then + db.raw.namespaces[name].profiles = nil + end + end + end + if not next(db.raw.namespaces[name]) then + db.raw.namespaces[name] = nil + end + end + end + if not next(db.raw.namespaces) then + db.raw.namespaces = nil + end + end + if db.raw.disabled and not next(db.raw.disabled) then + db.raw.disabled = nil + end + if db.raw.currentProfile then + for k,v in pairs(db.raw.currentProfile) do + if v:lower() == (defaultProfile or "Default"):lower() then + db.raw.currentProfile[k] = nil + end + end + if not next(db.raw.currentProfile) then + db.raw.currentProfile = nil + end + end + if _G[db.name] and not next(_G[db.name]) then + _G[db.name] = nil + end + end + end +end + +function AceDB:AcquireDBNamespace(name) + AceDB:argCheck(name, 2, "string") + local db = self.db + if not db then + AceDB:error("Cannot call `AcquireDBNamespace' before `RegisterDB' has been called.", 2) + end + if not db.namespaces then + rawset(db, 'namespaces', {}) + end + if not db.namespaces[name] then + local namespace = {} + db.namespaces[name] = namespace + namespace.db = db + namespace.name = name + setmetatable(namespace, namespace_mt) + end + return db.namespaces[name] +end + +function AceDB:GetAceOptionsDataTable(target) + if not target['acedb-profile-list'] then + target['acedb-profile-list'] = setmetatable({}, caseInsensitive_mt) + RecalculateAceDBProfileList(target) + end + if not target['acedb-profile-copylist'] then + target['acedb-profile-copylist'] = setmetatable({}, caseInsensitive_mt) + RecalculateAceDBCopyFromList(target) + end + return { + standby = { + cmdName = STATE, + guiName = ENABLED, + name = ACTIVE, + desc = TOGGLE_ACTIVE, + type = "toggle", + get = "IsActive", + set = "ToggleActive", + map = MAP_ACTIVESUSPENDED, + order = -3, + }, + profile = { + type = 'group', + name = PROFILE, + desc = SET_PROFILE, + order = -3.5, + get = "GetProfile", + args = { + choose = { + guiName = CHOOSE_PROFILE_GUI, + cmdName = PROFILE, + desc = CHOOSE_PROFILE_DESC, + type = 'text', + get = "GetProfile", + set = "SetProfile", + validate = target['acedb-profile-list'] + }, + copy = { + guiName = COPY_PROFILE_GUI, + cmdName = PROFILE, + desc = COPY_PROFILE_DESC, + type = 'text', + get = false, + set = "CopyProfileFrom", + validate = target['acedb-profile-copylist'], + disabled = function() + return not next(target['acedb-profile-copylist']) + end, + }, + other = { + guiName = OTHER_PROFILE_GUI, + cmdName = PROFILE, + desc = OTHER_PROFILE_DESC, + usage = OTHER_PROFILE_USAGE, + type = 'text', + get = "GetProfile", + set = "SetProfile", + }, + delete = { + name = DELETE_PROFILE, + desc = DELETE_PROFILE_DESC, + usage = DELETE_PROFILE_USAGE, + type = 'text', + set = "DeleteProfile", + get = false, + validate = target['acedb-profile-copylist'], + disabled = function() + return not next(target['acedb-profile-copylist']) + end, + }, + reset = { + name = RESET_PROFILE, + desc = RESET_PROFILE_DESC, + type = 'execute', + func = function() + target:ResetDB('profile') + end, + confirm = true, + } + } + }, + } +end + +local function activate(self, oldLib, oldDeactivate) + AceDB = self + AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0") + + self.addonsToBeInitialized = oldLib and oldLib.addonsToBeInitialized or {} + self.addonsLoaded = oldLib and oldLib.addonsLoaded or {} + self.registry = oldLib and oldLib.registry or {} + for k, v in pairs(self.registry) do + if v == true then + self.registry[k] = "Default" + end + end + + self:activate(oldLib, oldDeactivate) + + for t in pairs(self.embedList) do + if t.db then + rawset(t.db, 'char', nil) + rawset(t.db, 'realm', nil) + rawset(t.db, 'class', nil) + rawset(t.db, 'account', nil) + rawset(t.db, 'server', nil) + rawset(t.db, 'faction', nil) + rawset(t.db, 'profile', nil) + setmetatable(t.db, db_mt) + end + end + + if oldLib then + oldDeactivate(oldLib) + end +end + +local function external(self, major, instance) + if major == "AceEvent-2.0" then + AceEvent = instance + + AceEvent:embed(self) + + self:RegisterEvent("ADDON_LOADED") + self:RegisterEvent("PLAYER_LOGOUT") + elseif major == "Dewdrop-2.0" then + Dewdrop = instance + end +end + +AceLibrary:Register(AceDB, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) +AceDB = AceLibrary(MAJOR_VERSION)