changeset 132:8460855e3d90

Rewrote queueing module to insert a GUI. Minor mover window changes.
author Zerotorescue
date Tue, 18 Jan 2011 00:30:15 +0100
parents a27948591159
children 2efe61ca718e
files Frames.lua Modules/Queue.lua Modules/Scanner.lua
diffstat 3 files changed, 726 insertions(+), 87 deletions(-) [+]
line wrap: on
line diff
--- a/Frames.lua	Tue Jan 18 00:28:24 2011 +0100
+++ b/Frames.lua	Tue Jan 18 00:30:15 2011 +0100
@@ -4,7 +4,7 @@
 	-- If this function is called from a widget, self is the widget and self.frame the actual frame
 	local this = self.frame or self;
 	
-	if not this.tooltipTitle then return; end
+	if not this.tooltipTitle or addon.db.profile.defaults.hideHelp then return; end
 	
 	GameTooltip:SetOwner(this, "ANCHOR_NONE");
 	if this.tooltipLocation and this.tooltipLocation == "BOTTOM" then
@@ -26,6 +26,10 @@
 end
 
 function addon:CreateMoverFrame()
+	if InventoriumItemMover then
+		return;
+	end
+	
 	local frameWidth = 400;
 	
 	-- Main window
@@ -136,17 +140,16 @@
 		frame.lblDescription = lblDescription;
 	
 	-- Buttons
-		-- Move (proceed)
-			local btnMove = CreateFrame("Button", "$parentProceed", frame, "UIPanelButtonTemplate");
-			btnMove:SetHeight(21);
-			btnMove:SetWidth(125);
-			btnMove:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 15, 11);
-			btnMove:SetText("Move Items");
-			btnMove:SetScript("OnClick", function(this) this.OnClick(this); end);
-			btnMove:SetScript("OnEnter", ShowTooltip);
-			btnMove:SetScript("OnLeave", HideTooltip);
+		-- Proceed
+			local btnProceed = CreateFrame("Button", "$parentProceed", frame, "UIPanelButtonTemplate");
+			btnProceed:SetHeight(21);
+			btnProceed:SetWidth(125);
+			btnProceed:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 15, 11);
+			btnProceed:SetScript("OnClick", function(this) this.OnClick(this); end);
+			btnProceed:SetScript("OnEnter", ShowTooltip);
+			btnProceed:SetScript("OnLeave", HideTooltip);
 			
-			frame.btnMove = btnMove;
+			frame.btnProceed = btnProceed;
 			
 		-- Cancel
 			local btnCancel = CreateFrame("Button", "$parentCancel", frame, "UIPanelButtonTemplate");
@@ -169,12 +172,12 @@
 		
 	-- Scrolling table with a list of items to be moved
 		local ScrollingTable = LibStub("ScrollingTable");
-		local table = ScrollingTable:CreateST({}, 4, 15, nil, frame); -- inserting a dummy cols, real cols to be set in SetFrameSettings
-		table.frame:SetPoint("TOP", frame.lblDescription, "BOTTOM", 0, -18);
-		table.frame:SetPoint("LEFT", frame, "LEFT", 15, 0);
-		table.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -15, 35);
+		local scrollTable = ScrollingTable:CreateST({}, 4, 15, nil, frame); -- inserting a dummy cols, real cols to be set in SetFrameSettings
+		scrollTable.frame:SetPoint("TOP", frame.lblDescription, "BOTTOM", 0, -18);
+		scrollTable.frame:SetPoint("LEFT", frame, "LEFT", 15, 0);
+		scrollTable.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -15, 35);
 		-- When moving over a row, provide a tooltip for the item
