vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
--[[
Name: AceEvent-2.0
Revision: $Rev: 4599 $
Author(s): ckknight (ckknight@gmail.com)
Inspired By: AceEvent 1.x by Turan (<email here>)
Website: http://www.wowace.com/
Documentation: http://wiki.wowace.com/index.php/AceEvent-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0
Description: Mixin to allow for event handling and inter-addon communication.
Dependencies: AceLibrary, AceOO-2.0, Compost-2.0 (optional)
]]

local MAJOR_VERSION = "AceEvent-2.0"
local MINOR_VERSION = "$Revision: 4599 $"

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 AceOO = AceLibrary:GetInstance("AceOO-2.0")
local Mixin = AceOO.Mixin
local AceEvent = Mixin {
                                                "RegisterEvent",
                                                "UnregisterEvent",
                                                "UnregisterAllEvents",
                                                "TriggerEvent",
                                                "ScheduleEvent",
                                                "ScheduleRepeatingEvent",
                                                "CancelScheduledEvent",
                                                "CancelAllScheduledEvents",
                                                "TriggerDelayedEvent", -- remove on July 23
                                                "CancelDelayedEvent", -- remove on July 23
                                                "IsEventRegistered",
                                                "IsEventScheduled",
                                           }

local Compost = AceLibrary:HasInstance("Compost-2.0") and AceLibrary("Compost-2.0")

function AceEvent:RegisterEvent(event, method, once)
        AceEvent:argCheck(event, 2, "string")
        AceEvent:argCheck(method, 3, "string", "function", "nil")
        AceEvent:argCheck(once, 4, "boolean", "nil")
        if not method then
                method = event
        end
        if type(method) == "string" and type(self[method]) ~= "function" then
                AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
        end
        
        if not AceEvent.registry[event] then
                AceEvent.registry[event] = Compost and Compost:Acquire() or {}
                AceEvent.frame:RegisterEvent(event)
        end
        
        AceEvent.registry[event][self] = method
        
        if once then
                if not AceEvent.onceRegistry then
                        AceEvent.onceRegistry = Compost and Compost:Acquire() or {}
                end
                if not AceEvent.onceRegistry[event] then
                        AceEvent.onceRegistry[event] = Compost and Compost:Acquire() or {}
                end
                AceEvent.onceRegistry[event][self] = true
        else
                if AceEvent.onceRegistry and AceEvent.onceRegistry[event] then
                        AceEvent.onceRegistry[event][self] = nil
                end
        end
end

