vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:

--[[
Name: Metrognome-2.0
Revision: $Rev: 4736 $
Author: Tekkub Stoutwrithe (tekkub@gmail.com)
Website: http://wiki.wowace.com/index.php/Metrognome_Embedded_Library
Documentation: http://wiki.wowace.com/index.php/Metrognome_Embedded_Library_Methods
SVN: svn://svn.wowace.com/root/trunk/Metrognome/Metrognome-2.0
Description: OnUpdate timer managing library
Dependencies: AceLibrary, AceOO-2.0, Compost-2.0 (optional)
]]

local vmajor, vminor = "Metrognome-2.0", "$Revision: 4736 $"

if not AceLibrary then error(vmajor .. " requires AceLibrary.") end
if not AceLibrary:IsNewVersion(vmajor, vminor) then return end

if not AceLibrary:HasInstance("AceOO-2.0") then error(vmajor .. " requires AceOO-2.0") end


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

local AceOO = AceLibrary:GetInstance("AceOO-2.0")
local Mixin = AceOO.Mixin
local Metrognome = Mixin {
        "RegisterMetro",
        "UnregisterMetro",
        "StartMetro",
        "StopMetro",
        "ChangeMetroRate",
        "ClearMetroStats",
        "MetroStatus",
        "MetroProfile",
}

local function getnewtable() return compost and compost:Acquire() or {} end
local function reclaimtable(t) if compost then compost:Reclaim(t) end end


-- Sets up a new OnUpdate handler
-- name - A unique name, if you only need one handler then your addon's name will suffice here
-- func - Function to be called
-- rate (optional but highly reccomended) - The rate (in seconds) at which your function should be called
-- a1-4 (optional) - A args to be passed to func, this is a great place to pass self
--                   if a2 is defined then the elapsed time will not be passed to your function!
-- Returns true if you've been registered
function Metrognome:Register(name, func, rate, a1, a2, a3, a4, a5, a6)
        Metrognome:argCheck(name, 2, "string")
        Metrognome:argCheck(func, 3, "function")
        Metrognome:assert(not Metrognome.var.handlers[name], "A timer with the name "..name.." is already registered")

        local t = getnewtable()
        t.name, t.func, t.rate = name, func, rate or 0
        t.a1, t.a2, t.a3, t.a4, t.a5, t.a6 = a1, a2, a3, a4, a5, a6
        Metrognome.var.handlers[name] = t
        return true
end