-		table:RegisterEvents({
+		scrollTable:RegisterEvents({
 			["OnEnter"] = function(rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...)
 				if row and realrow then
 					-- Data row
@@ -205,7 +208,7 @@
 			end,
 		});
 		
-		frame.scrollTable = table;
+		frame.scrollTable = scrollTable;
 	
 	-- Change the amount of displayed rows based on the size of the frame
 		frame.AdjustScrollTableRows = function(this)
@@ -223,7 +226,7 @@
 	InventoriumItemMover:Show();
 end
 
-function addon:SetFrameSettings(title, description, proceed, cancel, headers)
+function addon:SetMoverFrameSettings(title, description, proceed, cancel, headers)
 	local frame = InventoriumItemMover;
 	
 	frame.lblTitle:SetText(title);
@@ -232,10 +235,10 @@
 	
 	frame.lblDescription:SetText(description);
 	
-	frame.btnMove:SetText(proceed.text);
-	frame.btnMove.tooltipTitle = proceed.tooltipTitle;
-	frame.btnMove.tooltip = proceed.tooltip;
-	frame.btnMove.OnClick = proceed.onClick;
+	frame.btnProceed:SetText(proceed.text);
+	frame.btnProceed.tooltipTitle = proceed.tooltipTitle;
+	frame.btnProceed.tooltip = proceed.tooltip;
+	frame.btnProceed.OnClick = proceed.onClick;
 	
 	frame.btnCancel:SetText(cancel.text);
 	frame.btnCancel.tooltipTitle = cancel.tooltipTitle;
@@ -244,3 +247,379 @@
 	
 	frame.scrollTable:SetDisplayCols(headers);
 end
+
+function addon:CreateQueueFrame()
+	if InventoriumQueuer then
+		return;
+	end
+	
+	do
+		local frameWidth = 400;
+		
+		-- Main window
+			local frame = CreateFrame("Frame", "InventoriumQueuer", UIParent);
+			-- Hide by default
+			frame:Hide();
+			-- Center the frame (will be adjusted later)
+			frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0);
+			-- Put in front of other windows
+			frame:SetFrameStrata("FULLSCREEN_DIALOG");
+			frame:SetToplevel(true);
+			-- Give it a size
+			frame:SetWidth(frameWidth);
+			frame:SetHeight(430);
+			-- Background
+			frame:SetBackdrop({
+				bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+				edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
+				edgeSize = 20,
+				insets = {
+					left = 5,
+					right = 5,
+					top = 5,
+					bottom = 5,
+				},
+			});
+			frame:SetBackdropColor(0, 0, 0, .8);
+			-- Mouse functions
+			frame:EnableMouse();
+			frame:SetMovable(true);
+			-- Set event handlers
+			frame:SetScript("OnMouseUp", function(this) this:StopMovingOrSizing(); end);
+			frame:SetScript("OnShow", function(this)
+				this:AdjustScrollTableRows();
+			end);
+			
+		-- Title (AceGUI frame-widget-title used as example)
+			local titleBackground = frame:CreateTexture(nil, "OVERLAY");
+			titleBackground:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header");
+			titleBackground:SetTexCoord(0.31, 0.67, 0, 0.63);
+			titleBackground:SetPoint("TOP", 0, 12);
+			titleBackground:SetWidth(150);
+			titleBackground:SetHeight(40);
+			
+			frame.titleBackground = titleBackground;
+			
+			local titleBackgroundLeft = frame:CreateTexture(nil, "OVERLAY");
+			titleBackgroundLeft:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header");
+			titleBackgroundLeft:SetTexCoord(0.21, 0.31, 0, 0.63);
+			titleBackgroundLeft:SetPoint("RIGHT", titleBackground, "LEFT");
+			titleBackgroundLeft:SetWidth(30);
+			titleBackgroundLeft:SetHeight(40);
+
+			local titleBackgroundRight = frame:CreateTexture(nil, "OVERLAY");
+			titleBackgroundRight:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header");
+			titleBackgroundRight:SetTexCoord(0.67, 0.77, 0, 0.63);
+			titleBackgroundRight:SetPoint("LEFT", titleBackground, "RIGHT");
+			titleBackgroundRight:SetWidth(30);
+			titleBackgroundRight:SetHeight(40);
+
+			local frmTitle = CreateFrame("Frame", nil, frame);
+			frmTitle:EnableMouse(true);
+			frmTitle:SetScript("OnMouseDown", function(this) this:GetParent():StartMoving(); end);
+			frmTitle:SetScript("OnMouseUp", function(this) this:GetParent():StopMovingOrSizing(); end);
+			frmTitle:SetAllPoints(titleBackground);
+
+			local lblTitle = frmTitle:CreateFontString(nil, "OVERLAY", "GameFontNormal");
+			lblTitle:SetPoint("TOP", titleBackground, "TOP", 0, -14);
+			
+			frame.lblTitle = lblTitle;
+			
+		-- Expand button
+			local btnExpander = CreateFrame("Button", "$parentExpander", frame);
+			btnExpander:SetWidth(32);
+			btnExpander:SetHeight(32);
+			btnExpander:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -10, -10);
+			btnExpander:SetNormalTexture("Interface\\Buttons\\UI-SpellbookIcon-NextPage-Up");
+			btnExpander:SetPushedTexture("Interface\\Buttons\\UI-SpellbookIcon-NextPage-Down");
+			btnExpander:SetDisabledTexture("Interface\\Buttons\\UI-SpellbookIcon-NextPage-Disabled");
+			btnExpander:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD");
+			btnExpander.tooltipTitle = "Show unqueueables";
+			btnExpander.tooltip = "Click to show a list of all unqueueable but tracked items.";
+			btnExpander:SetScript("OnEnter", ShowTooltip);
+			btnExpander:SetScript("OnLeave", HideTooltip);
+			btnExpander:SetScript("OnClick", function(this)
+				if this.Expanded then
+					-- Collapsing
+					this.Expanded = nil;
+					InventoriumQueuerUnqueueables:Hide();
+					PlaySound("igCharacterInfoClose");
+					
+					-- Next is an expand
+					this:SetNormalTexture("Interface\\Buttons\\UI-SpellbookIcon-NextPage-Up");
+					this:SetPushedTexture("Interface\\Buttons\\UI-SpellbookIcon-NextPage-Down");
+					this:SetDisabledTexture("Interface\\Buttons\\UI-SpellbookIcon-NextPage-Disabled");
+				else
+					-- Expanding
+					this.Expanded = true;
+					
+					-- Position the frame against the queuer window
+					InventoriumQueuerUnqueueables:ClearAllPoints();
+					InventoriumQueuerUnqueueables:SetPoint("TOPLEFT", this:GetParent(), "TOPRIGHT", 0, 0);
+					InventoriumQueuerUnqueueables:SetPoint("BOTTOMLEFT", this:GetParent(), "BOTTOMLEFT", 0, 0);
+					InventoriumQueuerUnqueueables:Show();
+					PlaySound("igCharacterInfoOpen");
+					
+					-- Next is a collapse
+					this:SetNormalTexture("Interface\\Buttons\\UI-SpellbookIcon-PrevPage-Up");
+					this:SetPushedTexture("Interface\\Buttons\\UI-SpellbookIcon-PrevPage-Down");
+					this:SetDisabledTexture("Interface\\Buttons\\UI-SpellbookIcon-PrevPage-Disabled");
+				end
+			end);
+		
+		-- Description
+			local lblDescription = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal");
+			lblDescription:SetPoint("TOPLEFT", frame, "TOPLEFT", 15, -27);
+			lblDescription:SetPoint("RIGHT", btnExpander, "LEFT", -15, 0);
+			lblDescription:SetJustifyH("LEFT");
+			lblDescription:SetJustifyV("TOP");
+			lblDescription:SetWidth(frameWidth - 70);
+			
+			frame.lblDescription = lblDescription;
+		
+		-- Buttons
+			-- Proceed
+				local btnProceed = CreateFrame("Button", "$parentProceed", frame, "UIPanelButtonTemplate");
+				btnProceed:SetHeight(21);
+				btnProceed:SetWidth(125);
+				btnProceed:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 15, 11);
+				btnProceed:SetScript("OnClick", function(this) this.OnClick(this); end);
+				btnProceed:SetScript("OnEnter", ShowTooltip);
+				btnProceed:SetScript("OnLeave", HideTooltip);
+				
+				frame.btnProceed = btnProceed;
+				
+			-- Cancel
+				local btnCancel = CreateFrame("Button", "$parentCancel", frame, "UIPanelButtonTemplate");
+				btnCancel:SetHeight(21);
+				btnCancel:SetWidth(125);
+				btnCancel:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -15, 11);
+				btnCancel:SetScript("OnClick", function(this) this.OnClick(this); end);
+				btnCancel:SetScript("OnEnter", ShowTooltip);
+				btnCancel:SetScript("OnLeave", HideTooltip);
+				
+				frame.btnCancel = btnCancel;
+			
+		-- Because the scrolling table code-behind will change the scrolltable element's height, we can't rely on that. Make a dummy frame which we can measure
+			local frmMeasureDummy = CreateFrame("Frame", nil, frame);
+			frmMeasureDummy:SetPoint("TOP", frame.lblDescription, "BOTTOM", 0, -18);
+			frmMeasureDummy:SetPoint("LEFT", frame, "LEFT", 15, 0);
+			frmMeasureDummy:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -15, 35);
+			
+			frame.frmMeasureDummy = frmMeasureDummy;
+			
+		-- Scrolling table with a list of items to be queued
+			local ScrollingTable = LibStub("ScrollingTable");
+			local scrollTable = ScrollingTable:CreateST({}, 4, 15, nil, frame); -- inserting a dummy cols, real cols to be set in SetFrameSettings
+			scrollTable.frame:SetPoint("TOP", frame.lblDescription, "BOTTOM", 0, -18);
+			scrollTable.frame:SetPoint("LEFT", frame, "LEFT", 15, 0);
+			scrollTable.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -15, 35);
+			-- When moving over a row, provide a tooltip for the item
+			scrollTable:RegisterEvents({
+				["OnEnter"] = function(rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...)
+					if row and realrow then
+						-- Data row
+						
+						if data[realrow] and data[realrow].rowData and data[realrow].rowData.itemId then
+							GameTooltip:SetOwner(rowFrame, "ANCHOR_NONE");
+							GameTooltip:SetPoint("TOPLEFT", rowFrame, "BOTTOMLEFT");
+							GameTooltip:SetHyperlink(("item:%d"):format(data[realrow].rowData.itemId));
+							GameTooltip:Show();
+						end
+					else
+						-- Header row
+						
+						if cols[column].tooltipTitle and type(cols[column].tooltipTitle) == "string" then
+							cellFrame.tooltipTitle = cols[column].tooltipTitle;
+							if cols[column].tooltip then
+								cellFrame.tooltip = cols[column].tooltip; -- Optional
+							else
+								cellFrame.tooltip = nil;
+							end
+							
+							ShowTooltip(cellFrame);
+						end
+					end
+				end,
+				["OnLeave"] = function(rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...)
+					HideTooltip();
+				end,
+			});
+			
+			frame.scrollTable = scrollTable;
+		
+		-- Change the amount of displayed rows based on the size of the frame
+			frame.AdjustScrollTableRows = function(this)
+				local newRows = math.floor(( this.frmMeasureDummy:GetHeight() - 5 ) / 15);
+				newRows = (newRows < 4 and 4) or newRows;
+				
+				this.scrollTable:SetDisplayRows(newRows, 15);
+			end;
+			frame:SetScript("OnSizeChanged", frame.AdjustScrollTableRows);
+	end
+	do
+		local frameWidth = 300;
+		
+		-- Main window
+			local frame = CreateFrame("Frame", "InventoriumQueuerUnqueueables", InventoriumQueuer);
+			-- Hide by default
+			frame:Hide();
+			-- Position the frame against the queuer window
+			frame:SetPoint("TOPLEFT", InventoriumQueuer, "TOPRIGHT", 0, 0);
+			frame:SetPoint("BOTTOMLEFT", InventoriumQueuer, "BOTTOMLEFT", 0, 0);
+			-- Put in front of other windows
+			frame:SetFrameStrata("FULLSCREEN_DIALOG");
+			frame:SetToplevel(true);
+			-- Give it a size
+			frame:SetWidth(frameWidth);
+			-- Background
+			frame:SetBackdrop({
+				bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+				edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
+				edgeSize = 20,
+				insets = {
+					left = 5,
+					right = 5,
+					top = 5,
+					bottom = 5,
+				},
+			});
+			frame:SetBackdropColor(0, 0, 0, .8);
+			-- Mouse functions
+			frame:EnableMouse();
+			frame:SetMovable(true);
+			-- Set event handlers
+			frame:SetScript("OnMouseUp", function(this) this:StopMovingOrSizing(); end);
+			frame:SetScript("OnShow", function(this)
+				this:AdjustScrollTableRows();
+			end);
+			
+		-- Title (AceGUI frame-widget-title used as example)
+			local titleBackground = frame:CreateTexture(nil, "OVERLAY");
+			titleBackground:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header");
+			titleBackground:SetTexCoord(0.31, 0.67, 0, 0.63);
+			titleBackground:SetPoint("TOP", 0, 12);
+			titleBackground:SetWidth(90);
+			titleBackground:SetHeight(40);
+			
+			frame.titleBackground = titleBackground;
+			
+			local titleBackgroundLeft = frame:CreateTexture(nil, "OVERLAY");
+			titleBackgroundLeft:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header");
+			titleBackgroundLeft:SetTexCoord(0.21, 0.31, 0, 0.63);
+			titleBackgroundLeft:SetPoint("RIGHT", titleBackground, "LEFT");
+			titleBackgroundLeft:SetWidth(30);
+			titleBackgroundLeft:SetHeight(40);
+
+			local titleBackgroundRight = frame:CreateTexture(nil, "OVERLAY");
+			titleBackgroundRight:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header");
+			titleBackgroundRight:SetTexCoord(0.67, 0.77, 0, 0.63);
+			titleBackgroundRight:SetPoint("LEFT", titleBackground, "RIGHT");
+			titleBackgroundRight:SetWidth(30);
+			titleBackgroundRight:SetHeight(40);
+
+			local frmTitle = CreateFrame("Frame", nil, frame);
+			frmTitle:EnableMouse(true);
+			frmTitle:SetScript("OnMouseDown", function(this) this:GetParent():StartMoving(); end);
+			frmTitle:SetScript("OnMouseUp", function(this) this:GetParent():StopMovingOrSizing(); end);
+			frmTitle:SetAllPoints(titleBackground);
+
+			local lblTitle = frmTitle:CreateFontString(nil, "OVERLAY", "GameFontNormal");
+			lblTitle:SetPoint("TOP", titleBackground, "TOP", 0, -14);
+			lblTitle:SetText("Unqueueables");
+			
+		-- Because the scrolling table code-behind will change this element's height, we can't rely on that. Make a dummy frame which we can measure
+			local frmMeasureDummy = CreateFrame("Frame", nil, frame);
+			frmMeasureDummy:SetPoint("TOPLEFT", frame, "TOPLEFT", 15, -42);
+			frmMeasureDummy:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -15, 15);
+			
+			frame.frmMeasureDummy = frmMeasureDummy;
+			
+		-- Scrolling table with a list of items to be queued
+			local ScrollingTable = LibStub("ScrollingTable");
+			local scrollTable = ScrollingTable:CreateST({}, 4, 15, nil, frame); -- inserting a dummy cols, real cols to be set in SetFrameSettings
+			scrollTable.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 15, -42);
+			scrollTable.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -15, 15);
+			-- When moving over a row, provide a tooltip for the item
+			scrollTable:RegisterEvents({
+				["OnEnter"] = function(rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...)
+					if row and realrow then
+						-- Data row
+						
+						if data[realrow] and data[realrow].rowData and data[realrow].rowData.itemId then
+							if column == 1 then
+								GameTooltip:SetOwner(rowFrame, "ANCHOR_NONE");
+								GameTooltip:SetPoint("TOPLEFT", rowFrame, "BOTTOMLEFT");
+								GameTooltip:SetHyperlink(("item:%d"):format(data[realrow].rowData.itemId));
+								GameTooltip:Show();
+							else
+								GameTooltip:SetOwner(cellFrame, "ANCHOR_NONE");
+								GameTooltip:SetPoint("TOPLEFT", cellFrame, "BOTTOMLEFT");
+								GameTooltip:SetText(data[realrow].rowData.reason[1]);
+								GameTooltip:AddLine(data[realrow].rowData.reason[2], 1, 1, 1, 1);
+								GameTooltip:Show();
+							end
+						end
+					else
+						-- Header row
+						
+						if cols[column].tooltipTitle and type(cols[column].tooltipTitle) == "string" then
+							cellFrame.tooltipTitle = cols[column].tooltipTitle;
+							if cols[column].tooltip then
+								cellFrame.tooltip = cols[column].tooltip; -- Optional
+							else
+								cellFrame.tooltip = nil;
+							end
+							
+							ShowTooltip(cellFrame);
+						end
+					end
+				end,
+				["OnLeave"] = function(rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...)
+					HideTooltip();
+				end,
+			});
+			
+			frame.scrollTable = scrollTable;
+		
+		-- Change the amount of displayed rows based on the size of the frame
+			frame.AdjustScrollTableRows = function(this)
+				local newRows = math.floor(( this.frmMeasureDummy:GetHeight() - 5 ) / 15);
+				newRows = (newRows < 4 and 4) or newRows;
+				
+				this.scrollTable:SetDisplayRows(newRows, 15);
+			end;
+			frame:SetScript("OnSizeChanged", frame.AdjustScrollTableRows);
+	end
+end
+
+function addon:SetQueueFrameData(queueable, unqueueables)
+	InventoriumQueuer.scrollTable:SetData(queueable);
+	InventoriumQueuerUnqueueables.scrollTable:SetData(unqueueables);
+	
+	InventoriumQueuer:Show();
+end
+
+function addon:SetQueueFrameSettings(title, description, proceed, cancel, headers, unqueueablesHeaders)
+	local frame = InventoriumQueuer;
+	
+	frame.lblTitle:SetText(title);
+	-- Adjust size for the title background
+	frame.titleBackground:SetWidth((frame.lblTitle:GetWidth() or 0) + 10); -- 10 pixels margin
+	
+	frame.lblDescription:SetText(description);
+	
+	frame.btnProceed:SetText(proceed.text);
+	frame.btnProceed.tooltipTitle = proceed.tooltipTitle;
+	frame.btnProceed.tooltip = proceed.tooltip;
+	frame.btnProceed.OnClick = proceed.onClick;
+	
+	frame.btnCancel:SetText(cancel.text);
+	frame.btnCancel.tooltipTitle = cancel.tooltipTitle;
+	frame.btnCancel.tooltip = cancel.tooltip;
+	frame.btnCancel.OnClick = cancel.onClick;
+	
+	frame.scrollTable:SetDisplayCols(headers);
+	
+	InventoriumQueuerUnqueueables.scrollTable:SetDisplayCols(unqueueablesHeaders);
+end
--- a/Modules/Queue.lua	Tue Jan 18 00:28:24 2011 +0100
+++ b/Modules/Queue.lua	Tue Jan 18 00:30:15 2011 +0100
@@ -1,7 +1,209 @@
 local addon = select(2, ...);
 local mod = addon:NewModule("Queue", "AceEvent-3.0", "AceTimer-3.0");
 
