WoWInterface SVN BrokerItemRack

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 1 to Rev 2
    Reverse comparison

Rev 1 → Rev 2

trunk/Broker_ItemRack.tga Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes : Added: svn:mime-type + application/octet-stream
trunk/embeds.xml New file
0,0 → 1,8
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
 
<Script file="libs\LibStub\LibStub.lua"/>
<Include file="libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
<Include file="libs\LibDataBroker-1.1\LibDataBroker-1.1.lua"/>
<Include file="Libs\AceLocale-3.0\AceLocale-3.0.xml"/>
</Ui>
\ No newline at end of file
trunk/Broker_ItemRack.lua New file
0,0 → 1,166
local L = LibStub("AceLocale-3.0"):GetLocale("Broker_ItemRack", true)
-- Check if ItemRack is loaded
-- in case some malicious user eliminated the ReqDep :p
if not IsAddOnLoaded("ItemRack") then
DisableAddOn("Broker_ItemRack")
DEFAULT_CHAT_FRAME:AddMessage(GREEN_FONT_COLOR_CODE.."Broker ItemRack: "..FONT_COLOR_CODE_CLOSE..L["ItemRack addon not loaded."])
return
end
 
local dataobj = LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject("Broker_ItemRack", {label = "Broker ItemRack", icon = "Interface\\Addons\\Broker_ItemRack\\Broker_ItemRack", text = L["<no outfit>"]})
local BRItemRack = CreateFrame("Frame", "Broker_ItemRack")
 
BRItemRack:RegisterEvent("VARIABLES_LOADED")
BRItemRack:RegisterEvent("PLAYER_ENTERING_WORLD");
BRItemRack:RegisterEvent("UNIT_INVENTORY_CHANGED");
BRItemRack:RegisterEvent("ADDON_LOADED")
 
BRItemRack:SetScript("OnEvent", function(self, event, arg1, ...)
 
if event == "ADDON_LOADED" then
if IsAddOnLoaded("ItemRackOptions") then
hooksecurefunc(ItemRackOpt, "SaveSet", BRItemRack_Update)
BRItemRack:UnregisterEvent("ADDON_LOADED")
end
end
 
if event == "VARIABLES_LOADED" then
if not Broker_ItemRackConfig then
-- initialize default configuration
Broker_ItemRackConfig = {
MenuOrientation = "VERTICAL"
}
end
end
 
if ((event == "UNIT_INVENTORY_CHANGED") and (arg1 == "player")) or event == "PLAYER_ENTERING_WORLD" then
BRItemRack_Update();
end
end);
 
 
function BRItemRack_Update()
 
local i =1;
local outfit = nil;
local usersets = ItemRackUser.Sets;
 
 
for i in next,usersets do
if not string.find(i, "~") then
if ItemRack.IsSetEquipped(i) then
outfit = i;
end
end
end
 
-- Set text
if outfit then
dataobj.text = outfit;
else
dataobj.text = L["Custom Outfit"];
end
 
-- Set icon if found and the display supports it
if ItemRackUser.Sets[ItemRackUser.CurrentSet] and ItemRackUser.Sets[ItemRackUser.CurrentSet].icon and ItemRack.IsSetEquipped(dataobj.text) then
dataobj.icon = ItemRackUser.Sets[ItemRackUser.CurrentSet].icon;
else
dataobj.icon = "Interface\\Addons\\Broker_ItemRack\\Broker_ItemRack";
end
 
end
 
local function GetTipAnchor(frame)
local x,y = frame:GetCenter()
if not x or not y then return "TOPLEFT", "BOTTOMLEFT" end
local hhalf = (x > UIParent:GetWidth()*2/3) and "RIGHT" or (x < UIParent:GetWidth()/3) and "LEFT" or ""
local vhalf = (y > UIParent:GetHeight()/2) and "TOP" or "BOTTOM"
return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf
end
 
 
function dataobj.OnLeave() GameTooltip:Hide() end
 
function dataobj.OnEnter(self)
GameTooltip:SetOwner(self, "ANCHOR_NONE")
GameTooltip:SetPoint(GetTipAnchor(self))
GameTooltip:ClearLines()
 
