changeset 3:ed0582126cae

The base features semms to work.
author contrebasse
date Sat, 02 Apr 2011 01:49:39 +0200
parents 04c5b817eead
children 3a1997d21468
files ReagentMaker.lua SecureMenu.lua data.lua utils.lua
diffstat 4 files changed, 520 insertions(+), 301 deletions(-) [+]
line wrap: on
line diff
--- a/ReagentMaker.lua	Tue Mar 29 22:06:36 2011 +0200
+++ b/ReagentMaker.lua	Sat Apr 02 01:49:39 2011 +0200
@@ -27,196 +27,190 @@
 ---------------------------------------------------
 A.EventsFrame = CreateFrame("Frame")
 
-A.EventsFrame:SetScript("OnEvent", function(self, event)
-	if event == "TRADE_SKILL_SHOW" then
+A.EventsFrame:SetScript("OnEvent", function(self, event, ...)
+	if event == "TRADE_SKILL_UPDATE" then
+		-- Scan availabe recipes
+		A:ScanSimpleRecipes()
+
+		-- Close the external window if the tradeskill changed
+		if A.currentTradeSkill ~= GetTradeSkillLine() then
+			A.MenuFrame:Hide()
+		end
+	elseif event == "TRADE_SKILL_SHOW" then
 		A:Initialize()
 	end -- if
-end); -- function
+end) -- function
 A.EventsFrame:RegisterEvent("TRADE_SKILL_SHOW")
-
+A.EventsFrame:RegisterEvent("TRADE_SKILL_UPDATE")
 
 ---------------------------------------------------
 -- Initialize
 ---------------------------------------------------
 local toInitialize = true
 function A:Initialize()
-	-- This part is done the first time only
-	if toInitialize then
+	A.EventsFrame:UnregisterEvent("TRADE_SKILL_SHOW")
 
-		-- Register clics on reagent's buttons
-		for i=1,7 do
-			local button = _G["TradeSkillReagent"..i];
-			button:HookScript("OnClick", function(btn) A.ToggleMenu(btn, i) end);
-			--button:HookScript("OnEnter", function() self:Entered(button, i) end)
-			--button:HookScript("OnLeave", function() self:Left(button, i) end)
-		end -- for
+	-- Register clics on reagent's buttons
+	for i=1,7 do
+		local btn = _G["TradeSkillReagent"..i];
+		btn:HookScript("OnDoubleClick", A.ProcessReagent);
+		btn:HookScript("OnEnter", A.btnEntered)
+		btn:HookScript("OnLeave", A.btnLeft)
+		btn.SplitStack = A.SplitStack
 
