/
## Interface: 60000 |
## Name: Broker_WoDCurrency |
## Notes: An LDB plugin to display currencies for WoD. |
## Author: Seerah |
## Version: 1.0 |
## SavedVariablesPerCharacter: WoDCurrencyDBPC |
LibStub\LibStub.lua |
CallBackHandler-1.0\CallbackHandler-1.0.xml |
LibDataBroker-1.1.lua |
Broker_WoDCurrency.lua |
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 |
-- 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 |
<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 965 2010-08-09 00:47:52Z 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. |
local f = CreateFrame("Frame") |
local strformat = string.format |
local tinsert = table.insert |
local tremove = table.remove |
local tlbsort = table.sort |
local db, _ |
local labelString = "|T%s:0|t %s" |
local separatedBankString = "%d (%d | %d)" |
local ids = { |
{id = 823, type = "currency"}, --apexis crystal |
{id = 824, type = "currency"}, --garrison resources |
{id = 944, type = "currency"}, --artifact fragment |
{id = 980, type = "currency"}, --dingy iron coins |
{id = 994, type = "currency"}, --seal of tempered fate |
{id = 116053, type = "item"}, --draenic seeds |
{id = 116415, type = "item"}, --pet charm |
{id = 120945, type = "item"}, --primal spirit |
{id = 118700, type = "item"}, --secret of draenor alchemy |
{id = 118720, type = "item"}, --secret of draenor blacksmithing |
{id = 119293, type = "item"}, --secret of draenor enchanting |
{id = 119299, type = "item"}, --secret of draenor engineering |
{id = 119297, type = "item"}, --secret of draenor inscription |
{id = 118723, type = "item"}, --secret of draenor jewelcrafting |
{id = 118721, type = "item"}, --secret of draenor leatherworking |
{id = 118722, type = "item"}, --secret of draenor tailoring |
} |
f.buttons = {} |
local i = 1 |
local function AddCheckbox(setting, name) |
f.buttons[i] = CreateFrame("CheckButton", nil, f, "UICheckButtonTemplate") |
local box = f.buttons[i] |
if i == 1 then |
box:SetPoint("TOPLEFT", 20, -85) |
elseif i == 2 then |
box:SetPoint("TOPLEFT", f.buttons[1], "TOPLEFT", 0, -80) |
elseif i == 13 then |
box:SetPoint("TOPLEFT", f.buttons[2], "TOPRIGHT", 250, 0) |
else |
box:SetPoint("TOPLEFT", f.buttons[i-1], "TOPLEFT", 0, -30) |
end |
box:SetChecked(WoDCurrencyDBPC[setting]) |
box:SetScript("OnClick", function(self) |
self:SetChecked(not WoDCurrencyDBPC[setting]) |
WoDCurrencyDBPC[setting] = not WoDCurrencyDBPC[setting] |
end) |
--labels |
box.text = box:CreateFontString(nil, "ARTWORK", "GameFontHighlight") |
box.text:SetPoint("LEFT", box, "RIGHT", 5, 0) |
box.text:SetText(name) |
i = i+1 |
return box |
end |
local function BuildOptions() |
--[[local OnEnter = function(self, title, text) |
GameTooltip:SetOwner(self, "ANCHOR_TOPRIGHT") |
GameTooltip:SetText(title) |
GameTooltip:AddLine(text,1,1,1,true) |
GameTooltip:Show() |
end]] |
local header = f:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge") |
header:SetParent(f) |
header:SetPoint("TOPLEFT", 20, -15) |
header:SetText("Broker_WoDCurrency") |
local subheader = f:CreateFontString(nil, "ARTWORK", "GameFontHighlight") |
subheader:SetParent(f) |
subheader:SetPoint("TOPLEFT", 20, -32) |
subheader:SetPoint("RIGHT", -20, -32) |
subheader:SetHeight(30) |
subheader:SetNonSpaceWrap(true) |
subheader:SetJustifyH("LEFT") |
subheader:SetText("An LDB plugin to display currencies for WoD.") |
local box = AddCheckbox("separateBank", "Separate items in bank from total count: "..TOTAL.." ("..BAGSLOT.." | "..BANK..")") |
local selectToTrack = f:CreateFontString(nil, "ARTWORK", "GameFontHighlight") |
selectToTrack:SetParent(f) |
selectToTrack:SetPoint("TOPLEFT", box, "BOTTOMLEFT", 0, -15) |
selectToTrack:SetHeight(30) |
selectToTrack:SetNonSpaceWrap(true) |
selectToTrack:SetJustifyH("LEFT") |
selectToTrack:SetText("Select which currencies and items to track.") |
end |
local itemsFound = 0 |
local checkAgain = {} |
local function ReQuery() |
for k, currency in pairs(checkAgain) do |
if currency.type == "currency" then |
currency.name, _, currency.texture = GetCurrencyInfo(currency.id) |
else |
currency.name, _, _, _, _, _, _, _, _, currency.texture = GetItemInfo(currency.id) |
end |
if currency.name then |
tremove(checkAgain, k) |
itemsFound = itemsFound + 1 |
end |
end |
if itemsFound == #ids then --if all of our items have been found by the client... |
f:UnregisterEvent("GET_ITEM_INFO_RECEIVED") |
--then, alphabetize based on locale |
tlbsort(ids, function(a, b) return a.name < b.name end) |
--now let's add our checkboxes in alphabetical order |
for _, currency in pairs(ids) do |
AddCheckbox(currency.id, currency.name) |
end |
end |
end |
local function Initialize() |
WoDCurrencyDBPC = WoDCurrencyDBPC or {} |
db = WoDCurrencyDBPC |
db.separateBank = db.separateBank or false |
for i = 1, #ids do |
local currency = ids[i] |
--first, store localized name (and texture, while we're at it) |
if currency.type == "currency" then |
currency.name, _, currency.texture = GetCurrencyInfo(currency.id) |
else |
currency.name, _, _, _, _, _, _, _, _, currency.texture = GetItemInfo(currency.id) |
end |
if not currency.name then --if this item isn't in the client's cache, try again when it's found |
currency.idsKey = i |
tinsert(checkAgain, currency) |
f:RegisterEvent("GET_ITEM_INFO_RECEIVED") |
else |
itemsFound = itemsFound + 1 |
end |
--also, while we're looping through, let's double-check our db |
if db[currency.id] == nil then |
db[currency.id] = true |
end |
end |
f.name = "Broker_WoDCurrency" |
InterfaceOptions_AddCategory(f) |
BuildOptions() |
if itemsFound == #ids then --if all of our items have been found by the client... |
f:UnregisterEvent("GET_ITEM_INFO_RECEIVED") |
--then, alphabetize based on locale |
tlbsort(ids, function(a, b) return a.name < b.name end) |
--now let's add our checkboxes in alphabetical order |
for _, currency in pairs(ids) do |
AddCheckbox(currency.id, currency.name) |
end |
end |
end |
local function PollCurrencyData(tt) |
for _, currency in pairs(ids) do |
if db[currency.id] then |
if currency.type == "currency" then |
local name, amount, texture = GetCurrencyInfo(currency.id) |
tt:AddDoubleLine(strformat(labelString, texture, name), amount) |
else |
local amount = GetItemCount(currency.id, true) |
if db.separateBank then |
local justBags = GetItemCount(currency.id) |
local justBank = amount - justBags |
tt:AddDoubleLine(strformat(labelString, currency.texture, currency.name), strformat(separatedBankString, amount, justBags, justBank)) |
else |
tt:AddDoubleLine(strformat(labelString, currency.texture, currency.name), amount) |
end |
end |
end |
end |
end |
local BWC = LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject("Broker_WoDCurrency", { |
type = "launcher", |
label = "WoD Currency", |
icon = "Interface\\Icons\\INV_Garrison_Resource", |
OnClick = function(self, btn) |
if btn == "RightButton" then |
InterfaceOptionsFrame_OpenToCategory("Broker_WoDCurrency") |
InterfaceOptionsFrame_OpenToCategory("Broker_WoDCurrency") |
else |
ToggleCharacter("TokenFrame") |
end |
end, |
}) |
function BWC.OnTooltipShow(self) |
self:AddLine("Broker_WoDCurrency",1,1,1) |
self:AddLine(" ") |
PollCurrencyData(self) |
self:AddLine(" ") |
self:AddLine("Left-click to toggle the Currency frame.") |
self:AddLine("Right-click to open the WoDCurrency options.") |
end |
f:RegisterEvent("PLAYER_ENTERING_WORLD") |
f:SetScript("OnEvent", function(self, event) |
if event == "GET_ITEM_INFO_RECEIVED" then |
ReQuery() |
else |
C_Timer.After(5, Initialize) |
--Initialize() |
f:UnregisterEvent("PLAYER_ENTERING_WORLD") |
end |
end) |