changeset 111:af23986010ef v1.1beta0

Rewrote the main part to clarify things, should have removed some hidden nasty bugs.
author contrebasse
date Thu, 02 Jun 2011 23:07:23 +0200
parents d60d6b4cab0c
children 69300309ac21
files CampFireButton.lua Craft.lua ReagentMaker.lua ReagentMaker.toc SecureMenu.lua utils.lua
diffstat 6 files changed, 267 insertions(+), 184 deletions(-) [+]
line wrap: on
line diff
--- a/CampFireButton.lua	Thu Jun 02 18:23:42 2011 +0200
+++ b/CampFireButton.lua	Thu Jun 02 23:07:23 2011 +0200
@@ -7,6 +7,7 @@
 local CooldownFrame_SetTimer = CooldownFrame_SetTimer
 local GetSpellCooldown = GetSpellCooldown
 
+-- The cooldown info is not directly obtainable
 local function WaitCooldown(self)
 	local start, duration, enable = GetSpellCooldown(CAMPFIRE_ID)
 	if start>0 then
@@ -19,7 +20,7 @@
 function A.InitialiseCampFireBtn()
 	if not GetTradeSkillLine() or InCombatLockdown() then return end
 
-	-- create the frame
+	-- Create the frame
 	btn = CreateFrame("Button", nil, TradeSkillFrame, "SecureActionButtonTemplate")
 	btn:SetNormalTexture(select(3,GetSpellInfo(CAMPFIRE_ID)))
 	btn:SetHighlightTexture("Interface\\BUTTONS\\ButtonHilight-Square")
@@ -67,6 +68,7 @@
 -- Hide button
 function A.HideCampFireBtn()
 	if btn then btn:Hide() end