-		toInitialize = nil
+		local textureHighlight = btn:CreateTexture()
+		textureHighlight:Hide()
+		textureHighlight:SetTexture("Interface\\BUTTONS\\CheckButtonHilight")
+		textureHighlight:SetBlendMode("ADD")
+		--textureHighlight:SetPoint("TOPLEFT")
+		--textureHighlight:SetSize(btn:GetHeight(),btn:GetHeight())
+		textureHighlight:SetAllPoints("TradeSkillReagent"..i.."IconTexture")
+		btn.textureHighlight = textureHighlight
+
+		local label = btn:CreateFontString(nil,"ARTWORK","GameFontHighlight")
+		label:SetSize(100,20)
+		label:SetPoint("TOPLEFT",btn,"TOPLEFT",4,-4)
+		label:SetJustifyH("LEFT")
+		label:SetJustifyV("TOP")
+		label:SetFont("Fonts\\FRIZQT__.TTF", 10, "OUTLINE")
+		btn.label = label
+	end -- for
+
+	--[[
+	A.tooltipRecipe = CreateFrame("GameTooltip", "ReagentMaker_tooltipRecipe",UIParent, "GameTooltipTemplate")
+	A.tooltipRecipe:SetFrameStrata("TOOLTIP")
+	A.tooltipRecipe:Hide()
+	A.tooltipReagent = CreateFrame("GameTooltip", "ReagentMaker_tooltipReagent",UIParent, "GameTooltipTemplate")
+	A.tooltipReagent:SetFrameStrata("TOOLTIP")
+	A.tooltipReagent:Hide()
+	--]]
+end -- function
+
+
+-- Function run avter selecting a item in the tradeskill window
+function A.ProcessReagent(btn, ...)
+	-- 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 reagentID = A.link2ID(GetTradeSkillReagentItemLink(GetTradeSkillSelectionIndex(), 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].spell then
+		local numMakable, reagentIndex = A.numMakable(reagentID)
+		if numMakable>0 then
+			A.craft(reagentID,reagentRecipeIndex,reagentIndex,numMakable,chooseNumberToCraft)
+			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[A.data[reagentID][1][1]] then
+			A.externalCraftWindow(A.data[reagentID][1][1])
+		else
+			print("Sorry, can not make reagent...")
+		end
+	else
+		A.externalCraftWindow(reagentID)
 	end -- if
-
-	-- Scan availabe recipes
-	A:ScanSimpleRecipes()
 end -- function
 
 
 ---------------------------------------------------
 -- Craft items
 ---------------------------------------------------
-do
-	-- WoW functions
-	local GetNumTradeSkills = GetNumTradeSkills
-	local GetTradeSkillInfo = GetTradeSkillInfo
-	local GetTradeSkillItemLink = GetTradeSkillItemLink
-
-
-	function A.findSkillIndex(itemID)
-		for i = 1,GetNumTradeSkills() do
-			local skillName, skillType, numAvailable, isExpanded, serviceType, numSkillUps = GetTradeSkillInfo(i)
-			if skillType == "header" then
-			else
-				if skillName then
-					local ID = A.link2ID(GetTradeSkillItemLink(i))
-					if ID and ID == itemID then
-						return i
-					end -- if
-				end -- if
-			end -- if
-		end -- for
-		A.DEBUG("Tradeskill not found")
-	end -- function
-end -- do
-
-
--- http://www.wowwiki.com/RunSlashCmd
-local _G = _G
-function RunSlashCmd(cmd)
-  local slash, rest = cmd:match("^(%S+)%s*(.-)$")
-  for name, func in pairs(SlashCmdList) do
-     local i, slashCmd = 1
-     repeat
-        slashCmd, i = _G["SLASH_"..name..i], i + 1
-        if slashCmd == slash then
-				A.DEBUG("Spell found !")
-           return true, func(rest)
-        end
-     until not slashCmd
-  end
-A.DEBUG("Spell not found :(")
+-- 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
 
-do
-	-- WoW functions
-	local DoTradeSkill = DoTradeSkill
+-- Craft the reagent of an item, given it's position in the recipe
+function A.craft(reagentID,reagentRecipeIndex,reagentIndex,numReagentMakable,chooseNumber)
+	-- Look at how many we need to make one item for the selected recipe
+	local numToMake = 1
+	local selectedIndex = GetTradeSkillSelectionIndex()
+	local skillName, skillType, numAvailable, isExpanded, serviceType, numSkillUps = GetTradeSkillInfo(selectedIndex)
+	if numAvailable==0 then
+		local reagentName, reagentTexture, reagentCount, playerReagentCount = GetTradeSkillReagentInfo(selectedIndex, reagentRecipeIndex)
+		numToMake = math.min(reagentCount - playerReagentCount,numReagentMakable)
+	end -- if
 
-	local function SplitStack(owner,split)
-		DoTradeSkill(owner.ReagentMakerIndex,tonumber(split))
+	-- Choose number or craft directly
+	if chooseNumber and numReagentMakable>1 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 tha the entered value will be erased on text entry
+	else
+		DoTradeSkill(reagentIndex,numToMake)
+	end -- if
+end -- function
+
+
+-- Button hovering
+function A.btnEntered(btn)
+	-- 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 reagentLink = GetTradeSkillReagentItemLink(GetTradeSkillSelectionIndex(), reagentRecipeIndex)
+	local reagentID = A.link2ID(reagentLink)
+
+	-- Continue only if the reagent is known
+	if not reagentID or not A.data[reagentID] then return end
+
+	btn.textureHighlight:Show()
+
+	if #(A.data[reagentID]) == 1 and not A.data[reagentID].spell then
+		local numMakable = A.numMakable(reagentID)
+		btn.label:SetText(numMakable)
+		if numMakable==0 then
+			btn.label:SetTextColor(1, 0, 0, 1)
+		else
+			btn.label:SetTextColor(0, 5, 0, 1)
+		end
+		btn.label:Show()
 	end
 
-	function A.craft(btn,...)
-		local index = A.findSkillIndex(btn:GetAttribute(target-item))
-		if index then
-			local skillName, skillType, numAvailable, isExpanded, serviceType, numSkillUps = GetTradeSkillInfo(index)
-			if IsShiftKeyDown() and numAvailable>1 and not spell then
-				if not btn.SplitStack then
-					btn.SplitStack = SplitStack
-				end
-				btn.ReagentMakerIndex = index
-
-				OpenStackSplitFrame(numAvailable, btn, "TOPLEFT", "TOPRIGHT")
-			else
-				DoTradeSkill(index,1)
-			end
-		end
-	end -- function
-end -- do
-
-
----------------------------------------------------
--- Manage the popup menu
----------------------------------------------------
-do
-	-- Lua functions
-	local tonumber = tonumber
-
-	-- WoW functions
-	local IsModifierKeyDown = IsModifierKeyDown
-	local GetTradeSkillReagentItemLink = GetTradeSkillReagentItemLink
-	local GetTradeSkillSelectionIndex = GetTradeSkillSelectionIndex
-
-	-- Toggles the reagent's menu
-	function A.ToggleMenu(button, index)
-		-- We want no modifiers
-		if IsModifierKeyDown() then return end
-
-		if A.menuIsOpen(button) then
-			A.menuClose()
-		else
-			local itemID = A.link2ID(GetTradeSkillReagentItemLink(GetTradeSkillSelectionIndex(), index))
-			if itemID and A.data[itemID] then
-				A.FillMenu(itemID)
-				A.menuOpen(button)
-			else
-				if A.menuIsOpen() then
-					A.menuClose()
-				end
-			end -- if
-		end -- if
-	end -- function
-end -- do
-
-do
-	-- Lua functions
-	local type = type
-
-	-- Wow functions
-	local GetItemInfo = GetItemInfo
-
-	-- persistent vars
-	local Minfo = {notCheckable = true, func = A.craft}
-
-	-- function to define the menu items
-	function A.FillMenu(itemID)
-		-- Title
-		--local itemName, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, vendorPrice = GetItemInfo(itemID)
-		--Tinfo.text = "Make "..link.." with:"
-
-		-- Loop over the available reciepes
-		for spellID,reagents in pairs(A.data[itemID]) do
-			--name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, vendorPrice = GetItemInfo(itemID) or GetItemInfo("itemName") or GetItemInfo("itemLink")
-			-- Right now, manage only sub-recipes with only one reagent
-			if type(reagents[1]) == "number" then
-				local name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, vendorPrice = GetItemInfo(reagents[1])
-
-				if link then
-					local text = reagents[2].."x |T"..texture..":0|t "..link
-
-					if reagents.spell then
-						-- Special spell
-						--	Minfo.func = RunMacroText
-						--	Minfo.arg1 = reagents.spell..itemName
-						--	Minfo.arg2 = nil
-						A.menuAddItem(text,action,itemID)
-					else
-						-- Standard tradeskill spell
-						A.menuAddItem(text,A.craft,itemID)
-					end -- if
-				else
-					A.DEBUG("Erreur, objet inconnu :"..reagents[1])
-				end -- if
-			end -- if
-		end -- for
-	end -- function
-end -- do
-
-
----------------------------------------------------
--- Manage the tradeskill window state
----------------------------------------------------
-function A:SaveState()
+	--[[
+	A.tooltipRecipe:SetOwner(btn)
+	A.tooltipRecipe:SetHyperlink(GetTradeSkillRecipeLink(A.findSkillIndex(reagentID)))
+	A.tooltipRecipe:SetHyperlink(reagentLink)
+	A.tooltipRecipe:ClearAllPoints()
+	A.tooltipRecipe:SetPoint("TOPLEFT",btn,"TOPRIGHT")
+	--A.tooltipRecipe:SetSize(100,100)
+	A.tooltipRecipe:Show()
+	A.tooltipReagent:SetOwner(btn)
+	A.tooltipReagent:SetHyperlink(reagentLink)
+	A.tooltipReagent:ClearAllPoints()
+	A.tooltipReagent:SetPoint("TOPLEFT",A.tooltipRecipe,"TOPRIGHT")
+	A.tooltipReagent:Show()
+	--]]
 end
 
-function A:SetState(state)
+function A.btnLeft(btn)
+	btn.textureHighlight:Hide()
+	btn.label:Hide()
+	--[[
+	A.tooltipRecipe:Hide()
+	A.tooltipReagent:Hide()
+	--]]
 end
-
-function A:ExpandAll()
-end
--- a/SecureMenu.lua	Tue Mar 29 22:06:36 2011 +0200
+++ b/SecureMenu.lua	Sat Apr 02 01:49:39 2011 +0200
@@ -1,102 +1,285 @@
 local addonName, A = ...
 
 -- Create the menu frame
-local MenuFrame = CreateFrame("Frame",nil,UIParent) --, "ReagentMakerDropDownMenu"); -- Needs a global name
+local MenuFrame = CreateFrame("Frame","ReagentMaker_ExternalFrame",UIParent) --, "ReagentMakerDropDownMenu"); -- Needs a global name ?
 MenuFrame:Hide()