-local pairs = pairs;
+local _G = _G;
+local tonumber, pairs, sformat, smatch, floor, ceil, tinsert, twipe = _G.tonumber, _G.pairs, _G.string.format, _G.string.match, _G.floor, _G.ceil, _G.table.insert, _G.table.wipe;
+
+local queue, skipped = {}, {};
+
+-- strings are passed by reference, so it takes no additional memory if one string was used in a thousand tables compared to any other reference type
+local skipReasons = {
+	["LOW_VALUE"] = { "|cffff6633Underpriced|r", "The recorded auction value of this item is below your price threshold." },
+	["CAPPED"] = { "|cff66ff33Fully stocked|r", "The recorded item count is above or equal to your minimum global stock setting." },
+	["MIN_CRAFTING_QUEUE"] = { "|cffffff00Min crafting queue|r", "The amount of missing items is below or equal to your \"don't queue if I only miss\"-setting." },
+	["NO_ITEMCOUNT_ADDON"] = { "|cffff0000No itemcount addon|r", "No compatible item count could be found." },
+	["NOT_CRAFTABLE"] = { "|cff3d3d3dNot in profession|r", "This item is not part of this profession." },
+};
+
+local function OnQueueCancel()
+	twipe(queue);
+	twipe(skipped);
+	
+	InventoriumQueuer:Hide();
+end
+
+local function OnQueueAccept()
+	-- Prepare a table with all possible tradeskill craftables
+	local craftables = mod:GetTradeskillCraftables();
+	
+	for _, q in pairs(queue) do
+		if craftables[q.itemId] then
+			if mod:QueueWithAddon(craftables[q.itemId].no, ceil(q.amount / craftables[q.itemId].quantity), q.groupName) == -1 then
+				addon:Print("Couldn't queue, no supported crafting addon found.", addon.Colors.Red);
+				
+				OnQueueCancel();
+				return;
+			end
+		else
+			addon:Debug("Lost %s", IdToItemLink(q.itemId));
+		end
+	end
+	
+	twipe(queue);
+	twipe(skipped);
+	
+	InventoriumQueuer:Hide();
+end
+
+local function MakeQueueWindow()
+	do
+		local frame = InventoriumQueuer; -- both for speed as code-consistency
+		
+		-- Scrolling table with a list of items to be moved
+		local scrollTableWidth = ( frame.frmMeasureDummy:GetWidth() - 30 ); -- adjust width by the scrollbar size
+		local headers = {
+			{
+				["name"] = "Item",
+				["width"] = (scrollTableWidth * .60),
+				["defaultsort"] = "asc",
+				["comparesort"] = function(this, aRow, bRow, column)
+					local aName, _, aRarity = GetItemInfo(this:GetRow(aRow).rowData.itemId);
+					local bName, _, bRarity = GetItemInfo(this:GetRow(bRow).rowData.itemId);
+					local template = "%d%s";
+					aName = template:format((10 - (aRarity or 10)), (aName or ""):lower());
+					bName = template:format((10 - (bRarity or 10)), (bName or ""):lower());
+					
+					if this.cols[column].sort == "dsc" then
+						return aName > bName;
+					else
+						return aName < bName;
+					end
+				end,
+				["sort"] = "asc", -- when the data is set, use this column so sort the default data
+				["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Item"),
+				["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by item quality then item name."),
+			},
+			{
+				["name"] = "Amount",
+				["width"] = (scrollTableWidth * .20),
+				["align"] = "RIGHT",
+				["defaultsort"] = "dsc",
+				["sortnext"] = 1,
+				["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Amount"),
+				["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the amount of items to be queued."),
+			},
+			{
+				["name"] = "Extra",
+				["width"] = (scrollTableWidth * .20),
+				["align"] = "RIGHT",
+				["defaultsort"] = "dsc",
+				["sortnext"] = 1,
+				["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Extra"),
+				["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the amount of bonus items."),
+			},
+		};
+		
+		local scrollTableWidth = ( InventoriumQueuerUnqueueables.frmMeasureDummy:GetWidth() - 30 ); -- adjust width by the scrollbar size
+		local unqueueablesHeaders = {
+			{
+				["name"] = "Item",
+				["width"] = (scrollTableWidth * .6),
+				["defaultsort"] = "asc",
+				["comparesort"] = function(this, aRow, bRow, column)
+					local aName, _, aRarity = GetItemInfo(this:GetRow(aRow).rowData.itemId);
+					local bName, _, bRarity = GetItemInfo(this:GetRow(bRow).rowData.itemId);
+					local template = "%d%s";
+					aName = template:format((10 - (aRarity or 10)), (aName or ""):lower());
+					bName = template:format((10 - (bRarity or 10)), (bName or ""):lower());
+					
+					if this.cols[column].sort == "dsc" then
+						return aName > bName;
+					else
+						return aName < bName;
+					end
+				end,
+				["sort"] = "asc", -- when the data is set, use this column so sort the default data
+				["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Item"),
+				["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by item quality then item name."),
+			},
+			{
+				["name"] = "Reason",
+				["width"] = (scrollTableWidth * .4),
+				["defaultsort"] = "dsc",
+				["sortnext"] = 1,
+				["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Reason"),
+				["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the reason the items couldn't be queued."),
+			},
+		};
+		
+		local proceedButton = {
+			text = "Queue",
+			tooltipTitle = (not addon.db.profile.defaults.hideHelp and "Queue"),
+			tooltip = (not addon.db.profile.defaults.hideHelp and "Add these items to the queue of your crafting addon."),
+			onClick = OnQueueAccept,
+		};
+		local cancelButton = {
+			text = "Cancel",
+			tooltipTitle = (not addon.db.profile.defaults.hideHelp and "Cancel"),
+			tooltip = (not addon.db.profile.defaults.hideHelp and "Do not queue anything and close the window."),
+			onClick = OnQueueCancel,
+		};
+		
+		addon:SetQueueFrameSettings("Inventorium Queue", "The following items can be added to the queue of your crafting addon. Do you wish to proceed?", proceedButton, cancelButton, headers, unqueueablesHeaders);
+	end
+end
+
+local function DisplayQueue()
+	MakeQueueWindow();
+	
+	-- This table is never copied, just referenced. It is the same for every row.
+	local queueablesColumns = {
+		{
+			["value"] = function(data, cols, realrow, column, table)
+				return IdToItemLink(data[realrow].rowData.itemId);
+			end,
+		}, -- item
+		{
+			["value"] = function(data, cols, realrow, column, table)
+				return data[realrow].rowData.amount;
+			end,
+		}, -- amount
+		{
+			["value"] = function(data, cols, realrow, column, table)
+				return (data[realrow].rowData.bonus == 0 and 0) or "+" .. data[realrow].rowData.bonus;
+			end,
+			["color"] = function(data, cols, realrow, column, table)
+				return ((data[realrow].rowData.bonus == 0) and { r = 1, g = 1, b = 1, a = 0.5 }) or { r = 0, g = 1, b = 0, a = 1 };
+			end,
+		}, -- extra
+	};
+	
+	-- Store the list with rows in this
+	local queueables = {};
+	
+	for _, q in pairs(queue) do
+		tinsert(queueables, {
+			["rowData"] = q, -- this is not a key usually found in a row item and ignored by the library
+			["cols"] = queueablesColumns,
+		});
+	end
+	
+	-- This table is never copied, just referenced. It is the same for every row.
+	local unqueueablesColumns = {
+		{
+			["value"] = function(data, cols, realrow, column, table)
+				return IdToItemLink(data[realrow].rowData.itemId);
+			end,
+		}, -- item
+		{
+			["value"] = function(data, cols, realrow, column, table)
+				return data[realrow].rowData.reason[1];
+			end,
+		}, -- reason
+	};
+	
+	-- Store the list with rows in this
+	local unqueueables = {};
+	
+	for _, s in pairs(skipped) do
+		tinsert(unqueueables, {
+			["rowData"] = s, -- this is not a key usually found in a row item and ignored by the library
+			["cols"] = unqueueablesColumns,
+		});
+	end
+	
+	addon:SetQueueFrameData(queueables, unqueueables);
+end
 
 function mod:OnEnable()
 	-- Register our own slash commands