+	btn:UnregisterAllEvents()
 end
 
 -- Show button if applicable
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Craft.lua	Thu Jun 02 23:07:23 2011 +0200
@@ -0,0 +1,167 @@
+local addonName, A = ...
+
+-- GLOBALS: SPELL_FAILED_ERROR, OpenStackSplitFrame, StackSplitFrame_OnChar, StackSplitFrame
+
+-- Lua functions
+local tostring = tostring
+local min = math.min
+local max = math.max
+local floor = math.floor
+
+-- Wow functions
+local IsTradeSkillGuild = IsTradeSkillGuild
+local IsTradeSkillLinked = IsTradeSkillLinked
+local IsModifierKeyDown = IsModifierKeyDown
+local IsShiftKeyDown = IsShiftKeyDown
+local GetTradeSkillSelectionIndex = GetTradeSkillSelectionIndex
+local GetTradeSkillReagentItemLink = GetTradeSkillReagentItemLink
+local GetTradeSkillReagentInfo = GetTradeSkillReagentInfo
+local GetTradeSkillLine = GetTradeSkillLine
+local GetItemInfo = GetItemInfo
+local DoTradeSkill = DoTradeSkill
+
+--SPELL_FAILED_REAGENTS = "Missing reagent: %s";
+--ERR_SPELL_FAILED_REAGENTS_GENERIC = "Missing reagent";
+--ERR_INTERNAL_BAG_ERROR = "Internal Bag Error";
+--SPELL_FAILED_ERROR = "Internal error";
+
+---------------------------------------------------
+-- Craft items
+---------------------------------------------------
+-- Function run after selecting a item in the tradeskill window
+-- It only "prefilters" the possibilities
+function A.ProcessReagent(btn)
+	-- Do not manage guild or linked tradeskill
+	if IsTradeSkillGuild() or IsTradeSkillLinked() then return end
+
+	-- We want no modifiers, or shift to choose the number of reagent to craft
+	if IsModifierKeyDown() and not IsShiftKeyDown() then return end
+	local chooseNumberToCraft = IsShiftKeyDown()
+
+	-- Index of the reagent in the recipe, taken from the button name
+	local reagentIndexInRecipe = A.buttonNumber(btn)
+
+	-- ID of the reagent we want to craft
+	local recipeIndex = GetTradeSkillSelectionIndex()
+	local reagentID = A.link2ID(GetTradeSkillReagentItemLink(recipeIndex, reagentIndexInRecipe))
+
+	-- Continue only if the reagent is known
+	if not reagentID or not A.data[reagentID] then return end
+
+	-- If only one recipe is known for the reagent and it is an actual recipe, use it
+	if #(A.data[reagentID]) == 1 and not A.data[reagentID][1].macro then
+		A.CraftItemWithRecipe(recipeIndex,reagentID,A.data[reagentID][1],reagentIndexInRecipe,chooseNumberToCraft,btn)
+
+	else -- Many recipes are known for this item, or it is not a standard tradeskill display them all
+		A.externalCraftWindow(reagentID,reagentIndexInRecipe)
+	end -- if
+	--A.RestoreActiveFilters()
+end -- function
+
+-- Launch the procedure for a standard recipe
+-- Can be called from the external window
+function A.CraftItemWithRecipe(recipeIndex,reagentID,recipeData,reagentIndexInRecipe,chooseNumberToCraft,btn)
+	-- Check that it's the same tradeskill
+	if recipeData.tradeskillName ~= GetTradeSkillLine() then
+		A.Error(A.L["The recipe to make this reagent is in another tradeskill. Currently ReagentMaker can not manage such a case, sorry."])
+		return
+	end
+
+	-- Check how many times the recipe is makable
+	local numMakable = A.numRecipeMakable(recipeData[1],recipeData[2])
+	if not numMakable then
+		A.Error(SPELL_FAILED_ERROR)
+		return
+	end
+
+	if numMakable<=0 then
+		-- If not makable, try a one-step recursion
+		-- enables e.g. to mill to create an ink
+		-- need a unique reagent
+		if recipeData[1] and A.data[recipeData[1]] then
+			if A.externalCraftWindow(recipeData[1],reagentIndexInRecipe,reagentID) ~= false then
+				-- there was no problem opening the external window
+				return
+			end
+		end
+
+		-- There isn't enough reagents
+		--@todo include name of reagent if unique
+		A.Error(A.L["You do not have enough reagents to craft [%s]"]:format(GetItemInfo(reagentID) or "item #"..reagentID))
+		return
+	end
+
+	-- Optimal number of items to craft
+	local numToMake = A.numToMake(recipeIndex, reagentIndexInRecipe,numMakable, recipeData[3], recipeData[4])
+
+	-- Choose number or craft directly
+	if chooseNumberToCraft then
+		-- Store info to be able to run the function later
+		btn.ReagentMaker_reagentID = reagentID
+		btn.ReagentMaker_recipeData = recipeData
+
+		-- Open dialog
+		OpenStackSplitFrame(numMakable, btn, "TOP", "BOTTOM")
+
+		-- Fill in the number to make
+		numToMake = tostring(numToMake)
+		for i = 1,numToMake:len() do
+			StackSplitFrame_OnChar(StackSplitFrame,numToMake:gsub(i,i))
+		end
+		StackSplitFrame.typing = 0 -- reinit the frame so that the entered value will be erased on text entry
+	else
+		A.DoCraft(reagentID,recipeData,numToMake)
+	end -- if
+end
+
+-- Compute optimal number of items to craft
+function A.numToMake(recipeIndex, reagentIndexInRecipe,numReagentMakable,minMade,maxMade)
+	-- Look at how many we need to make one item for the selected recipe
+	local numToMake = 1
+	local _, _, reagentCount, playerReagentCount = GetTradeSkillReagentInfo(recipeIndex, reagentIndexInRecipe)
+	-- make enough reagents to craft one more item
+	numToMake = min(floor(playerReagentCount/reagentCount+1)*reagentCount-playerReagentCount,numReagentMakable)
+
+	-- take into account that some recipe craft more than one item
+	-- use the mean between min and max, but make at least one...
+	if not minMade then
+		minMade = 1
+	elseif minMade<1 then
+		-- from the percentage, compute the mean number of crafts to make
+		minMade = 1/minMade
+	end
+	if not maxMade then
+		maxMade = minMade
+	end
+	numToMake = max(floor(2*numToMake/(maxMade+minMade)),1)
+	return numToMake
+end
+
+-- function used after choosing the number of reagent to craft
+function A.SplitStack(owner,split)
+	A.DoCraft(owner.ReagentMaker_reagentID,owner.ReagentMaker_recipeData,split)
+	owner.ReagentMaker_reagentID = nil
+	owner.ReagentMaker_recipeData = nil
+end
+
+-- Find the recipe and do the crafting
+function A.DoCraft(reagentID,recipeData,numToMake)
+	-- Remove filters
+	A.SaveActiveFilters(recipeData.header)
+
+	-- Find recipe index
+	local reagentIndex = A.findExactSkillIndex(reagentID,recipeData.spellLink) -- finds only the first recipe that creates the reagent, should check recipe name too
+
+	-- Error if not found
+	if not reagentIndex then
+		A.Error(A.L["The recipe to make the reagent seems to be hidden, it is not makable. Try to remove the filters on the recipes."])
+		A.RestoreActiveFilters()
+		return
+	end
+
+	-- Craft the item, finally !
+	DoTradeSkill(reagentIndex,numToMake)
+
+	-- Restore Filters
+	A.RestoreActiveFilters()
+end
--- a/ReagentMaker.lua	Thu Jun 02 18:23:42 2011 +0200
+++ b/ReagentMaker.lua	Thu Jun 02 23:07:23 2011 +0200
@@ -1,30 +1,33 @@
 local addonName, A = ...
 
