view Summary.lua @ 9:3bac0bdd59e2

Close the summary frame when opening the config. Remove ?track at? data when exporting/importing a group. Don?t add items when importing a group that are already inside a group.
author Zerotorescue
date Sun, 10 Oct 2010 04:37:21 +0200
parents 1a815139e4c3
children c4d0e5d47e10
line wrap: on
line source
local addon = LibStub("AceAddon-3.0"):GetAddon("Inventory");
local mod = addon:NewModule("Summary", "AceEvent-3.0", "AceTimer-3.0");

local AceGUI = LibStub("AceGUI-3.0");

function mod:OnEnable()
	self:RegisterWidgets();
	
	-- Register our own slash commands
	addon:RegisterSlash(function()
		mod:BuildMain();
		mod:Build();
	end, "s", "sum", "summary");
end

function mod:RegisterWidgets()
	-- Register InlineGroupWithButton-widget
	-- This widget adds a button next to the header of an inline group
	-- SetPoint doesn't seem usable within AceGUI.
	
	local widgetType = "InlineGroupWithButton";
	local widgetVersion = 1;

	local function Constructor() 
	    local widget = AceGUI:Create("InlineGroup");
	    widget.type = widgetType;
	    
	    widget.MakeButton = function(self, buttonSettings)
			if type(buttonSettings) == "table" then
				local button = CreateFrame("Button", nil, self.frame, "UIPanelButtonTemplate");
				button:SetText(buttonSettings.name);
				button:SetHeight(22);
				button:SetWidth(120);
				button:SetPoint("TOPRIGHT", self.frame, "TOPRIGHT", -10, 5);
				button:SetScript("OnClick", buttonSettings.exec);
				button.tooltipTitle = buttonSettings.name;
				button.tooltip = buttonSettings.desc or "";
				button:SetScript("OnEnter", function(self)
					GameTooltip:SetOwner(self, "ANCHOR_NONE")
					GameTooltip:SetPoint("BOTTOM", self, "TOP")
					GameTooltip:SetText(self.tooltipTitle, 1, .82, 0, 1)
					
					if type(self.tooltip) == "string" then
						GameTooltip:AddLine(self.tooltip, 1, 1, 1, 1);
					end
					
					GameTooltip:Show();
				end);
				button:SetScript("OnLeave", function(self)
					GameTooltip:Hide();
				end);
			else
				error("settings must be a table - usage: MakeButton(table);");
			end
	    end
		
	    return widget;
	end
    
	AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion);
end

local itemsCache = {};
local CACHE_ITEMS_TOTAL, CACHE_ITEMS_CURRENT, CACHE_ITEMS_PER_UPDATE = 0, 0, 5;
local cacheStart;

function mod:BuildMain()
	LibStub("AceConfigDialog-3.0"):Close("InventoryOptions");
	
	-- Main Window
	mod.frame = AceGUI:Create("Frame");
	mod.frame:SetTitle("Inventory Summary");
	mod.frame:SetLayout("Fill");
	mod.frame:SetCallback("OnClose", function(widget)
		mod:CancelTimer(self.tmrUpdater, true);
		mod:CloseFrame();
	end);
	
	-- ScrollFrame child
	mod.scrollFrame = AceGUI:Create("ScrollFrame");
	mod.scrollFrame:SetLayout("Flow");
	
	mod.frame:AddChild(mod.scrollFrame);
	
	-- Reset items cache
	table.wipe(itemsCache);
end

function mod:CloseFrame()
	if mod.frame then
		mod.frame:Release();
		mod.frame = nil;
	end
end

local sortMethod = "item";
local sortDirectory = "ASC";
local function ReSort(subject)
	if sortMethod == subject then
		sortDirectory = (sortDirectory == "ASC" and "DESC") or "ASC";
	else
		sortDirectory = "ASC";
	end
	sortMethod = subject;
	
	mod:Build();
end

