/
v1.0.1 2009-05-19 |
- Fixed the handling of initializing saved variables when updating from previous versions. |
1.0.2 -- 2009-05-22 |
- Added support for Data Broker. Left-click stacks bags, right-click opens menu. |
1.0.1 -- 2009-05-19 |
- Fixed the handling of initializing saved variables when updating from 0.9 |
- Fixed a bug that disabled guild tab switching when guild bank auto-stacking is turned off. |
v1.0 2009-05-18 |
1.0 -- 2009-05-18 |
- Added support for restacking guild vault tabs. |
- Hooked bag auto-stack to LOOT_OPEN and TRADE_SHOW, and guild bank auto-stack to GUILDBANKFRAME_OPENED and SetCurrentGuildBankTab(i). |
- Extended auto-stack toggle to enable or disable bags, bank, or guild bank individually. |
- Will now move items that can go into special class/profession bags into those bags if possible (soul shards, herbs, arrows, etc). |
- Cleaned up and commented code. |
v0.9 2009-05-13 |
0.9 -- 2009-05-13 |
- Added option to auto-stack when opening bags or bank, off by default. Toggle with '/restack auto' |
--[[ kRestack() is a global function and can be used in any addon or macro |
Some of the functionality is based on ArkInventory's restacking function, kudos! |
Usage: |
--[[ Usage: |
/restack [bags, bank, guild] - manual run |
/restack auto [bags, bank, guild] - toggles auto mode |
]] |
local col = { w = "|cffFFFFFF", g = "|cff55FF55", r = "|cffFF5555" } |
local kR = "|cff44CCFFk|cffFFFFAARestack"..col.w |
--[[ ldb plugin ]] |
local menu = { |
{ text = "= Restack =", isTitle = true }, |
{ text = "Backpack", func = function() kRestack("bags") end }, |
{ text = "Bank", disabled = true, func = function() kRestack("bank") end }, |
{ text = "Guild Vault", disabled = true, func = function() kRestack("guild") end }, |
{ disabled = true }, |
{ text = "= Set Auto =", isTitle = true }, |
{ text = "Backpack", checked = function() return (AutoRestack.bags and true or false) end, func = function() kRestack("auto bags") end }, |
{ text = "Bank", checked = function() return (AutoRestack.bank and true or false) end, func = function() kRestack("auto bank") end }, |
{ text = "Guild Vault", checked = function() return (AutoRestack.guild and true or false) end, func = function() kRestack("auto guild") end }, |
} |
local drop = CreateFrame("frame", "kRestack", nil, "UIDropDownMenuTemplate") |
LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject("kRestack", { |
type = "data source", |
text = "kRestack", |
icon = "Interface\\Icons\\INV_Crate_09", |
OnClick = function(self, button) |
if button == "RightButton" then |
GameTooltip:Hide() |
UIDropDownMenu_Initialize(drop, function() for _, opt in pairs(menu) do UIDropDownMenu_AddButton(opt, 1) end end, "MENU") |
return ToggleDropDownMenu(1, nil, drop, self, 0, 0) |
elseif button == "LeftButton" then |
kRestack("bags") |
end |
end, |
}) |
--[[ set user's container identifers ]] |
local container = { bags = { 0 }, bank = { -1 }, guild = { 42 } } |
for i = 1, NUM_BAG_SLOTS do table.insert(container.bags, i) end |
for i = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do table.insert(container.bank, i) end |
--[[ watching some events ]] |
local frame = CreateFrame("FRAME") |
local events = { "ADDON_LOADED", "BANKFRAME_OPENED", "LOOT_OPENED", "TRADE_SHOW", "GUILDBANKFRAME_OPENED" } |
for _, r in pairs(events) do frame:RegisterEvent(r) end |
frame:SetScript("OnEvent", function(_, e) |
local atBank, atVault |
local f = CreateFrame("frame") |
local events = { "ADDON_LOADED", "BANKFRAME_OPENED", "BANKFRAME_CLOSED", "LOOT_OPENED", "TRADE_SHOW", "GUILDBANKFRAME_OPENED", "GUILDBANKFRAME_CLOSED" } |
for _, r in pairs(events) do f:RegisterEvent(r) end |
f:SetScript("OnEvent", function(_, e) |
--[[ ldb stuff ]] |
if e == events[2] then |
menu[3].disabled = false |
elseif e == events[3] then |
menu[3].disabled = true |
elseif e == events[6] then |
local _, _, isViewable, canDeposit = GetGuildBankTabInfo(GetCurrentGuildBankTab()) |
if IsGuildLeader() or (isViewable and canDeposit) then menu[4].disabled = false end |
elseif e == events[7] then |
menu[4].disabled = true |
end |
if e == events[1] then |
--[[ initialize saved variables ]] |
if type(AutoRestack) ~= "table" then AutoRestack = { } end |
if AutoRestack.guild == nil then AutoRestack.guild = false end |
elseif AutoRestack.bank and e == events[2] then |
kRestack("bank", true) |
elseif AutoRestack.bags and (e == events[3] or e == events[4]) then |
elseif AutoRestack.bags and (e == events[4] or e == events[5]) then |
kRestack("bags", true) |
elseif AutoRestack.guild and e == events[5] then |
elseif AutoRestack.guild and e == events[6] then |
kRestack("guild", true) |
end |
end) |
local function coYield(loc, lb, ls, count) |
--[[ yielding function; can't be stacking too fast or else bad stuff happens ]] |
frame:SetScript("OnUpdate", function() |
f:SetScript("OnUpdate", function() |
if coroutine.status(restacker) == "suspended" then |
local locked = true |
if loc == "guild" then |
end |
end |
--[[ turn off yielding function ]] |
frame:SetScript("OnUpdate", nil) |
f:SetScript("OnUpdate", nil) |
end) |
coroutine.resume(restacker) |
else |
print(kR..col.r, "Restacking is already in progress. (use '/restack resume' if stuck)") |
end |
end |
end |
end |
LibDataBroker is a small WoW addon library designed to provide a "MVC":http://en.wikipedia.org/wiki/Model-view-controller interface for use in various addons. |
LDB's primary goal is to "detach" plugins for TitanPanel and FuBar from the display addon. |
Plugins can provide data into a simple table, and display addons can receive callbacks to refresh their display of this data. |
LDB also provides a place for addons to register "quicklaunch" functions, removing the need for authors to embed many large libraries to create minimap buttons. |
Users who do not wish to be "plagued" by these buttons simply do not install an addon to render them. |
Due to it's simple generic design, LDB can be used for any design where you wish to have an addon notified of changes to a table. |
h2. Links |
* "API documentation":http://github.com/tekkub/libdatabroker-1-1/wikis/api |
* "Data specifications":http://github.com/tekkub/libdatabroker-1-1/wikis/data-specifications |
* "Addons using LDB":http://github.com/tekkub/libdatabroker-1-1/wikis/addons-using-ldb |
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 |
------------------------------------------------------------------------ |
r90 | nevcairiel | 2008-09-30 06:01:35 +0000 (Tue, 30 Sep 2008) | 1 line |
Changed paths: |
A /tags/1.0 |
A /tags/1.0/.pkgmeta |
A /tags/1.0/LibStub.lua |
A /tags/1.0/LibStub.toc |
Add 1.0 tag |
------------------------------------------------------------------------ |
-- 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 |
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="CallbackHandler-1.0.lua"/> |
</Ui> |
--[[ $Id: CallbackHandler-1.0.lua 3 2008-09-29 16:54: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. |
## Interface: 30100 |
## Title: kRestack |
## Version: 1.0.1 |
## Version: 1.0.2 |
## Author: Katae of Anvilmar |
## Notes: Function for restacking inventory items. |
## Notes: Addon for all of your restacking needs. |
## X-Embeds: CallbackHandler-1.0, LibStub |
## SavedVariablesPerCharacter: AutoRestack |
kRestack.lua |