@@ -12,6 +214,10 @@
 	
 	self:RegisterMessage("IM_QUEUE_ALL");
 	self:RegisterMessage("IM_QUEUE_GROUP");
+	
+	if not InventoriumQueuer then
+		addon:CreateQueueFrame(OnMoveAccept, OnMoveCancel);
+	end
 end
 
 function mod:IM_QUEUE_ALL()
@@ -23,6 +229,13 @@
 end
 
 function mod:QueueAll()
+	-- Prepare a table with all possible tradeskill craftables
+	local craftables = self:GetTradeskillCraftables();
+	
+	-- Forget old queue
+	twipe(queue);
+	twipe(skipped);
+	
 	local playerName = UnitName("player");
 	
 	-- Go through all groups
@@ -30,108 +243,108 @@
 		local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters");
 		
 		if trackAt[playerName] then
-			self:QueueGroup(groupName);
+			self:QueueGroup(groupName, craftables);
 		end
 	end
+	
+	DisplayQueue();
 end
 
-function mod:QueueGroup(groupName)
-	if not addon.db.profile.groups[groupName] then
-		addon:Print(("Tried to queue items from a group named \"%s\", but no such group exists."):format(groupName), addon.Colors.Red);
+function mod:QueueGroup(groupName, craftables)
+	-- Prepare a table with all possible tradeskill craftables
+	if not craftables then
+		craftables = self:GetTradeskillCraftables(); -- nil when no tradeskill window is open
+	end
+	
+	if not craftables then
+		addon:Print("No tradeskill window detected.", addon.Colors.Red);
+		return;
+	elseif not addon.db.profile.groups[groupName] then
+		addon:Print(sformat("Tried to queue items from a group named \"%s\", but no such group exists.", groupName), addon.Colors.Red);
+		return;
+	elseif not addon.db.profile.groups[groupName].items then
+		addon:Debug("This group (%s) has no items.", groupName);
 		return;
 	end
 	