--- @todo clean the A table
--- @todo check local copy of globals functions
+-- @todo add support for cross tradeskill, like mining + forge/ingé
 -- @todo add support for dez ?
--- @todo add support for hidden recipes, removing filtering => save the reagent count to be able to compute the number of craftable items
--- @todo add support for cross tradeskill, like mining + forge/ingé
--- @todo when a reagent can not be crafted and the recipe has many reagents, do like ReverseEngeneering and go to this recipe (with a one step return button)
--- @todo scroll to the selected recipe on opening (usefull also for the previous todo)
--- @todo shift+clic on a reagent name while the serachbar is focused fills the serachbar with the reagent name (idem with the crafted item)
--- @todo add a button to clear search
--- @todo add a button to cast a campfire when coocking
+-- @todo scroll to the selected recipe on opening ?
+-- @todo add a button to clear search ?
+
+-- @bug Enter when choosing number of crafts
 
 ---------------------------------------------------
 -- Variables
 ---------------------------------------------------
 -- Used by findglobals
--- GLOBALS: _G, CreateFrame, DEFAULT_CHAT_FRAME
+-- GLOBALS: _G, CreateFrame, DEFAULT_CHAT_FRAME, UIParent
 
 -- Lua functions
 
 -- Wow functions
+local GetTradeSkillLine = GetTradeSkillLine
+local IsTradeSkillGuild = IsTradeSkillGuild
+local IsTradeSkillLinked = IsTradeSkillLinked
+local GetTradeSkillSelectionIndex = GetTradeSkillSelectionIndex
+local GetTradeSkillNumReagents = GetTradeSkillNumReagents
+local GetTradeSkillReagentItemLink = GetTradeSkillReagentItemLink
 
 -- constant vars
+local GameTooltip = GameTooltip
 
 ---------------------------------------------------
--- Manage events
+-- Manage events and throttling
 ---------------------------------------------------
 A.EventsFrame = CreateFrame("Frame")
 
@@ -63,6 +66,7 @@
 
 	elseif event == "PLAYER_REGEN_DISABLED" then
 		A.HideCampFireBtn()
+
 	elseif event == "PLAYER_REGEN_ENABLED" then
 		A.ManageCampFireBtn()
 
