Mercurial > wow > reaction
diff lib/AceModuleCore-2.0/AceModuleCore-2.0.lua @ 23:dba04d85c799
Added missing files in 1.0 dev tree
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Fri, 07 Mar 2008 22:17:51 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/AceModuleCore-2.0/AceModuleCore-2.0.lua Fri Mar 07 22:17:51 2008 +0000 @@ -0,0 +1,688 @@ +--[[ +Name: AceModuleCore-2.0 +Revision: $Rev: 43318 $ +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/AceModuleCore-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceModuleCore-2.0 +Description: Mixin to provide a module system so that modules or plugins can + use an addon as its core. +Dependencies: AceLibrary, AceOO-2.0, AceAddon-2.0, AceEvent-2.0 (optional) +License: LGPL v2.1 +]] + +local MAJOR_VERSION = "AceModuleCore-2.0" +local MINOR_VERSION = "$Revision: 43318 $" + +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:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end +end + +local AceEvent +local AceOO = AceLibrary:GetInstance("AceOO-2.0") +local AceModuleCore = AceOO.Mixin { + "NewModule", + "HasModule", + "GetModule", + "IsModule", + "IterateModules", + "IterateModulesWithMethod", + "CallMethodOnAllModules", + "SetModuleMixins", + "SetModuleClass", + "IsModuleActive", + "ToggleModuleActive", + "SetModuleDefaultState", + } +local AceAddon + +local function getlibrary(lib) + if type(lib) == "string" then + return AceLibrary(lib) + else + return lib + end +end + +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) + for k in pairs(t) do + t[k] = nil + end + list[t] = true + return nil + end +end + +local iterList = setmetatable({}, {__mode='v'}) +local modulesWithMethod = setmetatable({}, {__mode='kv'}) +do + local function func(t) + local i = t.i + 1 + local l = t.l + local k = l[i] + if k then + t.i = i + return k, l.m[k] + else + t = del(t) + end + end + function AceModuleCore:IterateModules() + local list = iterList[self] + if not list then + list = new() + for k in pairs(self.modules) do + list[#list+1] = k + end + table.sort(list) + list.m = self.modules + iterList[self] = list + end + local t = new() + t.i = 0 + t.l = list + return func, t, nil + end + + function AceModuleCore:IterateModulesWithMethod(method) + local masterList = modulesWithMethod[self] + if not masterList then + masterList = new() + modulesWithMethod[self] = masterList + end + local list = masterList[method] + if not list then + list = new() + for k, v in pairs(self.modules) do + if self:IsModuleActive(k) and type(v[method]) == "function" then + list[#list+1] = k + end + end + table.sort(list) + list.m = self.modules + masterList[method] = list + end + local t = new() + t.i = 0 + t.l = list + return func, t, nil + end + +--[[---------------------------------------------------------------------------------- +Notes: + Safely calls the given method on all active modules if it exists on said modules. This will automatically subvert any errors that occur in the modules. +Arguments: + string - the name of the method. + tuple - the list of arguments to call the method with. +Example: + core:CallMethodOnAllModules("OnSomething") + core:CallMethodOnAllModules("OnSomethingElse", 1, 2, 3, 4) +------------------------------------------------------------------------------------]] + function AceModuleCore:CallMethodOnAllModules(method, ...) + for name, module in self:IterateModulesWithMethod(method) do + local success, ret = pcall(module[method], module, ...) + if not success then + geterrorhandler()(ret) + end + end + end +end + +--[[---------------------------------------------------------------------------- +Notes: + Create a new module, parented to self. + The module created does, in fact, inherit from AceAddon-2.0. +Arguments: + string - name/title of the Module. + list of mixins the module is to inherit from. +Example: + MyModule = core:NewModule('MyModule', "AceEvent-2.0", "AceHook-2.1") +------------------------------------------------------------------------------]] +local tmp = {} +function AceModuleCore:NewModule(name, ...) + if not self.modules then + AceModuleCore:error("CreatePrototype() must be called before attempting to create a new module.", 2) + end + AceModuleCore:argCheck(name, 2, "string") + if name:len() == 0 then + AceModuleCore:error("Bad argument #2 to `NewModule`, string must not be empty") + end + if self.modules[name] then + AceModuleCore:error("The module %q has already been registered", name) + end + + if iterList[self] then + iterList[self] = del(iterList[self]) + end + + for i = 1, select('#', ...) do + tmp[i] = getlibrary((select(i, ...))) + end + + if self.moduleMixins then + for _,mixin in ipairs(self.moduleMixins) do + local exists = false + for _,v in ipairs(tmp) do + if mixin == v then + exists = true + break + end + end + if not exists then + tmp[#tmp+1] = mixin + end + end + end + + local module = AceOO.Classpool(self.moduleClass, unpack(tmp)):new(name) + self.modules[name] = module + module.name = name + module.title = name + + AceModuleCore.totalModules[module] = self + + if modulesWithMethod[self] then + for k,v in pairs(modulesWithMethod[self]) do + modulesWithMethod[self] = del(v) + end + end + + if type(self.OnModuleCreated) == "function" then + safecall(self.OnModuleCreated, self, name, module) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_ModuleCreated", module) + end + + local num = #tmp + for i = 1, num do + tmp[i] = nil + end + return module +end +--[[---------------------------------------------------------------------------------- +Notes: + Return whether the module names given are all available in the core. +Arguments: + list of strings that are the names of the modules. (typically you'd only check for one) +Returns: + * boolean - Whether all the modules are available in the core. +Example: + if core:HasModule('Bank') then + -- do banking + end +------------------------------------------------------------------------------------]] +function AceModuleCore:HasModule(...) + for i = 1, select('#', ...) do + if not self.modules[select(i, ...)] then + return false + end + end + + return true +end + +--[[------------------------------------------------------------------------------------ +Notes: + Return the module "name" if it exists. + If the module doesnot exist, an error is thrown. +Arguments: + string - the name of the module. +Returns: + The module requested, if it exists. +Example: + local bank = core:GetModule('Bank') +------------------------------------------------------------------------------------]] +function AceModuleCore:GetModule(name) + if not self.modules then + AceModuleCore:error("Error initializing class. Please report error.") + end + if not self.modules[name] then + AceModuleCore:error("Cannot find module %q.", name) + end + return self.modules[name] +end + +--[[---------------------------------------------------------------------------------- +Notes: + Return whether the given module is actually a module. +Arguments: + reference to the module +Returns: + * boolean - whether the given module is actually a module. +Example: + if core:IsModule(module) then + -- do something + end + -- alternatively + if AceModuleCore:IsModule(module) then + -- checks all modules, no matter the parent + end +------------------------------------------------------------------------------------]] +function AceModuleCore:IsModule(module) + if self == AceModuleCore then + return AceModuleCore.totalModules[module] + elseif type(module) == "table" then + if module.name and self.modules[module.name] and self.modules[module.name].name == module.name then + return true + end + for k,v in pairs(self.modules) do + if v == module then + return true + end + end + return false + end +end + +--[[---------------------------------------------------------------------------------- +Notes: + * Sets the default mixins for a given module. + * This cannot be called after :NewModule() has been called. + * This should really only be called if you use the mixins in your prototype. +Arguments: + list of mixins (up to 20) +Example: + core:SetModuleMixins("AceEvent-2.0", "AceHook-2.0") +------------------------------------------------------------------------------------]] +function AceModuleCore:SetModuleMixins(...) + if self.moduleMixins then + AceModuleCore:error('Cannot call "SetModuleMixins" twice') + elseif not self.modules then + AceModuleCore:error("Error initializing class. Please report error.") + elseif next(self.modules) then + AceModuleCore:error('Cannot call "SetModuleMixins" after "NewModule" has been called.') + end + + self.moduleMixins = { ... } + for i,v in ipairs(self.moduleMixins) do + self.moduleMixins[i] = getlibrary(v) + end +end + +-- #NODOC +function AceModuleCore:SetModuleClass(class) + class = getlibrary(class) + if not AceOO.inherits(class, AceOO.Class) then + AceModuleCore:error("Bad argument #2 to `SetModuleClass' (Class expected)") + end + if not self.modules then + AceModuleCore:error("Error initializing class. Please report error.") + end + if self.customModuleClass then + AceModuleCore:error("Cannot call `SetModuleClass' twice.") + end + self.customModuleClass = true + self.moduleClass = class + self.modulePrototype = class.prototype +end + +local mt = {__index=function(self, key) + self[key] = false + return false +end} +local defaultState = setmetatable({}, {__index=function(self, key) + local t = setmetatable({}, mt) + self[key] = t + return t +end}) + +local function isDisabled(core, module) + local moduleName + if type(module) == "table" then + moduleName = module.name + else + moduleName = module + end + local disabled + if type(module) == "table" and type(module.IsActive) == "function" then + return not module:IsActive() + elseif AceOO.inherits(core, "AceDB-2.0") then + local _,profile = core:GetProfile() + disabled = core.db and core.db.raw and core.db.raw.disabledModules and core.db.raw.disabledModules[profile] and core.db.raw.disabledModules[profile][moduleName] + else + disabled = core.disabledModules and core.disabledModules[moduleName] + end + if disabled == nil then + return defaultState[core][moduleName] + else + return disabled + end +end + +--[[---------------------------------------------------------------------------------- +Notes: + Sets the default active state of a module. This should be called before the ADDON_LOADED of the module. +Arguments: + string - name of the module. + table - reference to the module. + boolean - new state. false means disabled by default, true means enabled by default (true is the default). +Example: + self:SetModuleDefaultState('bank', false) +------------------------------------------------------------------------------------]] +function AceModuleCore:SetModuleDefaultState(module, state) + AceModuleCore:argCheck(module, 2, "table", "string") + AceModuleCore:argCheck(state, 3, "boolean") + + if type(module) == "table" then + if not self:IsModule(module) then + AceModuleCore:error("%q is not a module", module) + end + module = module.name + end + + defaultState[self][module] = not state +end + +--[[---------------------------------------------------------------------------------- +Notes: +Toggles the active state of a module. + +This calls module:ToggleActive([state]) if available. + +If suspending, This will call :OnDisable() on the module if it is available. Also, it will iterate through the addon's mixins and call :OnEmbedDisable(module) if available. - this in turn will, through AceEvent and others, unregister events/hooks/etc. depending on the mixin. Also, it will call :OnModuleDisable(module) on the core if it is available. + +If resuming, This will call :OnEnable(first) on the module if it is available. Also, it will iterate through the addon's mixins and call :OnEmbedEnable(module) if available. - this in turn will, through AceEvent and others, unregister events/hooks/etc. depending on the mixin. Also, it will call :OnModuleEnable(module) on the core if it is available. + +If you call :ToggleModuleActive("name or module, true) and it is already active, it silently returns, same if you pass false and it is inactive. + +Arguments: + string/table - name of the module or a reference to the module + [optional] boolean - new state. (default not :IsModuleActive("name" or module)) +Returns: + * boolean - Whether the module is now in an active (enabled) state. +Example: + self:ToggleModuleActive('bank') +------------------------------------------------------------------------------------]] +function AceModuleCore:ToggleModuleActive(module, state) + AceModuleCore:argCheck(module, 2, "table", "string") + AceModuleCore:argCheck(state, 3, "nil", "boolean") + + if type(module) == "string" then + if not self:HasModule(module) then + AceModuleCore:error("Cannot find module %q", module) + end + module = self:GetModule(module) + elseif not self:IsModule(module) then + AceModuleCore:error("%q is not a module", module) + end + + local disable + if state == nil then + disable = self:IsModuleActive(module) + else + disable = not state + if disable ~= self:IsModuleActive(module) then + return + end + end + + if type(module.ToggleActive) == "function" then + return module:ToggleActive(not disable) + elseif AceOO.inherits(self, "AceDB-2.0") then + if not self.db or not self.db.raw then + AceModuleCore:error("Cannot toggle a module until `RegisterDB' has been called and `ADDON_LOADED' has been fired.") + end + if type(self.db.raw.disabledModules) ~= "table" then + self.db.raw.disabledModules = {} + end + local _,profile = self:GetProfile() + if type(self.db.raw.disabledModules[profile]) ~= "table" then + self.db.raw.disabledModules[profile] = {} + end + if type(self.db.raw.disabledModules[profile][module.name]) ~= "table" then + local value = nil + if disable ~= defaultState[self][module.name] then + value = disable + end + self.db.raw.disabledModules[profile][module.name] = value + end + if not disable then + if not next(self.db.raw.disabledModules[profile]) then + self.db.raw.disabledModules[profile] = nil + end + if not next(self.db.raw.disabledModules) then + self.db.raw.disabledModules = nil + end + end + else + if type(self.disabledModules) ~= "table" then + self.disabledModules = {} + end + local value = nil + if disable ~= defaultState[self][module.name] then + value = disable + end + self.disabledModules[module.name] = value + end + if AceOO.inherits(module, "AceAddon-2.0") then + if not AceAddon.addonsStarted[module] then + return + end + end + if not disable then + local first = nil + if AceOO.inherits(module, "AceAddon-2.0") then + if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[module] then + AceAddon.addonsEnabled[module] = true + first = true + end + end + local current = module.class + while true do + if current == AceOO.Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable, mixin, module, first) + end + end + end + current = current.super + end + if type(module.OnEnable) == "function" then + safecall(module.OnEnable, module, first) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", module, first) + end + else + local current = module.class + while true do + if current == AceOO.Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedDisable) == "function" then + safecall(mixin.OnEmbedDisable, mixin, module) + end + end + end + current = current.super + end + if type(module.OnDisable) == "function" then + safecall(module.OnDisable, module) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonDisabled", module) + end + end + return not disable +end + +--[[----------------------------------------------------------------------- +Notes: + Returns whether the module is in an active (enabled) state. This calls module:IsActive() if available. if notLoaded is set, then "name" must be a string. +Arguments: + string/table - name of the module or a reference to the module + [optional] - boolean - if set, this will check modules that are not loaded as well. (default: false) +Returns: + * boolean - Whether the module is in an active (enabled) state. +Example: + assert(self:IsModuleActive('bank')) +------------------------------------------------------------------------]] +function AceModuleCore:IsModuleActive(module, notLoaded) + AceModuleCore:argCheck(module, 2, "table", "string") + AceModuleCore:argCheck(notLoaded, 3, "nil", "boolean") + if notLoaded then + AceModuleCore:argCheck(module, 2, "string") + end + + if AceModuleCore == self then + self:argCheck(module, 2, "table") + + local core = AceModuleCore.totalModules[module] + if not core then + self:error("Bad argument #2 to `IsModuleActive'. Not a module") + end + return core:IsModuleActive(module) + end + + if type(module) == "string" then + if not notLoaded and not self:HasModule(module) then + AceModuleCore:error("Cannot find module %q", module) + end + if not notLoaded then + module = self:GetModule(module) + else + module = self:HasModule(module) and self:GetModule(module) or module + end + else + if not self:IsModule(module) then + AceModuleCore:error("%q is not a module", module) + end + end + + return not isDisabled(self, module) +end + +-- #NODOC +function AceModuleCore:OnInstanceInit(target) + if target.modules then + do return end + AceModuleCore:error("OnInstanceInit cannot be called twice") + end + + if not AceAddon then + if AceLibrary:HasInstance("AceAddon-2.0") then + AceAddon = AceLibrary("AceAddon-2.0") + else + self:error(MAJOR_VERSION .. " requires AceAddon-2.0") + end + end + target.modules = {} + + target.moduleClass = AceOO.Class("AceAddon-2.0") + target.modulePrototype = target.moduleClass.prototype +end + +AceModuleCore.OnManualEmbed = AceModuleCore.OnInstanceInit + +function AceModuleCore.OnEmbedProfileDisable(AceModuleCore, self, newProfile) + if not AceOO.inherits(self, "AceDB-2.0") then + return + end + local _,currentProfile = self:GetProfile() + for k, module in pairs(self.modules) do + if type(module.IsActive) == "function" or type(module.ToggleActive) == "function" then + -- continue + else + local currentActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[currentProfile] or not self.db.raw.disabledModules[currentProfile][module.name] + local newActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[newProfile] or not self.db.raw.disabledModules[newProfile][module.name] + if currentActive ~= newActive then + self:ToggleModuleActive(module) + if not self.db.raw.disabledModules then + self.db.raw.disabledModules = {} + end + if not self.db.raw.disabledModules[currentProfile] then + self.db.raw.disabledModules[currentProfile] = {} + end + self.db.raw.disabledModules[currentProfile][module.name] = not currentActive or nil + end + end + end +end + +-- #NODOC +function AceModuleCore:Ace2_AddonEnabled(module, first) + local addon = self.totalModules[module] + if not addon then + return + end + + if modulesWithMethod[addon] then + for k,v in pairs(modulesWithMethod[addon]) do + modulesWithMethod[addon] = del(v) + end + end + if type(addon.OnModuleEnable) == "function" then + safecall(addon.OnModuleEnable, addon, module, first) + end +end + +-- #NODOC +function AceModuleCore:Ace2_AddonDisabled(module) + local addon = self.totalModules[module] + if not addon then + return + end + + if modulesWithMethod[addon] then + for k,v in pairs(modulesWithMethod[addon]) do + modulesWithMethod[addon] = del(v) + end + end + if type(addon.OnModuleDisable) == "function" then + safecall(addon.OnModuleDisable, addon, module) + end +end + +local function activate(self, oldLib, oldDeactivate) + AceModuleCore = self + + self.totalModules = oldLib and oldLib.totalModules or {} + + self:activate(oldLib, oldDeactivate) + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +local function external(self, major, instance) + if major == "AceEvent-2.0" then + AceEvent = instance + AceEvent:embed(self) + + self:UnregisterAllEvents() + self:RegisterEvent("Ace2_AddonEnabled") + self:RegisterEvent("Ace2_AddonDisabled") + elseif major == "AceAddon-2.0" then + AceAddon = instance + end +end + +AceLibrary:Register(AceModuleCore, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) +AceModuleCore = AceLibrary(MAJOR_VERSION)