changeset 1:9fff13c08f99

Initial release, config and summary windows mostly working. Base functionality available.
author Zerotorescue
date Fri, 08 Oct 2010 16:11:59 +0200
parents c6ff7ba0e8f6
children 4ff8bf1e63d7
files Core.lua Summary.lua
diffstat 2 files changed, 561 insertions(+), 151 deletions(-) [+]
line wrap: on
line diff
--- a/Core.lua	Thu Oct 07 17:17:43 2010 +0200
+++ b/Core.lua	Fri Oct 08 16:11:59 2010 +0200
@@ -1,5 +1,7 @@
 -- You can access this addon's object through: LibStub("AceAddon-3.0"):GetAddon("Inventory")
-local addon = LibStub("AceAddon-3.0"):NewAddon("Inventory", "AceEvent-3.0", "AceTimer-3.0");
+local addon = LibStub("AceAddon-3.0"):NewAddon("Inventory", "AceEvent-3.0");
+
+local AceGUI = LibStub("AceGUI-3.0");
 
 local Media = LibStub("LibSharedMedia-3.0");
 Media:Register("sound", "Cartoon FX", [[Sound\Doodad\Goblin_Lottery_Open03.wav]]);
@@ -38,6 +40,7 @@
 				minCraftingQueue = 0.05,
 				bonusQueue = 0.1,
 				priceThreshold = 0,