@@ -71,11 +75,14 @@
 		A.EventsFrame:UnregisterEvent("TRADE_SKILL_SHOW")
 	end -- if
 end) -- function
+
+--@todo Unregister events when not needed
 A.EventsFrame:RegisterEvent("TRADE_SKILL_SHOW")
 A.EventsFrame:RegisterEvent("TRADE_SKILL_UPDATE")
 A.EventsFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
 A.EventsFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
 
+
 ---------------------------------------------------
 -- Initialize
 ---------------------------------------------------
@@ -115,136 +122,19 @@
 	A.LoadEnchantOnScroll()
 end -- function
 
--- Function run after selecting a item in the tradeskill window
-function A.ProcessReagent(btn, ...)
-	--SPELL_FAILED_REAGENTS = "Missing reagent: %s";
-	--ERR_SPELL_FAILED_REAGENTS_GENERIC = "Missing reagent";
-	--ERR_INTERNAL_BAG_ERROR = "Internal Bag Error";
-	--SPELL_FAILED_ERROR = "Internal error";
-
-	-- Do not manage guild tradeskill
-	if IsTradeSkillGuild() or IsTradeSkillLinked() then return end
-
-	-- We want no modifiers, or shift to choose the number of reagent to craft
-	if IsModifierKeyDown() and not IsShiftKeyDown() then return end
-	local chooseNumberToCraft = IsShiftKeyDown()
-
-	-- Index of the reagent in the recipe, taken from the button name
-	local reagentRecipeIndex = A.buttonNumber(btn)
-
-	-- ID of the reagent we want to craft
-	local recipeIndex = GetTradeSkillSelectionIndex()
-	local reagentID = A.link2ID(GetTradeSkillReagentItemLink(recipeIndex, reagentRecipeIndex))
-
-	-- Continue only if the reagent is known
-	if not reagentID or not A.data[reagentID] then return end
-
-	-- If only one recipe is known for the reagent, use it
-	if #(A.data[reagentID]) == 1 and not A.data[reagentID][1].macro then
-		if A.data[reagentID][1].tradeskillName and A.data[reagentID][1].tradeskillName ~= GetTradeSkillLine() then
-			A.Error(A.L["The recipe to make this reagent is in another tradeskill. Currently ReagentMaker can not manage such a case, sorry."])
-			return
-		end
-
-		local reagentIndex = A.findSkillIndex(reagentID) -- finds only the first recipe that creates the reagent, should check recipe name too
-
-		-- Try to show the recipe by removing filters once if it was not found
-		if not reagentIndex then
-			A.SaveActiveFilters(A.data[reagentID][1].header)
-			reagentIndex = A.findSkillIndex(reagentID)
-		end
-		if not reagentIndex then
-			A.Error(A.L["The recipe to make the reagent seems to be hidden, it is not makable. Try to remove the filters on the recipes."])
-			A.RestoreActiveFilters()
-			return
-		end
-
-		local numMakable = A.numMakable(reagentID)
-		if not numMakable then
-			A.Error(A.L["There was a problem while retrieving informations, please retry."])
-			A.RestoreActiveFilters()
-			return
-		end
-		if numMakable>0 then
-			A.craft(recipeIndex,reagentRecipeIndex,reagentIndex,numMakable,chooseNumberToCraft)
-			A.RestoreActiveFilters()
-			return
-		end
-
-		-- If we can make the item needed to make the reagent, open a window to make it
-		-- one step recursion, enables to mill to create an ink
-		if A.data[reagentID][1][1] and A.data[A.data[reagentID][1][1]] then
-			if A.externalCraftWindow(A.data[reagentID][1][1],reagentID) ~= false then
-				A.RestoreActiveFilters()
-				return
-			end
-		end
-
-		A.Error(A.L["You do not have enough reagents to craft [%s]"]:format(GetItemInfo(reagentID) or "item #"..reagentID))
-		A.RestoreActiveFilters()
-		return
-	else
-		A.externalCraftWindow(reagentID)
-	end -- if
-	--A.RestoreActiveFilters()
-end -- function
-
-
 ---------------------------------------------------