-MenuFrame:SetBackdrop({
-	bgFile = "Interface\\DialogFrame\\UI-DialogBox-Gold-Background",  -- path to the background texture
-	edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Gold-Border",    -- path to the border texture
-	tile = true,      -- true to repeat the background texture to fill the frame, false to scale it
-	tileSize = 32,    -- size (width or height) of the square repeating background tiles (in pixels)
-	edgeSize = 32,    -- thickness of edge segments and square size of edge corners (in pixels)
-	insets = {        -- distance from the edges of the frame to those of the background texture (in pixels)
-		left = 11,
-		right = 12,
-		top = 12,
-		bottom = 11
-	}
-})
-MenuFrame:SetWidth(170)
+MenuFrame:SetSize(192,256)
 MenuFrame:SetFrameStrata("DIALOG")
+MenuFrame:EnableMouse(true)
+MenuFrame:SetPoint("CENTER")
+tinsert(UISpecialFrames,"ReagentMaker_ExternalFrame") -- make it closable with escape
 
-local MENU_ENTRY_HEIGHT = 12
+MenuFrame:SetScript("OnEvent",function(self,event,...)
+	if event == "TRADE_SKILL_CLOSE" or event == "PLAYER_REGEN_DISABLED" then
+		MenuFrame:Hide()
+	end
+end)
+MenuFrame:RegisterEvent("TRADE_SKILL_CLOSE")
+MenuFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
+A.MenuFrame = MenuFrame
+
+-- Background adaptable vertically
+local bg_top = MenuFrame:CreateTexture(nil,"BACKGROUND",nil,0)
+bg_top:SetTexture("Interface\\LootFrame\\UI-LootPanel")
+bg_top:SetSize(192,80)
+bg_top:SetPoint("TOP")
+bg_top:SetTexCoord(0,192/256,0,80/256)
+local bg_bot = MenuFrame:CreateTexture(nil,"BACKGROUND",nil,0)
+bg_bot:SetTexture("Interface\\LootFrame\\UI-LootPanel")
+bg_bot:SetSize(192,16)
+bg_bot:SetPoint("BOTTOM")
+bg_bot:SetTexCoord(0,192/256,240/256,1)
+local bg_mid = MenuFrame:CreateTexture(nil,"BACKGROUND",nil,0)
+bg_mid:SetTexture("Interface\\LootFrame\\UI-LootPanel")
+bg_mid:SetWidth(192)
+bg_mid:SetPoint("TOP",bg_top,"BOTTOM")
+bg_mid:SetPoint("BOTTOM",bg_bot,"TOP")
+bg_mid:SetTexCoord(0,192/256,80/256,240/256)
+
+-- Bouton de fermeture
+local CloseButton = CreateFrame("Button",nil,MenuFrame,"UIPanelCloseButton");
+CloseButton:SetPoint("TOPRIGHT",0,-10)
+
+-- Main icon
+local itemIcon = MenuFrame:CreateTexture(nil,"BACKGROUND",nil,-1)
+itemIcon:SetSize(64,64)
+itemIcon:SetPoint("TOPLEFT",8,-4)
+
+-- Title
+local TitleText = MenuFrame:CreateFontString(nil,"ARTWORK","GameFontHighlight")
+TitleText:SetSize(92,14)
+TitleText:SetPoint("RIGHT",CloseButton,"LEFT",4,1)
+
+local MENU_ENTRY_HEIGHT = 41
+local MENU_ENTRY_WIDTH = 147
+local MENU_ENTRY_ICON_RATIO = 40/48
 
 local numActiveEntries = 0
 local menuEntries = {}