+				hideFromSummaryWhenBelowPriceThreshold = false,
 				trackAtCharacters = {},
 				colors = {
 					red = 0;
@@ -86,7 +89,8 @@
 	-- And add it to the interface options
 	InterfaceOptions_AddCategory(frame);
 	
-	self:MakeWidgets();
+	self:MakeItemLinkButtonWidget();
+	self:MakeConfigItemLinkButtonWidget();
 	
 	-- Remember this character is mine
 	local playerName = UnitName("player");
@@ -98,36 +102,106 @@
 	end
 end
 
-function addon:MakeWidgets()
-	local AceGUI = LibStub("AceGUI-3.0");
-	
+local slashArgs = {};
+function addon:RegisterSlash(func, ...)
+	for _, arg in pairs({ ... }) do
+		slashArgs[arg] = func;
+	end
+end
+
+function addon:MakeItemLinkButtonWidget()
 	--[[
 	[ ItemLinkButton ]
-	UserData: itemId, onClick event
+	This custom widget has to show an icon with the item link next to it.
+	Upon hover it must show the item tooltip.
+	Upon click it must execute the function provided through user data.
+	
+	UserData: itemId, onClickEvent
+	
 	OnEnter: tooltip show
-	OnLeave: tooltip hid
-	
+	OnLeave: tooltip hide
+	OnClick: UserData.onClickEvent
 	]]
 	
-	-- Define out custom item link button widget
-	-- This will be called as if it's an input element, we overwrite some of the related functions which are called for default input fields
-	
 	local widgetType = "ItemLinkButton";
 	local widgetVersion = 1;
 	
-	-- Empty function for disabling functions
-	local function Dummy() end
-	
-	-- Overwrite the SetText-function of our custom widgets
-	local function CustomWidgetSetText(self, value, ...)
-    	if value then
-    		-- Remember the itemId in a local parameter (using :GetText, we'd have to run a pattern over it and it would all get silly)
-    		self.itemId = tonumber(value);
-    		
-    		if not self.itemId then error("itemId is not a number."); end
-    		
-    		-- Put the icon in front of it
-			self:SetImage(GetItemIcon(self.itemId));
+	local function Constructor()
+	    local widget = AceGUI:Create("InteractiveLabel");
+	    widget.type = widgetType;
+	    
+	    -- We overwrite the OnAcquire as we want to set our callbacks even
+	    -- when the widget is re-used from the widget pool
+	    widget.originalOnAcquire = widget.OnAcquire;
+	    widget.OnAcquire = function(self, ...)
+	    
+	    
+	    	-- We overwrite the setcallback because we don't want anything else 
+	    	-- to overwrite our OnEnter, OnLeave and OnClick events
+	    	-- which would be done by the AceConfigDialog after a widget gets re-used
+		    if not self.originalSetCallBack then
+			    self.originalSetCallBack = self.SetCallback;
+			    self.SetCallback = function(this, event, func, ...)
+			    	if event == "OnEnter" or event == "OnLeave" or event == "OnClick" then
+			    		-- Don't allow overwriting of these events
+			    		return;
+			    	elseif event == "CustomOnEnter" then
+			    		return this.originalSetCallBack(this, "OnEnter", func, ...);
+			    	elseif event == "CustomOnLeave" then
+			    		return this.originalSetCallBack(this, "OnLeave", func, ...);
+			    	elseif event == "CustomOnClick" then
+			    		return this.originalSetCallBack(this, "OnClick", func, ...);
+			    	else
+			    		return this.originalSetCallBack(this, event, func, ...);
+			    	end
+			    end;
+		    end
+		    
+		    
+		    -- Set our own events, since we disabled the normal event-names, we'll call them our custom versions
+		    self:SetCallback("CustomOnEnter", function(this)
+		    	local itemId = this:GetUserData("itemId");
+		    	
+		    	if itemId then
+			    	GameTooltip:SetOwner(this.frame, "ANCHOR_TOPRIGHT");
+			    	GameTooltip:SetHyperlink(("item:%d"):format(itemId));
+					GameTooltip:Show();
+		    	end
+		    end);
+		    self:SetCallback("CustomOnLeave", function(this)
+		    	GameTooltip:Hide();
+		    end);
+		    self:SetCallback("CustomOnClick", function(this)
+		    	if this.OnClick then
+		    		this.OnClick(this);
+		    	end
+		    	
+		    	local func = this:GetUserData("exec");
+		    	local itemId = this:GetUserData("itemId");
+		    	
+		    	if func then
+		    		-- If this is a config option we will need the group id
+		    		local path = this:GetUserData("path");
+		    		local groupId = (path and path[2]) or nil;
+		    		
+		    		func(groupId, itemId);
+		    	end
+		    end);
+		    
+		    
+		    
+		    -- Then also do whatever it wanted to do
+		    self.originalOnAcquire(self, ...);
+	    end;
+	    
+	    -- Remember the original SetText as this might get overwritten by the config-widget
+	    widget.originalSetText = widget.SetText;
+	    
+	    widget.SetItemId = function(self, itemId)
+ 		   	self:SetUserData("itemId", itemId);
+ 		   	
+	   		-- Put the icon in front of it
+			self:SetImage(GetItemIcon(itemId));
 			-- Standardize the size
 			self:SetImageSize(16, 16);
     		
@@ -135,73 +209,51 @@
     		self:SetFontObject(GameFontHighlight);
     		
     		-- We don't want to set the itemId as text, but rather the item link, so get that.
-    		value = select(2, GetItemInfo(value)) or ("Unknown (#%d)"):format(value);
+    		local itemLink = select(2, GetItemInfo(itemId)) or ("Unknown (#%d)"):format(itemId);
     		
-    		return self.originalSetText(self, value, ...);
-    	end
+    		self:originalSetText(itemLink);
+	    end;
+		
+		return widget;
 	end
+    
+	AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion);
+end
+
+function addon:MakeConfigItemLinkButtonWidget()
+	-- Define out custom item link button widget
+	-- This will be called as if it's an input element, we overwrite some of the related functions which are called for default input fields
 	
-	-- Overwrite the OnEnter-event of our custom widgets to use our own function
-	local function CustomWidgetOnEnter(self)
-    	if self.itemId then
-	    	GameTooltip:SetOwner(self.frame, "ANCHOR_TOPRIGHT");
-	    	GameTooltip:SetHyperlink(("item:%d"):format(self.itemId));
-			GameTooltip:Show();
-    	end
-    end
+	local widgetType = "ConfigItemLinkButton";
+	local widgetVersion = 1;
 	
-	-- Overwrite the OnClick-event of our custom widgets to use our own function
-    local function CustomWidgetOnClick(self)
-    	local info = self:GetUserDataTable()
-    	
-		info.option.set(self, info);
-    end
-    
-	-- Overwrite the SetCallback-function of our custom widgets
-	local function CustomWidgetSetCallback(self, event, func, ...)
-    	if event == "OnEnter" then
-	    	-- This event is called once when creating the widget
-	    	-- When it becomes hidden, all events appear to be removed, but once it's shown again, OnEnter will be re-applied
-	    	-- Hence any registered Callback must be done in here
-	    	
-	    	self.originalSetCallBack(self, "OnClick", CustomWidgetOnClick);
-	    	
-    		return self.originalSetCallBack(self, event, CustomWidgetOnEnter, ...);
-    	else
-    		return self.originalSetCallBack(self, event, func, ...);
-    	end
-	end
-	
-	local function CustomWidgetHideTooltip()
-		GameTooltip:Hide();
-	end
+	-- Empty function for disabling functions
+	local function Dummy() end
 	
 	-- Makes an instance of our ItemLinkButton widget
 	local function GetItemLinkButton()
-	    local widget = AceGUI:Create("InteractiveLabel");
+	    local widget = AceGUI:Create("ItemLinkButton");
 	    widget.type = widgetType;
 	    
 	    -- We can only provide custom widgets for input, select and multiselect fields
 	    -- Input being the simplest, we use that - however, it provides two parameters: label and text. We only need one, disable the other.
 	    widget.SetLabel = Dummy;
 	    
-	    if not widget.originalSetText then
-	    	-- When setting text we provide an itemId
-	    	-- Use this itemId to set the icon and do all fancy stuff
-		    widget.originalSetText = widget.SetText;
-		    widget.SetText = CustomWidgetSetText;
-		end
-	    
-	    
-	    if not widget.originalSetCallBack then
-	    	-- We don't want AceConfig to overwrite our OnEnter with the tooltip functions, so override that
-		    widget.originalSetCallBack = widget.SetCallback;
-		    widget.SetCallback = CustomWidgetSetCallback;
-		    
-		    -- Make sure it's called (AceConfig will probably repeat this, but we are prepared in case it's ever changed)
-		    widget:SetCallback("OnEnter", Dummy);
-		    widget:SetCallback("OnLeave", CustomWidgetHideTooltip);
-	    end
+	    -- SetText is called when this button is being created and contains the itemId
+	    -- Forward that itemId to the ItemLinkButton
+	    widget.SetText = function(self, value, ...)
+	    	if value and tonumber(value) then
+	    		self:SetItemId(tonumber(value));
+	    	end
+		end;
+		
+		widget.OnClick = function(self, ...)
+			local option = self:GetUserData("option");
+	    	
+	    	if option and option.set then
+				self:SetUserData("exec", option.set);
+			end
+		end;
 	    
 	    return widget;
 	end