function mod:Build()
	local start = GetTime();
	
	mod.scrollFrame:ReleaseChildren();
	
	-- We are going to add hunderds of widgets to this container, but don't want it to also cause hunderds of reflows, thus pause reflowing and just do it once when everything is prepared
	-- This appears to be required for each container we wish to pause, so also do this for the contents
	mod.scrollFrame:PauseLayout();
	
	local playerName = UnitName("player");
	
	-- Go through all our stored groups
	for groupName, values in pairs(addon.db.global.groups) do
		local trackAt = (values.trackAtCharacters or (values.trackAtCharacters == nil and addon.db.global.defaults.trackAtCharacters));
		
		-- Does this group have any items and do we want to track it at this char?
		if values.items and trackAt[playerName] then
			-- Get group settings
			local minimumStock = (values.minimumStock or (values.minimumStock == nil and addon.db.global.defaults.minimumStock));
			local showWhenBelow = (values.summaryThresholdShow or (values.summaryThresholdShow == nil and addon.db.global.defaults.summaryThresholdShow));
			local priceThreshold = (values.priceThreshold or (values.priceThreshold == nil and addon.db.global.defaults.priceThreshold));
			local hideWhenBelowPriceThreshold = (values.hideFromSummaryWhenBelowPriceThreshold or (values.hideFromSummaryWhenBelowPriceThreshold == nil and addon.db.global.defaults.hideFromSummaryWhenBelowPriceThreshold));
			
			-- Make group container
			local iGroup = AceGUI:Create("InlineGroupWithButton");
			iGroup:PauseLayout();
			iGroup:SetTitle(groupName);
			iGroup:SetFullWidth(true);
			iGroup:SetLayout("Flow");
			iGroup:MakeButton({
				name = "Queue",
				desc = "Queue all items in this group.",
				exec = function()
					print(groupName);
				end,
			});
			
			-- Headers
			
			-- Itemlink
			local lblItem = AceGUI:Create("InteractiveLabel");
			lblItem:SetText("|cfffed000Item|r");
			lblItem:SetFontObject(GameFontHighlight);
			lblItem:SetRelativeWidth(0.7);
			lblItem:SetCallback("OnClick", function() ReSort("item"); end);
			
			iGroup:AddChild(lblItem);
			
			-- Current quantity
			local lblQuantity = AceGUI:Create("InteractiveLabel");
			lblQuantity:SetText("|cfffed000Cur.|r");
			lblQuantity:SetFontObject(GameFontHighlight);
			lblQuantity:SetRelativeWidth(0.099);
			lblQuantity:SetCallback("OnClick", function() ReSort("current"); end);
			
			iGroup:AddChild(lblQuantity);
			
			-- Required stock
			local lblMinimumStock = AceGUI:Create("InteractiveLabel");
			lblMinimumStock:SetText("|cfffed000Req.|r");
			lblMinimumStock:SetFontObject(GameFontHighlight);
			lblMinimumStock:SetRelativeWidth(0.099);
			lblMinimumStock:SetCallback("OnClick", function() ReSort("percentage"); end);
			
			iGroup:AddChild(lblMinimumStock);
			
			-- Lowest value
			local lblValue = AceGUI:Create("InteractiveLabel");
			lblValue:SetText("|cfffed000Value|r");
			lblValue:SetFontObject(GameFontHighlight);
			lblValue:SetRelativeWidth(0.099);
			lblValue:SetCallback("OnClick", function() ReSort("value"); end);
			
			iGroup:AddChild(lblValue);
			
			if not itemsCache[groupName] then
				itemsCache[groupName] = {};
				
				-- Sort item list
				for itemId in pairs(values.items) do
					local itemName, itemLink, itemRarity = GetItemInfo(itemId);
					
					table.insert(itemsCache[groupName], {
						id = itemId,
						name = itemName,
						link = itemLink,
						value = -3,--addon:GetAuctionValue(itemLink),
						rarity = itemRarity,
						count = -3,--addon:GetItemCount(itemId),
						set = {},
					});
					CACHE_ITEMS_TOTAL = CACHE_ITEMS_TOTAL + 1;
				end
			end
			
			table.sort(itemsCache[groupName], function(a, b)
				if sortMethod == "item" and a.rarity == b.rarity then
					-- Do a name-compare for items of the same rarity
					-- Otherwise epics first, then junk
					if sortDirectory == "ASC" then
						return a.name:upper() < b.name:upper();
					else
						return a.name:upper() > b.name:upper();
					end
				elseif sortMethod == "item"  then
					if sortDirectory == "ASC" then
						return a.rarity > b.rarity; -- the comparers were reversed because we want epics first
					else
						return a.rarity < b.rarity; -- the comparers were reversed because we want epics first
					end
				elseif sortMethod == "current" then
					if sortDirectory == "ASC" then
						return a.count < b.count;
					else
						return a.count > b.count;
					end
				elseif sortMethod == "percentage" then
					if sortDirectory == "ASC" then
						return ( a.count / lblMinimumStock ) < ( b.count / lblMinimumStock );
					else
						return ( a.count / lblMinimumStock ) > ( b.count / lblMinimumStock );
					end
				elseif sortMethod == "value" then
					if sortDirectory == "ASC" then
						return a.value < b.value;
					else
						return a.value > b.value;
					end
				end
			end);
			
			-- Show stuff
			for i, item in pairs(itemsCache[groupName]) do
				if ( item.count / minimumStock ) < showWhenBelow and not (hideWhenBelowPriceThreshold and item.value < priceThreshold) then
					local btnItemLink = AceGUI:Create("ItemLinkButton");
					btnItemLink:SetUserData("exec", function()
						print("Win.");
					end);
					btnItemLink:SetRelativeWidth(0.7);
					btnItemLink:SetItemId(item.id);
					
					iGroup:AddChild(btnItemLink);
					
					-- Current quantity
					local lblQuantity = AceGUI:Create("Label");
					lblQuantity:SetText(self:DisplayItemCount(item.count, minimumStock));
					lblQuantity:SetRelativeWidth(0.099);
					
					iGroup:AddChild(lblQuantity);
					
					-- Required stock
					local lblMinimumStock = AceGUI:Create("Label");
					lblMinimumStock:SetText(minimumStock);
					lblMinimumStock:SetRelativeWidth(0.099);
					
					iGroup:AddChild(lblMinimumStock);
					
					-- Value
					local lblValue = AceGUI:Create("Label");
					lblValue:SetText(self:DisplayMoney(item.value, priceThreshold));
					lblValue:SetRelativeWidth(0.099);
					
					iGroup:AddChild(lblValue);
					
					-- Remember references to the value and current fields so we can fill them later
					if item.set then
						item.set.value = lblValue;
						item.set.current = lblQuantity;
					end
				end
			end
			
			iGroup:ResumeLayout();
			mod.scrollFrame:AddChild(iGroup); -- this can take up to .5 seconds, might need to look into again at a later time
		end
	end
	
	mod.scrollFrame:ResumeLayout();
	mod.scrollFrame:DoLayout();
	
	if CACHE_ITEMS_TOTAL > 0 then
		cacheStart = GetTime();
		self:CancelTimer(self.tmrUpdater, true);
		self.tmrUpdater = self:ScheduleRepeatingTimer("UpdateNextItem", .01); -- Once every 100 frames (or once every x frames if you have less than 100 FPS, basically, once every frame)
	end