-local parentBtn -- The button it's associated with
 
-function A.menuIsOpen(btn)
-	if btn then
-		return MenuFrame:IsShown() and (btn==parentBtn)
+-- Button hovering
+local function btnEntered(self)
+	--[[
+	-- Index of the reagent in the recipe, taken from the button name
+	local reagentRecipeIndex = A.buttonNumber(self)
+
+	-- ID of the reagent we want to craft
+	local reagentID = A.link2ID(GetTradeSkillReagentItemLink(GetTradeSkillSelectionIndex(), reagentRecipeIndex))
+
+	-- Continue only if the reagent is known
+	if not reagentID or not A.data[reagentID] then return end
+	--]]
+
+	self.textureHighlight:Show()
+
+	--[[
+	if #(A.data[reagentID]) == 1 and not A.data[reagentID].spell then
+		local numMakable = A.numMakable(reagentID)
+		self.itemName:SetText(numMakable)
+		if numMakable==0 then
+			self.itemName:SetTextColor(1, 0, 0, 1)
+		else
+			self.itemName:SetTextColor(0, 5, 0, 1)
+		end
+		self.itemName:Show()
+	end
+	--]]
+end
+local function btnLeft(self)
+	self.textureHighlight:Hide()
+	--self.itemName:Hide()
+end
+local function createMenuEntry()
+	--local btn = CreateFrame("Button", "ReagentMakerMenuButton"..(#menuEntries+1), MenuFrame, "SecureActionButtonTemplate")
+	local btn = CreateFrame("Button", nil, MenuFrame, "SecureActionButtonTemplate")
+	table.insert(menuEntries,btn)
+
+	btn:Hide()
+	btn:SetSize(MENU_ENTRY_WIDTH,MENU_ENTRY_HEIGHT)
+	btn:SetFrameStrata("DIALOG")
+
+	-- Set its position
+	if #menuEntries>1 then
+		btn:SetPoint("TOP",menuEntries[#menuEntries-1],"BOTTOM",0,-2)
 	else
-		return MenuFrame:IsShown()
+		btn:SetPoint("TOPLEFT",MenuFrame,"TOPLEFT",24,-79)
 	end
+
+	local icon = btn:CreateTexture(nil,"BACKGROUND")
+	icon:SetPoint("TOPLEFT")
+	icon:SetSize(39,39)
+	btn.icon = icon
+
+	local itemNameBG = btn:CreateTexture(nil,"BACKGROUND")
+	itemNameBG:SetTexture("Interface\\QuestFrame\\UI-QuestItemNameFrame")
+	itemNameBG:SetSize(128,64)
+	itemNameBG:SetPoint("LEFT",icon,"RIGHT",-10,0)
+
+	local itemName = btn:CreateFontString(nil,"BACKGROUND","GameFontHighlight")
+	itemName:SetSize(90,36)
+	itemName:SetPoint("LEFT",itemNameBG,"LEFT",15,0)
+	itemName:SetJustifyH("LEFT")
+	itemName:SetWordWrap(true)
+	itemName:SetNonSpaceWrap(false)
+	btn.itemName = itemName
+
+	local textureHighlight = btn:CreateTexture(nil,"BORDER")
+	textureHighlight:Hide()
+	textureHighlight:SetTexture("Interface\\BUTTONS\\CheckButtonHilight")
+	textureHighlight:SetBlendMode("ADD")
+	textureHighlight:SetAllPoints(icon)
+	--textureHighlight:SetSize(MENU_ENTRY_HEIGHT,MENU_ENTRY_HEIGHT)
+	btn.textureHighlight = textureHighlight
+	btn:HookScript("OnEnter", btnEntered)
+	btn:HookScript("OnLeave", btnLeft)
+
+	local countTotal = btn:CreateFontString(nil,"ARTWORK","NumberFontNormal")
+	--countTotal:SetSize(MENU_ENTRY_HEIGHT,MENU_ENTRY_HEIGHT/2)
+	countTotal:SetPoint("TOPLEFT",icon,"TOPLEFT",1,-1)
+	countTotal:SetJustifyH("LEFT")
+	countTotal:SetJustifyV("TOP")
+	--countTotal:SetFont("Fonts\\FRIZQT__.TTF", 10, "OUTLINE")
+	btn.countTotal = countTotal
+
+	local countDetail = btn:CreateFontString(nil,"ARTWORK","NumberFontNormal")
+	--countDetail:SetSize(MENU_ENTRY_HEIGHT,MENU_ENTRY_HEIGHT/2)
+	countDetail:SetPoint("BOTTOMRIGHT",icon,"BOTTOMRIGHT",-1,1)
+	countDetail:SetJustifyH("RIGHT")
+	countDetail:SetJustifyV("BOTTOM")
+	--countDetail:SetFont("Fonts\\FRIZQT__.TTF", 12, "OUTLINE")
+	btn.countDetail = countDetail
+	return btn
 end
-function A.menuOpen(parent)
-	A.DEBUG("menuOpen")
-	--if not InCombatLockDown() and numActiveEntries>0 then
-		parentBtn = parent
-		MenuFrame:ClearAllPoints()
-		MenuFrame:SetPoint("LEFT",parent,"RIGHT",0,0)
 
-		MenuFrame:SetHeight(240)
-		--MenuFrame:SetParent(UIParent)
 
-		MenuFrame:Show()
-	--end
+local function menuCraftItem()
+	action(itemID,reagentIndex,IsShiftKeyDown())
 end
-function A.menuClose()
-	MenuFrame:Hide()
-	MenuFrame:ClearAllPoints()
 
-	parentBtn = nil
+local function updateCounts()
+	local anyMakable
 	for i=1,numActiveEntries do
-		menuEntries[i]:Hide()
+		btn = menuEntries[i]
+		local itemCount = GetItemCount(btn.reagentID)
+
+		local numMakable = math.floor(itemCount/(btn.reagentsForOneRecipe or 1))
+		btn.countTotal:SetText(numMakable)
+		btn.countDetail:SetText(itemCount.."/"..(btn.reagentsForOneRecipe or 1))
+
+		if numMakable>0 then
+			anyMakable = true
+			btn.countTotal:SetTextColor(0, 5, 0, 1)
+			btn:Enable()
+			--btn.icon:SetDesaturated(false)
+			btn.icon:SetVertexColor(1,1,1);
+			btn.itemName:SetTextColor(1,1,1,1)
+		else
+			btn.countTotal:SetTextColor(1, 0, 0, 1)
+			btn:Disable()
+			--btn.icon:SetDesaturated(true)
+			btn.icon:SetVertexColor(0.5, 0.5, 0.5)
+			btn.itemName:SetTextColor(1,1,1,0.5)
+		end
+	end
+
+	if anyMakable then
+		itemIcon:SetVertexColor(1,1,1)
+		TitleText:SetTextColor(1,1,1,1)
+	else
+		itemIcon:SetVertexColor(0.5, 0.5, 0.5)
+		TitleText:SetTextColor(1,1,1,0.5)
 	end
 end
 
-function A.menuAddItem(text,action,itemID)
-	A.DEBUG("menuAddItem "..text)
+local function menuAddItem(action,itemID,reagentID,reagentsForOneRecipe)
 	local btn
 	-- Create a button only if necessary
 	if numActiveEntries >= #menuEntries then
-		btn = CreateFrame("Button", "ReagentMakerMenuButton"..(#menuEntries+1), MenuFrame) --, "SecureActionButtonTemplate")
-		table.insert(menuEntries,btn)
-
-		btn:SetHeight(MENU_ENTRY_HEIGHT)
-		btn:SetWidth(160)
-
-		-- Set its position
-		if #menuEntries==0 then
-			btn:SetPoint("TOPLEFT",MenuFrame,"TOPLEFT",0,0)
-		else
-			btn:SetPoint("TOPLEFT",menuEntries[#menuEntries-1],"BOTTOMLEFT",0,0)
-		end
+		btn = createMenuEntry()
 	else
 		btn = menuEntries[numActiveEntries+1]
 	end
 
-	-- Set its text
-	btn:SetText(text or "???")
+	-- Set text and icon
+	local name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, vendorPrice = GetItemInfo(reagentID)
+	if name then
+		btn.itemName:SetText(name)
+	else
+		A.DEBUG("No item name : "..reagentID)
+	end
 
-	-- Set its action
+	if texture then
+		btn.icon:SetTexture(texture)
+	else
+		A.DEBUG("No item texture : "..reagentID)
+	end
+
+	-- Save params
+	btn.itemID = itemID
+	btn.reagentID = reagentID
+	btn.reagentsForOneRecipe = reagentsForOneRecipe
+
+	-- Set action
 	if type(action)=="function" then
-		btn:SetScript("OnClick",action)
+		btn:SetScript("PreClick",action)
 		btn:SetAttribute("type", nil)
 		btn:SetAttribute("spell", nil)
-		btn:SetAttribute("target-item",itemID)
-	elseif type(action)=="string" then
-		btn:SetScript("OnClick",nil)
-		btn:SetAttribute("type", "spell")
-		btn:SetAttribute("spell", action)
-		btn:SetAttribute("target-item",GetItemInfo(itemID))
+		btn:SetAttribute("target-item",nil)
+	else --if type(action)=="string" then
+		btn:SetScript("PreClick",nil)
+		btn:SetAttribute("type", "macro")
+		btn:SetAttribute("macrotext", action..GetItemInfo(reagentID))
+		--btn:SetAttribute("type", "macro")
+		--btn:SetAttribute("macrotext1","/cast Mouture\n/use Gangrelette")
 	end -- if
 
 	-- Reposition MenuFrame
 	--MenuFrame:SetPoint("BOTTOMRIGHT",btn,"BOTTOMRIGHT",0,0)
-	MenuFrame:SetHeight((numActiveEntries+1)*MENU_ENTRY_HEIGHT)
+	--MenuFrame:SetHeight((numActiveEntries+1)*MENU_ENTRY_HEIGHT)
+
+	btn:Show()
 
 	-- Increase the entry number
 	numActiveEntries = numActiveEntries + 1
-	A.DEBUG("Item added ")
 end -- function
+
+-- Fill the window and open it
+function A.externalCraftWindow(itemID)
+	-- Do not open during combat
+	if InCombatLockdown() then return end
+
+	-- Save the tradeskill
+	A.currentTradeSkill = GetTradeSkillLine()
+
+	-- Close the previous menu
+	MenuFrame:Hide()
+	for i=1,numActiveEntries do
+		menuEntries[i]:Hide()
+	end
+	numActiveEntries = 0
+
+	-- Fill the info of the reagent to make
+	local name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, vendorPrice = GetItemInfo(itemID)
+	SetPortraitToTexture(itemIcon, texture)
+	TitleText:SetText(name)
+
+	-- Loop over the available recipes
+	for _,reagent in ipairs(A.data[itemID]) do
+			if A.data[itemID].spell then
+				-- Special spell
+				menuAddItem(A.data[itemID].spell,itemID,reagent[1],reagent[2])
+			else
+				-- Standard tradeskill spell UNTESTED
+				menuAddItem(A.craft,itemID,reagent[1],reagent[2])
+			end -- if
+	end -- for
+
+	MenuFrame:SetHeight(89 + numActiveEntries*(MENU_ENTRY_HEIGHT+2))
+
+	MenuFrame:ClearAllPoints()
+	MenuFrame:SetPoint("TOPLEFT",TradeSkillFrame,"TOPRIGHT",-2,14)
+
+	updateCounts()
+
+	MenuFrame:Show()
+end
--- a/data.lua	Tue Mar 29 22:06:36 2011 +0200
+++ b/data.lua	Sat Apr 02 01:49:39 2011 +0200
@@ -1,71 +1,89 @@
 local addonName, A = ...
 
-
-A.Mill = "/use "..GetSpellInfo(51005)
+MillID = 51005
+macroMill = "/cast "..GetSpellInfo(51005).."\n/use "
 A.data = {
 	-- Mill
 	[39151] = { -- Alabaster Pigment
-		{2447,5, spell = A.Mill}, -- Peacebloom
-		{765,5, spell = A.Mill},  -- Silverleaf
-		{2449,5, spell = A.Mill}}, -- Earthroot
+		spell = macroMill,
+		spellID = MillID,
+		{2447,5}, -- Peacebloom
+		{765,5},  -- Silverleaf
+		{2449,5}}, -- Earthroot
 	[39224] = { -- Dusky Pigment
-		{785,5, spell = A.Mill},  -- Mageroyal
-		{2450,5, spell = A.Mill}, -- Briarthorn
-		{2452,5, spell = A.Mill}, -- Swiftthistle
-		{2453,5, spell = A.Mill}, -- Bruiseweed
-		{3820,5, spell = A.Mill}}, -- Stranglekelp
+		spell = macroMill,
+		spellID = MillID,
+		{785,5},  -- Mageroyal
+		{2450,5}, -- Briarthorn
+		{2452,5}, -- Swiftthistle
+		{2453,5}, -- Bruiseweed
+		{3820,5}}, -- Stranglekelp
 	[39338] = { -- Golden Pigment
-		{3369,5, spell = A.Mill}, -- Grave Moss
-		{3355,5, spell = A.Mill}, -- Wild Steelbloom
-		{3356,5, spell = A.Mill}, -- Kingsblood
-		{3357,5, spell = A.Mill}}, -- Liferoot
+		spell = macroMill,
+		spellID = MillID,
+		{3369,5}, -- Grave Moss
+		{3355,5}, -- Wild Steelbloom
+		{3356,5}, -- Kingsblood
+		{3357,5}}, -- Liferoot
 	[39339] = { -- Emerald Pigment
-		{3818,5, spell = A.Mill}, -- Fadeleaf
-		{3821,5, spell = A.Mill}, -- Goldthorn
-		{3358,5, spell = A.Mill}, -- Khadgar's Whisker
-		{3819,5, spell = A.Mill}}, -- Dragon's Teeth
+		spell = macroMill,
+		spellID = MillID,
+		{3818,5}, -- Fadeleaf
+		{3821,5}, -- Goldthorn
+		{3358,5}, -- Khadgar's Whisker
+		{3819,5}}, -- Dragon's Teeth
 	[39340] = { -- Violet Pigment
-		{4625,5, spell = A.Mill}, -- Firebloom
-		{8831,5, spell = A.Mill}, -- Purple Lotus
-		{8836,5, spell = A.Mill}, -- Arthas' Tears
-		{8838,5, spell = A.Mill}, -- Sungrass
-		{8839,5, spell = A.Mill}, -- Blindweed
-		{8845,5, spell = A.Mill}, -- Ghost Mushroom
-		{8846,5, spell = A.Mill}}, -- Gromsblood
+		spell = macroMill,
+		spellID = MillID,
+		{4625,5}, -- Firebloom
+		{8831,5}, -- Purple Lotus
+		{8836,5}, -- Arthas' Tears
+		{8838,5}, -- Sungrass
+		{8839,5}, -- Blindweed
+		{8845,5}, -- Ghost Mushroom
+		{8846,5}}, -- Gromsblood
 	[39341] = { -- Silvery Pigment
-		{13464,5, spell = A.Mill}, -- Golden Sansam
-		{13463,5, spell = A.Mill}, -- Dreamfoil
-		{13465,5, spell = A.Mill}, -- Mountain Silversage
-		{13466,5, spell = A.Mill}, -- Sorrowmoss
-		{13467,5, spell = A.Mill}}, -- Icecap
+		spell = macroMill,
+		spellID = MillID,
+		{13464,5}, -- Golden Sansam
+		{13463,5}, -- Dreamfoil
+		{13465,5}, -- Mountain Silversage
+		{13466,5}, -- Sorrowmoss
+		{13467,5}}, -- Icecap
 	[39342] = { -- Nether Pigment
-		{22786,5, spell = A.Mill}, -- Dreaming Glory
-		{22785,5, spell = A.Mill}, -- Felweed
-		{22789,5, spell = A.Mill}, -- Terocone
-		{22787,5, spell = A.Mill}, -- Ragveil
-		{22790,5, spell = A.Mill}, -- Ancient Lichen
-		{22793,5, spell = A.Mill}, -- Mana Thistle
-		{22791,5, spell = A.Mill}, -- Netherbloom
-		{22792,5, spell = A.Mill}}, -- Nightmare Vine
+		spell = macroMill,
+		spellID = MillID,
+		{22786,5}, -- Dreaming Glory
+		{22785,5}, -- Felweed
+		{22789,5}, -- Terocone
+		{22787,5}, -- Ragveil
+		{22790,5}, -- Ancient Lichen
+		{22793,5}, -- Mana Thistle
+		{22791,5}, -- Netherbloom
+		{22792,5}}, -- Nightmare Vine
 	[39343] = { -- Azure Pigment
-		{37921,5, spell = A.Mill}, -- Deadnettle
-		{36901,5, spell = A.Mill}, -- Goldclover
-		{36907,5, spell = A.Mill}, -- Talandra's Rose
-		{36904,5, spell = A.Mill}, -- Tiger Lily
-		{39970,5, spell = A.Mill}, -- Fire Leaf
-		{39969,5, spell = A.Mill}, -- Fire Seed
-		{36903,5, spell = A.Mill}, -- Fire Seed
-		{36906,5, spell = A.Mill}, -- Icethorn
-		{36905,5, spell = A.Mill}}, -- Icethorn
+		spellID = MillID,
+		spell = macroMill,
+		{37921,5}, -- Deadnettle
+		{36901,5}, -- Goldclover
+		{36907,5}, -- Talandra's Rose
+		{36904,5}, -- Tiger Lily
+		{39970,5}, -- Fire Leaf
+		{39969,5}, -- Fire Seed
+		{36903,5}, -- Fire Seed
+		{36906,5}, -- Icethorn
+		{36905,5}}, -- Icethorn
 	[61979] = { -- Ashen Pigment
-		{52983,5, spell = A.Mill}, -- Cinderbloom
-		{52985,5, spell = A.Mill}, -- Azshara's Veil
-		{52984,5, spell = A.Mill}, -- Stormvine
-		{52986,5, spell = A.Mill}, -- Heartblossom
-		{52988,5, spell = A.Mill}, -- Whiptail
-		{52987,5, spell = A.Mill}}, -- Twilight Jasmine
+		spell = macroMill,
+		spellID = MillID,
+		{52983,5}, -- Cinderbloom
+		{52985,5}, -- Azshara's Veil
+		{52984,5}, -- Stormvine
+		{52986,5}, -- Heartblossom
+		{52988,5}, -- Whiptail
+		{52987,5}}, -- Twilight Jasmine
 