@@ -236,6 +288,8 @@
 			
 			print("New debug channel created.");
 		end
+	elseif slashArgs[cmd] then
+		slashArgs[cmd]();
 	else
 		print("Wrong command, available: /inventory config (or /inventory c)");
 	end
@@ -288,6 +342,62 @@
 	self:FillGroupOptions();
 end
 
+local goldText = "%s%d|cffffd700g|r ";
+local silverText = "%s%d|cffc7c7cfs|r ";
+local copperText = "%s%d|cffeda55fc|r";
+
+function addon:ReadableMoney(copper, clean)
+	local text = "";
+	
+	local gold = floor( copper / COPPER_PER_GOLD );
+	if gold > 0 then
+		text = goldText:format(text, gold);
+	end
+	
+	if not clean or (not gold or gold < 100) then
+		local silver = floor( ( copper % COPPER_PER_GOLD ) / COPPER_PER_SILVER );
+		if silver > 0 then
+			text = silverText:format(text, silver);
+		end
+		
+		if not clean or (not gold or gold < 10) then
+			local copper = floor( copper % COPPER_PER_SILVER );
+			if copper > 0 or text == "" then
+				text = copperText:format(text, copper);
+			end
+		end
+	end
+	
+	
+	return string.trim(text);
+end
+
+function addon:ReadableMoneyToCopper(value)
+	-- If a player enters a value it will be filled without color codes
+	-- If it is retrieved from the database, it will be colored coded
+	-- Thus we look for both
+	local gold = tonumber(string.match(value, "(%d+)|c[a-fA-F0-9]+g|r") or string.match(value, "(%d+)g"));
+	local silver = tonumber(string.match(value, "(%d+)|c[a-fA-F0-9]+s|r") or string.match(value, "(%d+)s"));
+	local copper = tonumber(string.match(value, "(%d+)|c[a-fA-F0-9]+c|r") or string.match(value, "(%d+)c"));
+		
+	return ( (gold or 0) * COPPER_PER_GOLD ) + ( (silver or 0) * COPPER_PER_SILVER ) + (copper or 0);
+end
+
+function addon:ValidateReadableMoney(info, value)
+	-- If a player enters a value it will be filled without color codes
+	-- If it is retrieved from the database, it will be colored coded
+	-- Thus we look for both
+	local gold = tonumber(string.match(value, "(%d+)|c[a-fA-F0-9]+g|r") or string.match(value, "(%d+)g"));
+	local silver = tonumber(string.match(value, "(%d+)|c[a-fA-F0-9]+s|r") or string.match(value, "(%d+)s"));
+	local copper = tonumber(string.match(value, "(%d+)|c[a-fA-F0-9]+c|r") or string.match(value, "(%d+)c"));
+	
+	if not gold and not silver and not copper then
+		return "The provided amount of money is invalid. Please provide the amount of money as #g#s#c, e.g. 591617g24s43c.";
+	else
+		return true;
+	end
+end
+
 function addon:FillGeneralOptions()
 	options.args.general = {
 		order = 100,
@@ -388,7 +498,6 @@
 					trackAtCharacters = {
 						order = 40,
 						type = "multiselect",
-						control = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead.
 						name = "Track at",
 						desc = "Select at which characters this should appear in the summary and generate alerts.",
 						values = function()
@@ -399,12 +508,19 @@
 							
 							return temp;
 						end,
-						get = function(i, v)
-							return self.db.global.defaults.trackAtCharacters[v];
-						end,
+						get = function(i, v) return self.db.global.defaults.trackAtCharacters[v]; end,
 						set = function(i, v, e)
 							self.db.global.defaults.trackAtCharacters[v] = e or nil;
+							
+							-- We MUST close this pullout or we can get errors or even game client crashes once we click another group or close the config dialog!
+							local count = AceGUI:GetNextWidgetNum("Dropdown-Pullout");
+							for i = 0, count do
+								if _G['AceGUI30Pullout' .. i] then
+									_G['AceGUI30Pullout' .. i]:Hide();
+								end
+							end
 						end,
+						dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead.
 					},
 				},
 			},