-	local temp = {};
+	-- Retrieve group settings
+	local restockTarget = addon:GetOptionByKey(groupName, "restockTarget");
+	local bonusQueue = addon:GetOptionByKey(groupName, "bonusQueue");
+	local minCraftingQueue = floor( addon:GetOptionByKey(groupName, "minCraftingQueue") * restockTarget ); -- If the minCraftingQueue is 5% and restockTarget is 60, this will result in 3
+	local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold");
 	
-	local tradeskillName, currentLevel, maxLevel = GetTradeSkillLine();
-	
-	if tradeskillName ~= "UNKNOWN" then
-		-- Go through all trade skills for the profession
-		for i = 1, GetNumTradeSkills() do
-			-- Process every single tradeskill
-			self:ProcessTradeSkill(i, groupName, temp);
-		end
-	end
-	
-	if addon.db.profile.groups[groupName].items then
-		for itemId, _ in pairs(addon.db.profile.groups[groupName].items) do
-			if not temp[itemId] then
-				local itemLink = select(2, GetItemInfo(itemId));
-				
-				addon:Print(("Couldn't queue %s (not part of this profession)"):format((itemLink or itemId or "???")), addon.Colors.Orange);
-			end
-		end
-	end
-end
-
-function mod:ProcessTradeSkill(i, groupName, temp)
-	-- Try to retrieve the itemlink, this will be nil if current item is a group instead
-	local itemLink = GetTradeSkillItemLink(i);
-	
-	if itemLink then
-		local itemId = addon:GetItemId(itemLink);
-		if not itemId then
-			-- If this isn't an item, it can only be an enchant instead
-			itemId = tonumber(itemLink:match("|Henchant:([-0-9]+)|h"));
-			
-			itemId = addon.scrollIds[itemId]; -- change enchantIds into scrollIds
-		end
-		
-		if addon.db.profile.groups[groupName].items and addon.db.profile.groups[groupName].items[itemId] then
-			-- This item is in this group, queue it!
-			
-			if temp then
-				-- Remember which items have been processed
-				temp[itemId] = true;
-			end
-			
+	for itemId in pairs(addon.db.profile.groups[groupName].items) do
+		if craftables[itemId] then
 			local currentStock = addon:GetItemCount(itemId, groupName);
 			
 			if currentStock >= 0 then
 				-- Current stock will be -1 when no itemcount addon was found
 				