-	-- To add : pigments, enchants, elementals
+	-- To add : enchants, elementals, prospecting
 }
 A.data[43103] = A.data[39224] -- Verdant Pigment
 A.data[43104] = A.data[39338] -- Burnt Pigment
@@ -81,7 +99,6 @@
 -- @todo rescan when a new refipe appears (TRADESKILL_UPDATE ?)
 -- @todo be sure that skillName is unique, or use something else
 -- @todo add support for multi-reagents recipes
-local sfind = string.find
 function A:ScanSimpleRecipes()
 	for i = 1,GetNumTradeSkills() do
 		local skillName, skillType, numAvailable, isExpanded, serviceType, numSkillUps = GetTradeSkillInfo(i)
@@ -91,8 +108,7 @@
 				local itemID = A.link2ID(GetTradeSkillItemLink(i))
 
 				-- reagent ID
-				local link = GetTradeSkillReagentItemLink(i, 1)
-				local reagentID = A.link2ID(link)
+				local reagentID = A.link2ID(GetTradeSkillReagentItemLink(i, 1))
 
 				-- reagent number needed
 				local reagentName, reagentTexture, reagentCount, playerReagentCount = GetTradeSkillReagentInfo(i, 1)
@@ -103,10 +119,9 @@
 					print(itemID," ",skillName," ",reagentID," ",reagentCount)
 				end
 
