diff --git a/Dongle.lua b/Dongle.lua
index e9f54b7..4590ba4 100644
--- a/Dongle.lua
+++ b/Dongle.lua
@@ -90,7 +90,7 @@ Begin Library Implementation
---------------------------------------------------------------------------]]
local major = "Dongle"
-local minor = tonumber(select(3,string.find("$Revision: 70 $", "(%d+)")) or 1)
+local minor = tonumber(select(3,string.find("$Revision: 92 $", "(%d+)")) or 1)
assert(DongleStub, string.format("%s requires DongleStub.", major))
if not DongleStub:IsNewerVersion(major, minor) then return end
@@ -98,9 +98,8 @@ if not DongleStub:IsNewerVersion(major, minor) then return end
Dongle = {}
local methods = {
"RegisterEvent", "UnregisterEvent", "UnregisterAllEvents", "TriggerEvent",
- "EnableDebug", "Print", "Debug",
- "InitializeDB", "RegisterDBDefaults", "ResetDB",
- "SetProfile", "CopyProfile", "ResetProfile", "DeleteProfile",
+ "EnableDebug", "Print", "PrintF", "Debug", "DebugF",
+ "InitializeDB",
"NewModule", "HasModule", "IterateModules",
}
@@ -109,6 +108,8 @@ local lookup = {}
local loadqueue = {}
local loadorder = {}
local events = {}
+local databases = {}
+local commands = {}
local function assert(level,condition,message)
if not condition then
@@ -132,17 +133,16 @@ end
local function safecall(func,...)
local success,err = pcall(func,...)
- if not success then
+ if not success then
geterrorhandler()(err)
end
end
-function Dongle:New(obj, name)
- argcheck(obj, 2, "table", "string", "nil")
- argcheck(name, 3, "string", "nil")
+function Dongle:New(name, obj)
+ argcheck(name, 2, "string")
+ argcheck(obj, 3, "table", "nil")
- if not name and type(obj) == "string" then
- name = obj
+ if not obj then
obj = {}
end
@@ -165,27 +165,29 @@ function Dongle:New(obj, name)
return obj,name
end
-function Dongle:NewModule(obj, name)
+function Dongle:NewModule(name, obj)
local reg = lookup[self]
assert(3, reg, "You must call 'NewModule' from a registered Dongle.")
- argcheck(obj, 2, "table", "string", "nil")
- argcheck(name, 3, "string", "nil")
+ argcheck(name, 2, "string")
+ argcheck(obj, 3, "table", "nil")
- obj,name = Dongle:New(obj, name)
+ obj,name = Dongle:New(name, obj)
if not reg.modules then reg.modules = {} end
+ reg.modules[obj] = obj
+ reg.modules[name] = obj
table.insert(reg.modules, name)
table.sort(reg.modules)
return obj,name
end
-function Dongle:HasModule(name)
+function Dongle:HasModule(module)
local reg = lookup[self]
assert(3, reg, "You must call 'HasModule' from a registered Dongle.")
- argcheck(name, 2, "string")
+ argcheck(module, 2, "string", "table")
- return lookup[name]
+ return reg.modules[module]
end
local EMPTY_TABLE = {}
@@ -228,7 +230,7 @@ function Dongle:TriggerEvent(event, ...)
if eventTbl then
for obj,func in pairs(eventTbl) do
if type(func) == "string" then
- if type(obj[func]) then
+ if type(obj[func]) == "function" then
safecall(obj[func], obj, event, ...)
end
else
@@ -243,7 +245,7 @@ function Dongle:OnEvent(frame, event, ...)
if eventTbl then
for obj,func in pairs(eventTbl) do
if type(func) == "string" then
- if type(obj[func]) then
+ if type(obj[func]) == "function" then
obj[func](obj, event, ...)
end
else
@@ -288,6 +290,10 @@ function Dongle:UnregisterAllEvents()
for event,tbl in pairs(events) do
tbl[self] = nil
+ if not next(tbl) then
+ events[event] = nil
+ frame:UnregisterEvent(event)
+ end
end
end
@@ -319,41 +325,80 @@ function Dongle:EnableDebug(level)
end
do
+ local function argsToStrings(a1, ...)
+ if select("#", ...) > 0 then
+ return tostring(a1), argsToStrings(...)
+ else
+ return tostring(a1)
+ end
+ end
+
local function printHelp(obj, method, msg, ...)
local reg = lookup[obj]
assert(4, reg, "You must call '"..method.."' from a registered Dongle.")
local name = reg.name
- local msg = string.format("|cFF33FF99%s|r: %s", name, msg)
+ msg = "|cFF33FF99"..name.."|r: "..tostring(msg)
+ if select("#", ...) > 0 then
+ msg = string.join(", ", msg, argsToStrings(...))
+ end
+
+ ChatFrame1:AddMessage(msg)
+ end
+
+ local function printFHelp(obj, method, msg, ...)
+ local reg = lookup[obj]
+ assert(4, reg, "You must call '"..method.."' from a registered Dongle.")
- local success,txt = pcall(string.format, msg, ...)
+ local name = reg.name
+ local success,txt = pcall(string.format, "|cFF33FF99%s|r: "..msg, name, ...)
if success then
- ChatFrame1:AddMessage(string.format(txt, ...))
+ ChatFrame1:AddMessage(txt)
else
error(string.gsub(txt, "'%?'", string.format("'%s'", method)), 3)
end
end
- function Dongle:Print(msg, ...)
- return printHelp(self, "Print", msg, ...)
+ function Dongle:Print(...)
+ return printHelp(self, "Print", ...)
+ end
+
+ function Dongle:PrintF(...)
+ return printFHelp(self, "PrintF", ...)
end
- function Dongle:Debug(level, msg, ...)
+ function Dongle:Debug(level, ...)
local reg = lookup[self]
assert(3, reg, "You must call 'Debug' from a registered Dongle.")
argcheck(level, 2, "number")
-
+
if reg.debugLevel and level >= reg.debugLevel then
- printHelp(self, "Debug", msg, ...)
+ printHelp(self, "Debug", ...)
+ end
+ end
+
+ function Dongle:DebugF(level, ...)
+ local reg = lookup[self]
+ assert(3, reg, "You must call 'DebugF' from a registered Dongle.")
+ argcheck(level, 2, "number")
+
+ if reg.debugLevel and level >= reg.debugLevel then
+ printFHelp(self, "DebugF", ...)
end
end
end
-function Dongle:InitializeDB(name, defaults)
+local dbMethods = {
+ "RegisterDefaults", "SetProfile", "GetProfiles", "DeleteProfile", "CopyProfile",
+ "ResetProfile", "ResetDB",
+}
+
+function Dongle:InitializeDB(name, defaults, defaultProfile)
local reg = lookup[self]
assert(3, reg, "You must call 'InitializeDB' from a registered Dongle.")
argcheck(name, 2, "string")
argcheck(defaults, 3, "table", "nil")
+ argcheck(defaultProfile, 4, "string", "nil")
local sv = getglobal(name)
@@ -384,6 +429,7 @@ function Dongle:InitializeDB(name, defaults)
if not sv.faction then sv.faction = {} end
if not sv.global then sv.global = {} end
if not sv.profiles then sv.profiles = {} end
+ if not sv.profileKeys then sv.profileKeys = {} end
-- Initialize this characters profiles
if not sv.char[char] then sv.char[char] = {} end
@@ -392,8 +438,8 @@ function Dongle:InitializeDB(name, defaults)
if not sv.faction[faction] then sv.faction[faction] = {} end
-- Try to get the profile selected from the char db
- local profileKey = sv.char[char].profileKey or char
- sv.char[char].profileKey = profileKey
+ local profileKey = sv.profileKeys[char] or defaultProfile or char
+ sv.profileKeys[char] = profileKey
if not sv.profiles[profileKey] then sv.profiles[profileKey] = {} end
@@ -407,40 +453,47 @@ function Dongle:InitializeDB(name, defaults)
["profiles"] = sv.profiles,
}
- local reg = lookup[self]
- reg.sv = sv
- reg.sv_name = name
- reg.db = db
- reg.db_char = char
- reg.db_realm = realm
- reg.db_class = class
- reg.db_faction = faction
- reg.db_profileKey = profileKey
+ -- Copy methods locally
+ for idx,method in pairs(dbMethods) do
+ db[method] = Dongle[method]
+ end
+
+ -- Set some properties in the object we're returning
+ db.sv = sv
+ db.sv_name = name
+ db.profileKey = profileKey
+ db.parent = reg.name
+ db.charKey = char
+ db.realmKey = realm
+ db.classKey = class
+ db.factionKey = faction
+
+ databases[db] = true
if defaults then
- self:RegisterDBDefaults(db, defaults)
+ db:RegisterDefaults(defaults)
end
return db
end
-local function copyDefaults(dest, src)
+local function copyDefaults(dest, src, force)
for k,v in pairs(src) do
if type(v) == "table" then
if not dest[k] then dest[k] = {} end
- copyDefaults(dest[k], v)
+ copyDefaults(dest[k], v, force)
else
- dest[k] = v
+ if not dest[k] or force then
+ dest[k] = v
+ end
end
end
end
-function Dongle:RegisterDBDefaults(db, defaults)
- local reg = lookup[self]
- assert(3, reg, "You must call 'RegisterDBDefaults' from a registered Dongle.")
- argcheck(db, 2, "table")
- argcheck(defaults, 3, "table")
- assert(3, reg.db, "You cannot call \"RegisterDBDefaults\" before calling \"InitializeDB\".")
+-- This function operates on a Dongle DB object
+function Dongle.RegisterDefaults(db, defaults)
+ assert(3, databases[db], "You must call 'RegisterDefaults' from a Dongle database object.")
+ argcheck(defaults, 2, "table")
if defaults.char then copyDefaults(db.char, defaults.char) end
if defaults.realm then copyDefaults(db.realm, defaults.realm) end
@@ -449,7 +502,7 @@ function Dongle:RegisterDBDefaults(db, defaults)
if defaults.global then copyDefaults(db.global, defaults.global) end
if defaults.profile then copyDefaults(db.profile, defaults.profile) end
- reg.dbDefaults = defaults
+ db.defaults = defaults
end
local function removeDefaults(db, defaults)
@@ -469,10 +522,9 @@ local function removeDefaults(db, defaults)
end
function Dongle:ClearDBDefaults()
- for name,obj in pairs(registry) do
- local db = obj.db
- local defaults = obj.dbDefaults
- local sv = obj.sv
+ for db in pairs(databases) do
+ local defaults = db.defaults
+ local sv = db.sv
if db and defaults then
if defaults.char then removeDefaults(db.char, defaults.char) end
@@ -480,152 +532,154 @@ function Dongle:ClearDBDefaults()
if defaults.class then removeDefaults(db.class, defaults.class) end
if defaults.faction then removeDefaults(db.faction, defaults.faction) end
if defaults.global then removeDefaults(db.global, defaults.global) end
- if defaults.profile then
+ if defaults.profile then
for k,v in pairs(sv.profiles) do
removeDefaults(sv.profiles[k], defaults.profile)
end
end
-- Remove any blank "profiles"
- if not next(db.char) then sv.char[obj.db_char] = nil end
- if not next(db.realm) then sv.realm[obj.db_realm] = nil end
- if not next(db.class) then sv.class[obj.db_class] = nil end
- if not next(db.faction) then sv.faction[obj.db_faction] = nil end
+ if not next(db.char) then sv.char[db.charKey] = nil end
+ if not next(db.realm) then sv.realm[db.realmKey] = nil end
+ if not next(db.class) then sv.class[db.classKey] = nil end
+ if not next(db.faction) then sv.faction[db.factionKey] = nil end
if not next(db.global) then sv.global = nil end
end
end
end
-function Dongle:SetProfile(name)
- local reg = lookup[self]
- assert(3, reg, "You must call 'SetProfile' from a registered Dongle.")
+function Dongle.SetProfile(db, name)
+ assert(3, databases[db], "You must call 'SetProfile' from a Dongle database object.")
argcheck(name, 2, "string")
- assert(3, reg.db, "You cannot call \"SetProfile\" before calling \"InitializeDB\".")
- local old = reg.sv.profiles[reg.db_profileKey]
+ local sv = db.sv
+ local old = sv.profiles[db.profileKey]
+ local new = sv.profiles[name]
- local new = reg.sv.profiles[name]
if not new then
- reg.sv.profiles[name] = {}
- new = reg.sv.profiles[name]
+ sv.profiles[name] = {}
+ new = sv.profiles[name]
end
- if reg.dbDefaults and reg.dbDefaults.profile then
+ if db.defaults and db.defaults.profile then
-- Remove the defaults from the old profile
- removeDefaults(old, reg.dbDefaults.profile)
+ removeDefaults(old, db.defaults.profile)
-- Inject the defaults into the new profile
- copyDefaults(new, reg.dbDefaults.profile)
+ copyDefaults(new, db.defaults.profile)
end
- reg.db.profile = new
+ db.profile = new
- -- Save this new profile name in db.char
- reg.db.char.profileKey = name
+ -- Save this new profile name
+ sv.profileKeys[db.charKey] = name
+ db.profileKey = name
- self:TriggerEvent("DONGLE_PROFILE_CHANGED", reg.name, name)
+ -- FIRE: DONGLE_PROFILE_CHANGED, "DongleName", "SVName", "ProfileName"
+ local parent = lookup[db.parent].obj
+ parent:TriggerEvent("DONGLE_PROFILE_CHANGED", db.parent, db.sv_name, name)
end
-function Dongle:DeleteProfile(name)
- local reg = lookup[self]
- assert(3, reg, "You must call 'DeleteProfile' from a registered Dongle.")
+function Dongle.GetProfiles(db, t)
+ assert(3, databases[db], "You must call 'GetProfiles' from a Dongle database object.")
+ argcheck(t, 2, "table", "nil")
+
+ t = t or {}
+ local i = 1
+ for profileKey in pairs(db.profiles) do
+ t[i] = profileKey
+ i = i + 1
+ end
+ return t, i - 1
+end
+
+function Dongle.DeleteProfile(db, name)
+ assert(3, databases[db], "You must call 'DeleteProfile' from a Dongle database object.")
argcheck(name, 2, "string")
- assert(3, reg.db, "You cannot call \"DeleteProfile\" before calling \"InitializeDB\".")
- if reg.db.char.profileKey == name then
+ if db.profileKey == name then
error("You cannot delete your active profile. Change profiles, then attempt to delete.", 2)
end
- reg.sv.profiles[name] = nil
- self:TriggerEvent("DONGLE_PROFILE_DELETED", reg.name, name)
+ db.sv.profiles[name] = nil
+ local parent = lookup[db.parent].obj
+ parent:TriggerEvent("DONGLE_PROFILE_DELETED", db.parent, db.sv_name, name)
end
-function Dongle:CopyProfile(name)
- local reg = lookup[self]
- assert(3, reg, "You must call 'CopyProfile' from a registered Dongle.")
+function Dongle.CopyProfile(db, name)
+ assert(3, databases[db], "You must call 'CopyProfile' from a Dongle database object.")
argcheck(name, 2, "string")
- assert(3, reg.db, "You cannot call \"CopyProfile\" before calling \"InitializeDB\".")
- assert(3, reg.db.char.profileKey ~= name, "Source/Destination profile cannot be the same profile")
- assert(3, type(reg.sv.profiles[name]) == "table", "Profile \""..name.."\" doesn't exist.")
+ assert(3, db.profileKey ~= name, "Source/Destination profile cannot be the same profile")
+ assert(3, type(db.sv.profiles[name]) == "table", "Profile \""..name.."\" doesn't exist.")
- local profile = reg.db.profile
- local source = reg.sv.profiles[name]
+ local profile = db.profile
+ local source = db.sv.profiles[name]
-- Don't do a destructive copy, just do what we're told
- copyDefaults(profile, source)
- self:TriggerEvent("DONGLE_PROFILE_COPIED", reg.name, name, reg.db.char.profileKey)
+ copyDefaults(profile, source, true)
+ -- FIRE: DONGLE_PROFILE_COPIED, "DongleName", "SVName", "SourceProfile", "DestProfile"
+ local parent = lookup[db.parent].obj
+ parent:TriggerEvent("DONGLE_PROFILE_COPIED", db.parent, db.sv_name, name, db.profileKey)
end
-function Dongle:ResetProfile()
- local reg = lookup[self]
- assert(3, reg, "You must call 'ResetProfile' from a registered Dongle.")
- assert(3, reg.db, "You cannot call \"ResetProfile\" before calling \"InitializeDB\".")
+function Dongle.ResetProfile(db)
+ assert(3, databases[db], "You must call 'ResetProfile' from a Dongle database object.")
- local profile = reg.db.profile
+ local profile = db.profile
for k,v in pairs(profile) do
profile[k] = nil
end
- if reg.dbDefaults and reg.dbDefaults.profile then
- copyDefaults(profile, reg.dbDefaults.profile)
+ if db.defaults and db.defaults.profile then
+ copyDefaults(profile, db.defaults.profile)
end
- self:TriggerEvent("DONGLE_PROFILE_RESET", reg.name, name)
+ -- FIRE: DONGLE_PROFILE_RESET, "DongleName", "SVName", "ProfileName"
+ local parent = lookup[db.parent].obj
+ parent:TriggerEvent("DONGLE_PROFILE_RESET", db.parent, db.sv_name, db.profileKey)
end
-function Dongle:ResetDB()
- local reg = lookup[self]
- assert(3, reg, "You must call 'ResetDB' from a registered Dongle.")
- assert(3, reg.db, "You cannot call \"ResetDB\" before calling \"InitializeDB\".")
- local sv = reg.sv
+function Dongle.ResetDB(db, defaultProfile)
+ assert(3, databases[db], "You must call 'ResetDB' from a Dongle database object.")
+ argcheck(defaultProfile, 2, "nil", "string")
+
+ local sv = db.sv
for k,v in pairs(sv) do
sv[k] = nil
end
-
- local db = self:InitializeDB(reg.sv_name, reg.dbDefaults)
- self:SetProfile(reg.db.char.profileKey)
- self:TriggerEvent("DONGLE_DATABASE_RESET", reg.name)
- return db
-end
--- Set up a basic slash command for /dongle and /reload
-SLASH_RELOAD1 = "/reload"
-SLASH_RELOAD2 = "/rl"
-SlashCmdList["RELOAD"] = ReloadUI
+ local parent = lookup[db.parent].obj
-SLASH_DONGLE1 = "/dongle"
+ local newdb = parent:InitializeDB(db.sv_name, db.defaults, defaultProfile)
+ newdb:SetProfile(newdb.profileKey)
+ local parent = lookup[db.parent].obj
+ parent:TriggerEvent("DONGLE_DATABASE_RESET", newdb.parent, newdb.sv_name, newdb.profileKey)
-SlashCmdList["DONGLE"] = function(msg)
- local s,e,cmd,args = string.find(msg, "([^%s]+)%s*(.*)")
- if not cmd then return end
+ -- Remove the old database from the lookup table
+ databases[db] = nil
+ return newdb
+end
- cmd = string.lower(cmd)
+function Dongle:RegisterSlashCommand(command, prefix, pattern, validator)
+ local reg = lookup[self]
+ assert(3, reg, "You must call 'RegisterSlashCommand' from a registered Dongle.")
+ argcheck(prefix, 2, "string")
+ argcheck(pattern, 3, "string", "nil")
+ argcheck(validator, 4, "function", "nil")
- if cmd == "enable" then
- local name,title,notes,enabled = GetAddOnInfo(args)
- if enabled then
- Dongle:Print("'%s' is already enabled.", args)
- elseif not name then
- Dongle:Print("'%s' is not a valid addon.", args)
- elseif args then
- EnableAddOn(args)
- Dongle:Print("Enabled AddOn '%s'", args)
- end
- elseif cmd == "disable" then
- local name,title,notes,enabled = GetAddOnInfo(args)
- if not name then
- Dongle:Print("'%s' is not a valid addon.", args)
- elseif not enabled then
- Dongle:Print("'%s' is already disabled.", args)
- elseif args then
- DisableAddOn(args)
- Dongle:Print("Disabled AddOn '%s'", args)
- end
+ if not reg.cmd then
+ reg.cmd = {}
end
-end
+ reg.cmd[prefix] = {
+ ["pattern"] = pattern,
+ ["validator"] = validator,
+ }
+
+ -- Register the slash command here
---]]
+
+end
--[[-------------------------------------------------------------------------
Begin DongleStub required functions and registration
@@ -635,17 +689,21 @@ function Dongle:GetVersion() return major,minor end
function Dongle:Activate(old)
if old then
- self.registry = old.registry
- self.lookup = old.lookup
- self.loadqueue = old.loadqueue
- self.loadorder = old.loadorder
- self.events = old.events
+ self.registry = old.registry or registry
+ self.lookup = old.lookup or lookup
+ self.loadqueue = old.loadqueue or loadqueue
+ self.loadorder = old.loadorder or loadorder
+ self.events = old.events or events
+ self.databases = old.databases or databases
+ self.commands = old.commands or commands
registry = self.registry
lookup = self.lookup
loadqueue = self.loadqueue
loadorder = self.loadorder
events = self.events
+ databases = self.databases
+ commands = self.commands
frame = old.frame
self.registry[major].obj = self
@@ -655,6 +713,8 @@ function Dongle:Activate(old)
self.loadqueue = loadqueue
self.loadorder = loadorder
self.events = events
+ self.databases = databases
+ self.commands = commands
local reg = {obj = self, name = "Dongle"}
registry[major] = reg
@@ -682,6 +742,13 @@ function Dongle:Activate(old)
obj[k] = self[v]
end
end
+
+ -- Convert all database methods
+ for db in pairs(databases) do
+ for idx,method in ipairs(dbMethods) do
+ db[method] = self[method]
+ end
+ end
end
function Dongle:Deactivate(new)