-				-- Retrieve group settings
-				local restockTarget = addon:GetOptionByKey(groupName, "restockTarget");
-				local bonusQueue = addon:GetOptionByKey(groupName, "bonusQueue");
-				local minCraftingQueue = floor( addon:GetOptionByKey(groupName, "minCraftingQueue") * restockTarget ); -- If the minCraftingQueue is 5% and restockTarget is 60, this will result in 3
-				
 				-- Calculate the amount to be queued
 				local amount = ( restockTarget - currentStock );
+				local bonus = 0;
 				
 				if currentStock == 0 and bonusQueue > 0 then
 					-- If we have none left and the bonus queue is enabled, modify the amount to  be queued
 					
-					amount = floor( ( amount * ( bonusQueue + 1 ) ) + .5 ); -- round
+					bonus = floor( ( amount * ( bonusQueue ) ) + .5 ); -- round
+					
+					-- Update amount
+					amount = (amount + bonus);
 				end
 				
 				if amount > 0 and amount >= minCraftingQueue then
 					-- If we are queueing at least one AND more than the minimum amount, then proceed
 					
 					-- Auction value settings
-					local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold");
 					local value = (priceThreshold ~= 0 and addon:GetAuctionValue(itemLink, groupName));
 					
 					if priceThreshold == 0 or value == -1 or value >= priceThreshold then
 						-- If no price threshold is set or the auction value is equal to or larger than the price threshold, then proceed
 						