-				if not A.data[itemID] or not A.data[itemID][skillName] then
-					A.data[itemID] = {[skillName] = {reagentID,reagentCount}}
+				if not A.data[itemID] then
+					A.data[itemID] = {{reagentID,reagentCount}}
 				end
-			else
 			end -- if
 		end -- if
 	end -- for
--- a/utils.lua	Tue Mar 29 22:06:36 2011 +0200
+++ b/utils.lua	Sat Apr 02 01:49:39 2011 +0200
@@ -1,21 +1,45 @@
 local addonName, A = ...
 
-do
-	-- WoW frames
-	local DEFAULT_CHAT_FRAME = DEFAULT_CHAT_FRAME
+-- DEBUG Print
+function A.DEBUG(msg)
+	DEFAULT_CHAT_FRAME:AddMessage(msg or "nil",1,0,0)
+end -- function
 
-	function A.DEBUG(msg)
-		DEFAULT_CHAT_FRAME:AddMessage(msg or "nil",1,0,0)
-	end -- function
+-- Returns the item ID from its link
+function A.link2ID(link)
+	return tonumber(select(3,string.find(link or "", "-*:(%d+)[:|].*")) or "")
+end -- function
+
+-- Returns the button number for the reagents buttons
+function A.buttonNumber(btn)
+	-- "TradeSkillReagentN"
+	return tonumber(btn:GetName():sub(-1))
 end
 