GameTooltip:AddLine(HIGHLIGHT_FONT_COLOR_CODE.."Broker ItemRack"..FONT_COLOR_CODE_CLOSE)
GameTooltip:AddLine(L["Left-Click to change outfit."])
GameTooltip:AddLine(L["Shift-Left Click to toggle ItemRack menu orientation."])
GameTooltip:AddLine(L["Right-Click to open configuration."])
GameTooltip:AddLine(" ")
GameTooltip:AddLine(GREEN_FONT_COLOR_CODE..L["Current menu orientation:"]..FONT_COLOR_CODE_CLOSE.." "..NORMAL_FONT_COLOR_CODE..L[Broker_ItemRackConfig.MenuOrientation]..FONT_COLOR_CODE_CLOSE)
 
GameTooltip:Show()
end
 
 
function dataobj.OnClick(self, button)
 
if (button == "LeftButton") and IsShiftKeyDown() then
if Broker_ItemRackConfig.MenuOrientation == "VERTICAL" then
Broker_ItemRackConfig.MenuOrientation = "HORIZONTAL"
else
Broker_ItemRackConfig.MenuOrientation = "VERTICAL"
end
dataobj.OnEnter(self)
return
end
 
GameTooltip:Hide()
 
if (button == "LeftButton") then
 
local tip1, frame, tip2
if self and self:GetName() then
-- get anchor according to frame 'self'
tip1, frame, tip2 = GetTipAnchor(self)
-- get anchor according to parent
elseif self:GetParent() and self:GetParent():GetName() then
tip1, frame, tip2 = GetTipAnchor(self:GetParent())
end
 
-- correct itemrack menu frame position
if tip1 == "TOP" and tip2 == "BOTTOM" then
tip1 = "TOPLEFT"
tip2 = "BOTTOMLEFT"
elseif tip1 == "BOTTOM" and tip2 == "TOP" then
tip1 = "BOTTOMRIGHT"
tip2 = "TOPRIGHT"
end
 
if self and self:GetName() then
-- normal docking if display provides a frame
ItemRack.DockWindows(tip1, self, tip2, Broker_ItemRackConfig.MenuOrientation);
ItemRack.BuildMenu(20)
ItemRackMenuFrame:SetFrameStrata("FULLSCREEN");
-- "hack" to reiterate an altered version of ItemRack's original function
-- in case the display does not provide a native frame
-- but rather a parent
elseif self:GetParent() and self:GetParent():GetName() then
ItemRackMenuFrame:ClearAllPoints()
ItemRack.currentDock = tip2..tip1
ItemRackMenuFrame:SetPoint(tip1,self,tip2,ItemRack.DockInfo[ItemRack.currentDock].xoff,ItemRack.DockInfo[ItemRack.currentDock].yoff)
ItemRackMenuFrame:SetParent(self:GetParent())
ItemRackMenuFrame:SetFrameStrata("FULLSCREEN");
ItemRack.mainDock = tip2
ItemRack.menuDock = tip1
ItemRack.menuOrient = Broker_ItemRackConfig.MenuOrientation
ItemRack.menuDockedTo = self:GetParent():GetName()
ItemRack.MenuMouseoverFrames[self:GetParent():GetName()] = 1 -- add frame to mouseover candidates
ItemRack.ReflectLock(not ItemRack.menuMovable)
ItemRack.ReflectMenuScale()
ItemRack.BuildMenu(20)
end
 
end
 
if (button == "RightButton") then
if ItemRackMenuFrame:IsVisible() then
ItemRackMenuFrame:Hide()
end
ItemRack.ToggleOptions(1);
end
end
trunk/Broker_ItemRack.toc New file
0,0 → 1,15
## Interface: 20400
## Title: Broker [ItemRack]
## Author: Tristanian
## Notes: Minimalistic LDB plugin for the ItemRack addon.
## Dependencies: ItemRack
## OptionalDeps: Ace3
## X-Embeds: Ace3
## X-Category: Miscellaneous
## SavedVariables: Broker_ItemRackConfig
## Version: 1.3
 
embeds.xml
 
locale-enUS.lua
Broker_ItemRack.lua
\ No newline at end of file
trunk/locale-enUS.lua New file
0,0 → 1,11
local L = LibStub("AceLocale-3.0"):NewLocale("Broker_ItemRack","enUS",true)
 
L["<no outfit>"] = true
L["Custom Outfit"] = true
L["ItemRack addon not loaded."] = true
L["Left-Click to change outfit."] = true
L["Right-Click to open configuration."] = true
L["Shift-Left Click to toggle ItemRack menu orientation."] = true
L["Current menu orientation:"] = true
L["VERTICAL"] = "VERTICAL"
L["HORIZONTAL"] = "HORIZONTAL"
\ No newline at end of file
trunk/libs/LibDataBroker-1.1/LibDataBroker-1.1.lua New file
0,0 → 1,66
 