--- Craft items
+-- Dynamic display
 ---------------------------------------------------
--- function used after choosing the number of reagent to craft
-function A.SplitStack(owner,split)
-	DoTradeSkill(owner.ReagentMaker_reagentIndex,tonumber(split))
-	owner.ReagentMaker_reagentIndex = nil
-end
-
--- Craft the reagent of an item, given it's position in the recipe
-function A.craft(recipeIndex,reagentRecipeIndex,reagentIndex,numReagentMakable,chooseNumber)
-	-- Look at how many we need to make one item for the selected recipe
-	local numToMake = 1
-	local _, _, reagentCount, playerReagentCount = GetTradeSkillReagentInfo(recipeIndex, reagentRecipeIndex)
-	-- make enough reagents to craft one more item
-	numToMake = math.min(math.floor(playerReagentCount/reagentCount+1)*reagentCount-playerReagentCount,numReagentMakable)
-
-	-- take into account that some recipe craft more than one item
-	-- use the mean between min and max, but make at least one...
-	local minMade, maxMade = GetTradeSkillNumMade(reagentIndex)
-	numToMake = math.max(math.floor(2*numToMake/(maxMade+minMade)),1)
-
-	-- Choose number or craft directly
-	if chooseNumber then
-		-- the dialog window is linked to the reagent button
-		local btn = _G["TradeSkillReagent"..reagentRecipeIndex]
-
-		-- Store info to be able to run the function later
-		btn.ReagentMaker_reagentIndex = reagentIndex
-
-		-- Open dialog
-		OpenStackSplitFrame(numReagentMakable, btn, "TOP", "BOTTOM")
-
-		-- Fill in the number to make
-		numToMake = tostring(numToMake)
-		for i = 1,numToMake:len() do
-			StackSplitFrame_OnChar(StackSplitFrame,numToMake:gsub(i,i))
-		end
-		StackSplitFrame.typing = 0 -- reinit the frame so that the entered value will be erased on text entry
-	else
-		DoTradeSkill(reagentIndex,numToMake)
-	end -- if
-end -- function
-
-
--- Button hovering
+-- Button hovering (entered)
 function A.btnEntered(btn)
 	-- Do not manage guild tradeskill
 	if IsTradeSkillGuild() or IsTradeSkillLinked() then return end
 
 	-- Index of the reagent in the recipe, taken from the button name
-	local reagentRecipeIndex = A.buttonNumber(btn)
+	local reagentIndexInRecipe = A.buttonNumber(btn)
 
 	-- ID of the reagent we want to craft
-	local reagentLink = GetTradeSkillReagentItemLink(GetTradeSkillSelectionIndex(), reagentRecipeIndex)
+	local reagentLink = GetTradeSkillReagentItemLink(GetTradeSkillSelectionIndex(), reagentIndexInRecipe)
 	local reagentID = A.link2ID(reagentLink)
 
 	-- Continue only if the reagent is known
@@ -266,20 +156,22 @@
 	end
 end
 
+-- Button hovering (left)
 function A.btnLeft(btn)
 	btn.textureHighlight:Hide()
 	A.tooltipRecipe:Hide()
 end -- function
 
+-- Show counts on buttons
 function A.updateCounts(recipeIndex)
 	-- Needs an argument
 	if not recipeIndex then return end
 
 	-- Do not manage guild tradeskill
 	if IsTradeSkillGuild() or IsTradeSkillLinked() then
-		for reagentRecipeIndex = 1,GetTradeSkillNumReagents(recipeIndex) do
+		for reagentIndexInRecipe = 1,GetTradeSkillNumReagents(recipeIndex) do
 			-- If the normal tradeskill hasn't been opened yet, the field 'label' doesn't exists yet
-			local label = _G["TradeSkillReagent"..reagentRecipeIndex].label
+			local label = _G["TradeSkillReagent"..reagentIndexInRecipe].label
 			if label then
 				label:Hide()
 			end