-do
-	-- Lua functions
-	local select = select
-	local tonumber = tonumber
-	local sfind = string.find
+-- Gives the number of craftable objects
+function A.numMakable(reagentID)
+	-- Look for the recipe to make the item
+	local reagentIndex = A.findSkillIndex(reagentID)
+	if not reagentIndex then return 0 end
 
-	function A.link2ID(link)
-		return tonumber(select(3,sfind(link or "", "-*:(%d+)[:|].*")) or "")
-	end -- function
-end -- do
+	-- Check how many items we can craft
+	local skillName, skillType, numReagentMakable, isExpanded, serviceType, numSkillUps = GetTradeSkillInfo(reagentIndex)
+	return numReagentMakable or 0, reagentIndex
+end
+
+-- Find the first tradeskill index of the recipe to make an item
+function A.findSkillIndex(itemID)
+	for i = 1,GetNumTradeSkills() do
+		local skillName, skillType, numAvailable, isExpanded, serviceType, numSkillUps = GetTradeSkillInfo(i)
+		if skillType == "header" then
+		else
+			if skillName then
+				local ID = A.link2ID(GetTradeSkillItemLink(i))
+				if ID and ID == itemID then
+					return i
+				end -- if
+			end -- if
+		end -- if
+	end -- for
+	A.DEBUG("Tradeskill not found for "..itemID)
+end -- function