assert(LibStub, "LibDataBroker-1.1 requires LibStub")
assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0")
 
local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 3)
if not lib then return end
oldminor = oldminor or 0
 
 
lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib)
lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {}
local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks
 
if oldminor < 2 then
lib.domt = {
__metatable = "access denied",
__index = function(self, key) return attributestorage[self] and attributestorage[self][key] end,
}
end
 
if oldminor < 3 then
lib.domt.__newindex = function(self, key, value)
if not attributestorage[self] then attributestorage[self] = {} end
if attributestorage[self][key] == value then return end
attributestorage[self][key] = value
local name = namestorage[self]
if not name then return end
callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self)
callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self)
callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self)
callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self)
end
end
 
if oldminor < 2 then
function lib:NewDataObject(name, dataobj)
if self.proxystorage[name] then return end
 
if dataobj then
assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table")
self.attributestorage[dataobj] = {}
for i,v in pairs(dataobj) do
self.attributestorage[dataobj][i] = v
dataobj[i] = nil
end
end
dataobj = setmetatable(dataobj or {}, self.domt)
self.proxystorage[name], self.namestorage[dataobj] = dataobj, name
self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj)
return dataobj
end
end
 
if oldminor < 1 then
function lib:DataObjectIterator()
return pairs(self.proxystorage)
end
 
function lib:GetDataObjectByName(dataobjectname)
return self.proxystorage[dataobjectname]
end
 
function lib:GetNameByDataObject(dataobject)
return self.namestorage[dataobject]
end
end
trunk/libs/LibStub/LibStub.lua New file
0,0 → 1,30
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
local LibStub = _G[LIBSTUB_MAJOR]
 
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
LibStub = LibStub or {libs = {}, minors = {} }
_G[LIBSTUB_MAJOR] = LibStub
LibStub.minor = LIBSTUB_MINOR
 
function LibStub:NewLibrary(major, minor)
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
 
local oldminor = self.minors[major]
if oldminor and oldminor >= minor then return nil end
self.minors[major], self.libs[major] = minor, self.libs[major] or {}
return self.libs[major], oldminor
end
 
function LibStub:GetLibrary(major, silent)
if not self.libs[major] and not silent then
error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
end
return self.libs[major], self.minors[major]
end
 
function LibStub:IterateLibraries() return pairs(self.libs) end
setmetatable(LibStub, { __call = LibStub.GetLibrary })
end
trunk/libs/LibStub/LibStub.toc New file
0,0 → 1,9
## Interface: 20100
## Title: Lib: LibStub
## Notes: Universal Library Stub
## Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel
## X-Website: http://jira.wowace.com/browse/LS | http://www.wowace.com/wiki/LibStub
## X-Category: Library
## X-License: Public Domain
 
LibStub.lua
trunk/libs/AceLocale-3.0/AceLocale-3.0.xml New file
0,0 → 1,4
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="AceLocale-3.0.lua"/>
</Ui>
\ No newline at end of file
trunk/libs/AceLocale-3.0/AceLocale-3.0.lua New file
0,0 → 1,103
--[[ $Id: AceLocale-3.0.lua 60131 2008-02-03 13:03:56Z nevcairiel $ ]]
local MAJOR,MINOR = "AceLocale-3.0", 1
 
local AceLocale, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
 
if not AceLocale then return end -- no upgrade needed
 
local gameLocale = GetLocale()
if gameLocale == "enGB" then
gameLocale = "enUS"
end
 
AceLocale.apps = AceLocale.apps or {} -- array of ["AppName"]=localetableref
AceLocale.appnames = AceLocale.appnames or {} -- array of [localetableref]="AppName"
 
-- This metatable is used on all tables returned from GetLocale
local readmeta = {
__index = function(self, key) -- requesting totally unknown entries: fire off a nonbreaking error and return key
geterrorhandler()(MAJOR..": "..tostring(AceLocale.appnames[self])..": Missing entry for '"..tostring(key).."'")
rawset(self, key, key) -- only need to see the warning once, really
return key
end
}
 
-- Remember the locale table being registered right now (it gets set by :NewLocale())
local registering
 
-- local assert false function
local assertfalse = function() assert(false) end
 