@@ -462,7 +578,7 @@
 						step = 0.01, -- 1%
 						isPercent = true,
 						name = "Bonus queue",
-						desc = "Get additional items when I have none left.\n\nExample: if your restock target is set to 60 and this is set to 10%, you will get 66 items instead of just 60 if you end up with none left.",
+						desc = "Get additional items when there are none left.\n\nExample: if your restock target is set to 60 and this is set to 10%, you will get 66 items instead of just 60 if you end up with none left while queueing.",
 						get = function() return self.db.global.defaults.bonusQueue; end,
 						set = function(i, v) self.db.global.defaults.bonusQueue = v; end,
 					},
@@ -470,18 +586,18 @@
 						order = 40,
 						type = "input",
 						name = "Price threshold",
-						desc = "Only queue craftable items when they are worth at least this much according to your auction house addon.",
-						validate = function()
-							-- gold check
-						end,
-						get = function() return self.db.global.defaults.priceThreshold; end,
-						set = function(i, v) self.db.global.defaults.priceThreshold = v; end,
+						desc = "Only queue craftable items when they are worth at least this much according to your auction house addon.\n\nSet to 0 to ignore auction prices.",
+						validate = function(info, value) return self:ValidateReadableMoney(info, value); end,
+						get = function() return self:ReadableMoney(self.db.global.defaults.priceThreshold); end,
+						set = function(i, v) self.db.global.defaults.priceThreshold = self:ReadableMoneyToCopper(v); end,
 					},
-					hideFromSummaryPriceThreshold = {
+					hideFromSummaryWhenBelowPriceThreshold = { -- I wish this var could be a little bit shorter...
 						order = 50,
 						type = "toggle",
-						name = "Don't show when below threshold",
+						name = "Hide when below threshold",
 						desc = "Hide items from the summary when their value is below the set price threshold.",
+						get = function() return self.db.global.defaults.hideFromSummaryWhenBelowPriceThreshold; end,
+						set = function(i, v) self.db.global.defaults.hideFromSummaryWhenBelowPriceThreshold = v; end,
 					},
 				},
 			},
@@ -558,7 +674,7 @@
 local count = 0;
 local temp = {};
 
