/
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. |
## Interface: 60000 |
## Name: Broker_GarrisonReport |
## Notes: An LDB plugin to replace the Garrison Report button on your minimap. |
## Author: Seerah |
## Version: 1.0 |
LibStub\LibStub.lua |
CallBackHandler-1.0\CallbackHandler-1.0.xml |
LibDataBroker-1.1.lua |
report.lua |
local f = CreateFrame("Frame") |
local strformat = string.format |
local strgsub = string.gsub |
local tinsert = table.insert |
local hasGarrison, _ = C_Garrison.GetGarrisonInfo() |
local NEW_IN_PROGRESS = "%d "..strgsub(GARRISON_LANDING_IN_PROGRESS, " %- %%d", "") |
local NEW_AVAILABLE = "%d "..strgsub(GARRISON_LANDING_AVAILABLE, " %- %%d", "") |
local NEW_WORK_COMPLETE = " "..CAPACITANCE_WORK_COMPLETE_TOOLTIP_TITLE |
local NEW_COMPLETE = " "..GARRISON_LANDING_COMPLETED |
local NEW_NEXT = " "..GARRISON_LANDING_SHIPMENT_READY_TO_START |
local NEW_MISSION_COMPLETE = GARRISON_MISSION_COMPLETE.."!" |
local workOrderBuildings = {} |
local missionData = {} |
local underConstruction = {} |
local GR = LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject("Broker_GarrisonReport", { |
type = "data source", |
label = GARRISON_LANDING_PAGE_TITLE, |
icon = "Interface\\Icons\\Ability_Garrison_OrangeBird", |
text = "No Garrison", |
OnClick = GarrisonLandingPageMinimapButton_OnClick, |
}) |
local function ScanBuildings() |
local buildings = C_Garrison.GetBuildings() |
for i = 1, #buildings do |
local bldg = buildings[i] |
local buildingID = bldg.buildingID |
workOrderBuildings[buildingID] = workOrderBuildings[buildingID] or {} |
workOrderBuildings[buildingID].plotID = bldg.plotID |
if C_Garrison.GetLandingPageShipmentInfo(buildingID) then |
workOrderBuildings[buildingID].canWork = true |
end |
local _, name, _, _, _, isBuilding, buildStart, buildDur, canActivate, _, _ = C_Garrison.GetOwnedBuildingInfoAbbrev(bldg.plotID) |
if isBuilding or canActivate then |
underConstruction[buildingID] = underConstruction[buildingID] or {} |
local newOrUpgrade = underConstruction[buildingID] |
newOrUpgrade.name = name |
newOrUpgrade.isBuilding = isBuilding |
newOrUpgrade.buildStart = buildStart |
newOrUpgrade.buildDur = buildDur |
newOrUpgrade.canActivate = canActivate |
end |
end |
end |
local function ShowConstruction(tt) |
if next(underConstruction) then --if there are buildings under construction |
tt:AddLine(" ") |
tt:AddLine(GARRISON_LANDING_STATUS_BUILDING) |
for building, data in pairs(underConstruction) do |
tt:AddLine(data.name, 1, 1, 1) |
if data.isBuilding then |
tt:AddLine(strformat(" %s", GARRISON_BUILDING_IN_PROGRESS), 1, 1, 1) |
elseif data.canActivate then |
tt:AddLine(strformat(" %s", GARRISON_LANDING_BUILDING_COMPLEATE), 0, .95, 0) |
end |
end |
end |
end |
local function GetWorkOrders() |
C_Garrison.RequestLandingPageShipmentInfo() --make sure we have the latest information |
for buildingID, data in pairs(workOrderBuildings) do |
if data.canWork then |
local name, texture, maxShipments, shipmentsReady, shipmentsTotal, |
_, _, timeLeft, _, itemIcon = C_Garrison.GetLandingPageShipmentInfo(buildingID) |
data.name = name |
data.texture = texture |
data.max = maxShipments |
data.ready = shipmentsReady |
data.total = shipmentsTotal |
data.icon = itemIcon |
data.time = timeLeft |
end |
end |
--let's update our work order data once per minute |
C_Timer.After(60, GetWorkOrders) |
end |
local function ShowWorkOrders(tt) |
tt:AddLine(CAPACITANCE_WORK_ORDERS) |
for building, data in pairs(workOrderBuildings) do |
if data.canWork then |
tt:AddLine(data.name, 1, 1, 1) |
if data.total then --if waiting on work orders |
if data.ready == data.total then --all done! |
tt:AddLine(NEW_WORK_COMPLETE, 0, .95, 0) |
else |
if data.ready > 0 then --if some are done |
tt:AddLine(strformat(NEW_COMPLETE, data.ready, data.total), .88, .84, .36) |
else --if none are done yet |
tt:AddLine(strformat(NEW_COMPLETE, data.ready, data.total), 1, 1, 1) |
end |
end |
else --no work orders made |
tt:AddLine(strformat(NEW_NEXT, data.max), 1, 1, 1) |
end |
end |
end |
end |
local function GetMissions() |
missionData.complete = #C_Garrison.GetCompleteMissions() |
missionData.onMission = #C_Garrison.GetInProgressMissions() --all the missions we're working on |
missionData.inProgress = missionData.onMission - missionData.complete --let's not include the complete # in our in progress # |
missionData.available = #C_Garrison.GetAvailableMissions() |
local textColor |
if missionData.onMission == 0 then |
textColor = "ffff0000" |
elseif missionData.complete == missionData.onMission then |
textColor = "ff00f200" |
else |
textColor = "ffffffff" |
end |
GR.text = strformat("|c%s%d/%d|r", textColor, missionData.complete, missionData.onMission) |
end |
local function ShowMissions(tt) |
tt:AddLine(GARRISON_MISSIONS) |
if missionData.inProgress == 0 then |
if missionData.onMission == 0 then |
tt:AddLine(GARRISON_EMPTY_IN_PROGRESS_LIST, 1, 0, 0) |
elseif missionData.complete == missionData.onMission then |
tt:AddLine(NEW_MISSION_COMPLETE, 0, .95, 0) |
end |
tt:AddLine(strformat(NEW_AVAILABLE, missionData.available), 1, 1, 1) |
else |
if missionData.complete > 0 then |
tt:AddLine(strformat(GARRISON_NUM_COMPLETED_MISSIONS, missionData.complete), 0, .95, 0) |
else |
tt:AddLine(strformat(GARRISON_NUM_COMPLETED_MISSIONS, missionData.complete), 1, 1, 1) |
end |
tt:AddLine(strformat(NEW_IN_PROGRESS, missionData.inProgress), 1, 1, 1) |
end |
end |
function GR.OnTooltipShow(self) |
self:AddLine("Broker_GarrisonReport",1,1,1) |
self:AddLine(" ") |
if hasGarrison then |
ShowMissions(self) |
self:AddLine(" ") |
ShowWorkOrders(self) |
ShowConstruction(self) |
self:AddLine(" ") |
end |
self:AddLine(MINIMAP_GARRISON_LANDING_PAGE_TOOLTIP) |
end |
f:RegisterEvent("PLAYER_LOGIN") |
f:RegisterEvent("GARRISON_MISSION_FINISHED") |
f:RegisterEvent("GARRISON_MISSION_LIST_UPDATE") |
f:RegisterEvent("GARRISON_MISSION_NPC_OPENED") |
f:RegisterEvent("GARRISON_BUILDING_PLACED") |
f:RegisterEvent("GARRISON_BUILDING_REMOVED") |
f:RegisterEvent("GARRISON_BUILDING_ACTIVATABLE") |
f:RegisterEvent("GARRISON_BUILDING_ACTIVATED") |
f:RegisterEvent("GARRISON_UPDATE") |
f:SetScript("OnEvent", function(self, event, ...) |
if event == "PLAYER_LOGIN" then |
--initialize any sv's? |
GarrisonLandingPageMinimapButton:Hide() |
hooksecurefunc(GarrisonLandingPageMinimapButton, "Show", GarrisonLandingPageMinimapButton.Hide) |
elseif event == "GARRISON_UPDATE" then |
hasGarrison = C_Garrison.GetGarrisonInfo() |
GetMissions() |
ScanBuildings() |
GetWorkOrders() |
elseif event == "GARRISON_BUILDING_ACTIVATED" then |
local plotID, buildingID = ... |
underConstruction[buildingID] = nil |
elseif event == "GARRISON_MISSION_FINISHED" or event == "GARRISON_MISSION_LIST_UPDATE" or event == "GARRISON_MISSION_NPC_OPENED" then |
GetMissions() |
else |
ScanBuildings() |
end |
end) |