-						self:Queue(i, amount, groupName);
-						
-						addon:Print(("Queued %d of %s"):format(amount, itemLink));
+						self:Queue(itemId, amount, bonus, groupName);
+					else
+						self:Skip(itemId, skipReasons.LOW_VALUE);
+					end
+				else
+					if amount <= 0 then
+						self:Skip(itemId, skipReasons.CAPPED);
+					else
+						self:Skip(itemId, skipReasons.MIN_CRAFTING_QUEUE);
 					end
 				end
 			else
+				self:Skip(itemId, skipReasons.NO_ITEMCOUNT_ADDON);
 				addon:Print("No usable itemcount addon found.");
+				return;
 			end
+		else
+			self:Skip(itemId, skipReasons.NOT_CRAFTABLE);
 		end
 	end
 end
 
-function mod:Queue(tradeSkillIndex, amount, group)
+function mod:Queue(itemId, amount, bonus, groupName)
+	tinsert(queue, {
+		["itemId"] = itemId,
+		["amount"] = amount,
+		["bonus"] = bonus,
+		["groupName"] = groupName,
+	});
+end
+
+function mod:Skip(itemId, reason)
+	tinsert(skipped, {
+		["itemId"] = itemId,
+		["reason"] = reason,
+	});
+end
+
+function mod:QueueWithAddon(tradeSkillIndex, amount, group)
+	-- Sanity check
 	tradeSkillIndex = tonumber(tradeSkillIndex);
 	amount = tonumber(amount);
 	
