vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
cgc = AceLibrary("AceAddon-2.0"):new("AceDB-2.0", "AceConsole-2.0", "AceDebug-2.0", "AceEvent-2.0", "AceHook-2.0")

-- Dewdrop: handles our dropdown
local dewdrop =  AceLibrary("Dewdrop-2.0")

-- Our container for all frames.
local frames = {}

-- The search type should default to "by name"
cgc:RegisterDB("cgCraftyDB")
cgc:RegisterDefaults('profile', {
    searchType = {},
        searchHistory = {},
})

function cgc:OnInitialize()
        self:SetDebugging(false)
        self:RegisterChatCommand({ "/cgcrafty", "/cgc" }, nil)
        
        -- Tradeskill frame references
        frames.trade = {}
        frames.trade.elements = {
                ["Main"]                = "TradeSkillFrame",
                ["Title"]               = "TradeSkillFrameTitleText",
                ["Scroll"]              = "TradeSkillListScrollFrame",
                ["ScrollBar"]   = "TradeSkillListScrollFrameScrollBar",
                ["Highlight"]   = "TradeSkillHighlightFrame",
                ["CollapseAll"] = "TradeSkillCollapseAllButton",
        }
        frames.trade.anchor = "TradeSkillCreateAllButton"
        frames.trade.anchor_offset_x = -9
        frames.trade.anchor_offset_y = -8
        frames.trade.update      = "TradeSkillFrame_Update"

        -- Crafting frame references
        frames.craft = {}
        frames.craft.elements = {
                ["Main"]                = "CraftFrame",
                ["Title"]               = "CraftFrameTitleText",
                ["Scroll"]              = "CraftListScrollFrame",
                ["ScrollBar"]   = "CraftListScrollFrameScrollBar",
                ["Highlight"]   = "CraftHighlightFrame",
        }
        frames.craft.anchor = "CraftCancelButton"
        frames.craft.anchor_offset_x = 6
        frames.craft.anchor_offset_y = -8
        frames.craft.update = "CraftFrame_Update"
        
        -- Setup some closures for use later.
        -- History search text setting variable get/save
        self.getHistory = function(var) return self.db.profile.searchHistory[var] end
        self.setHistory = function(val) self.db.profile.searchHistory[self.lastSearchTrade or getglobal(self.currentFrame.elements.Title):GetText()] = val end
        self.clearHistory = function() self.db.profile.searchHistory = {} end
        -- History search type setting variable get/save
        self.getSearchType = function(val) return self.db.profile.searchType[getglobal(self.currentFrame.elements.Title):GetText()] or self.LOCALS.FRAME_SEARCH_TYPES[val] end
        self.setSearchType = function(val) self.db.profile.searchType[getglobal(self.currentFrame.elements.Title):GetText()] = val end
        
        -- Search Text
        self.searchText = nil
        -- Clear our the search history
        self.clearHistory()
        -- Handler for the currently opened frame.
        self.currentFrame = nil
end

