changeset 80:c0bf2ddb5288

Added initial item refilling from the bank/guild. Not yet fully functional.
author Zerotorescue
date Wed, 05 Jan 2011 13:05:15 +0100
parents b89b6981783f
children 58617c7827fa
files Inventorium.toc ItemMove.class.lua Mover.lua Scanner.lua Todo.txt
diffstat 5 files changed, 736 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/Inventorium.toc	Sun Dec 26 03:50:06 2010 +0100
+++ b/Inventorium.toc	Wed Jan 05 13:05:15 2011 +0100
@@ -14,12 +14,15 @@
 
 # Modules
 Config.lua
+Mover.lua
+Scanner.lua
 Summary.lua
 Queue.lua
 
 # Stuff
 Widgets.lua
 ItemData.class.lua
+ItemMove.class.lua
 
 # Data
 Data\PremadeGroups.lua
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ItemMove.class.lua	Wed Jan 05 13:05:15 2011 +0100
@@ -0,0 +1,37 @@
+local addon = select(2, ...);
+
+-- Define the class
+
+addon.ItemMove = {};
+addon.ItemMove.__index = addon.ItemMove;
+
+-- Construct
+function addon.ItemMove:New(id)
+	local self = {};
+	
+	setmetatable(self, addon.ItemMove);
+	
+	-- Standard info everything needs
+	self.id = id;
+	self.totalCount = 0;
+	self.locations = {};
+	
+	return self;
+end
+
+function addon.ItemMove:AddLocation(container, slot, count)
+	table.insert(self.locations, {
+		container = container,
+		slot = slot,
+		count = count,
+	});
+	
+	self.totalCount = (self.totalCount + count);
+	
+	return true;
+end
+
+function addon.ItemMove:Move(location, targetBag, targetSlot)
+	-- move location (container, slot, count) to targetBag, targetSlot
+	return true;
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Mover.lua	Wed Jan 05 13:05:15 2011 +0100
@@ -0,0 +1,458 @@
+local addon = select(2, ...);
+local mod = addon:NewModule("Mover", "AceEvent-3.0", "AceTimer-3.0");
+
+local Scanner;
+local queuedMoves = {}; -- table storing all queued moves before BeginMove is called
+local combinedMoves = {}; -- table storing all combined moves (with source and target) that is to be processed by the actual mover in the order of the index (1 to #)
+
+function mod:AddMove(itemId, amount)
+	table.insert(queuedMoves, {
+		id = itemId,
+		num = amount,
+	});
+end
+
+function mod:BeginMove(location, onFinish)
+	
+	-- Find the outgoing moves
+	-- We need the source container and slot, find all the requires sources and put them in a list which we go through later to find matching targets
+	
+	-- Get a list of items in the source container
+	local sourceContents = Scanner:CacheLocation(location, false);
+	
+	local outgoingMoves = {};
+
+	for singleMove in pairs(queuedMoves) do
+		local sourceItem = sourceContents[singleMove.id];
+		if not sourceItem then
+			print("Can't move " .. IdToItemLink(singleMove.id) .. ", non-existant in source");
+		else
+			-- We want to move the smallest stacks first to keep stuff pretty
+			table.sort(sourceItem.locations, function(a, b)
+				return a.count > b.count;
+			end);
+			
+			for itemLocation in pairs(sourceItem.locations) do
+				-- if this location has more items than we need, only move what we need, otherwise move everything in this stack
+				local movingNum = ((itemLocation.count > singleMove.num and singleMove.num) or itemLocation.count);
+				
+				table.insert(outgoingMoves, {
+					itemId = singleMove.id,
+					container = itemLocation.container,
+					slot = itemLocation.slot,
+					count = movingNum,
+				});
+				
+				singleMove.num = (singleMove.num - movingNum);
+				
+				if singleMove.num == 0 then
+					-- If we have prepared everything we wanted, go to the next queued move
+					break;
+				end
+			end
+		end
+	end
+	
+	-- No longer needed
+	table.wipe(queuedMoves);
+	
+	-- Process every single outgoing move and find fitting targets
+	
+	-- Get a list of items already in the target container
+	local targetContents = Scanner:CacheLocation(addon.Locations.Bag, false);
+	
+	-- Find all empty slots
+	
+	local emptySlots = {};
+	
+	local start = 0;
+	local stop = NUM_BAG_SLOTS;
+	
+	-- Go through all our bags, including the backpack
+	for bagId = start, stop do
+		-- Go through all our slots
+		for slotId = 1, GetContainerNumSlots(bagId) do
+			local itemId = GetContainerItemID(bagId, slotId);
+			
+			if not itemId then
+				table.insert(emptySlots, {
+					container: bagId,
+					slot: slotId,
+				});
+			end
+		end
+	end
+	
+	while #outgoingMoves ~= 0 do
+		-- A not equal-comparison should be quicker than a larger/smaller than-comparison
+		
+		for outgoingMove in pairs(outgoingMoves) do
+			-- itemId  will be set to nil when this outgoing move was processed - sanity check
+			if outgoingMove.itemId then
+				local targetItem = targetContents[outgoingMove.itemId];
+				
+				if not targetItem then
+					-- grab an empty slot
+					-- make new instance of ItemMove
+					-- populate targetContents with it so future moves of this item can be put on top of it if this isn't a full stack
+					
+					local firstAvailableSlot = emptySlots[1];
+					
+					if not firstAvailableSlot then
+						print("Bags are full. Skipping " .. IdToItemLink(outgoingMove.itemId) .. ".");
+						
+						outgoingMove.itemId = nil;
+					else
+						table.insert(combinedMoves, {
+							sourceContainer = outgoingMove.container,
+							sourceSlot = outgoingMove.slot,
+							targetContainer = firstAvailableSlot.container,
+							targetSlot = firstAvailableSlot.slot,
+							itemId = outgoingMove.itemId,
+							num = outgoingMove.count,
+						});
+						
+						-- We filled an empty slot so the target contents now has one more item,
+						-- make a new instance of the ItemMove class so any additional items with this id can be stacked on top of it
+						local itemMove = addon.ItemMove:New();
+						itemMove.AddLocation(firstAvailableSlot.container, firstAvailableSlot.slot, outgoingMove.count);
+						targetContents[outgoingMove.itemId] = itemMove;
+						
+						firstAvailableSlot = nil; -- no longer empty
+						
+						outgoingMove.count = 0; -- nothing remaining - sanity check
+						outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
+					end
+				else
+					-- Find the maximum stack size for this item
+					local itemStackCount = select(8, GetItemInfo(outgoingMove.itemId));
+					
+					-- We want to move to the largest stacks first to keep stuff pretty
+					table.sort(targetItem.locations, function(a, b)
+						return a.count < b.count;
+					end);
+					
+					for itemLocation in pairs(targetItem.locations) do
+						if itemLocation.count < itemStackCount and outgoingMove.count > 0 then
+							-- Check if this stack isn't already full (and we still need to move this item)
+							
+							local remainingSpace = (itemStackCount - itemLocation.count);
+							if remainingSpace > outgoingMove.count then
+								-- Enough room to move this entire stack
+								-- Deposit this item and then forget this outgoing move as everything in it was processed
+								
+								table.insert(combinedMoves, {
+									sourceContainer = outgoingMove.container,
+									sourceSlot = outgoingMove.slot,
+									targetContainer = itemLocation.container,
+									targetSlot = itemLocation.slot,
+									itemId = outgoingMove.itemId,
+									num = outgoingMove.count,
+								});
+								
+								itemLocation.count = (itemLocation.count + outgoingMove.count);
+								outgoingMove.count = 0; -- nothing remaining
+								outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
+								break; -- stop the locations-loop
+							else
+								-- Deposit this item but don't remove the outgoing move as there are some items left to move
+								
+								table.insert(combinedMoves, {
+									sourceContainer = outgoingMove.container,
+									sourceSlot = outgoingMove.slot,
+									targetContainer = itemLocation.container,
+									targetSlot = itemLocation.slot,
+									itemId = outgoingMove.itemId,
+									num = outgoingMove.count,
+								});
+								
+								-- The target will be full when we complete, but the source will still have remaining items left to be moved
+								itemLocation.count = itemStackCount;
+								outgoingMove.count = (outgoingMove.count - remainingSpace);
+							end
+						end
+					end
+					
+					if outgoingMove.count > 0 then
+						-- We went through all matching items and checked their stack sizes if we could move this there, no room available
+						-- So forget about the target item (even though it may just have full locations, these are useless anyway) and the next loop move it onto an empty slot
+						targetItem = nil;
+					end
+				end
+			end
+		end
+		
+		-- Loop through the array to find items that should be removed, start with the last element or the loop would break
+		local numOutgoingMoves = #outgoingMoves; -- since LUA-tables start at an index of 1, this is actually an existing index (outgoingMoves[#outgoingMoves] would return a value)
+		while numOutgoingMoves ~= 0 do
+			-- A not equal-comparison should be quicker than a larger/smaller than-comparison
+			
+			-- Check if the item id is nil, this is set to nil when this outgoing move has been processed
+			if not outgoingMoves[numOutgoingMoves].itemId then
+				-- Remove this element from the array
+				table.remove(outgoingMoves, numOutgoingMoves);
+			end
+			
+			-- Proceed with the next element (or previous considering we're going from last to first)
+			numOutgoingMoves = (numOutgoingMoves - 1);
+		end
+	end
+	
+	-- No longer needed
+	table.wipe(emptySlots);
+	
+	DoMoveNow();
+	
+	--ProcessMove();
+	
+	--mod:RegisterEvent("BAG_UPDATE", BAG_UPDATE);
+	
+	--onFinish();
+end
+
+function DoMoveNow()
+	-- combinedMoves now has all moves in it (source -> target)
+	-- go through list, move everything inside it
+	-- add source and target to one single list
+	-- if either is already in this list, skip this move
+	-- repeat every 5 seconds until we're completely done
+	
+	local sourceLocationsLocked = {};
+	local targetLocationsLocked = {};
+	
+	local numCurrentMove = #combinedMoves;
+	while numCurrentMove ~= 0 do
+		local move = combinedMoves[numCurrentMove];
+		
+		-- sourceContainer, sourceSlot, targetContainer, targetSlot, itemId, num
+		if (not sourceLocationsLocked[move.sourceContainer] or not sourceLocationsLocked[move.sourceContainer][move.sourceSlot]) and 
+			(not targetLocationsLocked[move.targetContainer] or not targetLocationsLocked[move.targetContainer][move.targetSlot]) then
+			
+			print("Moving " .. IdToItemLink(move.itemId));
+			
+			-- Pickup stack
+			SplitGuildBankItem(move.sourceContainer, move.sourceSlot, move.num);
+			
+			-- Remember we picked this item up and thus it is now locked
+			if not sourceLocationsLocked[move.sourceContainer] then
+				sourceLocationsLocked[move.sourceContainer] = {};
+			end
+			sourceLocationsLocked[move.sourceContainer][move.sourceSlot] = true;
+			
+			if CursorHasItem() then
+				-- And drop it
+				PickupContainerItem(move.targetContainer, move.targetSlot);
+				
+				-- Remember we dropped an item here and thus this is now locked
+				if not sourceLocationsLocked[move.targetContainer] then
+					sourceLocationsLocked[move.targetContainer] = {};
+				end
+				sourceLocationsLocked[move.targetContainer][move.targetSlot] = true;
+				
+				-- This move was processed
+				table.remove(combinedMoves, numCurrentMove);
+			end
+		end
+		
+		-- Proceed with the next element (or previous considering we're going from last to first)
+		numCurrentMove = (numCurrentMove - 1);
+	end
+end
+
+local tmrProcessNext;
+function BAG_UPDATE()
+	mod:CancelTimer(tmrProcessNext, true); -- silent
+	tmrProcessNext = mod:ScheduleTimer(function()
+		ProcessMove();
+	end, 2);
+end
+
+function ProcessMove()
+	local currentMove = queuedMoves[1];
+	
+	if currentMove then
+		addon:Debug("Moving " .. currentMove.num .. " of " .. IdToItemLink(currentMove.id));
+	
+		local requestedMoves = currentMove.num;
+		
+		if currentMove.src == addon.Locations.Bank then
+			MoveBankItem(currentMove);
+		elseif currentMove.src == addon.Locations.Guild then
+			MoveGuildItem(currentMove);
+		end
+		
+		if requestedMoves == currentMove.num then
+			print("Skipping " .. IdToItemLink(move.id));
+			move.num = 0;
+		elseif currentMove.num > 0 then
+			-- bags are full
+			print("bags are full");
+		end
+		
+		if currentMove.num == 0 then
+			table.remove(queuedMoves, 1);
+		end
+	end
+end
+
+function MoveGuildItem(move)
+	local tabId = GetCurrentGuildBankTab();
+	local slotId = (MAX_GUILDBANK_SLOTS_PER_TAB or 98); -- start by scanning the last slot
+	
+	if tabId == nil or tabId < 1 then return; end
+	
+	while slotId ~= 0 do
+		-- A not equal-comparison should be quicker than a larger than-comparison
+		
+		local itemLink = GetGuildBankItemLink(tabId, slotId);
+		if itemLink then
+			-- If there is actually an item in this slot
+			
+			local itemId = GetItemId(itemLink);
+			
+			if itemId and move.id == itemId then
+				-- This is one of the items we're looking for
+				
+				local itemCount = select(2, GetGuildBankItemInfo(tabId, slotId));
+				
+				if itemCount and itemCount > 0 then
+					-- if the amount we still have to move is more than this stack, move the entire stack, otherwise move the still needed part of the stack
+					local moveable = (move.num > itemCount and itemCount) or move.num;
+					
+					-- Pickup stack
+					SplitGuildBankItem(tabId, slotId, moveable);
+					
+					-- Find an target slot and put it there
+					local reallyMoved = DropItem(itemId, moveable);
+					if reallyMoved then
+						-- Keep track of how many we have moved
+						moved = (moved + reallyMoved);
+						
+						-- Update the required amount of items so it has the remaining num
+						move.num = (move.num - reallyMoved);
+						
+						--if reallyMoved ~= moveable then
+						--	-- Scan this slot again because if we moved a 18 stack onto a 16 stack, we'd actually have moved only 4 items and still need to move the remaining 14 (we're capping stacks before using empty slots)
+						--	slotId = (slotId + 1);
+						--end
+						
+						--if move.num == 0 then
+							-- if no required items are left to move, then stop and tell the caller function how many we moved
+							return moved;
+						--end
+					end
+				end
+			end
+		end
+		
+		-- Continue scanning a different slot
+		slotId = (slotId - 1);
+	end
+end
+
+function MoveBankItem(move)
+	local start = 0;
+	local stop = NUM_BAG_SLOTS;
+	
+	-- If we requested the bank then we don't want the bag info
+	start = ( NUM_BAG_SLOTS + 1 );
+	stop = ( NUM_BAG_SLOTS + NUM_BANKBAGSLOTS );
+	
+	-- Scan the default 100 slots
+	move.num = (move.num - MoveFromContainter(BANK_CONTAINER, move));
+	
+	-- Go through all our bags, including the backpack
+	for bagID = start, stop do
+		move.num = (move.num - MoveFromContainter(bagID, move));
+	end
+end
+
+-- Go through all slots of this bag and if a match was found, move the items
+function MoveFromContainter(bagID, move)
+	-- Keep track of how many we have moved
+	local moved = 0;
+	
+	-- Go through all slots of this bag
+	for slot = 1, GetContainerNumSlots(bagID) do
+		local itemId = GetContainerItemID(bagID, slot);
+		
+		if itemId and move.id == itemId then
+			-- This is one of the items we're looking for
+			
+			local itemCount = select(2, GetContainerItemInfo(bagID, slot));
+			
+			if itemCount and itemCount > 0 then
+				-- if the amount we still have to move is more than this stack, move the entire stack, otherwise move the still needed part of the stack
+				local moveable = (move.num > itemCount and itemCount) or move.num;
+				
+				-- Pickup stack
+				SplitContainerItem(bagID, slot, moveable);
+				
+				addon:Debug("Picked " .. IdToItemLink(itemId) .. " up");
+				
+				-- Find an target slot and put it there
+				if CursorHasItem() then
+					local reallyMoved = DropItem(itemId, moveable);
+					
+					if reallyMoved then
+						addon:Debug("Dropped " .. reallyMoved .. " of " .. IdToItemLink(itemId));
+						
+						-- Keep track of how many we have moved
+						moved = (moved + reallyMoved);
+						
+						-- Update the required amount of items so it has the remaining num
+						move.num = (move.num - reallyMoved);
+						
+						--if reallyMoved ~= moveable then
+							-- Scan this slot again because if we moved a 18 stack onto a 16 stack, we'd actually have moved only 4 items and still need to move the remaining 14 (we're capping stacks before using empty slots)
+						--	slot = (slot - 1);
+						--end
+						
+						--if move.num == 0 then
+							-- if no required items are left to move, then stop and tell the caller function how many we moved
+							return moved;
+						--end
+					end
+				end
+			end
+		end
+	end
+	
+	return moved;
+end
+
+
+-- This currently only uses empty slots, it will have to fill stacks in the future
+function DropItem(itemId, amount)
+	local start = 0;
+	local stop = NUM_BAG_SLOTS;
+	
+	-- Go through all our bags, including the backpack
+	for bagID = start, stop do
+		-- Go through all our slots
+		for slot = 1, GetContainerNumSlots(bagID) do
+			local itemId = GetContainerItemID(bagID, slot);
+			
+			if not itemId then
+				-- If this slot is empty, put the item here
+				PickupContainerItem(bagID, slot);
+				
+				return amount;
+			end
+		end
+	end
+	
+	return;
+end
+
+function IdToItemLink(itemId)
+	return select(2, GetItemInfo(itemId));
+end
+
+function mod:OnEnable()
+	Scanner = addon:GetModule("Scanner");
+end
+
+function mod:OnDisable()
+	Scanner = nil;
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Scanner.lua	Wed Jan 05 13:05:15 2011 +0100
@@ -0,0 +1,236 @@
+local addon = select(2, ...);
+local mod = addon:NewModule("Scanner", "AceEvent-3.0");
+
+addon.Locations = {
+	Bag = 0,
+	Bank = 1,
+	Guild = 2,
+};
+
+local Mover, paused;
+local itemCache = {};
+
+local function _GetItemCount(itemId, location)
+	if location == addon.Locations.Bank then
+		-- No longer using GetItemCount as this includes equiped items and containers (e.g. bank bags)
+		--return (GetItemCount(itemId, true) - GetItemCount(itemId, false)); -- GetItemCount(X, true) provides count for bag+bank, GetItemCount(X, false) provides just bag, so (GetItemCount(X, true) - GetItemCount(X, false)) = just bank
+		return ((itemCache[itemId] and itemCache[itemId].totalCount) or 0);
+	elseif location == addon.Locations.Guild then
+		return ((itemCache[itemId] and itemCache[itemId].totalCount) or 0);
+	else
+		error("Invalid location provided for the local _GetItemCount. Must be Bag or Bank.");
+	end
+end
+
+local function GetItemID(link)
+	return tonumber(link:match("|Hitem:([-0-9]+):"));
+end
+
+function mod:ClearCache()
+	table.wipe(itemCache);
+end
+
+function mod:CacheLocation(location, remember)
+	if location == addon.Locations.Bag or location == addon.Locations.Bank then
+		-- Reset cache just in case it was filled
+		self:ClearCache();
+		
+		local start, stop;
+		if location == addon.Locations.Bag then
+			start = 0;
+			stop = NUM_BAG_SLOTS;
+		else
+			-- If we requested the bank then we don't want the bag info
+			start = ( NUM_BAG_SLOTS + 1 );
+			stop = ( NUM_BAG_SLOTS + NUM_BANKBAGSLOTS );
+		end
+		
+		-- Go through all our bags, including the backpack
+		for i = start, ((addon.Locations.Bag and stop) or (addon.Locations.Bank and (stop + 1))) do -- if scanning bags stop at normal bag slot, if scanning bank, stop one later to allow BANK_CONTAINER to be scanned too
+			-- Scan the default 100 slots whenever we're at a non-existing index
+			local bagId = (i == (stop + 1) and BANK_CONTAINER) or i;
+			local slotId = GetContainerNumSlots(bagId);
+			
+			while slotId ~= 0 do
+				-- A not equal-comparison should be quicker than a larger than-comparison
+				
+				local itemId = GetContainerItemID(bagId, slotId);
+				local itemCount = itemId and select(2, GetContainerItemInfo(bagId, slotId));
+				
+				if itemId and itemCount and itemCount > 0 then
+					local itemMove;
+					if not itemCache[itemId] then
+						-- If this is the first time we see this item, make a new object
+						itemMove = addon.ItemMove:New();
+					else
+						-- If we had this item in another slot too
+						itemMove = itemCache[itemId];
+					end
+					
+					itemMove.AddLocation(bagId, slotId, itemCount);
+				end
+			
+				-- Continue scanning a different slot
+				slotId = (slotId - 1);
+			end
+		end
+	elseif location == addon.Locations.Guild then
+		-- Reset cache before we scan
+		self:ClearCache();
+		
+		local tabId = GetCurrentGuildBankTab();
+		local slotId = (MAX_GUILDBANK_SLOTS_PER_TAB or 98); -- start by scanning the last slot
+		
+		if tabId == nil or tabId < 1 then return; end
+		
+		while slotId ~= 0 do
+			-- A not equal-comparison should be quicker than a larger than-comparison
+			
+				-- If there is actually an item in this slot
+				
+			local itemLink = GetGuildBankItemLink(tabId, slotId);
+			local itemId = itemLink and GetItemId(itemLink);
+			local itemCount = itemLink and select(2, GetGuildBankItemInfo(tabId, slotId));
+				
+			if itemLink and itemId and itemCount and itemCount > 0 then
+				local itemMove;
+				if not itemCache[itemId] then
+					-- If this is the first time we see this item, make a new object
+					itemMove = addon.ItemMove:New();
+				else
+					-- If we had this item in another slot too
+					itemMove = itemCache[itemId];
+				end
+				
+				itemMove.AddLocation(tabId, slotId, itemCount);
+			end
+			
+			-- Continue scanning a different slot
+			slotId = (slotId - 1);
+		end
+	else
+		error("Invalid location provided for the local _GetItemCount. Must be Bank or Guild.");
+	end
+	
+	if not remember then
+		-- Copy the table as clearing the cache wipes it empty (and tables are passed by reference)
+		local cacheCopy = CopyTable(itemCache);
+		
+		self:ClearCache();
+		
+		return cacheCopy;
+	end
+end
+
+function mod:Scan(location)
+	-- We might pause the scanning when we invoke moves ourself
+	if paused then
+		return;
+	end
+	
+	local playerName = UnitName("player");
+	
+	self:CacheLocation(location, true);
+	
+	-- Go through all groups
+	for groupName, values in pairs(addon.db.profile.groups) do
+		local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters");
+		
+		if values.items and trackAt[playerName] then
+			-- Is this character interested in this data?
+			
+			local minLocalStock = addon:GetOptionByKey(groupName, "minLocalStock");
+			
+			-- Go through all items
+			for itemId, _ in pairs(values.items) do
+				
+				-- Check if we have enough items local
+				local missingItems = (minLocalStock - addon:GetLocalItemCount(itemId, groupName));
+				
+				if missingItems > 0 then
+					-- Check how many are available
+					local availableItems = _GetItemCount(itemId, location);
+					
+					if availableItems > 0 then
+						print("Insufficient " .. select(2, GetItemInfo(itemId)) .. " but this location has " .. availableItems .. " (moving " .. missingItems .. ")");
+						
+						Mover:AddMove(itemId, missingItems);
+					else
+						print("Insufficient " .. select(2, GetItemInfo(itemId)));
+					end
+				end
+			end
+		end
+	end
+	
+	self:ClearCache();
+	
+	self:Pause();
+	Mover:BeginMove(location, self.Unpause);
+end
+
+local function BANKFRAME_OPENED()
+	-- Scan once when the bank is opened, but no need to scan after
+	mod:Scan(addon.Locations.Bank);
+	
+	addon:Debug("Scanner:BANKFRAME_OPENED");
+end
+
+-- Remember which tabs were scanned and don't scan them again
+local guildBankTabsScanned = {};
+
+local function GUILDBANKFRAME_CLOSED()
+	mod:UnregisterEvent("GUILDBANKFRAME_CLOSED");
+	mod:UnregisterEvent("GUILDBANKBAGSLOTS_CHANGED");
+	
+	table.wipe(guildBankTabsScanned);
+	
+	addon:Debug("Scanner:GUILDBANKFRAME_CLOSED");
+end
+
+local function GUILDBANKBAGSLOTS_CHANGED()
+	if not guildBankTabsScanned[GetCurrentGuildBankTab()] then
+		mod:Scan(addon.Locations.Guild);
+		guildBankTabsScanned[GetCurrentGuildBankTab()] = true;
+		
+		addon:Debug("Scanner:GUILDBANKBAGSLOTS_CHANGED (" .. GetCurrentGuildBankTab() .. ") - scanning");
+	else
+		addon:Debug("Scanner:GUILDBANKBAGSLOTS_CHANGED (" .. GetCurrentGuildBankTab() .. ") - not scanning");
+	end
+end
+
+local function GUILDBANKFRAME_OPENED()
+	table.wipe(guildBankTabsScanned);
+	
+	mod:RegisterEvent("GUILDBANKFRAME_CLOSED", GUILDBANKFRAME_CLOSED);
+	mod:RegisterEvent("GUILDBANKBAGSLOTS_CHANGED", GUILDBANKBAGSLOTS_CHANGED);
+	
+	addon:Debug("Scanner:GUILDBANKFRAME_OPENED");
+end
+
+function mod:OnEnable()
+	-- Scan once when the bankframe is opened
+	mod:RegisterEvent("BANKFRAME_OPENED", BANKFRAME_OPENED);
+	mod:RegisterEvent("GUILDBANKFRAME_OPENED", GUILDBANKFRAME_OPENED);
+	
+	Mover = addon:GetModule("Mover");
+end
+
+function mod:OnDisable()
+	Mover = nil;
+	
+	-- Bank
+	mod:UnregisterEvent("BANKFRAME_OPENED");
+	
+	-- Guild
+	GUILDBANKFRAME_CLOSED();
+	mod:UnregisterEvent("GUILDBANKFRAME_OPENED");
+end
+
+function mod:Pause()
+	paused = true;
+end
+
+function mod:Unpause()
+	paused = nil;
+end
--- a/Todo.txt	Sun Dec 26 03:50:06 2010 +0100
+++ b/Todo.txt	Wed Jan 05 13:05:15 2011 +0100
@@ -3,9 +3,9 @@
 		Might also need an undo button
 	- Advanced implementation: record queueing with exact time and date and show a nice linechart for each item. Probably would have to write a new lib for the symbols. (low)
  * Restock from vendor (similar to ATSW reagent buying) (medium)
- * Window containing a list of items that were unqueueable when a group is queued (low-medium)
+ * Automatic refill from the bank
+ * Window containing a list of items that were unqueueable when a group is queued (low)
  * Enchanting -> scroll self-learning (low)
  * Local item count display (bags or AH), thresholds and alerts (high)
 	- Simple implementation: add a column displaying this, settings to specify how much should be local and an alert box
- * Verifiy Cauldron support
  * Verify AuctionLite support
\ No newline at end of file