@@ -288,33 +180,31 @@
 	end
 
 	-- Count makable items and show it
-	for reagentRecipeIndex = 1,GetTradeSkillNumReagents(recipeIndex) do
+	for reagentIndexInRecipe = 1,GetTradeSkillNumReagents(recipeIndex) do
 		-- ID of the reagent we want to craft
-		local reagentLink = GetTradeSkillReagentItemLink(recipeIndex, reagentRecipeIndex)
-		local reagentID = A.link2ID(reagentLink)
+		local reagentID = A.link2ID(GetTradeSkillReagentItemLink(recipeIndex, reagentIndexInRecipe))
 
-		local label = _G["TradeSkillReagent"..reagentRecipeIndex].label
-		if not label then break end -- Shouldn't happen...
-
-		-- Continue only if the reagent is known
-		if not reagentID or not A.data[reagentID] then
-			label:Hide()
-		else
-			-- Count and show
-			local numMakable = A.numMakable(reagentID)
-			--if not numMakable or #(A.data[reagentID]) ~= 1 or A.data[reagentID][1].macro then
-			if not numMakable then
-				label:SetText("?")
-				label:SetTextColor(0, 0.5, 1, 1) -- blue
+		local label = _G["TradeSkillReagent"..reagentIndexInRecipe].label
+		if label then
+			-- Continue only if the reagent is known
+			if not reagentID or not A.data[reagentID] then
+				label:Hide()
 			else
-				label:SetText(numMakable)
-				if numMakable==0 then
-					label:SetTextColor(1, 0, 0, 1) -- red
+				-- Count and show
+				local numMakable = A.numMakable(reagentID)
+				if not numMakable then
+					label:SetText("?")
+					label:SetTextColor(0, 0.5, 1, 1) -- blue
 				else
-					label:SetTextColor(0, 1, 0, 1) -- green
-				end
+					label:SetText(numMakable)
+					if numMakable==0 then
+						label:SetTextColor(1, 0, 0, 1) -- red
+					else
+						label:SetTextColor(0, 1, 0, 1) -- green
+					end
+				end -- if
+				label:Show()
 			end -- if
-			label:Show()
 		end -- if
 	end -- for
 end -- function
--- a/ReagentMaker.toc	Thu Jun 02 18:23:42 2011 +0200
+++ b/ReagentMaker.toc	Thu Jun 02 23:07:23 2011 +0200
@@ -11,8 +11,10 @@
 MillingData.lua
 
 CampFireButton.lua
+
 utils.lua
 data.lua
+Craft.lua
 SecureMenu.lua
 ReagentMaker.lua
 
--- a/SecureMenu.lua	Thu Jun 02 18:23:42 2011 +0200
+++ b/SecureMenu.lua	Thu Jun 02 23:07:23 2011 +0200
@@ -1,5 +1,11 @@
 local addonName, A = ...
 
+-- Lua functions
+local ipairs = ipairs
+local type = type
+
+-- Wow variables
+
 -- Create the menu frame
 local MenuFrame = CreateFrame("Frame","ReagentMaker_ExternalFrame",UIParent)
 MenuFrame:Hide()