function cgc:OnEnable()
        self:Debug("OnEnable called.")
        
        -- Tradeskill window --
        self:RegisterEvent("TRADE_SKILL_SHOW")
        -- Enchanting window --
        self:RegisterEvent("CRAFT_SHOW")
        
        if not self.frame then
                self:Debug("Creating cgcFrame.")
                -- Create main frame 
                self.frame = CreateFrame("Frame", "cgcFrame", UIParent)
                self.frame:Hide()
                -- Set main frame properties
                self.frame:SetPoint("CENTER", "UIParent", "CENTER", 0, 0)
                self.frame:SetWidth(342)  
                self.frame:SetHeight(45)
                self.frame:SetFrameStrata("MEDIUM")
                self.frame:SetMovable(false)
                self.frame:EnableMouse(true)
                -- Set the main frame backdrop
                self.frame:SetBackdrop({
                                bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background", tile = true, tileSize = 32,
                                edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", edgeSize = 20,
                                insets = {left = 5, right = 6, top = 6, bottom = 5},
                })
                self.frame:SetBackdropColor(1, 1, 1, 1)
                self.frame:SetBackdropBorderColor(0, .8, 0, 1)
                self.frame:SetScript("OnShow", function() self:OnShow() end)
                
                -- Create sub-frames
                -- Editbox for search text
                self.frame.SearchBox = CreateFrame("EditBox", nil, self.frame, "InputBoxTemplate")
                self.frame.SearchBox:SetAutoFocus(false)
                self.frame.SearchBox:SetWidth(110)
                self.frame.SearchBox:SetHeight(20)
                self.frame.SearchBox:SetPoint("LEFT", self.frame, "LEFT", 20, 0)
                self.frame.SearchBox:SetBackdropColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
                self.frame.SearchBox:SetBackdropBorderColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
                self.frame.SearchBox:SetScript("OnEnterPressed", function() self:Search(this:GetText()) end)
                
                -- Reset Button
                self.frame.ResetButton = CreateFrame("Button", nil, self.frame, "GameMenuButtonTemplate")
                self.frame.ResetButton:SetWidth(20)
                self.frame.ResetButton:SetHeight(25)
                self.frame.ResetButton:SetPoint("RIGHT", self.frame, "RIGHT", -15, 0)
                self.frame.ResetButton:SetText(self.LOCALS.FRAME_RESET_TEXT)
                self.frame.ResetButton:SetScript("OnClick", function() self:Reset() end)
                
                -- Submit Button
                self.frame.SubmitButton = CreateFrame("Button", nil, self.frame, "GameMenuButtonTemplate")
                self.frame.SubmitButton:SetWidth(20)
                self.frame.SubmitButton:SetHeight(25)
                self.frame.SubmitButton:SetPoint("RIGHT", self.frame.ResetButton, "LEFT", -10, 0)
                self.frame.SubmitButton:SetText(self.LOCALS.FRAME_SUBMIT_TEXT)
                self.frame.SubmitButton:SetScript("OnClick", function() self:Search(self.frame.SearchBox:GetText()) end)
        
                -- SearchType dropdown button to show the menu when clicked.
                self.frame.SearchTypeButton = CreateFrame("Button", nil, self.frame, "GameMenuButtonTemplate")
                self.frame.SearchTypeButton:SetWidth(70)
                self.frame.SearchTypeButton:SetHeight(25)
                self.frame.SearchTypeButton:SetPoint("LEFT", self.frame.SearchBox, "RIGHT", 8, 0)
                self.frame.SearchTypeButton:SetText("Type")
                self.frame.SearchTypeButton:SetScript("OnClick", function() 
                                if ( dewdrop:IsOpen(self.frame.SearchTypeButton) ) then 
                                        dewdrop:Close() 
                                else 
                                        dewdrop:Open(self.frame.SearchTypeButton) 
                                end
                        end
                )       
                
                -- Create the SearchType dropdown menu
                dewdrop:Register(self.frame.SearchTypeButton,
                        'point', function (parent)
                                return "TOPLEFT", "BOTTOMLEFT"
                        end,
                        'dontHook', true,
                        'children', function ()
                                dewdrop:AddLine(
                                        'text', cgc.LOCALS.FRAME_SEARCH_TYPE_TITLE,
                                        'isTitle', true
                                )
                                for i,type in self.LOCALS.FRAME_SEARCH_TYPES do
                                        dewdrop:AddLine(
                                                'text', type,
                                                'func', function(val)
                                                        self.setSearchType(val)
                                                        self.frame.SearchTypeButton:SetText(val)
                                                end,
                                                'arg1', type,
                                                'tooltipTitle', type,
                                                'tooltipText', self.LOCALS.FRAME_SEARCH_TYPES_DESC[i],
                                                'closeWhenClicked', true
                                        )
                                end
                        end
                )
                
                -- Link Reagents dropdown button to show the menu when clicked.
                self.frame.LinkReagentButton = CreateFrame("Button", nil, self.frame, "GameMenuButtonTemplate")
                self.frame.LinkReagentButton:SetWidth(50)
                self.frame.LinkReagentButton:SetHeight(25)
                self.frame.LinkReagentButton:SetPoint("LEFT", self.frame.SearchTypeButton, "RIGHT", 8, 0)
                self.frame.LinkReagentButton:SetText(self.LOCALS.FRAME_LINK_REAGENTS)
                self.frame.LinkReagentButton:SetScript("OnClick", function() 
                                if ( dewdrop:IsOpen(self.frame.LinkReagentButton) ) then 
                                        dewdrop:Close() 
                                else 
                                        dewdrop:Open(self.frame.LinkReagentButton) 
                                end
                        end
                )
                
                -- Create the LinkReagents dropdown menu
                dewdrop:Register(self.frame.LinkReagentButton,
                        'point', function (parent)
                                return "TOPLEFT", "BOTTOMLEFT"
                        end,
                        'dontHook', true,
                        'children', function ()
                                dewdrop:AddLine(
                                        'text', cgc.LOCALS.FRAME_LINK_REAGENTS_TITLE,
                                        'isTitle', true
                                )
                                for i,channel in self.LOCALS.FRAME_LINK_TYPES do
                                        -- There are two types lines: 
                                        -- 1. Those that are straightforward and do not require user input
                                        -- 2. Those that require the user to input information to take it a step further.
                                        -- channel[1] is the "common name"
                                        -- channel[2] is the "channel"
                                        -- channel[3] is the "desc"
                                        if ( channel[2] ~= "WHISPER" and channel[2] ~= "CHANNEL" ) then
                                                dewdrop:AddLine(
                                                        'text', channel[1],
                                                        'func', function(val)
                                                                self:SendReagentsMessage(val, nil)
                                                        end,
                                                        'arg1', channel[2],
                                                        'closeWhenClicked', true
                                                )
                                        else
                                                dewdrop:AddLine(
                                                        'text', channel[1],
                                                        'hasArrow', true,
                                                        'hasEditBox', true,
                                                        'tooltipTitle', channel[1],
                                                        'tooltipText', channel[3],
                                                        'editBoxFunc', function(channel, text)
                                                                self:SendReagentsMessage(channel, text)
                                                        end,
                                                        'editBoxArg1', channel[2]
                                                )
                                        end
                                end
                        end
                )
        end
        
        -- If the mod was disabled when WoW loaded, then the main frame will not be visible. So we'll make it visible again.
        if ( getglobal(frames.trade.elements.Main) and getglobal(frames.trade.elements.Main):IsShown() ) then
                cgc:TRADE_SKILL_SHOW()
        elseif ( getglobal(frames.craft.elements.Main) and getglobal(frames.craft.elements.Main):IsShown() ) then
                cgc:CRAFT_SHOW()
        end
