From ec6a8577059faced79c5fd63c8f0192d04e1161e Mon Sep 17 00:00:00 2001 From: James Whitehead II Date: Fri, 15 Dec 2006 22:37:35 +0000 Subject: [PATCH] * Updated to Dongle-0.3.0-Alpha --- Dongle.lua | 359 ++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 213 insertions(+), 146 deletions(-) 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) -- 1.7.9.5