-local function SetOption(info, value)
+local function SetOption(info, value, multiSelectEnabled)
 	local groupName = groupIdToName[info[2]];
 	local optionName = info[#info];
 	
@@ -570,10 +686,19 @@
 		addon.db.global.groups[groupName][info.arg] = nil;
 	end
 	
-	addon.db.global.groups[groupName][optionName] = value;
+	if multiSelectEnabled ~= nil then
+		-- The saved vars for a multiselect will always be an array, it may not yet exist in which case it must be created.
+		if not addon.db.global.groups[groupName][optionName] then
+			addon.db.global.groups[groupName][optionName] = {};
+		end
+		
+		addon.db.global.groups[groupName][optionName][value] = multiSelectEnabled or nil;
+	else
+		addon.db.global.groups[groupName][optionName] = value;
+	end
 end
 
-local function GetOptionByKey(groupName, optionName, noDefault)
+function addon:GetOptionByKey(groupName, optionName, noDefault)
 	if addon.db.global.groups[groupName][optionName] ~= nil then
 		return addon.db.global.groups[groupName][optionName];
 	elseif addon.db.global.defaults[optionName] and not noDefault then
@@ -587,7 +712,20 @@
 	local groupName = groupIdToName[info[2]];
 	local optionName = info[#info];
 	
-	return GetOptionByKey(groupName, optionName);
+	return addon:GetOptionByKey(groupName, optionName);
+end
+
+local function GetMultiOption(info, value)
+	local groupName = groupIdToName[info[2]];
+	local optionName = info[#info];
+	
+	if addon.db.global.groups[groupName][optionName] ~= nil then
+		return addon.db.global.groups[groupName][optionName][value];
+	elseif addon.db.global.defaults[optionName] then
+		return addon.db.global.defaults[optionName][value];
+	else
+		return nil;
+	end
 end
 
 local function GetDisabled(info)
@@ -598,7 +736,7 @@
 	local groupName = groupIdToName[info[2]];
 	local optionName = info[#info];
 	
-	return (GetOptionByKey(groupName, info.arg, true) == nil);
+	return (addon:GetOptionByKey(groupName, info.arg, true) == nil);
 end
 
 local function ValidateGroupName(_, value)
@@ -652,19 +790,19 @@
 	get = function(info)
 		return tostring(info[#info]); -- Ace is going to be anal about this if it's a numeric value, so we transmute it into a string here then back to a number on the other side
 	end,
-	set = function(widget, info)
+	set = function(groupId, itemId)
     	-- This is NOT a real "set", we pass the widget reference to this function which contains similar, but not the same, info.
     	
-    	if widget.itemId then
-	    	local groupName = groupIdToName[info[2]];
+    	if itemId then
+	    	local groupName = groupIdToName[groupId];
 	    	
-	    	if not AddToGroup(groupName, widget.itemId) then
-	    		print("|cffff0000Couldn't add the item with itemId (" .. widget.itemId .. ") because it is already in a group.|r");
+	    	if not AddToGroup(groupName, itemId) then
+	    		print("|cffff0000Couldn't add the item with itemId (" .. itemId .. ") because it is already in a group.|r");
 	    	end
     	end
 	end,
 	width = "double",
-	dialogControl = "ItemLinkButton",
+	dialogControl = "ConfigItemLinkButton",
 };
 
 local tblRemoveItemTemplate = {
@@ -677,21 +815,21 @@
 	get = function(info)
 		return tostring(info[#info]); -- Ace is going to be anal about this if it's a numeric value, so we transmute it into a string here then back to a number on the other side
 	end,
-	set = function(widget, info)
+	set = function(groupId, itemId)
     	-- This is NOT a real "set", we pass the widget reference to this function which contains similar, but not the same, info.
     	
-    	if widget.itemId then
-	    	local groupName = groupIdToName[info[2]];
+    	if itemId then
+	    	local groupName = groupIdToName[groupId];
 	    	
 	    	-- Unset this item
-	    	addon.db.global.groups[groupName].items[widget.itemId] = nil;
+	    	addon.db.global.groups[groupName].items[itemId] = nil;
 	    	
 	    	-- Now rebuild the list
 	    	AceConfigRegistry:NotifyChange("InventoryOptions");
     	end
 	end,
 	width = "double",
-	dialogControl = "ItemLinkButton",
+	dialogControl = "ConfigItemLinkButton",
 };
 
 local function UpdateAddItemList(info)
@@ -843,6 +981,38 @@
 							desc = "Show an alert when an item in this group gets below the minimum stock threshold.",
 							arg = "overrideAlertBelowMinimum",
 						},
+						overrideTrackAtCharacters = {
+							order = 39,
+							type = "toggle",
+							name = "Override track at",
+							desc = "Allows you to override at which characters items in this group should appear in the summary and generate alerts.",
+							arg = "trackAtCharacters",
+						},
+						trackAtCharacters = {
+							order = 40,
+							type = "multiselect",
+							name = "Track at",
+							desc = "Select at which characters this should appear in the summary and generate alerts.",
+							values = function()
+								local temp = {};
+								for charName in pairs(addon.db.factionrealm.characters) do
+									temp[charName] = charName;
+								end
+								
+								-- We MUST close this pullout or we can get errors or even game client crashes once we click another group or close the config dialog!
+								local count = AceGUI:GetNextWidgetNum("Dropdown-Pullout");
+								for i = 0, count do
+									if _G['AceGUI30Pullout' .. i] then
+										_G['AceGUI30Pullout' .. i]:Hide();
+									end
+								end
+								
+								return temp;
+							end,
+							get = GetMultiOption,
+							dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead.
+							arg = "overrideTrackAtCharacters",
+						},
 					},
 				},
 				refill = {
@@ -861,7 +1031,7 @@
 								local groupName = groupIdToName[info[2]];
 								local r = "Here you can specify the amount of items to which you wish to restock when you are collecting new items for the currently selected group. This may be higher than the minimum stock.\n\n";
 								
-								r = r .. "When restocking the target amount is |cfffed000" .. GetOptionByKey(groupName, "restockTarget") .. "|r of every item. Not queueing craftable items when only missing |cfffed000" .. floor( GetOptionByKey(groupName, "minCraftingQueue") * GetOptionByKey(groupName, "restockTarget") ) .. "|r (|cfffed000" .. ( GetOptionByKey(groupName, "minCraftingQueue") * 100 ) .. "%|r) of the restock target.";
+								r = r .. "When restocking the target amount is |cfffed000" .. addon:GetOptionByKey(groupName, "restockTarget") .. "|r of every item. Not queueing craftable items when only missing |cfffed000" .. floor( addon:GetOptionByKey(groupName, "minCraftingQueue") * addon:GetOptionByKey(groupName, "restockTarget") ) .. "|r (|cfffed000" .. ( addon:GetOptionByKey(groupName, "minCraftingQueue") * 100 ) .. "%|r) of the restock target.";
 								
 								return r;
 							end,
@@ -907,6 +1077,55 @@
 							desc = "Don't add a craftable item to the queue if I only miss this much or less of the restock target.\n\nExample: if your restock target is set to 60 and this is set to 5%, an item won't be queued unless you are missing more than 3 of it.",
 							arg = "overrideMinCraftingQueue",
 						},
+						overrideBonusQueue = {
+							order = 29,
+							type = "toggle",
+							name = "Override bonus queue",
+							desc = "Allows you to override the bonus craftable items queue setting for this group.",
+							arg = "bonusQueue",
+						},
+						bonusQueue = {
+							order = 30,
+							type = "range",
+							min = 0,
+							max = 10, -- 1000%
+							step = 0.01, -- 1%
+							isPercent = true,
+							name = "Bonus queue",
+							desc = "Get additional items when there are none left.\n\nExample: if your restock target is set to 60 and this is set to 10%, you will get 66 items instead of just 60 if you end up with none left while queueing.",
+							arg = "overrideBonusQueue",
+						},
+						overridePriceThreshold = {
+							order = 39,
+							type = "toggle",
+							name = "Override price threshold",
+							desc = "Allows you to override the price threshold setting for this group.",
+							arg = "priceThreshold",
+						},
+						priceThreshold = {
+							order = 40,
+							type = "input",
+							name = "Price threshold",
+							desc = "Only queue craftable items when they are worth at least this much according to your auction house addon.\n\nSet to 0 to ignore auction prices.",
+							validate = function(info, value) return addon:ValidateReadableMoney(info, value); end,
+							get = function(i) return addon:ReadableMoney(GetOption(i)); end,
+							set = function(i, v) SetOption(i, addon:ReadableMoneyToCopper(v)); end,
+							arg = "overridePriceThreshold",
+						},
+						overrideHideFromSummaryWhenBelowPriceThreshold = {
+							order = 49,
+							type = "toggle",
+							name = "Override summary showing",
+							desc = "Allows you to override if items in this group should be hidden from the summary while their value is below the price threshold.",
+							arg = "hideFromSummaryWhenBelowPriceThreshold",
+						},
+						hideFromSummaryWhenBelowPriceThreshold = { -- I wish this var could be a little bit shorter...
+							order = 50,
+							type = "toggle",
+							name = "Hide when below threshold",
+							desc = "Hide items from the summary when their value is below the set price threshold.",
+							arg = "overrideHideFromSummaryWhenBelowPriceThreshold",
+						},
 					},
 				},
 			},
@@ -1283,6 +1502,33 @@
 	return Altoholic:GetItemCount(itemId);
 end
 
+function addon:GetAuctionValue(link)
+	if GetAuctionBuyout then
+		-- Auctionator support
+		
+		local lowBuy = GetAuctionBuyout(link);
+		
+		if lowBuy == nil then
+			-- No auctions up at this time
+			return -1;
+		end
+		
+		return lowBuy;
+	elseif AucAdvanced ~= nil and AucAdvanced.Modules.Util.SimpleAuction ~= nil and AucAdvanced.Modules.Util.SimpleAuction.Private.GetItems ~= nil then
+		-- Auctioneer support
+		
+		local imgSeen, _, _, _, _, lowBuy, _, _ = AucAdvanced.Modules.Util.SimpleAuction.Private.GetItems(link);
+		--local imgseen, image, matchBid, matchBuy, lowBid, lowBuy, aveBuy, aSeen 
+		
+		if imgSeen <= 0 then
+			-- No auctions up at this time
+			return -1;
+		end
+	
+		return lowBuy;
+	end
+end
+
 
 
 
--- a/Summary.lua	Thu Oct 07 17:17:43 2010 +0200
+++ b/Summary.lua	Fri Oct 08 16:11:59 2010 +0200
@@ -1,13 +1,16 @@
 local addon = LibStub("AceAddon-3.0"):GetAddon("Inventory");
-local mod = addon:NewModule("Summary", "AceEvent-3.0");
+local mod = addon:NewModule("Summary", "AceEvent-3.0", "AceTimer-3.0");
 
 local AceGUI = LibStub("AceGUI-3.0");
 
 function mod:OnEnable()
 	self:RegisterWidgets();
 	
-	self:BuildMain();
-	self:Build();
+	-- Register our own slash commands
+	addon:RegisterSlash(function()
+		mod:BuildMain();
+		mod:Build();
+	end, "s", "sum", "summary");
 end
 
 function mod:RegisterWidgets()
@@ -57,21 +60,31 @@
 	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()
-	local frame = AceGUI:Create("Frame");
-	frame:SetTitle("Inventory Summary");
-	frame:SetLayout("Fill");
+	LibStub("AceConfigDialog-3.0"):Close("InventoryOptions");
 	
-	local scrollFrame = AceGUI:Create("ScrollFrame");
-	scrollFrame:SetLayout("Flow");
+	-- Main Window
+	mod.frame = AceGUI:Create("Frame");
+	mod.frame:SetTitle("Inventory Summary");
+	mod.frame:SetLayout("Fill");
+	mod.frame:SetCallback("OnClose", function(widget)
+		AceGUI:Release(widget);
+	end);
 	
-	frame:AddChild(scrollFrame);
+	-- ScrollFrame child
+	mod.scrollFrame = AceGUI:Create("ScrollFrame");
+	mod.scrollFrame:SetLayout("Flow");
 	
-	mod.scrollFrame = scrollFrame;
+	mod.frame:AddChild(mod.scrollFrame);
+	
+	-- Reset items cache
+	table.wipe(itemsCache);
 end
 
-local temp = {};
-
 local sortMethod = "item";
 local sortDirectory = "ASC";
 local function ReSort(subject)
@@ -86,16 +99,31 @@
 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
-		if values.items then
+		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 stockRequired = values.minimumStock or addon.db.global.defaults.minimumStock;
-			local showWhenBelow = (values.summaryThresholdShow or addon.db.global.defaults.summaryThresholdShow);
+			local stockRequired = (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");
@@ -108,6 +136,7 @@
 			});
 			
 			-- Headers