end

function cgc:OnDisable()
        if self.frame then
                self.frame:Hide()
        end
        
        -- Clear our search history and current search text.
        self.clearHistory()
        self.searchText = ""
        
        -- Finally, update the trade/craft windows just in case we have any lingering search results.
        if ( self.hooks and self.hooks[frames.trade.update] ) then
                getglobal(frames.trade.update)()
        end
        
        if ( self.hooks and self.hooks[frames.craft.update] ) then
                getglobal(frames.craft.update)()
        end
end

function cgc:OnShow()
        self:Debug("OnShow called.")
        -- Insert previous search text, if any.
        if self.searchText ~= nil then
                self.frame.SearchBox:SetText(self.searchText)
        end
        
        self:Debug("OnShowCurrentWindow:"..getglobal(self.currentFrame.elements.Title):GetText())
        -- Update the "Type" dropdown so that it reflects the current searchType or "Name" by default.
        if ( self.getSearchType() ~= nil ) then
                self.frame.SearchTypeButton:SetText(self.getSearchType())
        else
                self.frame.SearchTypeButton:SetText(self.getSearchType(1))
        end
end
        
function cgc:CRAFT_SHOW()
        self:Debug("Crafting window open.")
        -- first time window has been opened
        if ( not self.hooks or not self.hooks[frames.craft.update] ) then
                self:Debug("First time crafting window has opened, register close and hooking the update.")
                self:RegisterEvent("CRAFT_CLOSE", "OnClose")
                self:Hook(frames.craft.update, function () self:Update(true) end)
        end
        
        -- Have to set our current frame for the widgets that load.
        self.currentFrame = frames.craft
        
        -- Is the tradeskill window open? If so we'll need to close it.
        if ( getglobal(frames.trade.elements.Main) and getglobal(frames.trade.elements.Main):IsShown() ) then
                getglobal(frames.trade.elements.Main):Hide()
        end
        
        -- We need to dynmically position the addon because the trade/craft anchors are different.
        self.frame:ClearAllPoints()
        self.frame:SetPoint("TOPRIGHT", frames.craft.anchor, "BOTTOMRIGHT" , frames.craft.anchor_offset_x, frames.craft.anchor_offset_y)
        -- Check if the frame was already shown, which means they just changed tradeskills and no update with the OnShow.
        -- Otherwise, lets just show the frame.
        if ( self.frame:IsShown() ) then
                self:OnShow()
        else
                self.frame:Show()
        end

        -- Run our update.
        self:Update(true)