-- Removes an OnUpdate handler
-- name - the hander you want to remove
-- Returns true if successful
function Metrognome:Unregister(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
        Metrognome:argCheck(a1, 2, "string")

        if not Metrognome.var.handlers[a1] then return end
        reclaimtable(Metrognome.var.handlers[a1])
        Metrognome.var.handlers[a1] = nil
        if a2 then Metrognome:Unregister(a2,a3,a4,a5,a6,a7,a8,a9,a10)
        elseif not Metrognome:HasHandlers() then Metrognome.var.frame:Hide() end
        return true
end


-- Begins triggering updates
-- name - the hander you want to start
-- numexec (optional) - Limit the number of times the timer runs
-- Returns true if successful
function Metrognome:Start(name, numexec)
        Metrognome:argCheck(name, 2, "string")

        if not Metrognome.var.handlers[name] then return end
        Metrognome.var.handlers[name].limit = numexec
        Metrognome.var.handlers[name].elapsed = 0
        Metrognome.var.handlers[name].running = true
        Metrognome.var.frame:Show()
        return true
end


-- Stops triggering updates
-- name - the hander you want to stop
-- Returns true if successful
function Metrognome:Stop(name)
        Metrognome:argCheck(name, 2, "string")

        if not Metrognome.var.handlers[name] then return end
        Metrognome.var.handlers[name].running = nil
        Metrognome.var.handlers[name].limit = nil
        if not Metrognome:HasHandlers() then Metrognome.var.frame:Hide() end
        return true
end


-- Changes the execution rate of a timer.
-- This will also reset the timer's elapsed time to 0
-- name - The timer you wish to change
-- newrate (optional)- The new exec rate, in seconds.  If nil or 0 default OnUpdate timing will be used
-- n#,r# (optional) - Recusivly calls ChangeRate to allow you to set up to 5 rates in one call.
-- Returns true if successful
function Metrognome:ChangeRate(name, newrate, n2,r2,n3,r3,n4,r4,n5,r5)
        Metrognome:argCheck(name, 2, "string")

        if not Metrognome.var.handlers[name] then
                if n2 then return nil, Metrognome:ChangeRate(n2,r2,n3,r3,n4,r4,n5,r5)
                else return end
        end

        local t = Metrognome.var.handlers[name]
        t.elapsed = 0
        t.rate = newrate or 0
        if n2 then return true, Metrognome:ChangeRate(n2,r2,n3,r3,n4,r4,n5,r5)
        else return true end
end


-- Resets the profile stats for a timer
-- Accepts up to 10 timer names to clear
function Metrognome:ClearStats(name, n2, n3, n4, n5, n6, n7, n8, n9, n10)
        Metrognome:argCheck(name, 2, "string")

        if not Metrognome.var.handlers[name] then
                if n2 then return nil, Metrognome:ClearStats(n2,r2,n3,r3,n4,r4,n5,r5)
                else return end
        end

        local t = Metrognome.var.handlers[name]
        t.count, t.mem, t.time = 0, 0, 0
        if n2 then return true, Metrognome:ClearStats(n2,r2,n3,r3,n4,r4,n5,r5)
        else return true end
end


-- Query a timer's status
-- Args: name - the schedule you wish to look up
-- Returns: registered - true if a schedule exists with this name
--          rate - the registered rate, if defined
--          running - true if this schedule is currently running
--          limit - limit of times to repeat this timer
--          elapsed - time elapsed this cycle of the timer
function Metrognome:Status(name)
        Metrognome:argCheck(name, 2, "string")

        if not Metrognome.var.handlers[name] then return end
        return true, Metrognome.var.handlers[name].rate, Metrognome.var.handlers[name].running, Metrognome.var.handlers[name].limit, Metrognome.var.handlers[name].elapsed
end


-- Query a timer's profile info
-- Args: name - the schedule you wish to look up
-- Returns: mem - the total memory consumed by the timer's execution (in KiB)
--          time - the total time consumed by the timer's execution (in sec)
--          count - the number of times the timer has been triggered
--          rate - the rate at which the timer triggers (0 means the default OnUpdate rate)
function Metrognome:Profile(name)
        Metrognome:argCheck(name, 2, "string")

        if not Metrognome.var.handlers[name] then return end
        local t = Metrognome.var.handlers[name]
        return t.mem, t.time, t.count, t.rate
end


function Metrognome:OnUpdate()
        for i,v in pairs(this.owner.var.handlers) do
                if v.running then
                        v.elapsed = v.elapsed + arg1
                        if v.elapsed >= v.rate then
                                local mem, time = gcinfo(), GetTime()
                                v.func(v.a1 or v.arg, v.a2 or v.elapsed, v.a3, v.a4, v.a5, v.a6)
                                mem, time = gcinfo() - mem, GetTime() - time
                                if mem >= 0 then v.mem, v.time, v.count = (v.mem or 0) + mem, (v.time or 0) + time, (v.count or 0) + 1 end
                                v.elapsed = 0
                                if v.limit then v.limit = v.limit - 1 end
                                if v.limit and v.limit <= 0 then this.owner:Stop(i) end
                        end
                end
        end
end


function Metrognome:HasHandlers()
        if next(Metrognome.var.handlers) then return true end
end


------------------------------
--      Mixins Methods      --
------------------------------

Metrognome.StartMetro = Metrognome.Start
Metrognome.StopMetro = Metrognome.Stop
Metrognome.ChangeMetroRate = Metrognome.ChangeRate
Metrognome.ClearMetroStats = Metrognome.ClearStats
Metrognome.MetroStatus = Metrognome.Status
Metrognome.MetroProfile = Metrognome.Profile


function Metrognome:RegisterMetro(name, func, rate, a1, a2, a3, a4, a5, a6)
        if not Metrognome.var.addons[self] then Metrognome.var.addons[self] = getnewtable() end
        Metrognome.var.addons[self][name] = Metrognome:Register(name, func, rate, a1, a2, a3, a4, a5, a6)
end


function Metrognome:UnregisterMetro(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
        Metrognome:argCheck(a1, 2, "string")

        if Metrognome.var.addons[self] then Metrognome.var.addons[self][a1] = nil end
        Metrognome:Unregister(a1)
        if a2 then self:UnregisterMetro(a2,a3,a4,a5,a6,a7,a8,a9,a10) end
end


function Metrognome:OnEmbedDisable(target)
        if self.var.addons[target] then
                for i in pairs(Metrognome.var.addons[target]) do
                        self:Stop(i)
                end
        end
end


--------------------------------
--      Load this bitch!      --
--------------------------------

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

        if oldLib then self.var = oldLib.var
        else
                self.var = {  -- "Local" variables go here
                        frame = CreateFrame("Frame"),
                        handlers = {},
                        addons = {},
                }
                self.var.frame:Hide()
                self.var.frame.name = "Metrognome-2.0 Frame"
        end
        if not self.var.addons then self.var.addons = {} end
        self.var.frame:SetScript("OnUpdate", self.OnUpdate)
        self.var.frame.owner = self

        self.super.activate(self, oldLib, oldDeactivate)
        if oldDeactivate then oldDeactivate(oldLib) end
end


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


AceLibrary:Register(Metrognome, vmajor, vminor, Metrognome.activate, nil, external)