-- This metatable proxy is used when registering nondefault locales
local writeproxy = setmetatable({}, {
__newindex = function(self, key, value)
rawset(registering, key, value == true and key or value) -- assigning values: replace 'true' with key string
end,
__index = assertfalse
})
 
-- This metatable proxy is used when registering the default locale.
-- It refuses to overwrite existing values
-- Reason 1: Allows loading locales in any order
-- Reason 2: If 2 modules have the same string, but only the first one to be
-- loaded has a translation for the current locale, the translation
-- doesn't get overwritten.
--
local writedefaultproxy = setmetatable({}, {
__newindex = function(self, key, value)
if not rawget(registering, key) then
rawset(registering, key, value == true and key or value)
end
end,
__index = assertfalse
})
 
-- AceLocale:NewLocale(application, locale, isDefault)
--
-- application (string) - unique name of addon / module
-- locale (string) - name of locale to register, e.g. "enUS", "deDE", etc...
-- isDefault (string) - if this is the default locale being registered
--
-- Returns a table where localizations can be filled out, or nil if the locale is not needed
function AceLocale:NewLocale(application, locale, isDefault)
 
-- GAME_LOCALE allows translators to test translations of addons without having that wow client installed
-- Ammo: I still think this is a bad idea, for instance an addon that checks for some ingame string will fail, just because some other addon
-- gives the user the illusion that they can run in a different locale? Ditch this whole thing or allow a setting per 'application'. I'm of the
-- opinion to remove this.
local gameLocale = GAME_LOCALE or gameLocale
 
if locale ~= gameLocale and not isDefault then
return -- nop, we don't need these translations
end
 
local app = AceLocale.apps[application]
 
if not app then
app = setmetatable({}, readmeta)
AceLocale.apps[application] = app
AceLocale.appnames[app] = application
end
 
registering = app -- remember globally for writeproxy and writedefaultproxy
 
if isDefault then
return writedefaultproxy
end
 
return writeproxy
end
 
-- AceLocale:GetLocale(application [, silent])
--
-- application (string) - unique name of addon
-- silent (boolean) - if true, the locale is optional, silently return nil if it's not found
--
-- Returns localizations for the current locale (or default locale if translations are missing)
-- Errors if nothing is registered (spank developer, not just a missing translation)
function AceLocale:GetLocale(application, silent)
if not silent and not AceLocale.apps[application] then
error("Usage: GetLocale(application[, silent]): 'application' - No locales registered for '"..tostring(application).."'", 2)
end
return AceLocale.apps[application]
end
trunk/libs/CallbackHandler-1.0/CallbackHandler-1.0.xml New file
0,0 → 1,4
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="CallbackHandler-1.0.lua"/>
</Ui>
\ No newline at end of file
trunk/libs/CallbackHandler-1.0/CallbackHandler-1.0.lua New file
0,0 → 1,239
--[[ $Id: CallbackHandler-1.0.lua 60697 2008-02-09 16:51:20Z nevcairiel $ ]]
local MAJOR, MINOR = "CallbackHandler-1.0", 3
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
 
if not CallbackHandler then return end -- No upgrade needed
 
local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end}
 
local type = type
local pcall = pcall
local pairs = pairs
local assert = assert
local concat = table.concat
local loadstring = loadstring
local next = next
local select = select
local type = type
local xpcall = xpcall
 
local function errorhandler(err)
return geterrorhandler()(err)
end
 
local function CreateDispatcher(argCount)
local code = [[
local next, xpcall, eh = ...
 
local method, ARGS
local function call() method(ARGS) end
 
local function dispatch(handlers, ...)
local index
index, method = next(handlers)
if not method then return end
local OLD_ARGS = ARGS
ARGS = ...
repeat
xpcall(call, eh)
index, method = next(handlers, index)
until not method
ARGS = OLD_ARGS
end
 
return dispatch
]]
 
local ARGS, OLD_ARGS = {}, {}
for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end
code = code:gsub("OLD_ARGS", concat(OLD_ARGS, ", ")):gsub("ARGS", concat(ARGS, ", "))
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler)
end
 
local Dispatchers = setmetatable({}, {__index=function(self, argCount)
local dispatcher = CreateDispatcher(argCount)
rawset(self, argCount, dispatcher)
return dispatcher
end})
 
--------------------------------------------------------------------------
-- CallbackHandler:New
--
-- target - target object to embed public APIs in
-- RegisterName - name of the callback registration API, default "RegisterCallback"
-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback"
-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API.
 