end

function cgc:TRADE_SKILL_SHOW()
        self:Debug("Tradeskill window open.")
        -- first time window has been opened
        if ( not self.hooks or not self.hooks[frames.trade.update] ) then
                self:Debug("First time trade skill window has opened, register close and hooking the update.")
                self:RegisterEvent("TRADE_SKILL_CLOSE", "OnClose")
                self:Hook(frames.trade.update, function () self:Update() end)
                
                -- Check if AutoCraft exists, if it does we're going to have to anchor to something different.
                if ( AutoCraftFrame ) then 
                        frames.trade.anchor = "AutoCraftRunAutomatically"
                        frames.trade.anchor_offset_x = -7
                end
        end
                
        -- Have to set our current frame for the widgets that load.
        self.currentFrame = frames.trade
        
        -- Is the crafting window open? If so we'll need to close it.
        if ( getglobal(frames.craft.elements.Main) and getglobal(frames.craft.elements.Main):IsShown() ) then
                getglobal(frames.craft.elements.Main):Hide()
        end
        
        -- We need to dynmically position the addon because the trade/craft anchors are different.
        self.frame:ClearAllPoints()
        self.frame:SetPoint("TOPLEFT", getglobal(frames.trade.anchor), "BOTTOMLEFT" , frames.trade.anchor_offset_x, frames.trade.anchor_offset_y)
        -- Check if the frame was already shown, which means they just changed tradeskills and no update with the OnShow.
        -- Otherwise, lets just show the frame.
        if ( self.frame:IsShown() ) then
                self:OnShow()
        else
                self.frame:Show()
        end
        
        -- Run the update.
        self:Update()
end

function cgc:OnClose()
        self:Debug("Tradeskill/Craft window close.")
        self.frame:Hide()
end