+			
 			-- Itemlink
 			local lblItem = AceGUI:Create("InteractiveLabel");
 			lblItem:SetText("|cfffed000Item|r");
@@ -121,7 +150,7 @@
 			local lblQuantity = AceGUI:Create("InteractiveLabel");
 			lblQuantity:SetText("|cfffed000Cur.|r");
 			lblQuantity:SetFontObject(GameFontHighlight);
-			lblQuantity:SetRelativeWidth(0.149);
+			lblQuantity:SetRelativeWidth(0.099);
 			lblQuantity:SetCallback("OnClick", function() ReSort("current"); end);
 			
 			iGroup:AddChild(lblQuantity);
@@ -130,31 +159,54 @@
 			local lblStockRequired = AceGUI:Create("InteractiveLabel");
 			lblStockRequired:SetText("|cfffed000Req.|r");
 			lblStockRequired:SetFontObject(GameFontHighlight);
-			lblStockRequired:SetRelativeWidth(0.149);
+			lblStockRequired:SetRelativeWidth(0.099);
 			lblStockRequired:SetCallback("OnClick", function() ReSort("percentage"); end);
 			
 			iGroup:AddChild(lblStockRequired);
 			
-			-- Sort item list
-			for itemId in pairs(values.items) do
-				local itemName, itemLink, itemRarity = GetItemInfo(itemId);
+			-- 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] = {};
 				