end

function mod:UpdateNextItem()
	local i = 0;
	
	for groupName, items in pairs(itemsCache) do
		local minimumStock = addon:GetOptionByKey(groupName, "minimumStock");
		local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold");
		local groupUpdated;
		
		for _, item in pairs(items) do
			if item.set then
				item.count = addon:GetItemCount(item.id);
				if item.set.current and item.set.current.SetText then
					item.set.current:SetText(self:DisplayItemCount(item.count, minimumStock));
				end
				
				item.value = addon:GetAuctionValue(item.link);
				if item.set.value and item.set.value.SetText then
					item.set.value:SetText(self:DisplayMoney(item.value, priceThreshold));
				end
				
				item.set = nil;
				
				i = i + 1;
				CACHE_ITEMS_CURRENT = CACHE_ITEMS_CURRENT + 1;
				groupUpdated = true;
				
				if mod.frame then
					mod.frame:SetStatusText(("Caching auction values and item-counts... %d%% has already been processed."):format(floor(CACHE_ITEMS_CURRENT / CACHE_ITEMS_TOTAL * 100)));
				end
				
				if i >= CACHE_ITEMS_PER_UPDATE then
					return;
				end
			end
		end
		
		if groupUpdated then
			-- Rebuild list so hidden items due to too low prices get added
			self:Build();
		end
	end
	
	-- Reset trackers
	CACHE_ITEMS_TOTAL = 0;
	CACHE_ITEMS_CURRENT = 0;
	
	-- Stop timer
	self:CancelTimer(self.tmrUpdater, true);
	
	-- Announce
	mod.frame:SetStatusText("All prices and itemcounts have been cached. This process took " .. ceil(GetTime() - cacheStart) .. " seconds.");
	
	-- Forget time
	cacheStart = nil;
end

function mod:ColorCode(num, required)
	local percentage = ( num / required );
	
	if percentage >= addon.db.global.defaults.colors.green then
		return ("|cff00ff00%d|r"):format(num);
	elseif percentage >= addon.db.global.defaults.colors.yellow then
		return ("|cffffff00%d|r"):format(num);
	elseif percentage >= addon.db.global.defaults.colors.orange then
		return ("|cffff9933%d|r"):format(num);
	elseif percentage >= addon.db.global.defaults.colors.red then
		return ("|cffff0000%d|r"):format(num);
	end
end

function mod:DisplayMoney(value, priceThreshold)
	if value == -1 then
		return "|cff0000ffNone up|r";
	elseif value == -2 then
		return "|cff0000ffNo AH mod|r";
	elseif value == -3 then
		return "|cffffff00Unknown|r";
	elseif value < priceThreshold then
		return ("|cffff0000%s|r"):format(addon:ReadableMoney(value or 0, true));
	else
		return addon:ReadableMoney(value or 0, true);
	end
end

function mod:DisplayItemCount(value, minimumStock)
	if value == -3 then
		return "|cffffff00Unknown|r";
	else
		return self:ColorCode(value, minimumStock);
	end
end

function mod:NumberFormat(num)
	local formatted = string.gsub(num, "(%d)(%d%d%d)$", "%1,%2", 1);
	
	while true do
		formatted, matches = string.gsub(formatted, "(%d)(%d%d%d),", "%1,%2,", 1);
		
		if matches == 0 then
			break;
		end
	end
	
	return formatted;
end

--[[
No longer used, we're now caching complete item data instead of just AH values.
local AuctionValueCache = {};
function mod:GetAuctionValue(link)
	local itemId = addon:GetItemId(link);
	
	if AuctionValueCache[itemId] then
		return AuctionValueCache[itemId];
	else
		local value = addon:GetAuctionValue(link);
		
		AuctionValueCache[itemId] = value;
	
		-- Reset the cache 1 minute after last updating it
		self:CancelTimer(self.tmrResetCache, true);
		self.tmrResetCache = self:ScheduleTimer(function()
			table.wipe(AuctionValueCache);
			
			mod.frame:SetStatusText("The auction item value cache has been reset.");
		end, 60);
		mod.frame:SetStatusText("");
		
		return value;
	end
end
]]