-	if not tradeSkillIndex or not amount then return; end
-	
 	local selectedExternalAddon = addon:GetOptionByKey(group, "craftingAddon");
 	
 	if addon.supportedAddons.crafting[selectedExternalAddon] and addon.supportedAddons.crafting[selectedExternalAddon].IsEnabled() then
@@ -148,5 +361,52 @@
 		end
 	end
 	
-	return -2;
+	return -1;
 end
+
+-- Expand all categories
+local function ExpandSubClasses()
+	for i = GetNumTradeSkills(), 1, -1 do
+		local _, skillType, _, isExpanded = GetTradeSkillInfo(i);
+
+		if skillType == "header" and not isExpanded then
+			ExpandTradeSkillSubClass(i);
+		end
+	end
+end
+
+function mod:GetTradeskillCraftables()
+	local craftables = {};
+	
+	if GetTradeSkillLine() ~= "UNKNOWN" then
+		ExpandSubClasses();
+		
+		-- Cache all craftable items
+		for i = 1, GetNumTradeSkills() do
+			local itemLink = GetTradeSkillItemLink(i);
+			
+			if itemLink then
+				local itemId = addon:GetItemId(itemLink);
+				if not itemId then
+					-- If this isn't an item, it can only be an enchant instead
+					itemId = tonumber(smatch(itemLink, "|Henchant:([-0-9]+)|h"));
+					
+					itemId = addon.scrollIds[itemId]; -- change enchantIds into scrollIds
+				end
+				
+				-- Remember the average amount of items created per craft (doesn't need to be a round number, since we multiply this by the amount of items to be queued we're better off rounding at that time)
+				local minMade, maxMade = GetTradeSkillNumMade(i);
+				local average = ((minMade == maxMade) and minMade) or ((minMade + maxMade) / 2);
+				
+				craftables[itemId] = {
+					["no"] = i,
+					["quantity"] = average,
+				};
+			end
+		end
+	else
+		return;
+	end
+	
+	return craftables;
+end
--- a/Modules/Scanner.lua	Tue Jan 18 00:28:24 2011 +0100
+++ b/Modules/Scanner.lua	Tue Jan 18 00:30:15 2011 +0100
@@ -105,7 +105,7 @@
 		onClick = OnMoveCancel,
 	};
 	
-	addon:SetFrameSettings("Inventorium Storage Refill", "The items listed below can be refilled from this location, do you wish to move them to your bags?", proceedButton, cancelButton, headers);
+	addon:SetMoverFrameSettings("Inventorium Storage Refill", "The items listed below can be refilled from this location, do you wish to move them to your bags?", proceedButton, cancelButton, headers);
 end
 
 -- Merchant restock window: restock from a merchant by buying items needed
@@ -169,7 +169,7 @@
 		onClick = OnMoveCancel,
 	};
 	
-	addon:SetFrameSettings("Inventorium Merchant Restock", ("The following items can be restocked from this merchant for a total of %s. Do you wish to proceed?"):format(GetSmallCoinTextureString(totalCost)), proceedButton, cancelButton, headers);
+	addon:SetMoverFrameSettings("Inventorium Merchant Restock", ("The following items can be restocked from this merchant for a total of %s. Do you wish to proceed?"):format(GetSmallCoinTextureString(totalCost)), proceedButton, cancelButton, headers);
 end
 
 function mod:ClearCache()
@@ -627,7 +627,7 @@
 	Mover = addon:GetModule("Mover");
 	
 	if not InventoriumItemMover then
-		addon:CreateMoverFrame(OnMoveAccept, OnMoveCancel);
+		addon:CreateMoverFrame();
 	end
 end