function cgc:Update(craft) 
        self:Debug("cgc:Update called.")

        -- Update the search history and searh type dropdown
        self.setHistory(self.searchText)
        -- The user has changed their trade window, reset the search text if they haven't searched before
        if ( not self.lastSearchTrade or self.lastSearchTrade ~= getglobal(self.currentFrame.elements.Title):GetText() ) then
                -- Now we update last searched to the current trade/craft type/window name
                self.lastSearchTrade = getglobal(self.currentFrame.elements.Title):GetText()
                -- Does old text for the current window exist? 
                if ( self.getHistory(self.lastSearchTrade) ) then
                        self:Debug("Found history: "..self.lastSearchTrade)
                        self.searchText = self.getHistory(self.lastSearchTrade)
                else
                        self:Debug("No history, searchText=''")
                        self.searchText = ""
                end
                -- Finally, update the actual searchbox.
                self.frame.SearchBox:SetText(self.searchText)
        end
        
        -- The user has decided to search for 
        if ( self:GetSearchText() and getglobal(self.currentFrame.elements.Main):IsShown() ) then
                local searchType = self.getSearchType()
                local skillOffset = FauxScrollFrame_GetOffset(getglobal(self.currentFrame.elements.Scroll))     
                local skillButton = nil
                
                -- Keeps the list from being rebuilt unncessarily when the user is scrolling through search results.
                if ( self.found and getn(self.found) == 0 ) then
                        if ( searchType == "Reagent" ) then
                                -- search by reagent results
                                self:BuildListByReagent(self:GetSearchText(), craft)
                        elseif ( searchType == "Requires" ) then
                                -- search by requires results
                                self:BuildListByRequire(self:GetSearchText(), craft)
                        else
                                -- search by name results
                                self:BuildListByName(self:GetSearchText(), craft)
                        end
                end
                                                
                -- If we're doing tradeskills, we don't have categories, so we don't need a collapse.
                if ( not craft ) then
                        getglobal(frames.trade.elements.CollapseAll):Disable();
                end
                
                -- Update the scroll frame.
                FauxScrollFrame_Update(getglobal(self.currentFrame.elements.Scroll), getn(self.found), (craft and CRAFTS_DISPLAYED or TRADE_SKILLS_DISPLAYED), (craft and CRAFT_SKILL_HEIGHT or TRADE_SKILL_HEIGHT), nil, nil, nil, getglobal(self.currentFrame.elements.Highlight), 293, 316 )
                getglobal(self.currentFrame.elements.Highlight):Hide()
                
                if ( getn(self.found) > 0 ) then
                        -- Do the actual display of the list now.
                        for i=1, (craft and CRAFTS_DISPLAYED or TRADE_SKILLS_DISPLAYED), 1 do
                                local skillIndex = i + skillOffset
                                skillButton = getglobal((craft and "Craft" or "TradeSkillSkill")..i)
                                
                                if ( i <= getn(self.found) ) then
                                        -- Set button widths if scrollbar is shown or hidden
                                        if ( getglobal(self.currentFrame.elements.Scroll):IsVisible() ) then
                                                skillButton:SetWidth(293)
                                        else
                                                skillButton:SetWidth(323)
                                        end
                                        
                                        self:Debug("self.found["..skillIndex.."].type="..self.found[skillIndex].type)
                                        local color = (craft and CraftTypeColor[self.found[skillIndex].type] or TradeSkillTypeColor[self.found[skillIndex].type])
                                        if ( color ) then
                                                skillButton:SetTextColor(color.r, color.g, color.b)
                                        end
                                        skillButton:SetID(self.found[skillIndex].index)
                                        skillButton:Show()
                                        
                                        if ( self.found[skillIndex].name == "" ) then
                                                return
                                        end
                                        
                                        skillButton:SetNormalTexture("")
                                        getglobal((craft and "Craft" or "TradeSkillSkill")..i.."Highlight"):SetTexture("")
                                        if ( self.found[skillIndex].available == 0 ) then
                                                skillButton:SetText(" "..self.found[skillIndex].name)
                                        else
                                                skillButton:SetText(" ".. self.found[skillIndex].name .." [".. self.found[skillIndex].available .."]")
                                        end
                                        
                                        -- Place the highlight and lock the highlight state
                                        if ( (craft and GetCraftSelectionIndex() or GetTradeSkillSelectionIndex()) == self.found[skillIndex].index ) then
                                                getglobal(self.currentFrame.elements.Highlight):SetPoint("TOPLEFT", skillButton, "TOPLEFT", 0, 0)
                                                getglobal(self.currentFrame.elements.Highlight):Show()
                                                skillButton:LockHighlight()
                                                -- Setting the num avail so the create all button works for tradeskills
                                                if (not craft and getglobal(frames.trade.elements.Main)) then
                                                        getglobal(self.currentFrame.elements.Main).numAvailable = self.found[skillIndex].available
                                                end
                                        else
                                                -- The highlight is shown, but it's on an entry that we haven't selected. Probably a remnant from a selection before we did our search,
                                                -- so we'll go ahead and hide the frame.
                                                if ( not self:SelectionInList(skillOffset, craft)  ) then
                                                        getglobal(self.currentFrame.elements.Highlight):Hide()
                                                end
                                                skillButton:UnlockHighlight()
                                        end
                                else
                                        skillButton:Hide()                                      
                                end
                        end
                else
                        getglobal(self.currentFrame.elements.Scroll):Hide()
                        for i=1, (craft and CRAFTS_DISPLAYED or TRADE_SKILLS_DISPLAYED), 1 do
                                skillButton = getglobal((craft and "Craft" or "TradeSkillSkill")..i)
                                
                                skillButton:SetWidth(323)
                                skillButton:SetTextColor(1, 1, 1)
                                skillButton:SetID(1)
                                skillButton:SetNormalTexture("")
                                getglobal((craft and "Craft" or "TradeSkillSkill")..i.."Highlight"):Hide()
                                skillButton:UnlockHighlight();
                                skillButton:Show()
                                
                                if ( i == 1 ) then
                                        getglobal(self.currentFrame.elements.Highlight):Hide()
                                        skillButton:SetText(self.LOCALS.FRAME_NO_RESULTS)
                                else 
                                        skillButton:SetText("")
                                end
                        end
                end
        else
                cgc:Debug("Calling original update functions:"..(craft and frames.craft.update or frames.trade.update))
                self.hooks[ craft and frames.craft.update or frames.trade.update ].orig()
        end
end

----------------------------------------
-- Retrieve the current searchText.
function cgc:GetSearchText()
        -- The second argument is a search with only spaces.
        if ( self.searchText ~= nil and string.len(string.gsub(self.searchText, "%s", "")) > 0 ) then
                return self.searchText
        else
                return false
        end
end

