/
local addonName = ... |
local L = LibStub("AceLocale-3.0"):GetLocale(addonName, true) |
local db |
local dataobj |
local ShowKeybindFrame -- local function, forward declaration |
local frame -- first used for ADDON_LOADED, then can be recycled for keybinding |
-- Print with colored prefix. |
local printPrefix = "|cff33ff99"..addonName.."|r:" |
local function print(...) |
_G.print(printPrefix, ...) |
end |
-- Globals for bindings |
BINDING_HEADER_BROKER_BUBBLETOGGLE = addonName |
BINDING_NAME_BROKER_BUBBLETOGGLE_TOGGLE = L["Toggle Chat Bubbles"] |
-- Global. Used in Bindings. |
function Broker_BubbleToggle_Toggle() |
SetCVar("ChatBubbles", 1 - GetCVar("ChatBubbles")) |
UpdateDataObject() |
if GameTooltip:IsShown() then |
GameTooltip:Hide() |
end |
end |
-- Updates aspects of the dataobj that change when we change settings. |
function UpdateDataObject() |
if db.showText then |
dataobj.type = "data source" |
else |
dataobj.type = "launcher" |
end |
if GetCVarBool("ChatBubbles") then |
dataobj.text = L["Chat Bubbles"] |
dataobj.icon = "Interface\\Addons\\Broker_BubbleToggle\\images\\green.tga" |
dataobj.modeDescription = "Chat Bubbles" |
dataobj.otherModeDescription = L["No Bubbles"] |
else |
dataobj.text = L["No Bubbles"] |
dataobj.icon = "Interface\\Addons\\Broker_BubbleToggle\\images\\red.tga" |
dataobj.modeDescription = L["No Bubbles"] |
dataobj.otherModeDescription = "Chat Bubbles" |
end |
end |
-- Init. Use frame to wait for ADDON_LOADED. |
frame = CreateFrame("Frame") |
frame:SetScript("OnEvent", function(self, event, name) |
if name ~= addonName then return end |
frame:UnregisterEvent("ADDON_LOADED") |
frame:SetScript("OnEvent", nil) |
Broker_BubbleToggleDB = Broker_BubbleToggleDB or {} |
db = Broker_BubbleToggleDB |
if db.showText == nil then |
db.showText = true |
end |
dataobj = {} |
UpdateDataObject() |
dataobj = LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject("BubbleToggle", dataobj) |
--hooksecurefunc("RestartGx", UpdateDataObject) |
function dataobj:OnTooltipShow() |
self:AddLine(dataobj.modeDescription) |
self:AddDoubleLine(L["Left-Click"], dataobj.otherModeDescription) |
self:AddDoubleLine(L["Middle-Click"], (db.showText and L["Hide Text"] or L["Show Text"])) |
self:AddDoubleLine(L["Right-Click"], KEY_BINDING) |
end |
function dataobj:OnClick(button) |
if button == "LeftButton" then |
Broker_BubbleToggle_Toggle() |
elseif button == "MiddleButton" then |
db.showText = not db.showText |
UpdateDataObject() |
elseif button == "RightButton" then |
if not InCombatLockdown() then |
ShowKeybindFrame() |
else |
print(L["Can't keybind in combat."]) |
end |
end |
end |
end) |
frame:RegisterEvent("ADDON_LOADED") |
----------------------------------- |
-- Keybind Frame |
----------------------------------- |
-- This can be called many times; it only does extensive init the first time. |
function ShowKeybindFrame() -- declared local |
frame:SetFrameStrata("DIALOG") |
frame:EnableKeyboard(true) |
frame:SetClampedToScreen(true) |
frame:Hide() |
frame:SetPoint("CENTER", 0, UIParent:GetHeight() / 7) |
frame:SetBackdrop({ |
bgFile=[[Interface\Tooltips\UI-Tooltip-Background]], |
edgeFile=[[Interface\Tooltips\UI-Tooltip-Border]], tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 4, right = 4, top = 4, bottom = 4 } |
}) |
frame:SetBackdropColor(0, 0, 0) |
frame.text = frame:CreateFontString(nil, "ARTWORK", "GameFontNormal") |
frame.text:SetPoint("TOP", 0, -40) |
frame.text:SetText(L[ [=[ |
Press a key to bind. |
Or press ESC to cancel.]=] ]) |
frame:SetScript("OnShow", function(self) |
local width = self.text:GetWidth() + 80 |
local height = self.text:GetHeight() + 80 |
self:SetWidth(width) |
self:SetHeight(height) |
self:RegisterEvent("PLAYER_REGEN_DISABLED") -- so we can hide in combat |
end) |
frame:SetScript("OnHide", function(self) |
self:UnregisterEvent("PLAYER_REGEN_DISABLED") |
end) |
frame:SetScript("OnEvent", function(self, event, ...) |
if event == "PLAYER_REGEN_DISABLED" then |
print(L["Can't keybind in combat."]) |
self:Hide() -- hide in combat |
end |
end) |
-- These generate OnKeyDown events, but aren't bindable and should be discarded. |
local unbindableKeys = { |
LALT = 1, |
LCTRL = 1, |
LSHIFT = 1, |
RALT = 1, |
RCTRL = 1, |
RSHIFT = 1, |
UNKNOWN = 1, |
} |
frame:SetScript("OnKeyDown", function(self, key) |
if unbindableKeys[key] then |
return |
end |
if key == "ESCAPE" then |
self:Hide() |
return |
end |
if GetBindingFromClick(key) == "SCREENSHOT" then |
TakeScreenshot() |
return |
end |
if IsShiftKeyDown() then |
key = "SHIFT-"..key |
end |
if IsControlKeyDown() then |
key = "CTRL-"..key |
end |
if IsAltKeyDown() then |
key = "ALT-"..key |
end |
if SetBinding(key, "BROKER_BUBBLETOGGLE_TOGGLE") then |
print(KEY_BOUND..": "..key) |
SaveBindings(GetCurrentBindingSet()) |
else |
print(KEY_BINDING..": "..FAILED) |
end |
self:Hide() |
end) |
ShowKeybindFrame = function() frame:Show() end |
frame:Show() |
end |
<?xml version="1.0" encoding="UTF-8"?> |
<projectDescription> |
<name>BubbleToggle</name> |
<comment></comment> |
<projects> |
</projects> |
<buildSpec> |
<buildCommand> |
<name>org.keplerproject.ldt.core.luaProjectBuilder</name> |
<arguments> |
</arguments> |
</buildCommand> |
</buildSpec> |
<natures> |
<nature>org.keplerproject.ldt.core.luaProjectNature</nature> |
</natures> |
</projectDescription> |
## Interface: 30300 |
## Title: Broker: BubbleToggle |
## Notes: Toggle chat bubbles with click or keybind. Totally borrowed the code from ZombiePope |
## Author: Monk1nr |
## OptionalDeps: Ace3 |
## SavedVariables: Broker_BubbleToggleDB |
## LoadManagers: AddonLoader |
## X-LoadOn-Always: delayed |
#@no-lib-strip@ |
libs\LibStub\LibStub.lua |
libs\CallbackHandler-1.0\CallbackHandler-1.0.xml |
libs\AceLocale-3.0\AceLocale-3.0.xml |
#@end-no-lib-strip@ |
libs\LibDataBroker-1.1.lua |
Localization.lua |
Broker_BubbleToggle.lua |
The "chat bubble" icons are from GoSquared. They're public domain, and this credit isn't necessary; it's just a courtesy: |
http://www.gosquared.com/liquidicity/archives/122 |
-- Broker_BubbleToggle Localization File |
-- THIS CONTENTS OF THIS FILE ARE AUTO-GENERATED BY THE WOWACE PACKAGER |
-- Please use the Localization App on WowAce to update this |
-- at http://www.wowace.com/projects/broker-windowed-mode/localization/ |
local addonName = ... |
local AL3 = LibStub("AceLocale-3.0") |
local debug = false |
--[===[@debug@ |
debug = true |
--@end-debug@]===] |
local L = AL3:NewLocale(addonName, "enUS", true, debug) |
if L then |
L["Can't keybind in combat."] = true |
L["Bubbles"] = true |
L["Chat Bubbles"] = true |
L["Hide Text"] = true |
L["Left-Click"] = true |
L["Middle-Click"] = true |
L[ [=[Press a key to bind. |
Or press ESC to cancel.]=] ] = true |
L["Right-Click"] = true |
L["Show Text"] = true |
L["Toggle Chat Bubbles"] = true |
L["No Bubbles"] = true |
if GetLocale() == "enUS" or GetLocale() == "enGB" then return end |
end |
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", 4) |
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 |
if oldminor < 4 then |
local next = pairs(attributestorage) |
function lib:pairs(dataobject_or_name) |
local t = type(dataobject_or_name) |
assert(t == "string" or t == "table", "Usage: ldb:pairs('dataobjectname') or ldb:pairs(dataobject)") |
local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name |
assert(attributestorage[dataobj], "Data object not found") |
return next, attributestorage[dataobj], nil |
end |
local ipairs_iter = ipairs(attributestorage) |
function lib:ipairs(dataobject_or_name) |
local t = type(dataobject_or_name) |
assert(t == "string" or t == "table", "Usage: ldb:ipairs('dataobjectname') or ldb:ipairs(dataobject)") |
local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name |
assert(attributestorage[dataobj], "Data object not found") |
return ipairs_iter, attributestorage[dataobj], 0 |
end |
end |
-- 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 |
## Interface: 20400 |
## Title: Lib: LibStub |
## Notes: Universal Library Stub |
## Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel |
## X-Website: http://jira.wowace.com/browse/LS |
## X-Category: Library |
## X-License: Public Domain |
## X-Curse-Packaged-Version: 1.0 |
## X-Curse-Project-Name: LibStub |
## X-Curse-Project-ID: libstub |
## X-Curse-Repository-ID: wow/libstub/mainline |
LibStub.lua |
<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> |
--- **AceLocale-3.0** manages localization in addons, allowing for multiple locale to be registered with fallback to the base locale for untranslated strings. |
-- @class file |
-- @name AceLocale-3.0 |
-- @release $Id: AceLocale-3.0.lua 895 2009-12-06 16:28:55Z nevcairiel $ |
local MAJOR,MINOR = "AceLocale-3.0", 2 |
local AceLocale, oldminor = LibStub:NewLibrary(MAJOR, MINOR) |
if not AceLocale then return end -- no upgrade needed |
-- Lua APIs |
local assert, tostring, error = assert, tostring, error |
local setmetatable, rawset, rawget = setmetatable, rawset, rawget |
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded |
-- List them here for Mikk's FindGlobals script |
-- GLOBALS: GAME_LOCALE, geterrorhandler |
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 |
rawset(self, key, key) -- only need to see the warning once, really |
geterrorhandler()(MAJOR..": "..tostring(AceLocale.appnames[self])..": Missing entry for '"..tostring(key).."'") |
return key |
end |
} |
-- This metatable is used on all tables returned from GetLocale if the silent flag is true, it does not issue a warning on unknown keys |
local readmetasilent = { |
__index = function(self, key) -- requesting totally unknown entries: return key |
rawset(self, key, key) -- only need to invoke this function once |
return key |
end |
} |
-- Remember the locale table being registered right now (it gets set by :NewLocale()) |
-- NOTE: Do never try to register 2 locale tables at once and mix their definition. |
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 |
}) |
--- Register a new locale (or extend an existing one) for the specified application. |
-- :NewLocale will return a table you can fill your locale into, or nil if the locale isn't needed for the players |
-- game locale. |
-- @paramsig application, locale[, isDefault[, silent]] |
-- @param application Unique name of addon / module |
-- @param locale Name of the locale to register, e.g. "enUS", "deDE", etc. |
-- @param isDefault If this is the default locale being registered (your addon is written in this language, generally enUS) |
-- @param silent If true, the locale will not issue warnings for missing keys. Can only be set on the default locale. |
-- @usage |
-- -- enUS.lua |
-- local L = LibStub("AceLocale-3.0"):NewLocale("TestLocale", "enUS", true) |
-- L["string1"] = true |
-- |
-- -- deDE.lua |
-- local L = LibStub("AceLocale-3.0"):NewLocale("TestLocale", "deDE") |
-- if not L then return end |
-- L["string1"] = "Zeichenkette1" |
-- @return Locale Table to add localizations to, or nil if the current locale is not required. |
function AceLocale:NewLocale(application, locale, isDefault, silent) |
if silent and not isDefault then |
error("Usage: NewLocale(application, locale[, isDefault[, silent]]): 'silent' can only be specified for the default locale", 2) |
end |
-- 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({}, silent and readmetasilent or 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 |
--- 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) |
-- @param application Unique name of addon / module |
-- @param silent If true, the locale is optional, silently return nil if it's not found (defaults to false, optional) |
-- @return The locale table for the current language. |
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 |
<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> |
--[[ $Id: CallbackHandler-1.0.lua 14 2010-08-09 00:43:38Z mikk $ ]] |
local MAJOR, MINOR = "CallbackHandler-1.0", 6 |
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} |
-- Lua APIs |
local tconcat = table.concat |
local assert, error, loadstring = assert, error, loadstring |
local setmetatable, rawset, rawget = setmetatable, rawset, rawget |
local next, select, pairs, type, tostring = next, select, pairs, type, tostring |
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded |
-- List them here for Mikk's FindGlobals script |
-- GLOBALS: geterrorhandler |
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", tconcat(OLD_ARGS, ", ")):gsub("ARGS", tconcat(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" or self=thread |
if type(self)~="table" and type(self)~="string" and type(self)~="thread" then |
error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string or thread 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. |
<Bindings> |
<Binding name="BROKER_BUBBLETOGGLE_TOGGLE" header="BROKER_BUBBLETOGGLE"> |
Broker_BubbleToggle_Toggle() |
</Binding> |
</Bindings> |