function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName, OnUsed, OnUnused)
-- TODO: Remove this after beta has gone out
assert(not OnUsed and not OnUnused, "ACE-80: OnUsed/OnUnused are deprecated. Callbacks are now done to registry.OnUsed and registry.OnUnused")
 
RegisterName = RegisterName or "RegisterCallback"
UnregisterName = UnregisterName or "UnregisterCallback"
if UnregisterAllName==nil then -- false is used to indicate "don't want this method"
UnregisterAllName = "UnregisterAllCallbacks"
end
 
-- we declare all objects and exported APIs inside this closure to quickly gain access
-- to e.g. function names, the "target" parameter, etc
 
 
-- Create the registry object
local events = setmetatable({}, meta)
local registry = { recurse=0, events=events }
 
-- registry:Fire() - fires the given event/message into the registry
function registry:Fire(eventname, ...)
if not rawget(events, eventname) or not next(events[eventname]) then return end
local oldrecurse = registry.recurse
registry.recurse = oldrecurse + 1
 
Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...)
 
registry.recurse = oldrecurse
 
if registry.insertQueue and oldrecurse==0 then
-- Something in one of our callbacks wanted to register more callbacks; they got queued
for eventname,callbacks in pairs(registry.insertQueue) do
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
for self,func in pairs(callbacks) do
events[eventname][self] = func
-- fire OnUsed callback?
if first and registry.OnUsed then
registry.OnUsed(registry, target, eventname)
first = nil
end
end
end
registry.insertQueue = nil
end
end
 
-- Registration of a callback, handles:
-- self["method"], leads to self["method"](self, ...)
-- self with function ref, leads to functionref(...)
-- "addonId" (instead of self) with function ref, leads to functionref(...)
-- all with an optional arg, which, if present, gets passed as first argument (after self if present)
target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]])
if type(eventname) ~= "string" then
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2)
end
 
method = method or eventname
 
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
 
if type(method) ~= "string" and type(method) ~= "function" then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2)
end
 
local regfunc
 
if type(method) == "string" then
-- self["method"] calling style
if type(self) ~= "table" then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2)
elseif self==target then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2)
elseif type(self[method]) ~= "function" then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2)
end
 
if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
local arg=select(1,...)
regfunc = function(...) self[method](self,arg,...) end
else
regfunc = function(...) self[method](self,...) end
end
else
-- function ref with self=object or self="addonId"
if type(self)~="table" and type(self)~="string" then
error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string expected.", 2)
end
 
if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
local arg=select(1,...)
regfunc = function(...) method(arg,...) end
else
regfunc = method
end
end
 
 
if events[eventname][self] or registry.recurse<1 then
-- if registry.recurse<1 then
-- we're overwriting an existing entry, or not currently recursing. just set it.
events[eventname][self] = regfunc
-- fire OnUsed callback?
if registry.OnUsed and first then
registry.OnUsed(registry, target, eventname)
end
else
-- we're currently processing a callback in this registry, so delay the registration of this new entry!
-- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency
registry.insertQueue = registry.insertQueue or setmetatable({},meta)
registry.insertQueue[eventname][self] = regfunc
end
end
 
-- Unregister a callback
target[UnregisterName] = function(self, eventname)
if not self or self==target then
error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2)
end
if type(eventname) ~= "string" then
error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2)
end
if rawget(events, eventname) and events[eventname][self] then
events[eventname][self] = nil
-- Fire OnUnused callback?
if registry.OnUnused and not next(events[eventname]) then
registry.OnUnused(registry, target, eventname)
end
end
if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then
registry.insertQueue[eventname][self] = nil
end
end
 
-- OPTIONAL: Unregister all callbacks for given selfs/addonIds
if UnregisterAllName then
target[UnregisterAllName] = function(...)
if select("#",...)<1 then
error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2)
end
if select("#",...)==1 and ...==target then
error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2)
end
 
 
for i=1,select("#",...) do
local self = select(i,...)
if registry.insertQueue then
for eventname, callbacks in pairs(registry.insertQueue) do
if callbacks[self] then
callbacks[self] = nil
end
end
end
for eventname, callbacks in pairs(events) do
if callbacks[self] then
callbacks[self] = nil
-- Fire OnUnused callback?
if registry.OnUnused and not next(callbacks) then
registry.OnUnused(registry, target, eventname)
end
end
end
end
end
end
 
return registry
end
 
 
-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it
-- try to upgrade old implicit embeds since the system is selfcontained and
-- relies on closures to work.