----------------------------------------
-- Begin the search and clear out the scrolling frame so it's not out of position.
function cgc:Search(searchText)
        self:Debug("Search called; currentFrame="..self.currentFrame.elements.Main)
        self.searchText = self.frame.SearchBox:GetText()
        -- Need to reset / create our self.found array.
        self.found = {}
        
        self.frame.SearchBox:ClearFocus()
        -- We have to clear the offset on the scroll frame, otherwise we error out and it's misscrolled.
        -- self.currentFrame = (getglobal(frames.craft.elements.Main) and getglobal(frames.craft.elements.Main):IsShown() and "CraftListScrollFrame" or getglobal(frames.trade.elements.Main) and getglobal(frames.trade.elements.Main):IsShown() and "TradeSkillListScrollFrame"
        FauxScrollFrame_SetOffset(getglobal(self.currentFrame.elements.Main), 0)
        getglobal(self.currentFrame.elements.ScrollBar):SetValue(0)
        -- Finally, do the update.
        self:Update(getglobal(frames.craft.elements.Main) and getglobal(frames.craft.elements.Main):IsShown())
end

----------------------------------------
-- Reset the skill frames.
function cgc:Reset()
        self.searchText = ""
        self.frame.SearchBox:ClearFocus();
        self.frame.SearchBox:SetText(self.searchText);
        self.clearHistory()
        self:Update(getglobal(frames.craft.elements.Main) and getglobal(frames.craft.elements.Main):IsShown())
end

--[[ ---------------------------------------------------------------------------------------------------- ]]--
-- Building functions which do the bulk work of this mod.  They traverse through the current skill opened
-- and check for any matches against the users requested searchText, or build chat links to be sent
-- through SendChatMessage()
--[[ ---------------------------------------------------------------------------------------------------- ]]--

----------------------------------------
-- Build search list by item name.
function cgc:BuildListByName(searchText, craft)
        local foundIndex = 0
        self.found = {}
        
        if ( craft ) then
                self:Debug("BuildingListByName for crafting.")
        end
        
        local skillName, skillType, numAvailable, isExpanded
        for i=1, (craft and GetNumCrafts() or GetNumTradeSkills()), 1 do
                if ( craft ) then
                        skillName, _, skillType, numAvailable, isExpanded = GetCraftInfo(i)
                else
                        skillName, skillType, numAvailable, isExpanded = GetTradeSkillInfo(i)
                end
                
                if ( strfind(string.lower(skillName), string.lower(searchText)) and skillType ~= "header" ) then
                        cgc:Debug("Match #"..foundIndex..": skillType="..skillType)
                        foundIndex = foundIndex + 1
                        self.found[foundIndex] = {}
                        self.found[foundIndex] = {
                                name                    = skillName,
                                type                    = skillType,
                                available               = numAvailable,
                                index                   = i
                        }       
                elseif ( skillType == "header" and not isExpanded ) then
                        -- We need to expand any unexpanded header types, otherwise we can't parse their sub data.
                        ExpandTradeSkillSubClass(i)
                end
        end
        
        self:Debug("We found ".. foundIndex .." matches.")
end

----------------------------------------
-- Build search list by required.
function cgc:BuildListByRequire(searchText, craft)
        local foundIndex = 0
        self.found = {}

        local skillName, skillType, numAvailable, isExpanded
        local requires  
        for i=1, (craft and GetNumCrafts() or GetNumTradeSkills()), 1 do
                if ( craft ) then
                        skillName, _, skillType, numAvailable, isExpanded = GetCraftInfo(i)
                        requires = GetCraftSpellFocus(i)
                else
                        skillName, skillType, numAvailable, isExpanded = GetTradeSkillInfo(i)
                        requires = GetTradeSkillTools(i)
                end
                
                if ( requires and strfind(string.lower(BuildColoredListString(requires)), string.lower(searchText)) and skillType ~= "header" ) then
                        self:Debug("Found matching require for '"..searchText.."': "..requires.." ")
                        foundIndex = foundIndex + 1
                        self.found[foundIndex] = {}
                        self.found[foundIndex] = {
                                name                    = skillName,
                                type                    = skillType,
                                available               = numAvailable,
                                index                   = i
                        }       
                elseif ( skillType == "header" and not isExpanded ) then
                        -- We need to expand any unexpanded header types, otherwise we can't parse their sub data.
                        ExpandTradeSkillSubClass(i)
                end
        end
end

----------------------------------------
-- Build search list reagent name.
function cgc:BuildListByReagent(searchText, craft)
        local foundIndex = 0
        self.found = {}
        
        local skillName, skillType, numAvailable, isExpanded, reagentName
        for i=1, (craft and GetNumCrafts() or GetNumTradeSkills()), 1 do
                if ( craft ) then
                        skillName, _, skillType, numAvailable, isExpanded = GetCraftInfo(i)
                else
                        skillName, skillType, numAvailable, isExpanded = GetTradeSkillInfo(i)
                end
                
                if ( skillType ~= "header" ) then
                        for e=1, (craft and GetCraftNumReagents(i) or GetTradeSkillNumReagents(i)), 1 do
                                if ( craft ) then
                                        reagentName, _, _, _ = GetCraftReagentInfo(i, e)
                                else
                                        reagentName, _, _, _ = GetTradeSkillReagentInfo(i, e)
                                end
                                
                                
                                if ( reagentName and strfind(string.lower(reagentName), string.lower(searchText)) ) then
                                        foundIndex = foundIndex + 1
                                        self.found[foundIndex] = {}
                                        self.found[foundIndex] = {
                                                name                    = skillName,
                                                type                    = skillType,
                                                available               = numAvailable,
                                                index                   = i
                                        }       
                                        -- Some reagents can share a similar name so if we already matched one, we can break here.
                                        break
                                end
                        end
                elseif ( skillType == "header" and not isExpanded ) then
                        -- We need to expand any unexpanded header types, otherwise we can't parse their sub data.
                        ExpandTradeSkillSubClass(i)
                end
        end
end

----------------------------------------
-- Utility function to reagent information to chat.
function cgc:SendReagentsMessage(channel, who)
        local sIndex, itemString
        local tempString = ""
        local reagentLinks = {}
        local reagentCounts = {}
        local craft = getglobal(frames.craft.elements.Main) and getglobal(frames.craft.elements.Main):IsShown()
        
        -- Figure out which window is open or exit function
        if ( getglobal(self.currentFrame.elements.Main):IsShown() ) then
                sIndex = craft and GetCraftSelectionIndex() or GetTradeSkillSelectionIndex()
                self:Debug("SRM: GetTradeSkillSelectionIndex="..GetTradeSkillSelectionIndex())
        else
                return
        end
        
        self:Debug("SRM: sIndex = "..sIndex)
        -- Double check the user has a skill selected.
        if ( sIndex > 0 ) then
                -- Insert the name first.
                itemString = craft and GetCraftItemLink(sIndex) or GetTradeSkillItemLink(sIndex)
                
                -- Cycle through all the reagents and get the required amounts for each into two nice arrays
                for rIndex=1, (craft and GetCraftNumReagents(sIndex) or GetTradeSkillNumReagents(sIndex)), 1 do
                        if ( craft ) then
                                reagentLinks[rIndex] = GetCraftReagentItemLink(sIndex, rIndex) 
                                _, _, reagentCounts[rIndex], _ = GetCraftReagentInfo(sIndex, rIndex) 
                        else
                                reagentLinks[rIndex] = GetTradeSkillReagentItemLink(sIndex, rIndex)
                                _, _, reagentCounts[rIndex], _ = GetTradeSkillReagentInfo(sIndex, rIndex) 
                        end
                end
                
                -- Now we'll make strings out of them, 4 reagent links at a time and print them out when we reach our limit.
                
                for i=1, getn(reagentLinks) do 
                        tempString = tostring(tempString) .. reagentLinks[i] .. "x" .. reagentCounts[i] .. " "
                        if ( mod(i,3) == 0 or i == getn(reagentLinks) ) then
                                -- Combine the item name, add (cont) if this is the second go round.
                                tempString = itemString .. (i > 3 and " (cont) = " or " = ")  .. tempString
                                SendChatMessage(tempString, channel, GetDefaultLanguage("player"), who)
                                tempString = ""
                        end
                end
        end
end

----------------------------------------
-- Utility function for Update(): checks to see if the selected item is currently visible in the scrolling frame.
function cgc:SelectionInList(skillOffset, craft)
        for i=skillOffset+1, skillOffset+(craft and CRAFTS_DISPLAYED or TRADE_SKILLS_DISPLAYED), 1 do
                if ( self.found[i] and self.found[i].index == (craft and GetCraftSelectionIndex() or GetTradeSkillSelectionIndex()) ) then
                        return true
                end
        end
        
        return false
end

Generated by GNU Enscript 1.6.5.90.