-				table.insert(temp, {
-					id = itemId,
-					name = itemName,
-					link = itemLink,
-					rarity = itemRarity,
-					count = addon:GetItemCount(itemId),
-				});
+				-- 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 = 0,--addon:GetAuctionValue(itemLink),
+						rarity = itemRarity,
+						count = 0,--addon:GetItemCount(itemId),
+						set = {},
+					});
+					CACHE_ITEMS_TOTAL = CACHE_ITEMS_TOTAL + 1;
+				end
 			end
 			
-			local sortNameFormat = "%d%s"; --rarity .. name
-			table.sort(temp, function(a, b)
-				if sortMethod == "item" then
+			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 sortNameFormat:format((7 - a.rarity), a.name):upper() < sortNameFormat:format((7 - b.rarity), b.name):upper();
+						return a.name:upper() < b.name:upper();
 					else
-						return sortNameFormat:format((7 - a.rarity), a.name):upper() > sortNameFormat:format((7 - b.rarity), b.name):upper();
+						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
@@ -168,41 +220,118 @@
 					else
 						return ( a.count / stockRequired ) > ( b.count / stockRequired );
 					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 _, item in pairs(temp) do
-				if ( item.count / stockRequired ) < showWhenBelow then
+			for i, item in pairs(itemsCache[groupName]) do
+				if ( item.count / stockRequired ) < showWhenBelow and not (hideWhenBelowPriceThreshold and item.value < priceThreshold) then
 					local btnItemLink = AceGUI:Create("ItemLinkButton");
-					btnItemLink:SetText(item.id);
+					btnItemLink:SetUserData("exec", function()
+						print("Win.");
+					end);
 					btnItemLink:SetRelativeWidth(0.7);
-					btnItemLink:SetCallback("OnEnter", function() end);
+					btnItemLink:SetItemId(item.id);
 					
 					iGroup:AddChild(btnItemLink);
 					
 					-- Current quantity
 					local lblQuantity = AceGUI:Create("Label");
 					lblQuantity:SetText(self:ColorCode(item.count, stockRequired));
-					lblQuantity:SetRelativeWidth(0.149);
+					lblQuantity:SetRelativeWidth(0.099);
 					
 					iGroup:AddChild(lblQuantity);
 					
 					-- Required stock
 					local lblStockRequired = AceGUI:Create("Label");
 					lblStockRequired:SetText(stockRequired);
-					lblStockRequired:SetRelativeWidth(0.149);
+					lblStockRequired:SetRelativeWidth(0.099);
 					
 					iGroup:AddChild(lblStockRequired);
+					
+					-- 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
 			
-			-- We no longer need this, so forget about it
-			table.wipe(temp);
-	
-			mod.scrollFrame:AddChild(iGroup);
+			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.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");
+		
+		for _, item in pairs(items) do
+			if item.set then
+				item.count = addon:GetItemCount(item.id);
+				if item.set.current then
+					item.set.current:SetText(self:ColorCode(item.count, minimumStock));
+				end
+				
+				item.value = addon:GetAuctionValue(item.link);
+				if item.set.value then
+					item.set.value:SetText(self:DisplayMoney(item.value, priceThreshold));
+				end
+				
+				item.set = nil;
+				
+				i = i + 1;
+				CACHE_ITEMS_CURRENT = CACHE_ITEMS_CURRENT + 1;
+				
+				mod.frame:SetStatusText("Caching auction values and item-counts... " .. floor(CACHE_ITEMS_CURRENT / CACHE_ITEMS_TOTAL * 100) .. "% has already been processed.");
+				
+				if i >= CACHE_ITEMS_PER_UPDATE then
+					return;
+				end
+			end
+		end
+	end
+	
+	-- Reset trackers
+	CACHE_ITEMS_TOTAL = 0;
+	CACHE_ITEMS_CURRENT = 0;
+	
+	-- Rebuild list so hidden items due to too low prices get added
+	self:Build();
+	
+	-- 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)
@@ -219,6 +348,14 @@
 	end
 end
 
+function mod:DisplayMoney(value, priceThreshold)
+	if 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:NumberFormat(num)
 	local formatted = string.gsub(num, "(%d)(%d%d%d)$", "%1,%2", 1);
 	
@@ -231,4 +368,31 @@
 	end
 	
 	return formatted;
-end
\ No newline at end of file
+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
+]]
\ No newline at end of file