Mercurial > wow > reaction
diff modules/FuBar_ReActionFu/lib/AceEvent-2.0/AceEvent-2.0.lua @ 28:21bcaf8215ff
- converted to Ace3
- rearranged file layout
- configGUI menus not working right now
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Mon, 17 Mar 2008 18:24:53 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/FuBar_ReActionFu/lib/AceEvent-2.0/AceEvent-2.0.lua Mon Mar 17 18:24:53 2008 +0000 @@ -0,0 +1,1082 @@ +--[[ +Name: AceEvent-2.0 +Revision: $Rev: 49307 $ +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/AceEvent-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0 +Description: Mixin to allow for event handling, scheduling, and inter-addon + communication. +Dependencies: AceLibrary, AceOO-2.0 +License: LGPL v2.1 +]] + +local MAJOR_VERSION = "AceEvent-2.0" +local MINOR_VERSION = "$Revision: 49307 $" + +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", + "RegisterAllEvents", + "UnregisterEvent", + "UnregisterAllEvents", + "TriggerEvent", + "ScheduleEvent", + "ScheduleRepeatingEvent", + "CancelScheduledEvent", + "CancelAllScheduledEvents", + "IsEventRegistered", + "IsEventScheduled", + "RegisterBucketEvent", + "UnregisterBucketEvent", + "UnregisterAllBucketEvents", + "IsBucketEventRegistered", + "ScheduleLeaveCombatAction", + "CancelAllCombatSchedules", +} + +local weakKey = {__mode="k"} + +local FAKE_NIL +local RATE + +local eventsWhichHappenOnce = { + PLAYER_LOGIN = true, + AceEvent_FullyInitialized = true, + VARIABLES_LOADED = true, + PLAYER_LOGOUT = true, +} +local next = next +local pairs = pairs +local pcall = pcall +local type = type +local GetTime = GetTime +local gcinfo = gcinfo +local unpack = unpack +local geterrorhandler = geterrorhandler + +local build = GetBuildInfo() +local useTablesAsIDs = build:find("^2%.0%.") or build:find("^2%.1%.") or build:find("^0%.1%.") + +local new, del +do + local cache = setmetatable({}, {__mode='k'}) + function new(...) + local t = next(cache) + if t then + cache[t] = nil + for i = 1, select('#', ...) do + t[i] = select(i, ...) + end + return t + else + return { ... } + end + end + function del(t) + for k in pairs(t) do + t[k] = nil + end + cache[t] = true + return nil + end +end + +local registeringFromAceEvent +--[[---------------------------------------------------------------------------------- +Notes: + * Registers the addon with a Blizzard event or a custom AceEvent, which will cause the given method to be called when that is triggered. +Arguments: + string - name of the event to register + [optional] string or function - name of the method or function to call. Default: same name as "event". + [optional] boolean - whether to have method called only once. Default: false +------------------------------------------------------------------------------------]] +function AceEvent:RegisterEvent(event, method, once) + AceEvent:argCheck(event, 2, "string") + if self == AceEvent and not registeringFromAceEvent then + AceEvent:argCheck(method, 3, "function") + self = method + else + AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number") + if type(method) == "boolean" or type(method) == "number" then + AceEvent:argCheck(once, 4, "nil") + once, method = method, event + end + end + AceEvent:argCheck(once, 4, "number", "boolean", "nil") + if eventsWhichHappenOnce[event] then + once = true + end + local throttleRate + if type(once) == "number" then + throttleRate, once = once + end + 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) + else + assert(type(method) == "function" or type(method) == "string") + end + + local AceEvent_registry = AceEvent.registry + if not AceEvent_registry[event] then + AceEvent_registry[event] = new() + AceEvent.frame:RegisterEvent(event) + end + + local remember = true + if AceEvent_registry[event][self] then + remember = false + end + AceEvent_registry[event][self] = method + + local AceEvent_onceRegistry = AceEvent.onceRegistry + if once then + if not AceEvent_onceRegistry then + AceEvent.onceRegistry = {} + AceEvent_onceRegistry = AceEvent.onceRegistry + end + if not AceEvent_onceRegistry[event] then + AceEvent_onceRegistry[event] = new() + end + AceEvent_onceRegistry[event][self] = true + else + if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then + AceEvent_onceRegistry[event][self] = nil + if not next(AceEvent_onceRegistry[event]) then + AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event]) + end + end + end + + local AceEvent_throttleRegistry = AceEvent.throttleRegistry + if throttleRate then + if not AceEvent_throttleRegistry then + AceEvent.throttleRegistry = {} + AceEvent_throttleRegistry = AceEvent.throttleRegistry + end + if not AceEvent_throttleRegistry[event] then + AceEvent_throttleRegistry[event] = new() + end + if AceEvent_throttleRegistry[event][self] then + AceEvent_throttleRegistry[event][self] = nil + end + AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey) + local t = AceEvent_throttleRegistry[event][self] + t[RATE] = throttleRate + else + if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then + if AceEvent_throttleRegistry[event][self] then + AceEvent_throttleRegistry[event][self] = nil + end + if not next(AceEvent_throttleRegistry[event]) then + AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event]) + end + end + end + + if remember then + AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event) + end +end + +local ALL_EVENTS + +--[[---------------------------------------------------------------------------------- +Notes: + * Registers all events to the given method + * To access the current event, check AceEvent.currentEvent + * To access the current event's unique identifier, check AceEvent.currentEventUID + * This is only for debugging purposes. +Arguments: + [optional] string or function - name of the method or function to call. Default: same name as "event". +------------------------------------------------------------------------------------]] +function AceEvent:RegisterAllEvents(method) + if self == AceEvent then + AceEvent:argCheck(method, 1, "function") + self = method + else + AceEvent:argCheck(method, 1, "string", "function") + if type(method) == "string" and type(self[method]) ~= "function" then + AceEvent:error("Cannot register all events to method %q, it does not exist", method) + end + end + + local AceEvent_registry = AceEvent.registry + if not AceEvent_registry[ALL_EVENTS] then + AceEvent_registry[ALL_EVENTS] = new() + AceEvent.frame:RegisterAllEvents() + end + + local remember = not AceEvent_registry[ALL_EVENTS][self] + AceEvent_registry[ALL_EVENTS][self] = method + if remember then + AceEvent:TriggerEvent("AceEvent_EventRegistered", self, "all") + end +end + +--[[---------------------------------------------------------------------------------- +Notes: + * Trigger a custom AceEvent. + * This should never be called to simulate fake Blizzard events. + * Custom events should be in the form of AddonName_SpecificEvent +Arguments: + string - name of the event + tuple - list of arguments to pass along +------------------------------------------------------------------------------------]] +function AceEvent:TriggerEvent(event, ...) + if type(event) ~= "string" then + DEFAULT_CHAT_FRAME:AddMessage(debugstack()) + end + AceEvent:argCheck(event, 2, "string") + local AceEvent_registry = AceEvent.registry + if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then + return + end + local lastEvent = AceEvent.currentEvent + AceEvent.currentEvent = event + local lastEventUID = AceEvent.currentEventUID + local uid = AceEvent.UID_NUM + 1 + AceEvent.UID_NUM = uid + AceEvent.currentEventUID = uid + + local tmp = new() + + local AceEvent_onceRegistry = AceEvent.onceRegistry + if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then + for obj, method in pairs(AceEvent_onceRegistry[event]) do + tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil + end + local obj = next(tmp) + while obj do + local method = tmp[obj] + AceEvent.UnregisterEvent(obj, event) + if type(method) == "string" then + local obj_method = obj[method] + if obj_method then + local success, err = pcall(obj_method, obj, ...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + end + elseif method then -- function + local success, err = pcall(method, ...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + end + tmp[obj] = nil + obj = next(tmp) + end + end + + local AceEvent_throttleRegistry = AceEvent.throttleRegistry + local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] + if AceEvent_registry[event] then + for obj, method in pairs(AceEvent_registry[event]) do + tmp[obj] = method + end + local obj = next(tmp) + while obj do + local method = tmp[obj] + local continue = false + if throttleTable and throttleTable[obj] then + local a1 = ... + if a1 == nil then + a1 = FAKE_NIL + end + if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then + throttleTable[obj][a1] = GetTime() + else + continue = true + end + end + if not continue then + if type(method) == "string" then + local obj_method = obj[method] + if obj_method then + local success, err = pcall(obj_method, obj, ...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + end + elseif method then -- function + local success, err = pcall(method, ...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + end + end + tmp[obj] = nil + obj = next(tmp) + end + end + if AceEvent_registry[ALL_EVENTS] then + for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do + tmp[obj] = method + end + local obj = next(tmp) + while obj do + local method = tmp[obj] + if type(method) == "string" then + local obj_method = obj[method] + if obj_method then + local success, err = pcall(obj_method, obj, ...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + end + elseif method then -- function + local success, err = pcall(method, ...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + end + tmp[obj] = nil + obj = next(tmp) + end + end + tmp = del(tmp) + AceEvent.currentEvent = lastEvent + AceEvent.currentEventUID = lastEventUID +end + +local delayRegistry +local OnUpdate +do + local tmp = {} + OnUpdate = function() + local t = GetTime() + for k,v in pairs(delayRegistry) do + tmp[k] = true + end + for k in pairs(tmp) do + local v = delayRegistry[k] + if v then + local v_time = v.time + if not v_time then + delayRegistry[k] = nil + elseif v_time <= t then + local v_repeatDelay = v.repeatDelay + if v_repeatDelay then + -- use the event time, not the current time, else timing inaccuracies add up over time + v.time = v_time + v_repeatDelay + end + local event = v.event + if type(event) == "function" then + local uid = AceEvent.UID_NUM + 1 + AceEvent.UID_NUM = uid + AceEvent.currentEventUID = uid + local success, err = pcall(event, unpack(v, 1, v.n)) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + AceEvent.currentEventUID = nil + else + AceEvent:TriggerEvent(event, unpack(v, 1, v.n)) + end + if not v_repeatDelay then + local x = delayRegistry[k] + if x and x.time == v_time then -- check if it was manually reset + if not useTablesAsIDs or type(k) == "string" then + del(delayRegistry[k]) + end + delayRegistry[k] = nil + end + end + end + end + end + for k in pairs(tmp) do + tmp[k] = nil + end + if not next(delayRegistry) then + AceEvent.frame:Hide() + end + end +end + +local function ScheduleEvent(self, repeating, event, delay, ...) + local id + if type(event) == "string" or (useTablesAsIDs and type(event) == "table") then + if useTablesAsIDs and type(event) == "table" then + if not delayRegistry or 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 = event, delay, ... + 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 delayRegistry then + AceEvent.delayRegistry = {} + delayRegistry = AceEvent.delayRegistry + AceEvent.frame:SetScript("OnUpdate", OnUpdate) + end + local t + if useTablesAsIDs and type(id) == "table" then + for k in pairs(id) do + id[k] = nil + end + t = id + for i = 2, select('#', ...) do + t[i-1] = select(i, ...) + end + t.n = select('#', ...) - 1 + elseif id then + t = new(select(2, ...)) + t.n = select('#', ...) - 1 + else + t = new(...) + t.n = select('#', ...) + end + t.event = event + t.time = GetTime() + delay + t.self = self + t.id = id or t + t.repeatDelay = repeating and delay + delayRegistry[t.id] = t + AceEvent.frame:Show() + if useTablesAsIDs then + return t.id + else + return + end +end + +--[[---------------------------------------------------------------------------------- +Notes: + * Schedule an event to fire. + * To fire on the next frame, specify a delay of 0. +Arguments: + string or function - name of the event to fire, or a function to call. + number - the amount of time to wait until calling. + tuple - a list of arguments to pass along. +------------------------------------------------------------------------------------]] +function AceEvent:ScheduleEvent(event, delay, ...) + if type(event) == "string" or (useTablesAsIDs and type(event) == "table") then + if useTablesAsIDs and type(event) == "table" then + if not delayRegistry or not delayRegistry[event] then + AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") + end + end + if type(delay) ~= "number" then + AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") + AceEvent:argCheck(..., 4, "number") + end + else + AceEvent:argCheck(event, 2, "string", "function") + AceEvent:argCheck(delay, 3, "number") + end + + return ScheduleEvent(self, false, event, delay, ...) +end + +function AceEvent:ScheduleRepeatingEvent(event, delay, ...) + if type(event) == "string" or (useTablesAsIDs and type(event) == "table") then + if useTablesAsIDs and type(event) == "table" then + if not delayRegistry or not delayRegistry[event] then + AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") + end + end + if type(delay) ~= "number" then + AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") + AceEvent:argCheck(..., 4, "number") + end + else + AceEvent:argCheck(event, 2, "string", "function") + AceEvent:argCheck(delay, 3, "number") + end + + return ScheduleEvent(self, true, event, delay, ...) +end + +function AceEvent:CancelScheduledEvent(t) + if useTablesAsIDs then + AceEvent:argCheck(t, 2, "string", "table") + else + AceEvent:argCheck(t, 2, "string") + end + if delayRegistry then + local v = delayRegistry[t] + if v then + if not useTablesAsIDs or type(t) == "string" then + del(delayRegistry[t]) + end + delayRegistry[t] = nil + if not next(delayRegistry) then + AceEvent.frame:Hide() + end + return true + end + end + return false +end + +function AceEvent:IsEventScheduled(t) + if useTablesAsIDs then + AceEvent:argCheck(t, 2, "string", "table") + else + AceEvent:argCheck(t, 2, "string") + end + if delayRegistry then + local v = delayRegistry[t] + if v then + return true, v.time - GetTime() + end + end + return false, nil +end + +function AceEvent:UnregisterEvent(event) + AceEvent:argCheck(event, 2, "string") + local AceEvent_registry = AceEvent.registry + if AceEvent_registry[event] and AceEvent_registry[event][self] then + AceEvent_registry[event][self] = nil + local AceEvent_onceRegistry = AceEvent.onceRegistry + if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then + AceEvent_onceRegistry[event][self] = nil + if not next(AceEvent_onceRegistry[event]) then + AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event]) + end + end + local AceEvent_throttleRegistry = AceEvent.throttleRegistry + if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then + AceEvent_throttleRegistry[event][self] = nil + if not next(AceEvent_throttleRegistry[event]) then + AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event]) + end + end + if not next(AceEvent_registry[event]) then + AceEvent_registry[event] = del(AceEvent_registry[event]) + if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then + AceEvent.frame:UnregisterEvent(event) + end + end + else + if self == AceEvent then + error(("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0."):format(event), 2) + else + AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self) + end + end + AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) +end + +function AceEvent:UnregisterAllEvents() + local AceEvent_registry = AceEvent.registry + if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then + AceEvent_registry[ALL_EVENTS][self] = nil + if not next(AceEvent_registry[ALL_EVENTS]) then + AceEvent_registry[ALL_EVENTS] = del(AceEvent_registry[ALL_EVENTS]) + AceEvent.frame:UnregisterAllEvents() + for k,v in pairs(AceEvent_registry) do + AceEvent.frame:RegisterEvent(k) + end + end + end + if AceEvent_registry.AceEvent_EventUnregistered then + local event, data = "AceEvent_EventUnregistered", AceEvent_registry.AceEvent_EventUnregistered + local x = data[self] + data[self] = nil + if x then + if not next(data) then + if not AceEvent_registry[ALL_EVENTS] then + AceEvent.frame:UnregisterEvent(event) + end + AceEvent_registry[event] = del(AceEvent_registry[event]) + end + AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) + end + end + for event, data in pairs(AceEvent_registry) do + local x = data[self] + data[self] = nil + if x and event ~= ALL_EVENTS then + if not next(data) then + if not AceEvent_registry[ALL_EVENTS] then + AceEvent.frame:UnregisterEvent(event) + end + AceEvent_registry[event] = del(AceEvent_registry[event]) + end + AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) + end + end + if AceEvent.onceRegistry then + for event, data in pairs(AceEvent.onceRegistry) do + data[self] = nil + end + end +end + +function AceEvent:CancelAllScheduledEvents() + if delayRegistry then + for k,v in pairs(delayRegistry) do + if v.self == self then + if not useTablesAsIDs or type(k) == "string" then + del(delayRegistry[k]) + end + delayRegistry[k] = nil + end + end + if not next(delayRegistry) then + AceEvent.frame:Hide() + end + end +end + +function AceEvent:IsEventRegistered(event) + AceEvent:argCheck(event, 2, "string") + local AceEvent_registry = AceEvent.registry + if self == AceEvent then + return AceEvent_registry[event] and next(AceEvent_registry[event]) or AceEvent_registry[ALL_EVENTS] and next(AceEvent_registry[ALL_EVENTS]) and true or false + end + if AceEvent_registry[event] and AceEvent_registry[event][self] then + return true, AceEvent_registry[event][self] + end + if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then + return true, AceEvent_registry[ALL_EVENTS][self] + end + return false, nil +end + +local UnitExists = UnitExists +local bucketfunc +function AceEvent:RegisterBucketEvent(event, delay, method, ...) + AceEvent:argCheck(event, 2, "string", "table") + if type(event) == "table" then + for k,v in pairs(event) do + if type(k) ~= "number" then + AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.") + elseif type(v) ~= "string" then + AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.") + end + end + end + AceEvent:argCheck(delay, 3, "number") + if AceEvent == self then + AceEvent:argCheck(method, 4, "function") + self = method + else + if type(event) == "string" then + AceEvent:argCheck(method, 4, "string", "function", "nil") + if not method then + method = event + end + else + AceEvent:argCheck(method, 4, "string", "function") + 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 + end + local buckets = AceEvent.buckets + if not buckets[event] then + buckets[event] = new() + end + if not buckets[event][self] then + local t = new() + t.current = new() + t.self = self + buckets[event][self] = t + else + AceEvent.CancelScheduledEvent(self, buckets[event][self].id) + end + local bucket = buckets[event][self] + bucket.method = method + + local n = select('#', ...) + if n > 0 then + for i = 1, n do + bucket[i] = select(i, ...) + end + end + bucket.n = n + + local func = function(arg1) + bucket.run = true + if arg1 then + bucket.current[arg1] = true + end + end + buckets[event][self].func = func + local isUnitBucket = true + if type(event) == "string" then + AceEvent.RegisterEvent(self, event, func) + if not event:find("^UNIT_") then + isUnitBucket = false + end + else + for _,v in ipairs(event) do + AceEvent.RegisterEvent(self, v, func) + if isUnitBucket and not v:find("^UNIT_") then + isUnitBucket = false + end + end + end + bucket.unit = isUnitBucket + if not bucketfunc then + bucketfunc = function(bucket) + local current = bucket.current + local method = bucket.method + local self = bucket.self + if bucket.run then + if bucket.unit then + for unit in pairs(current) do + if not UnitExists(unit) then + current[unit] = nil + end + end + end + if type(method) == "string" then + self[method](self, current, unpack(bucket, 1, bucket.n)) + elseif method then -- function + method(current, unpack(bucket, 1, bucket.n)) + end + for k in pairs(current) do + current[k] = nil + k = nil + end + bucket.run = false + end + end + end + bucket.id = "AceEvent-Bucket-" .. tostring(bucket) + AceEvent.ScheduleRepeatingEvent(self, bucket.id, bucketfunc, delay, bucket) +end + +function AceEvent:IsBucketEventRegistered(event) + AceEvent:argCheck(event, 2, "string", "table") + return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self] +end + +function AceEvent:UnregisterBucketEvent(event) + AceEvent:argCheck(event, 2, "string", "table") + if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then + AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self) + end + + local bucket = AceEvent.buckets[event][self] + + if type(event) == "string" then + AceEvent.UnregisterEvent(self, event) + else + for _,v in ipairs(event) do + AceEvent.UnregisterEvent(self, v) + end + end + AceEvent:CancelScheduledEvent(bucket.id) + + bucket.current = del(bucket.current) + AceEvent.buckets[event][self] = del(bucket) + if not next(AceEvent.buckets[event]) then + AceEvent.buckets[event] = del(AceEvent.buckets[event]) + end +end + +function AceEvent:UnregisterAllBucketEvents() + if not AceEvent.buckets or not next(AceEvent.buckets) then + return + end + for k,v in pairs(AceEvent.buckets) do + if v == self then + AceEvent.UnregisterBucketEvent(self, k) + k = nil + end + end +end + +local combatSchedules +function AceEvent:CancelAllCombatSchedules() + local i = 0 + while true do + i = i + 1 + if not combatSchedules[i] then + break + end + local v = combatSchedules[i] + if v.self == self then + v = del(v) + table.remove(combatSchedules, i) + i = i - 1 + end + end +end + +local inCombat = false + +function AceEvent:PLAYER_REGEN_DISABLED() + inCombat = true +end + +do + local tmp = {} + function AceEvent:PLAYER_REGEN_ENABLED() + inCombat = false + for i, v in ipairs(combatSchedules) do + tmp[i] = v + combatSchedules[i] = nil + end + for i, v in ipairs(tmp) do + local func = v.func + if func then + local success, err = pcall(func, unpack(v, 1, v.n)) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + else + local obj = v.obj or v.self + local method = v.method + local obj_method = obj[method] + if obj_method then + local success, err = pcall(obj_method, obj, unpack(v, 1, v.n)) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + end + end + tmp[i] = del(v) + end + end +end + +function AceEvent:ScheduleLeaveCombatAction(method, ...) + local style = type(method) + if self == AceEvent then + if style == "table" then + local func = (...) + AceEvent:argCheck(func, 3, "string") + if type(method[func]) ~= "function" then + AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", func) + end + else + AceEvent:argCheck(method, 2, "function", --[[so message is right]] "table") + end + self = method + else + AceEvent:argCheck(method, 2, "function", "string", "table") + if style == "string" and type(self[method]) ~= "function" then + AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", method) + elseif style == "table" then + local func = (...) + AceEvent:argCheck(func, 3, "string") + if type(method[func]) ~= "function" then + AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", func) + end + end + end + + if not inCombat then + local success, err + if type(method) == "function" then + success, err = pcall(method, ...) + elseif type(method) == "table" then + local func = (...) + success, err = pcall(method[func], method, select(2, ...)) + else + success, err = pcall(self[method], self, ...) + end + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + return + end + local t + local n = select('#', ...) + if style == "table" then + t = new(select(2, ...)) + t.obj = method + t.method = (...) + t.n = n-1 + else + t = new(...) + t.n = n + if style == "function" then + t.func = method + else + t.method = method + end + end + t.self = self + table.insert(combatSchedules, t) +end + +function AceEvent:OnEmbedDisable(target) + self.UnregisterAllEvents(target) + + self.CancelAllScheduledEvents(target) + + self.UnregisterAllBucketEvents(target) + + self.CancelAllCombatSchedules(target) +end + +function AceEvent:IsFullyInitialized() + return self.postInit or false +end + +function AceEvent:IsPostPlayerLogin() + return IsLoggedIn() and true or false +end + +local function activate(self, oldLib, oldDeactivate) + AceEvent = self + + self.onceRegistry = oldLib and oldLib.onceRegistry or {} + self.throttleRegistry = oldLib and oldLib.throttleRegistry or {} + self.delayRegistry = oldLib and oldLib.delayRegistry or {} + self.buckets = oldLib and oldLib.buckets or {} + self.registry = oldLib and oldLib.registry or {} + self.frame = oldLib and oldLib.frame or CreateFrame("Frame", "AceEvent20Frame") + self.playerLogin = IsLoggedIn() and true + self.postInit = oldLib and oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true + self.ALL_EVENTS = oldLib and oldLib.ALL_EVENTS or _G.newproxy() + self.FAKE_NIL = oldLib and oldLib.FAKE_NIL or _G.newproxy() + self.RATE = oldLib and oldLib.RATE or _G.newproxy() + self.combatSchedules = oldLib and oldLib.combatSchedules or {} + self.UID_NUM = oldLib and oldLib.UID_NUM or 0 + + -- Delete this down the road. Makes sure that the addonframes from revisions 33121 - 36174 get their events unregistered. + local addonframes = oldLib and oldLib.addonframes + if addonframes then + for _, v in pairs(addonframes) do + v:UnregisterAllEvents() + end + end + + combatSchedules = self.combatSchedules + ALL_EVENTS = self.ALL_EVENTS + FAKE_NIL = self.FAKE_NIL + RATE = self.RATE + local inPlw = false + local blacklist = { + UNIT_INVENTORY_CHANGED = true, + BAG_UPDATE = true, + ITEM_LOCK_CHANGED = true, + ACTIONBAR_SLOT_CHANGED = true, + } + self.frame:SetScript("OnEvent", function(_, event, ...) + if event == "PLAYER_ENTERING_WORLD" then + inPlw = false + elseif event == "PLAYER_LEAVING_WORLD" then + inPlw = true + end + if event and (not inPlw or not blacklist[event]) then + self:TriggerEvent(event, ...) + end + end) + if self.delayRegistry then + delayRegistry = self.delayRegistry + self.frame:SetScript("OnUpdate", OnUpdate) + end + + self:UnregisterAllEvents() + self:CancelAllScheduledEvents() + + registeringFromAceEvent = true + self:RegisterEvent("LOOT_OPENED", function() + SendAddonMessage("LOOT_OPENED", "", "RAID") + end) + registeringFromAceEvent = nil + + local function handleFullInit() + if not self.postInit then + local function func() + self.postInit = true + self:TriggerEvent("AceEvent_FullyInitialized") + if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then + self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE") + end + if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then + self:UnregisterEvent("MEETINGSTONE_CHANGED") + end + if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then + self:UnregisterEvent("MINIMAP_ZONE_CHANGED") + end + if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then + self:UnregisterEvent("LANGUAGE_LIST_CHANGED") + end + collectgarbage('collect') + end + registeringFromAceEvent = true + local f = function() + self.playerLogin = true + self:ScheduleEvent("AceEvent_FullyInitialized", func, 1) + end + self:RegisterEvent("MEETINGSTONE_CHANGED", f, true) + self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function() + self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.15) + end) + self:RegisterEvent("LANGUAGE_LIST_CHANGED", function() + if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then + registeringFromAceEvent = true + self:UnregisterEvent("MEETINGSTONE_CHANGED") + self:RegisterEvent("MINIMAP_ZONE_CHANGED", fd, true) + registeringFromAceEvent = nil + end + end) + self:ScheduleEvent("AceEvent_FullyInitialized", func, 10) + registeringFromAceEvent = nil + end + end + + if not self.playerLogin then + registeringFromAceEvent = true + self:RegisterEvent("PLAYER_LOGIN", function() + self.playerLogin = true + handleFullInit() + handleFullInit = nil + collectgarbage('collect') + end, true) + registeringFromAceEvent = nil + else + handleFullInit() + handleFullInit = nil + end + + if not AceEvent20EditBox then + CreateFrame("Editbox", "AceEvent20EditBox") + end + local editbox = AceEvent20EditBox + function editbox:Execute(line) + local defaulteditbox = DEFAULT_CHAT_FRAME.editBox + self:SetAttribute("chatType", defaulteditbox:GetAttribute("chatType")) + self:SetAttribute("tellTarget", defaulteditbox:GetAttribute("tellTarget")) + self:SetAttribute("channelTarget", defaulteditbox:GetAttribute("channelTarget")) + self:SetText(line) + ChatEdit_SendText(self) + end + editbox:Hide() + _G["SLASH_IN1"] = "/in" + SlashCmdList["IN"] = function(msg) + local seconds, command, rest = msg:match("^([^%s]+)%s+(/[^%s]+)(.*)$") + seconds = tonumber(seconds) + if not seconds then + DEFAULT_CHAT_FRAME:AddMessage("Error, bad arguments to /in. Must be in the form of `/in 5 /say hi'") + return + end + if IsSecureCmd(command) then + DEFAULT_CHAT_FRAME:AddMessage(("Error, /in cannot call secure command: %s"):format(command)) + return + end + self:ScheduleEvent("AceEventSlashIn-" .. math.random(1, 1000000000), editbox.Execute, seconds, editbox, command .. rest) + end + registeringFromAceEvent = true + self:RegisterEvent("PLAYER_REGEN_ENABLED") + self:RegisterEvent("PLAYER_REGEN_DISABLED") + inCombat = InCombatLockdown() + registeringFromAceEvent = nil + + -- another hack to make sure that we clean up properly from rev 33121 - 36174 + if self.registry[ALL_EVENTS] then + self.frame:RegisterAllEvents() + else + for event in pairs(self.registry) do + self.frame:RegisterEvent(event) + end + end + + self:activate(oldLib, oldDeactivate) + if oldLib then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, activate)