@@ -56,8 +62,8 @@
 -- Hide frame when selecting a recipe which doesn't need this reagent
 hooksecurefunc("SelectTradeSkill",function()
 	local selectedIndex = GetTradeSkillSelectionIndex()
-	for reagentRecipeIndex = 1,GetTradeSkillNumReagents(selectedIndex) do
-		local reagentID = A.link2ID(GetTradeSkillReagentItemLink(selectedIndex, reagentRecipeIndex))
+	for reagentIndexInRecipe = 1,GetTradeSkillNumReagents(selectedIndex) do
+		local reagentID = A.link2ID(GetTradeSkillReagentItemLink(selectedIndex, reagentIndexInRecipe))
 		if reagentID == MenuFrame.itemID or (MenuFrame.superItemID and reagentID == MenuFrame.superItemID) then
 			return
 		end
@@ -204,30 +210,14 @@
 end
 
 local function CraftFromExternal(btn)
-	local chooseNumber = IsShiftKeyDown()
-	local numReagentMakable = A.numRecipeMakable(btn.reagentID,btn.reagentsForOneRecipe)
-	local reagentIndex = A.findExactSkillIndex(btn.itemID,btn.itemNameString)
+	local chooseNumberToCraft = IsShiftKeyDown()
 
-	-- to define
-	local numToMake = 1
-
-	-- Choose number or craft directly
-	if chooseNumber then
-		-- Store info to be able to run the function later
-		btn.ReagentMaker_reagentIndex = reagentIndex
-
-		-- Open dialog
-		OpenStackSplitFrame(numReagentMakable, btn, "BOTTOMRIGHT", "TOP")
-
-		-- Fill in the number to make
-		numToMake = tostring(numToMake)
-		for i = 1,numToMake:len() do
-			StackSplitFrame_OnChar(StackSplitFrame,numToMake:gsub(i,i))
-		end
-		StackSplitFrame.typing = 0 -- reinit the frame so that the entered value will be erased on text entry
-	else
-		DoTradeSkill(reagentIndex,numToMake)
-	end -- if
+	A.CraftItemWithRecipe(	GetTradeSkillSelectionIndex(),
+									btn.itemID,
+									btn.reagent,
+									MenuFrame.reagentIndexInRecipe,
+									IsShiftKeyDown(),
+									btn)
 end
 
 -- Update counts and button actions
@@ -346,6 +336,7 @@
 
 	-- Save params
 	btn.itemID = itemID
+	btn.reagent = reagent
 	btn.reagentID = reagent[1]
 	btn.reagentLink = link
 	btn.reagentsForOneRecipe = reagent[2]
@@ -368,11 +359,11 @@
 	MenuFrame:SetScript("OnUpdate",nil)
 
 	-- reopen
-	A.externalCraftWindow(MenuFrame.itemID,MenuFrame.superItemID)
+	A.externalCraftWindow(MenuFrame.itemID,MenuFrame.reagentIndexInRecipe,MenuFrame.superItemID)
 end
 
 -- Fill the window and open it
-function A.externalCraftWindow(itemID,superItemID)
+function A.externalCraftWindow(itemID,reagentIndexInRecipe,superItemID)
 	-- Do not open during combat
 	if InCombatLockdown() then
 		A.Error(SPELL_FAILED_AFFECTING_COMBAT)
@@ -400,6 +391,7 @@
 	MenuFrame.reagentLink = link
 	MenuFrame.spellLink = A.isRecipeUnique(A.data[itemID]) and A.data[itemID][1].spellLink
 	MenuFrame.itemID = itemID
+	MenuFrame.reagentIndexInRecipe = reagentIndexInRecipe
 	MenuFrame.superItemID = superItemID -- optional, will be nil if not set
 
 	-- Loop over the available recipes
--- a/utils.lua	Thu Jun 02 18:23:42 2011 +0200
+++ b/utils.lua	Thu Jun 02 23:07:23 2011 +0200
@@ -1,17 +1,21 @@
 local addonName, A = ...
 
+-- GLOBALS: UIErrorsFrame, UIErrorsFrame_OnEvent
+
 -- Lua functions
 local tonumber = tonumber
 local select = select
 local sfind = string.find
+local min = math.min
+local floor = math.floor
+local pairs = pairs
+local ipairs = ipairs
+local wipe = wipe
+local tinsert = tinsert
+local tremove = tremove
 
 -- Wow functions
-
--- DEBUG Print
-function A.DEBUG(msg)
-	-- GLOBALS: DEFAULT_CHAT_FRAME
-	DEFAULT_CHAT_FRAME:AddMessage(msg or "nil",1,0,0)
-end -- function
+local GetItemCount = GetItemCount
 
 -- Messages to the user
 function A.Warn(msg)
@@ -41,21 +45,23 @@
 	local GetTradeSkillInfo = GetTradeSkillInfo
 	local GetNumTradeSkills = GetNumTradeSkills
 	local GetTradeSkillItemLink = GetTradeSkillItemLink
+	local GetTradeSkillRecipeLink = GetTradeSkillRecipeLink
 
 	function A.numRecipeMakable(reagentIDIfUnique,reagents)
+		local itemCount
 		if reagentIDIfUnique then -- only one reagent
 			itemCount = GetItemCount(reagentIDIfUnique)
 			if not itemCount then return end
-			return math.floor(itemCount/reagents)
+			return floor(itemCount/reagents)
 		else -- many reagents
 			local m
 			for _,reagent in pairs(reagents) do
 				itemCount = GetItemCount(reagent[1])
 				if not itemCount then return end
 				if not m then
-					m = math.floor(itemCount/reagent[2])
+					m = floor(itemCount/reagent[2])
 				else
-					m = math.min(m,math.floor(itemCount/reagent[2]))
+					m = min(m,floor(itemCount/reagent[2]))
 				end
 				if m==0 then break end
 			end
@@ -63,7 +69,7 @@
 		end -- if
 	end
 
-	-- Gives the number of craftable objects
+	-- Gives the total number of craftable objects
 	function A.numMakable(reagentID)
 		-- No recipe
 		if not A.data[reagentID] then return 0 end
@@ -88,11 +94,10 @@
 	end -- function
 
 	-- Find the exact tradeskill index of the recipe to make an item
-	function A.findExactSkillIndex(itemID,recipeName)
-		if not itemID or not recipeName then return end
+	function A.findExactSkillIndex(itemID,recipeLink)
+		if not itemID or not recipeLink then return end
 		for i = 1,GetNumTradeSkills() do
-			local skillName, skillType = GetTradeSkillInfo(i)
-			if skillType ~= "header" and skillName==recipeName and A.link2ID(GetTradeSkillItemLink(i)) == itemID then
+			if select(2,GetTradeSkillInfo(i)) ~= "header" and GetTradeSkillRecipeLink(i)==recipeLink and A.link2ID(GetTradeSkillItemLink(i)) == itemID then
 				return i
 			end -- if
 		end -- for
@@ -119,6 +124,10 @@
 	function A.SaveActiveFilters(headerName)
 		A.blockScan = true
 
+		-- Save position
+		filtersState.positionOffset = FauxScrollFrame_GetOffset(TradeSkillListScrollFrame)
+		filtersState.positionValue = TradeSkillListScrollFrameScrollBar:GetValue()
+
 		-- Save filters
 		filtersState.text = GetTradeSkillItemNameFilter()
 		filtersState.minLevel, filtersState.maxLevel = GetTradeSkillItemLevelFilter()
@@ -143,9 +152,9 @@
 			if (skillType == "header") and skillName==headerName then
 				if not isExpanded then
 					ExpandTradeSkillSubClass(i)
-					table.insert(headersState,true)
+					tinsert(headersState,true)
 				else
-					table.insert(headersState,false)
+					tinsert(headersState,false)
 				end
 			end
 		end
@@ -164,7 +173,7 @@
 		-- restore headers
 		for i = GetNumTradeSkills(), 1, -1 do
 			local skillName, skillType  = GetTradeSkillInfo(i)
-			if (skillType == "header") and skillName==headersState.headerName and table.remove(headersState,1) then
+			if (skillType == "header") and skillName==headersState.headerName and tremove(headersState,1) then
 					CollapseTradeSkillSubClass(i)
 			end
 		end
@@ -179,6 +188,10 @@
 		TradeSkillFrame.filterTbl.slotValue = filtersState.slotValue
 		ApplyFilters()
 
+		-- Re set position
+		FauxScrollFrame_SetOffset(TradeSkillListScrollFrame,filtersState.positionOffset)
+		TradeSkillListScrollFrameScrollBar:SetValue(filtersState.positionValue)
+
 		stateSaved = nil
 		A.blockScan = nil