local _G = getfenv(0)
function AceEvent:TriggerEvent(event, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
        AceEvent:argCheck(event, 2, "string")
        local _G_event = _G.event
        _G.event = event

        if AceEvent.registry[event] then
                if AceEvent.onceRegistry and AceEvent.onceRegistry[event] then
                        for obj in pairs(AceEvent.onceRegistry[event]) do
                                local mem, time
                                if AceEvent.debugTable then
                                        if not AceEvent.debugTable[event] then
                                                AceEvent.debugTable[event] = Compost and Compost:Acquire() or {}
                                        end
                                        if not AceEvent.debugTable[event][obj] then
                                                AceEvent.debugTable[event][obj] = Compost and Compost:AcquireHash(
                                                        'mem', 0,
                                                        'time', 0
                                                ) or {
                                                        mem = 0,
                                                        time = 0
                                                }
                                        end
                                        mem, time = gcinfo(), GetTime()
                                end
                                local method = AceEvent.registry[event][obj]
                                AceEvent.registry[event][obj] = nil
                                AceEvent.onceRegistry[event][obj] = nil
                                if type(method) == "string" then
                                        if obj[method] then
                                                obj[method](obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
                                        end
                                elseif method then -- function
                                        method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
                                end
                                if AceEvent.debugTable then
                                        mem, time = mem - gcinfo(), time - GetTime()
                                        AceEvent.debugTable[event][obj].mem = AceEvent.debugTable[event][obj].mem + mem
                                        AceEvent.debugTable[event][obj].time = AceEvent.debugTable[event][obj].time + time
                                end
                                obj = nil
                        end
                end
                for obj, method in pairs(AceEvent.registry[event]) do
                        local mem, time
                        if AceEvent.debugTable then
                                if not AceEvent.debugTable[event] then
                                        AceEvent.debugTable[event] = Compost and Compost:Acquire() or {}
                                end
                                if not AceEvent.debugTable[event][obj] then
                                        AceEvent.debugTable[event][obj] = Compost and Compost:AcquireHash(
                                                'mem', 0,
                                                'time', 0
                                        ) or {
                                                mem = 0,
                                                time = 0
                                        }
                                end
                                mem, time = gcinfo(), GetTime()
                        end
                        if type(method) == "string" then
                                if obj[method] then
                                        obj[method](obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
                                end
                        else -- function
                                method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
                        end
                        if AceEvent.debugTable then
                                mem, time = mem - gcinfo(), time - GetTime()
                                AceEvent.debugTable[event][obj].mem = AceEvent.debugTable[event][obj].mem + mem
                                AceEvent.debugTable[event][obj].time = AceEvent.debugTable[event][obj].time + time
                        end
                end
        end
        _G.event = _G_event
end

local GetTime = GetTime
local delayRegistry
local function OnUpdate()
        local t = GetTime()
        local last = nil
        for k,v in pairs(delayRegistry) do
                if v.time <= t then
                        if v.repeatDelay then
                                v.time = t + v.repeatDelay
                                last = k
                        else
                                delayRegistry[k] = nil
                        end
                        local event = v.event
                        if type(event) == "function" then
                                event(unpack(v))
                        else
                                AceEvent:TriggerEvent(event, unpack(v))
                        end
                        if not v.repeatDelay and Compost then
                                Compost:Reclaim(v)
                        end
                        k = last
                else
                        last = k
                end
        end
        if not next(delayRegistry) then
                AceEvent.frame:Hide()
        end
end

local stage = 3
if tonumber(date("%Y%m%d")) < 20060716 then
        stage = 1
elseif tonumber(date("%Y%m%d")) < 20060723 then
        stage = 2
end

if stage <= 2 then
        function AceEvent:TriggerDelayedEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
                if stage == 2 then
                        local line = string.gsub(debugstack(), ".-\n(.-)\n.*", "%1")
                        DEFAULT_CHAT_MESSAGE:AddMessage(line .. " - `TriggerDelayedEvent' has been replaced with `ScheduleEvent'. This will cause an error on July 23, 2006.")
                end
                self:ScheduleEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
        end
        function AceEvent:CancelDelayedEvent(t)
                if stage == 2 then
                        local line = string.gsub(debugstack(), ".-\n(.-)\n.*", "%1")
                        DEFAULT_CHAT_MESSAGE:AddMessage(line .. " - `CancelDelayedEvent' has been replaced with `CancelScheduledEvent'. This will cause an error on July 23, 2006.")
                end
                self:CancelScheduledEvent(t)
        end
end

local function ScheduleEvent(self, repeating, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
        local id
        if type(event) == "string" or type(event) == "table" then
                if type(event) == "table" then
                        if not delayRegistry[event] then
                                AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
                        end
                end
                if type(delay) ~= "number" then
                        id, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
                        AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number")
                        AceEvent:argCheck(delay, 4, "number")
                        self:CancelScheduledEvent(id)
                end
        else
                AceEvent:argCheck(event, 2, "string", "function")
                AceEvent:argCheck(delay, 3, "number")
        end
        
        if not AceEvent.delayRegistry then
                AceEvent.delayRegistry = Compost and Compost:Acquire() or {}
                delayRegistry = AceEvent.delayRegistry
                AceEvent.frame:SetScript("OnUpdate", OnUpdate)
        end
        local t
        if type(id) == "table" then
                for k in pairs(id) do
                        id[k] = nil
                end
                table.setn(id, 0)
                t = id
        else
                t = Compost and Compost:Acquire() or {}
        end
        t.n = 0
        table.insert(t, a1)
        table.insert(t, a2)
        table.insert(t, a3)
        table.insert(t, a4)
        table.insert(t, a5)
        table.insert(t, a6)
        table.insert(t, a7)
        table.insert(t, a8)
        table.insert(t, a9)
        table.insert(t, a10)
        table.insert(t, a11)
        table.insert(t, a12)
        table.insert(t, a13)
        table.insert(t, a14)
        table.insert(t, a15)
        table.insert(t, a16)
        table.insert(t, a17)
        table.insert(t, a18)
        table.insert(t, a19)
        table.insert(t, a20)
        t.event = event
        t.time = GetTime() + delay
        t.self = self
        t.id = id or t
        t.repeatDelay = repeating and delay
        AceEvent.delayRegistry[t.id] = t
        AceEvent.frame:Show()
        return t.id
end

function AceEvent:ScheduleEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
        return ScheduleEvent(self, false, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end

function AceEvent:ScheduleRepeatingEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
        return ScheduleEvent(self, true, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end

function AceEvent:CancelScheduledEvent(t)
        AceEvent:argCheck(t, 2, "string", "table")
        if AceEvent.delayRegistry then
                local v = AceEvent.delayRegistry[t]
                if v then
                        AceEvent.delayRegistry[t] = nil
                        if Compost then
                                Compost:Reclaim(v)
                        end
                        if not next(AceEvent.delayRegistry) then
                                AceEvent.frame:Hide()
                        end
                        return true
                end
        end
        return false
end

function AceEvent:IsEventScheduled(t)
        AceEvent:argCheck(t, 2, "string", "table")
        if AceEvent.delayRegistry then
                local v = AceEvent.delayRegistry[t]
                if v then
                        if Compost then
                                Compost:Reclaim(v)
                        end
                        return true, v.time - GetTime()
                end
        end
        return false, nil
end

function AceEvent:UnregisterEvent(event)
        AceEvent:argCheck(event, 2, "string")

        if AceEvent.registry[event] and AceEvent.registry[event][self] then
                AceEvent.registry[event][self] = nil
        else
                AceEvent:error("Cannot unregister an event that you are not registered with.")
        end
end

function AceEvent:UnregisterAllEvents()
        for event, data in pairs(AceEvent.registry) do
                data[self] = nil
        end
end

function AceEvent:CancelAllScheduledEvents()
        if AceEvent.delayRegistry then
                for k,v in pairs(AceEvent.delayRegistry) do
                        if v.self == target then
                                if Compost then
                                        Compost:Reclaim(v)
                                end
                                AceEvent.delayRegistry[k] = nil
                        end
                end
        end
end

function AceEvent:IsEventRegistered(event)
        AceEvent:argCheck(event, 2, "string")
        if AceEvent.registry[event] and AceEvent.registry[event][self] then
                return true, AceEvent.registry[event][self]
        end
        return false, nil
end

function AceEvent:OnEmbedDisable(target)
        self.UnregisterAllEvents(target)

        self.CancelAllScheduledEvents(target)
end

function AceEvent:EnableDebugging()
        if not self.debugTable then
                self.debugTable = {}
        end
end

function AceEvent:IsFullyInitialized()
        return self.postInit or false
end

function AceEvent:IsPostPlayerLogin()
        return self.playerLogin or false
end

function AceEvent:activate(oldLib, oldDeactivate)
        AceEvent = self

        if oldLib then
                self.onceRegistry = oldLib.onceRegistry
                self.delayRegistry = oldLib.delayRegistry
                self.registry = oldLib.registry
                self.frame = oldLib.frame
                self.debugTable = oldLib.debugTable
                self.playerLogin = oldLib.pew or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage and true
                self.postInit = oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true
        end
        if not self.registry then
                self.registry = {}
        end
        if not self.frame then
                self.frame = CreateFrame("Frame")
        end
        self.frame:SetScript("OnEvent", function()
                if event then
                        self:TriggerEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
                end
        end)
        if self.delayRegistry then
                delayRegistry = self.delayRegistry
                self.frame:SetScript("OnUpdate", OnUpdate)
        end
        
        self:UnregisterAllEvents()
        self:CancelAllScheduledEvents()
        
        if not self.playerLogin then
                self:RegisterEvent("PLAYER_LOGIN", function()
                        self.playerLogin = true
                end, true)
        end
        
        if not self.postInit then
                local isReload = true
                local function func()
                        self.postInit = true
                        self:TriggerEvent("AceEvent_FullyInitialized")
                        self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE")
                        self:UnregisterEvent("SPELLS_CHANGED")
                end
                self:RegisterEvent("MEETINGSTONE_CHANGED", function()
                        self.playerLogin = true
                        self:ScheduleEvent("AceEvent_FullyInitialized", func, isReload and 1 or 4)
                end, true)
                self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function()
                        self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.05)
                end)
                self:RegisterEvent("SPELLS_CHANGED", function()
                        isReload = false
                end)
        end
        
        self.super.activate(self, oldLib, oldDeactivate)
        if oldLib then
                oldDeactivate(oldLib)
        end
end

local function external(self, major, instance)
        if major == "Compost-2.0" then
                Compost = instance
        end
end

AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, AceEvent.activate, nil, external)
AceEvent = AceLibrary(MAJOR_VERSION)