Quantcast

Split the code for the ListFrame off into its own file.

James D. Callahan III [07-10-10 - 05:13]
Split the code for the ListFrame off into its own file.
Filename
Frame.lua
Interface/List.lua
interface.xml
diff --git a/Frame.lua b/Frame.lua
index 9691a92..4328a90 100644
--- a/Frame.lua
+++ b/Frame.lua
@@ -81,15 +81,9 @@ local FACTION_HORDE		= BFAC["Horde"]
 local FACTION_ALLIANCE		= BFAC["Alliance"]
 local FACTION_NEUTRAL		= BFAC["Neutral"]

-local CATEGORY_COLORS		= private.category_colors
 local BASIC_COLORS		= private.basic_colors

-local SF = private.recipe_state_flags
-
 local A = private.acquire_types
-local A_MAX = 9
-
-local COMMON1 = private.common_flags_word1

 -------------------------------------------------------------------------------
 -- Define the static popups we're going to call when people haven't scanned or
@@ -147,7 +141,6 @@ local SetTextColor = private.SetTextColor
 local AcquireTable = private.AcquireTable
 local ReleaseTable = private.ReleaseTable

-local ListFrame
 local MainPanel

 local FilterValueMap		-- Assigned in InitializeFrame()
@@ -307,486 +300,6 @@ do
 end	-- do

 -------------------------------------------------------------------------------
--- Tooltip functions and data.
--------------------------------------------------------------------------------
-local spell_tip = CreateFrame("GameTooltip", "arlSpellTooltip", UIParent, "GameTooltipTemplate")
-local acquire_tip
-
--- Font Objects needed for acquire_tip
-local narrowFont
-local normalFont
-
-local ListItem_ShowTooltip
-do
-	-- Fallback in case the user doesn't have LSM-3.0 installed
-	if not LibStub:GetLibrary("LibSharedMedia-3.0", true) then
-
-		local locale = GetLocale()
-		-- Fix for font issues on koKR
-		if locale == "koKR" then
-			narrowFont = "Fonts\\2002.TTF"
-			normalFont = "Fonts\\2002.TTF"
-		else
-			narrowFont = "Fonts\\ARIALN.TTF"
-			normalFont = "Fonts\\FRIZQT__.TTF"
-		end
-	else
-		-- Register LSM 3.0
-		local LSM3 = LibStub("LibSharedMedia-3.0")
-
-		narrowFont = LSM3:Fetch(LSM3.MediaType.FONT, "Arial Narrow")
-		normalFont = LSM3:Fetch(LSM3.MediaType.FONT, "Friz Quadrata TT")
-	end
-	local narrowFontObj = CreateFont(MODNAME.."narrowFontObj")
-	local normalFontObj = CreateFont(MODNAME.."normalFontObj")
-
-	-- I want to do a bit more comprehensive tooltip processing. Things like changing font sizes,
-	-- adding padding to the left hand side, and using better color handling. So... this function
-	-- will do that for me.
-	local function ttAdd(
-			leftPad,		-- number of times to pad two spaces on left side
-			textSize,		-- add to or subtract from addon.db.profile.tooltip.acquire_fontsize to get fontsize
-			narrow,			-- if 1, use ARIALN instead of FRITZQ
-			str1,			-- left-hand string
-			hexcolor1,		-- hex color code for left-hand side
-			str2,			-- if present, this is the right-hand string
-			hexcolor2)		-- if present, hex color code for right-hand side
-
-		-- are we changing fontsize or narrow?
-		local fontSize
-
-		if narrow or textSize ~= 0 then
-			local font = narrow and narrowFont or normalFont
-			local fontObj = narrow and narrowFontObj or normalFontObj
-
-			fontSize = addon.db.profile.tooltip.acquire_fontsize + textSize
-
-			fontObj:SetFont(font, fontSize)
-			acquire_tip:SetFont(fontObj)
-		end
-
-		-- Add in our left hand padding
-		local loopPad = leftPad
-		local leftStr = str1
-
-		while loopPad > 0 do
-			leftStr = "    " .. leftStr
-			loopPad = loopPad - 1
-		end
-		-- Set maximum width to match fontSize to maintain uniform tooltip size. -Torhal
-		local width = math.ceil(fontSize * 37.5)
-		local line = acquire_tip:AddLine()
-
-		if str2 then
-			width = width / 2
-
-			acquire_tip:SetCell(line, 1, "|cff"..hexcolor1..leftStr.."|r", "LEFT", nil, nil, 0, 0, width, width)
-			acquire_tip:SetCell(line, 2, "|cff"..hexcolor2..str2.."|r", "RIGHT", nil, nil, 0, 0, width, width)
-		else
-			acquire_tip:SetCell(line, 1, "|cff"..hexcolor1..leftStr.."|r", nil, "LEFT", 2, nil, 0, 0, width, width)
-		end
-	end
-
-	local function SetSpellTooltip(owner, loc, link)
-		spell_tip:SetOwner(owner, "ANCHOR_NONE")
-		spell_tip:ClearAllPoints()
-
-		if loc == "Top" then
-			spell_tip:SetPoint("BOTTOMLEFT", owner, "TOPLEFT")
-		elseif loc == "Bottom" then
-			spell_tip:SetPoint("TOPLEFT", owner, "BOTTOMLEFT")
-		elseif loc == "Left" then
-			spell_tip:SetPoint("TOPRIGHT", owner, "TOPLEFT")
-		elseif loc == "Right" then
-			spell_tip:SetPoint("TOPLEFT", owner, "TOPRIGHT")
-		end
-
-		-- Add TipTac Support
-		if _G.TipTac and _G.TipTac.AddModifiedTip and not spell_tip.tiptac then
-			_G.TipTac:AddModifiedTip(spell_tip)
-			spell_tip.tiptac = true
-		end
-
-		-- Set the spell tooltip's scale, and copy its other values from GameTooltip so AddOns which modify it will work.
-		spell_tip:SetBackdrop(GameTooltip:GetBackdrop())
-		spell_tip:SetBackdropColor(GameTooltip:GetBackdropColor())
-		spell_tip:SetBackdropBorderColor(GameTooltip:GetBackdropBorderColor())
-		spell_tip:SetScale(addon.db.profile.tooltip.scale)
-		spell_tip:SetClampedToScreen(true)
-		spell_tip:SetHyperlink(link)
-		spell_tip:Show()
-	end
-
-	local function GetTipFactionInfo(comp_faction)
-		local display_tip
-		local color
-
-		if comp_faction == FACTION_NEUTRAL then
-			color = private.reputation_colors["neutral"]
-			display_tip = true
-		elseif comp_faction == BFAC[Player.faction] then
-			color = private.reputation_colors["exalted"]
-			display_tip = true
-		else
-			color = private.reputation_colors["hated"]
-			display_tip = addon.db.profile.filters.general.faction
-		end
-		return display_tip, color
-	end
-
-	-------------------------------------------------------------------------------
-	-- Functions for adding individual acquire type data to the tooltip.
-	-------------------------------------------------------------------------------
-	local function Tooltip_AddTrainer(id_num, location, addline_func)
-		local trainer = private.trainer_list[id_num]
-
-		if location and trainer.location ~= location then
-			return
-		end
-		local display_tip, name_color = GetTipFactionInfo(trainer.faction)
-
-		if display_tip then
-			local coord_text = ""
-
-			if trainer.coord_x ~= 0 and trainer.coord_y ~= 0 then
-				coord_text = "(" .. trainer.coord_x .. ", " .. trainer.coord_y .. ")"
-			end
-			addline_func(0, -2, false, L["Trainer"], CATEGORY_COLORS["trainer"], trainer.name, name_color)
-			addline_func(1, -2, true, trainer.location, CATEGORY_COLORS["location"], coord_text, CATEGORY_COLORS["coords"])
-		end
-	end
-
-	local function Tooltip_AddVendor(recipe_id, id_num, location, addline_func)
-		local vendor = private.vendor_list[id_num]
-
-		if location and vendor.location ~= location then
-			return
-		end
-		local type_color = CATEGORY_COLORS["vendor"]
-		local display_tip, name_color = GetTipFactionInfo(vendor.faction)
-
-		if display_tip then
-			local coord_text = ""
-
-			if vendor.coord_x ~= 0 and vendor.coord_y ~= 0 then
-				coord_text = "(" .. vendor.coord_x .. ", " .. vendor.coord_y .. ")"
-			end
-			addline_func(0, -1, false, L["Vendor"], type_color, vendor.name, name_color)
-			addline_func(1, -2, true, vendor.location, CATEGORY_COLORS["location"], coord_text, CATEGORY_COLORS["coords"])
-
-			local quantity = vendor.item_list[recipe_id]
-
-			if type(quantity) == "number" then
-				addline_func(2, -2, true, L["LIMITED_SUPPLY"], type_color, string.format("(%d)", quantity), BASIC_COLORS["white"])
-			end
-		end
-	end
-
-	local function Tooltip_AddMobDrop(id_num, location, addline_func)
-		local mob = private.mob_list[id_num]
-
-		if location and mob.location ~= location then
-			return
-		end
-		local coord_text = ""
-
-		if mob.coord_x ~= 0 and mob.coord_y ~= 0 then
-			coord_text = "(" .. mob.coord_x .. ", " .. mob.coord_y .. ")"
-		end
-		addline_func(0, -1, false, L["Mob Drop"], CATEGORY_COLORS["mobdrop"], mob.name, private.reputation_colors["hostile"])
-		addline_func(1, -2, true, mob.location, CATEGORY_COLORS["location"], coord_text, CATEGORY_COLORS["coords"])
-	end
-
-	local function Tooltip_AddQuest(id_num, location, addline_func)
-		local quest = private.quest_list[id_num]
-
-		if location and quest.location ~= location then
-			return
-		end
-		local type_color = CATEGORY_COLORS["quest"]
-		local display_tip, name_color = GetTipFactionInfo(quest.faction)
-
-		if display_tip then
-			local coord_text = ""
-
-			if quest.coord_x ~= 0 and quest.coord_y ~= 0 then
-				coord_text = "(" .. quest.coord_x .. ", " .. quest.coord_y .. ")"
-			end
-			addline_func(0, -1, false, L["Quest"], type_color, private.quest_names[id_num], name_color)
-			addline_func(1, -2, true, quest.location, CATEGORY_COLORS["location"], coord_text, CATEGORY_COLORS["coords"])
-		end
-	end
-
-	local function Tooltip_AddRepVendor(id_num, location, rep_id, rep_level, vendor_id, addline_func)
-		local rep_vendor = private.vendor_list[vendor_id]
-
-		if location and rep_vendor.location ~= location then
-			return
-		end
-		local display_tip, name_color = GetTipFactionInfo(rep_vendor.faction)
-
-		if display_tip then
-			local rep_color = private.reputation_colors
-			local rep_str = ""
-			local type_color
-
-			if rep_level == 0 then
-				rep_str = FACTION_NEUTRAL
-				type_color = rep_color["neutral"]
-			elseif rep_level == 1 then
-				rep_str = BFAC["Friendly"]
-				type_color = rep_color["friendly"]
-			elseif rep_level == 2 then
-				rep_str = BFAC["Honored"]
-				type_color = rep_color["honored"]
-			elseif rep_level == 3 then
-				rep_str = BFAC["Revered"]
-				type_color = rep_color["revered"]
-			else
-				rep_str = BFAC["Exalted"]
-				type_color = rep_color["exalted"]
-			end
-			addline_func(0, -1, false, _G.REPUTATION, CATEGORY_COLORS["reputation"], private.reputation_list[id_num].name, CATEGORY_COLORS["repname"])
-			addline_func(1, -2, false, rep_str, type_color, rep_vendor.name, name_color)
-
-			local coord_text = ""
-
-			if rep_vendor.coord_x ~= 0 and rep_vendor.coord_y ~= 0 then
-				coord_text = "(" .. rep_vendor.coord_x .. ", " .. rep_vendor.coord_y .. ")"
-			end
-			addline_func(2, -2, true, rep_vendor.location, CATEGORY_COLORS["location"], coord_text, CATEGORY_COLORS["coords"])
-		end
-	end
-
-	local function Tooltip_AddWorldDrop(recipe_id, id_num, location, addline_func)
-		local drop_location = type(id_num) == "string" and BZ[id_num] or nil
-
-		if location and drop_location ~= location then
-			return
-		end
-		local item_id = private.spell_to_recipe_map[recipe_id]
-		local _, item_level
-
-		if item_id then
-			_, _, _, item_level = GetItemInfo(item_id)
-		end
-		local _, _, _, quality_color = GetItemQualityColor(private.recipe_list[recipe_id].quality)
-		local type_color = string.gsub(quality_color, "|cff", "")
-
-		if type(id_num) == "string" then
-			local location_text = item_level and string.format("%s (%d - %d)", drop_location, item_level - 5, item_level + 5) or drop_location
-
-			addline_func(0, -1, false, L["World Drop"], type_color, location_text, CATEGORY_COLORS["location"])
-		else
-			local location_text = item_level and string.format("%s (%d - %d)", _G.UNKNOWN, item_level - 5, item_level + 5) or _G.UNKNOWN
-
-			addline_func(0, -1, false, L["World Drop"], type_color, location_text, CATEGORY_COLORS["location"])
-		end
-	end
-
-	-------------------------------------------------------------------------------
-	-- Public API function for displaying a recipe's acquire data.
-	-- * The addline_func paramater must be a function which accepts the same
-	-- * arguments as ARL's ttAdd function.
-	-------------------------------------------------------------------------------
-	function addon:DisplayAcquireData(recipe_id, acquire_id, location, addline_func)
-		local recipe = private.recipe_list[recipe_id]
-
-		if not recipe then
-			return
-		end
-
-		for acquire_type, acquire_data in pairs(recipe.acquire_data) do
-			local can_display = (not acquire_id or acquire_type == acquire_id)
-
-			if can_display then
-				for id_num, info in pairs(acquire_data) do
-					if acquire_type == A.TRAINER then
-						Tooltip_AddTrainer(id_num, location, addline_func)
-					elseif acquire_type == A.VENDOR then
-						Tooltip_AddVendor(recipe_id, id_num, location, addline_func)
-					elseif acquire_type == A.MOB_DROP then
-						Tooltip_AddMobDrop(id_num, location, addline_func)
-					elseif acquire_type == A.QUEST then
-						Tooltip_AddQuest(id_num, location, addline_func)
-					elseif acquire_type == A.SEASONAL then
-						color_1 = CATEGORY_COLORS["seasonal"]
-						addline_func(0, -1, 0, private.acquire_names[A.SEASONAL], color_1, private.seasonal_list[id_num].name, color_1)
-					elseif acquire_type == A.REPUTATION then
-						for rep_level, level_info in pairs(info) do
-							for vendor_id in pairs(level_info) do
-								Tooltip_AddRepVendor(id_num, location, rep_id, rep_level, vendor_id, addline_func)
-							end
-						end
-					elseif acquire_type == A.WORLD_DROP then
-						Tooltip_AddWorldDrop(recipe_id, id_num, location, addline_func)
-					elseif acquire_type == A.CUSTOM then
-						addline_func(0, -1, false, private.custom_list[id_num].name, CATEGORY_COLORS["custom"])
-						--@alpha@
-					elseif can_display then
-						-- Unhandled
-						addline_func(0, -1, 0, L["Unhandled Recipe"], BASIC_COLORS["normal"])
-						--@end-alpha@
-					end
-				end	-- for id_num
-			end	-- if can_display
-		end	-- for acquire_type
-	end
-
-	-------------------------------------------------------------------------------
-	-- Main tooltip-generating function.
-	-------------------------------------------------------------------------------
-	local BINDING_FLAGS = {
-		[COMMON1.IBOE] = L["BOEFilter"],
-		[COMMON1.IBOP] = L["BOPFilter"],
-		[COMMON1.IBOA] = L["BOAFilter"],
-		[COMMON1.RBOE] = L["RecipeBOEFilter"],
-		[COMMON1.RBOP] = L["RecipeBOPFilter"],
-		[COMMON1.RBOA] = L["RecipeBOAFilter"]
-	}
-
-	function ListItem_ShowTooltip(owner, list_entry)
-		if not list_entry then
-			return
-		end
-		local recipe_id = list_entry.recipe_id
-		local recipe = private.recipe_list[recipe_id]
-
-		if not recipe then
-			return
-		end
-		local spell_tip_anchor = addon.db.profile.spelltooltiplocation
-		local acquire_tip_anchor = addon.db.profile.acquiretooltiplocation
-		local spell_link = GetSpellLink(recipe.spell_id)
-		local MainPanel = addon.Frame
-
-		if acquire_tip_anchor == _G.OFF then
-			QTip:Release(acquire_tip)
-
-			-- If we have the spell link tooltip, anchor it to MainPanel instead so it shows
-			if spell_tip_anchor ~= _G.OFF and spell_link then
-				SetSpellTooltip(MainPanel, spell_tip_anchor, spell_link)
-			else
-				spell_tip:Hide()
-			end
-			return
-		end
-		acquire_tip = QTip:Acquire(MODNAME.." Tooltip", 2, "LEFT", "LEFT")
-		acquire_tip:ClearAllPoints()
-
-		if acquire_tip_anchor == "Right" then
-			acquire_tip:SetPoint("TOPLEFT", MainPanel, "TOPRIGHT", MainPanel.is_expanded and -90 or -35, 0)
-		elseif acquire_tip_anchor == "Left" then
-			acquire_tip:SetPoint("TOPRIGHT", MainPanel, "TOPLEFT")
-		elseif acquire_tip_anchor == "Top" then
-			acquire_tip:SetPoint("BOTTOMLEFT", MainPanel, "TOPLEFT")
-		elseif acquire_tip_anchor == "Bottom" then
-			acquire_tip:SetPoint("TOPLEFT", MainPanel, "BOTTOMLEFT", 0, 55)
-		elseif acquire_tip_anchor == "Mouse" then
-			local x, y = GetCursorPosition()
-			local uiscale = UIParent:GetEffectiveScale()
-
-			acquire_tip:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", x / uiscale, y / uiscale)
-		end
-		acquire_tip:SetClampedToScreen(true)
-
-		if _G.TipTac and _G.TipTac.AddModifiedTip then
-			-- Pass true as second parameter because hooking OnHide causes C stack overflows -Torhal
-			_G.TipTac:AddModifiedTip(acquire_tip, true)
-		end
-		local _, _, _, quality_color = GetItemQualityColor(recipe.quality)
-
-		acquire_tip:Clear()
-		acquire_tip:SetScale(addon.db.profile.tooltip.scale)
-		acquire_tip:AddHeader()
-		acquire_tip:SetCell(1, 1, quality_color..recipe.name, "CENTER", 2)
-
-		-- check if the recipe is excluded
-		if addon.db.profile.exclusionlist[recipe_id] then
-			ttAdd(0, -1, true, L["RECIPE_EXCLUDED"], "ff0000")
-		end
-
-		-- Add in skill level requirement, colored correctly
-		local color_1 = BASIC_COLORS["normal"]
-		local color_2
-
-		local skill_level = Player["ProfessionLevel"]
-		local recipe_level = recipe.skill_level
-		local optimal_level = recipe.optimal_level
-		local medium_level = recipe.medium_level
-		local easy_level = recipe.easy_level
-		local trivial_level = recipe.trivial_level
-		local difficulty = private.difficulty_colors
-
-		if recipe_level > skill_level then
-			color_2 = difficulty["impossible"]
-		elseif skill_level >= trivial_level then
-			color_2 = difficulty["trivial"]
-		elseif skill_level >= easy_level then
-			color_2 = difficulty["easy"]
-		elseif skill_level >= medium_level then
-			color_2 = difficulty["medium"]
-		elseif skill_level >= optimal_level then
-			color_2 = difficulty["optimal"]
-		else
-			color_2 = difficulty["trivial"]
-		end
-		ttAdd(0, -1, false, string.format("%s:", _G.SKILL_LEVEL), color_1, recipe.skill_level, color_2)
-
-		-- Binding info
-		acquire_tip:AddSeparator()
-		color_1 = BASIC_COLORS["normal"]
-
-		for flag, label in pairs(BINDING_FLAGS) do
-			if bit.band(recipe.flags.common1, flag) == flag then
-				ttAdd(0, -1, true, label, color_1)
-			end
-		end
-		acquire_tip:AddSeparator()
-
-		if recipe.specialty then
-			local spec = recipe.specialty
-			local spec_name = GetSpellInfo(spec)
-			local known = (spec == Player["Specialty"])
-
-			ttAdd(0, -1, false, string.format(_G.ITEM_REQ_SKILL, spec_name), known and BASIC_COLORS["white"] or difficulty["impossible"])
-			acquire_tip:AddSeparator()
-		end
-
-		ttAdd(0, -1, false, L["Obtained From"] .. " : ", BASIC_COLORS["normal"])
-
-		local acquire_id = list_entry.acquire_id
-		local location = list_entry.location_id
-
-		addon:DisplayAcquireData(recipe_id, acquire_id, location, ttAdd)
-
-		if not addon.db.profile.hide_tooltip_hint then
-			-- Give the tooltip hint a unique color.
-			color_1 = "c9c781"
-
-			acquire_tip:AddSeparator()
-			acquire_tip:AddSeparator()
-
-			ttAdd(0, -1, 0, L["ALT_CLICK"], color_1)
-			ttAdd(0, -1, 0, L["CTRL_CLICK"], color_1)
-			ttAdd(0, -1, 0, L["SHIFT_CLICK"], color_1)
-
-			if acquire_id ~= A.WORLD_DROP and acquire_id ~= A.CUSTOM and (_G.TomTom or _G.Cartographer_Waypoints) and (addon.db.profile.worldmap or addon.db.profile.minimap) then
-				ttAdd(0, -1, 0, L["CTRL_SHIFT_CLICK"], color_1)
-			end
-		end
-		acquire_tip:Show()
-
-		-- If we have the spell link tooltip, link it to the acquire tooltip.
-		if spell_tip_anchor ~= _G.OFF and spell_link then
-			SetSpellTooltip(acquire_tip, spell_tip_anchor, spell_link)
-		else
-			spell_tip:Hide()
-		end
-	end
-end	-- do
-
--------------------------------------------------------------------------------
 -- Create the MainPanel and set its values
 -------------------------------------------------------------------------------
 local MainPanel
@@ -1163,7 +676,7 @@ MainPanel.search_editbox = SearchBox
 SearchBox:SetText(_G.SEARCH)
 SearchBox:SetHistoryLines(10)

--- Resets the SearchBox text and the state of all ListFrame and recipe_list entries.
+-- Resets the SearchBox text and the state of all MainPanel.list_frame and recipe_list entries.
 function SearchBox:Reset()
 	local recipe_list = private.recipe_list

@@ -1174,7 +687,7 @@ function SearchBox:Reset()

 	self:SetText(_G.SEARCH)
 	self:ClearFocus()
-	ListFrame:Update(nil, false)
+	MainPanel.list_frame:Update(nil, false)
 end

 -- If there is text in the search box, return the recipe's RELEVANT state.
@@ -1206,7 +719,7 @@ SearchBox:SetScript("OnEnterPressed",
 			    self:HighlightText()
 			    self:AddHistoryLine(searchtext)
 			    SearchRecipes(searchtext)
-			    ListFrame:Update(nil, false)
+			    MainPanel.list_frame:Update(nil, false)
 		    end)

 SearchBox:SetScript("OnEditFocusGained",
@@ -1252,7 +765,7 @@ do
 					  last_update = 0

 					  SearchRecipes(SearchBox:GetText())
-					  ListFrame:Update(nil, false)
+					  MainPanel.list_frame:Update(nil, false)
 					  self:Hide()
 				  end
 			  end)
@@ -1300,7 +813,7 @@ ExpandButtonFrame.middle:SetTexture("Interface\\QuestFrame\\UI-QuestLogSortTab-M

 local ExpandButton = GenericCreateButton(nil, MainPanel, 16, 16, "GameFontNormalSmall", _G.ALL, "LEFT", L["EXPANDALL_DESC"], 2)

-MainPanel.expand_all_button = ExpandButton
+MainPanel.expand_button = ExpandButton

 ExpandButton:SetPoint("LEFT", ExpandButtonFrame.left, "RIGHT", -3, -3)

@@ -1324,10 +837,10 @@ ExpandButton:SetScript("OnClick",

 				       table.wipe(current_tab[prof_name.." expanded"])
 			       end
-			       -- ListFrame:Update() must be called before the button can be expanded or contracted, since
+			       -- MainPanel.list_frame:Update() must be called before the button can be expanded or contracted, since
 			       -- the button is contracted from there.
 			       -- If expand_mode is nil, that means expand nothing.
-			       ListFrame:Update(expand_mode, false)
+			       MainPanel.list_frame:Update(expand_mode, false)

 			       if expanded then
 				       self:Contract(current_tab)
@@ -1372,7 +885,7 @@ SkillToggle.text:SetPoint("LEFT", SkillToggle, "RIGHT", 0, 0)
 SkillToggle:SetScript("OnClick",
 		      function(self, button, down)
 			      addon.db.profile.skill_view = not addon.db.profile.skill_view
-			      ListFrame:Update(nil, false)
+			      MainPanel.list_frame:Update(nil, false)
 		      end)

 SkillToggle:SetScript("OnShow",
@@ -1403,7 +916,7 @@ ExcludeToggle.text:SetPoint("LEFT", ExcludeToggle, "RIGHT", 0, 0)
 ExcludeToggle:SetScript("OnClick",
 			function(self, button, down)
 				addon.db.profile.ignoreexclusionlist = not addon.db.profile.ignoreexclusionlist
-				ListFrame:Update(nil, false)
+				MainPanel.list_frame:Update(nil, false)
 			end)

 ExcludeToggle:SetScript("OnShow",
@@ -1561,7 +1074,7 @@ do
 		if MainPanel:IsVisible() then
 			MainPanel:UpdateTitle()
 			UpdateFilterMarks()
-			ListFrame:Update(nil, false)
+			MainPanel.list_frame:Update(nil, false)
 		end
 	end
 	MainPanel.filter_reset:SetScript("OnClick", ResetFilters)
@@ -1578,7 +1091,7 @@ do

 		FilterValueMap[script_val].svroot[script_val] = FilterValueMap[script_val].cb:GetChecked() and true or false
 		MainPanel:UpdateTitle()
-		ListFrame:Update(nil, false)
+		MainPanel.list_frame:Update(nil, false)
 	end

 	local function CreateCheckButton(parent, ttText, scriptVal, row, col)
@@ -1612,30 +1125,6 @@ do
 end	-- do

 -------------------------------------------------------------------------------
--- Create the ListFrame and set its scripts.
--------------------------------------------------------------------------------
-ListFrame = CreateFrame("Frame", "ARL_MainPanelScrollFrame", MainPanel)
-
-MainPanel.list_frame = ListFrame
-
-ListFrame:SetHeight(335)
-ListFrame:SetWidth(295)
-ListFrame:SetPoint("TOPLEFT", MainPanel, "TOPLEFT", 22, -75)
-ListFrame:SetBackdrop({
-			      bgFile = [[Interface\DialogFrame\UI-DialogBox-Background-Dark]],
-			      tile = true,
-			      tileSize = 16,
-		      })
-ListFrame:SetBackdropColor(1, 1, 1)
-ListFrame:EnableMouse(true)
-ListFrame:EnableMouseWheel(true)
-
-ListFrame.entries = {}
-ListFrame.button_containers = {}
-ListFrame.state_buttons = {}
-ListFrame.entry_buttons = {}
-
--------------------------------------------------------------------------------
 -- Sort-mode toggle button.
 -------------------------------------------------------------------------------
 local SortToggle = GenericCreateButton(nil, MainPanel, 24, 24, nil, nil, nil, L["SORTING_DESC"], 2)
@@ -1651,7 +1140,7 @@ SortToggle:SetScript("OnClick",
 			     addon.db.profile.sorting = (sort_type == "Ascending" and "Descending" or "Ascending")

 			     self:SetTextures()
-			     ListFrame:Update(nil, false)
+			     MainPanel.list_frame:Update(nil, false)
 		     end)

 SortToggle:SetHighlightTexture([[Interface\CHATFRAME\UI-ChatIcon-BlinkHilight]])
@@ -1671,1026 +1160,6 @@ function SortToggle:SetTextures()
 end

 -------------------------------------------------------------------------------
--- Create ListFrame.scroll_bar, and set its scripts.
--------------------------------------------------------------------------------
-local ScrollBar = CreateFrame("Slider", nil, ListFrame)
-
-ScrollBar:SetPoint("TOPLEFT", ListFrame, "TOPRIGHT", 5, -11)
-ScrollBar:SetPoint("BOTTOMLEFT", ListFrame, "BOTTOMRIGHT", 5, 12)
-ScrollBar:SetWidth(24)
-
-ScrollBar:EnableMouseWheel(true)
-ScrollBar:SetOrientation("VERTICAL")
-
-ScrollBar:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob")
-ScrollBar:SetMinMaxValues(0, 1)
-ScrollBar:SetValueStep(1)
-
-ListFrame.scroll_bar = ScrollBar
-
--------------------------------------------------------------------------------
--- Create ListFrame.button_up, then set its scripts and textures.
--- Parented to ScrollBar so it hides simultaneously.
--------------------------------------------------------------------------------
-local ScrollUpButton = CreateFrame("Button", nil, ScrollBar, "UIPanelScrollUpButtonTemplate")
-
-ScrollUpButton:SetHeight(16)
-ScrollUpButton:SetWidth(18)
-ScrollUpButton:SetPoint("BOTTOM", ScrollBar, "TOP", 0, -4)
-
--------------------------------------------------------------------------------
--- Create ListFrame.button_down, then set its scripts and textures.
--- Parented to ScrollBar so it hides simultaneously.
--------------------------------------------------------------------------------
-local ScrollDownButton = CreateFrame("Button", nil, ScrollBar,"UIPanelScrollDownButtonTemplate")
-
-ScrollDownButton:SetHeight(16)
-ScrollDownButton:SetWidth(18)
-ScrollDownButton:SetPoint("TOP", ScrollBar, "BOTTOM", 0, 4)
-
-do
-	-- Number of visible lines in the scrollframe.
-	local NUM_RECIPE_LINES = 25
-	local SCROLL_DEPTH = 5
-
-	local function ScrollBar_Scroll(delta)
-		if not ScrollBar:IsShown() then
-			return
-		end
-		local cur_val = ScrollBar:GetValue()
-		local min_val, max_val = ScrollBar:GetMinMaxValues()
-
-		if delta < 0 and cur_val < max_val then
-			cur_val = math.min(max_val, cur_val + SCROLL_DEPTH)
-			ScrollBar:SetValue(cur_val)
-		elseif delta > 0 and cur_val > min_val then
-			cur_val = math.max(min_val, cur_val - SCROLL_DEPTH)
-			ScrollBar:SetValue(cur_val)
-		end
-	end
-
-	ScrollUpButton:SetScript("OnClick",
-				 function(self, button, down)
-					 if _G.IsAltKeyDown() then
-						 local min_val = ScrollBar:GetMinMaxValues()
-						 ScrollBar:SetValue(min_val)
-					 else
-						 ScrollBar_Scroll(1)
-					 end
-				 end)
-
-	ScrollDownButton:SetScript("OnClick",
-				 function(self, button, down)
-					 if _G.IsAltKeyDown() then
-						 local _, max_val = ScrollBar:GetMinMaxValues()
-						 ScrollBar:SetValue(max_val)
-					 else
-						 ScrollBar_Scroll(-1)
-					 end
-				 end)
-
-	ScrollBar:SetScript("OnMouseWheel",
-			    function(self, delta)
-				    ScrollBar_Scroll(delta)
-			    end)
-
-	ListFrame:SetScript("OnMouseWheel",
-			    function(self, delta)
-				    ScrollBar_Scroll(delta)
-			    end)
-
-	-- This can be called either from ListFrame's OnMouseWheel script, manually
-	-- sliding the thumb, or from clicking the up/down buttons.
-	ScrollBar:SetScript("OnValueChanged",
-			    function(self, value)
-				    local min_val, max_val = self:GetMinMaxValues()
-				    local current_tab = MainPanel.tabs[MainPanel.current_tab]
-				    local member = "profession_"..MainPanel.profession.."_scroll_value"
-
-				    current_tab[member] = value
-
-				    if value == min_val then
-					    ScrollUpButton:Disable()
-					    ScrollDownButton:Enable()
-				    elseif value == max_val then
-					    ScrollUpButton:Enable()
-					    ScrollDownButton:Disable()
-				    else
-					    ScrollUpButton:Enable()
-					    ScrollDownButton:Enable()
-				    end
-				    ListFrame:Update(nil, true)
-			    end)
-
-	local function Button_OnEnter(self)
-		ListItem_ShowTooltip(self, ListFrame.entries[self.string_index])
-	end
-
-	local function Button_OnLeave()
-		QTip:Release(acquire_tip)
-		spell_tip:Hide()
-	end
-
-	local function Bar_OnEnter(self)
-		ListItem_ShowTooltip(self, ListFrame.entries[self.string_index])
-	end
-
-	local function Bar_OnLeave()
-		QTip:Release(acquire_tip)
-		spell_tip:Hide()
-	end
-
-	local function ListItem_OnClick(self, button, down)
-		local clickedIndex = self.string_index
-
-		-- Don't do anything if they've clicked on an empty button
-		if not clickedIndex or clickedIndex == 0 then
-			return
-		end
-		local clicked_line = ListFrame.entries[clickedIndex]
-		local traverseIndex = 0
-
-		if not clicked_line then
-			return
-		end
-		-- First, check if this is a "modified" click, and react appropriately
-		if clicked_line.recipe_id and _G.IsModifierKeyDown() then
-			if _G.IsControlKeyDown() and _G.IsShiftKeyDown() then
-				addon:AddWaypoint(clicked_line.recipe_id, clicked_line.acquire_id, clicked_line.location_id, clicked_line.npc_id)
-			elseif _G.IsShiftKeyDown() then
-				local itemID = private.recipe_list[clicked_line.recipe_id].item_id
-
-				if itemID then
-					local _, itemLink = _G.GetItemInfo(itemID)
-
-					if itemLink then
-						local edit_box = _G.ChatEdit_ChooseBoxForSend()
-
-						_G.ChatEdit_ActivateChat(edit_box)
-						edit_box:Insert(itemLink)
-					else
-						addon:Print(L["NoItemLink"])
-					end
-				else
-					addon:Print(L["NoItemLink"])
-				end
-			elseif _G.IsControlKeyDown() then
-				local edit_box = _G.ChatEdit_ChooseBoxForSend()
-
-				_G.ChatEdit_ActivateChat(edit_box)
-				edit_box:Insert(GetSpellLink(private.recipe_list[clicked_line.recipe_id].spell_id))
-			elseif _G.IsAltKeyDown() then
-				local exclusion_list = addon.db.profile.exclusionlist
-				local recipe_id = clicked_line.recipe_id
-
-				exclusion_list[recipe_id] = (not exclusion_list[recipe_id] and true or nil)
-				ListFrame:Update(nil, false)
-			end
-		elseif clicked_line.type == "header" or clicked_line.type == "subheader" then
-			-- three possibilities here (all with no modifiers)
-			-- 1) We clicked on the recipe button on a closed recipe
-			-- 2) We clicked on the recipe button of an open recipe
-			-- 3) we clicked on the expanded text of an open recipe
-			if clicked_line.is_expanded then
-				traverseIndex = clickedIndex + 1
-
-				local check_type = clicked_line.type
-				local entry = ListFrame.entries[traverseIndex]
-				local current_tab = MainPanel.tabs[MainPanel.current_tab]
-
-				-- get rid of our expanded lines
-				while entry and entry.type ~= check_type do
-					-- Headers are never removed.
-					if entry.type == "header" then
-						break
-					end
-					current_tab:ModifyEntry(entry, false)
-					ReleaseTable(table.remove(ListFrame.entries, traverseIndex))
-					entry = ListFrame.entries[traverseIndex]
-
-					if not entry then
-						break
-					end
-				end
-				current_tab:ModifyEntry(clicked_line, false)
-				clicked_line.is_expanded = false
-			else
-				ListFrame:ExpandEntry(clickedIndex)
-				clicked_line.is_expanded = true
-			end
-		else
-			-- clicked_line is an expanded entry - find the index for its parent, and remove all of the parent's child entries.
-			local parent = clicked_line.parent
-
-			if parent then
-				local parent_index
-				local entries = ListFrame.entries
-
-				for index = 1, #entries do
-					if entries[index] == parent then
-						parent_index = index
-						break
-					end
-				end
-
-				if not parent_index then
-					addon:Debug("clicked_line (%s): parent wasn't found in ListFrame.entries", clicked_line.text)
-					return
-				end
-				local current_tab = MainPanel.tabs[MainPanel.current_tab]
-
-				parent.is_expanded = false
-				current_tab:ModifyEntry(parent, false)
-
-				local child_index = parent_index + 1
-
-				while entries[child_index] and entries[child_index].parent == parent do
-					ReleaseTable(table.remove(entries, child_index))
-				end
-			else
-				addon:Debug("Error: clicked_line has no parent.")
-			end
-		end
-		QTip:Release(acquire_tip)
-		spell_tip:Hide()
-
-		ListFrame:Update(nil, true)
-	end
-	local LISTFRAME_WIDTH = ListFrame:GetWidth()
-
-	-------------------------------------------------------------------------------
-	-- Create the state/entry buttons and the container frames which hold them.
-	-------------------------------------------------------------------------------
-	for i = 1, NUM_RECIPE_LINES do
-		local cur_container = CreateFrame("Frame", nil, ListFrame)
-
-		cur_container:SetHeight(16)
-		cur_container:SetWidth(LISTFRAME_WIDTH)
-
-		local cur_state = GenericCreateButton(nil, ListFrame, 16, 16, nil, nil, nil, nil, 2)
-		local cur_entry = GenericCreateButton(nil, ListFrame, 16, LISTFRAME_WIDTH, "GameFontNormalSmall", "Blort", "LEFT", nil, 0)
-
-		if i == 1 then
-			cur_container:SetPoint("TOPLEFT", ListFrame, "TOPLEFT", 0, 0)
-			cur_state:SetPoint("TOPLEFT", cur_container, "TOPLEFT", 0, 0)
-			cur_entry:SetPoint("TOPLEFT", cur_state, "TOPRIGHT", -3, 0)
-		else
-			local prev_container = ListFrame.button_containers[i - 1]
-
-			cur_container:SetPoint("TOPLEFT", prev_container, "BOTTOMLEFT", 0, 3)
-			cur_state:SetPoint("TOPLEFT", cur_container, "TOPLEFT", 0, 0)
-			cur_entry:SetPoint("TOPLEFT", cur_state, "TOPRIGHT", -3, 0)
-		end
-		cur_state.container = cur_container
-
-		cur_state:SetScript("OnClick", ListItem_OnClick)
-		cur_entry:SetScript("OnClick", ListItem_OnClick)
-
-		ListFrame.button_containers[i] = cur_container
-		ListFrame.state_buttons[i] = cur_state
-		ListFrame.entry_buttons[i] = cur_entry
-	end
-
-	function ListFrame:InsertEntry(entry, parent_entry, entry_index, entry_type, entry_expanded, expand_mode)
-		entry.type = entry_type
-
-		if parent_entry then
-			if parent_entry ~= entry then
-				entry.parent = parent_entry
-
-				local recipe_id = parent_entry.recipe_id
-				local acquire_id = parent_entry.acquire_id
-				local location_id = parent_entry.location_id
-				local npc_id = parent_entry.npc_id
-
-				if recipe_id then
-					entry.recipe_id = recipe_id
-				end
-
-				if acquire_id then
-					entry.acquire_id = acquire_id
-				end
-
-				if location_id then
-					entry.location_id = location_id
-				end
-
-				if npc_id then
-					entry.npc_id = npc_id
-				end
-			else
-				addon:Debug("Attempting to parent an entry to itself.")
-			end
-		elseif entry.type ~= "header" then
-			addon:Debug("Non-header entry without a parent: %s - %s", entry.type, entry.text)
-		end
-		local insert_index = entry_index
-
-		-- If we have acquire information for this entry, push the data table into the list
-		-- and start processing the acquires.
-		if expand_mode then
-			local current_tab = MainPanel.tabs[MainPanel.current_tab]
-
-			entry.is_expanded = true
-			table.insert(self.entries, insert_index, entry)
-
-			current_tab:ModifyEntry(entry, entry_expanded)
-
-			if entry_type == "header" or entry_type == "subheader" then
-				insert_index = self:ExpandEntry(insert_index, expand_mode)
-			else
-				insert_index = insert_index + 1
-			end
-		else
-			entry.is_expanded = entry_expanded
-			table.insert(self.entries, insert_index, entry)
-
-			insert_index = insert_index + 1
-		end
-		return insert_index
-	end
-
-	function ListFrame:Initialize(expand_mode)
-		for i = 1, #self.entries do
-			ReleaseTable(self.entries[i])
-		end
-		table.wipe(self.entries)
-
-		addon:UpdateFilters(MainPanel.is_linked)
-
-		-------------------------------------------------------------------------------
-		-- Mark all exclusions in the recipe database to not be displayed, and update
-		-- the player's known and unknown counts.
-		-------------------------------------------------------------------------------
-		local exclusion_list = addon.db.profile.exclusionlist
-		local ignored = not addon.db.profile.ignoreexclusionlist
-		local recipe_list = private.recipe_list
-		local current_prof = ORDERED_PROFESSIONS[MainPanel.profession]
-		local known_count = 0
-		local unknown_count = 0
-
-		for spell_id in pairs(exclusion_list) do
-			local recipe = recipe_list[spell_id]
-
-			if recipe then
-				if recipe:HasState("KNOWN") and recipe.profession == current_prof then
-					known_count = known_count + 1
-				elseif recipe_profession == current_prof then
-					unknown_count = unknown_count + 1
-				end
-			end
-		end
-		Player.excluded_recipes_known = known_count
-		Player.excluded_recipes_unknown = unknown_count
-
-		-------------------------------------------------------------------------------
-		-- Initialize the expand button and entries for the current tab.
-		-------------------------------------------------------------------------------
-		local current_tab = MainPanel.tabs[addon.db.profile.current_tab]
-		local expanded_button = current_tab["expand_button_"..MainPanel.profession]
-
-		if expanded_button then
-			ExpandButton:Expand(current_tab)
-		else
-			ExpandButton:Contract(current_tab)
-		end
-		local recipe_count = current_tab:Initialize(expand_mode)
-
-		-------------------------------------------------------------------------------
-		-- Update the progress bar display.
-		-------------------------------------------------------------------------------
-		local profile = addon.db.profile
-		local max_value = profile.includefiltered and Player.recipes_total or Player.recipes_total_filtered
-		local cur_value = profile.includefiltered and Player.recipes_known or Player.recipes_known_filtered
-
-		if not profile.includeexcluded and not profile.ignoreexclusionlist then
-			max_value = max_value - Player.excluded_recipes_known
-		end
-		local progress_bar = MainPanel.progress_bar
-
-		progress_bar:SetMinMaxValues(0, max_value)
-		progress_bar:SetValue(cur_value)
-
-		local percentage = cur_value / max_value * 100
-
-		if (floor(percentage) < 101) and cur_value >= 0 and max_value >= 0 then
-			local results = string.format(_G.SINGLE_PAGE_RESULTS_TEMPLATE, recipe_count)
-			progress_bar.text:SetFormattedText("%d/%d - %1.2f%% (%s)", cur_value, max_value, percentage, results)
-		else
-			progress_bar.text:SetFormattedText("%s", L["NOT_YET_SCANNED"])
-		end
-	end
-
-	-- Reset the current buttons/lines
-	function ListFrame:ClearLines()
-		local font_object = addon.db.profile.frameopts.small_list_font and "GameFontNormalSmall" or "GameFontNormal"
-
-		for i = 1, NUM_RECIPE_LINES do
-			local entry = self.entry_buttons[i]
-			local state = self.state_buttons[i]
-
-			entry.string_index = 0
-			entry.text:SetFontObject(font_object)
-
-			entry:SetText("")
-			entry:SetScript("OnEnter", nil)
-			entry:SetScript("OnLeave", nil)
-			entry:SetWidth(LISTFRAME_WIDTH)
-			entry:Disable()
-
-			state.string_index = 0
-
-			state:Hide()
-			state:SetScript("OnEnter", nil)
-			state:SetScript("OnLeave", nil)
-			state:Disable()
-
-			state:ClearAllPoints()
-		end
-	end
-
-	function ListFrame:Update(expand_mode, refresh)
-		if not refresh then
-			self:Initialize(expand_mode)
-		end
-
-		local num_entries = #self.entries
-
-		if num_entries == 0 then
-			self:ClearLines()
-
-			-- disable expand button, it's useless here and would spam the same error again
-			ExpandButton:SetNormalFontObject("GameFontDisableSmall")
-			ExpandButton:Disable()
-			self.scroll_bar:Hide()
-
-			local showpopup = false
-
-			if not addon.db.profile.hidepopup then
-				showpopup = true
-			end
-
-			-- If we haven't run this before we'll show pop-ups for the first time.
-			if addon.db.profile.addonversion ~= addon.version then
-				addon.db.profile.addonversion = addon.version
-				showpopup = true
-			end
-			local editbox_text = SearchBox:GetText()
-
-			if Player.recipes_total == 0 then
-				if showpopup then
-					_G.StaticPopup_Show("ARL_NOTSCANNED")
-				end
-			elseif Player.recipes_known == Player.recipes_total then
-				if showpopup then
-					_G.StaticPopup_Show("ARL_ALLKNOWN")
-				end
-			elseif (Player.recipes_total_filtered - Player.recipes_known_filtered) == 0 then
-				if showpopup then
-					_G.StaticPopup_Show("ARL_ALLFILTERED")
-				end
-			elseif Player.excluded_recipes_unknown ~= 0 then
-				if showpopup then
-					_G.StaticPopup_Show("ARL_ALLEXCLUDED")
-				end
-			elseif editbox_text ~= "" and editbox_text ~= _G.SEARCH then
-				_G.StaticPopup_Show("ARL_SEARCHFILTERED")
-			else
-				addon:Print(L["NO_DISPLAY"])
-				addon:Debug("Current tab is %s", tostring(addon.db.profile.current_tab))
-				addon:Debug("recipes_total check for 0")
-				addon:Debug("recipes_total: " .. Player.recipes_total)
-				addon:Debug("recipes_total check for equal to recipes_total")
-				addon:Debug("recipes_known: " .. Player.recipes_known)
-				addon:Debug("recipes_total: " .. Player.recipes_total)
-				addon:Debug("recipes_total_filtered - recipes_known_filtered = 0")
-				addon:Debug("recipes_total_filtered: " .. Player.recipes_total_filtered)
-				addon:Debug("recipes_known_filtered: " .. Player.recipes_known_filtered)
-				addon:Debug("excluded_recipes_unknown ~= 0")
-				addon:Debug("excluded_recipes_unknown: " .. Player.excluded_recipes_unknown)
-			end
-			return
-		end
-		local offset = 0
-
-		addon:ClosePopups()
-
-		ExpandButton:SetNormalFontObject("GameFontNormalSmall")
-		ExpandButton:Enable()
-
-		if num_entries <= NUM_RECIPE_LINES then
-			self.scroll_bar:Hide()
-		else
-			local max_val = num_entries - NUM_RECIPE_LINES
-			local current_tab = MainPanel.tabs[MainPanel.current_tab]
-			local scroll_value = current_tab["profession_"..MainPanel.profession.."_scroll_value"] or 0
-
-			scroll_value = math.max(0, math.min(scroll_value, max_val))
-			offset = scroll_value
-
-			self.scroll_bar:SetMinMaxValues(0, math.max(0, max_val))
-			self.scroll_bar:SetValue(scroll_value)
-			self.scroll_bar:Show()
-		end
-		self:ClearLines()
-
-		local button_index = 1
-		local string_index = button_index + offset
-
-		-- Populate the buttons with new values
-		while button_index <= NUM_RECIPE_LINES and string_index <= num_entries do
-			local cur_state = self.state_buttons[button_index]
-			local cur_entry = self.entries[string_index]
-
-			if cur_entry.type == "header" or cur_entry.type == "subheader" then
-				cur_state:Show()
-
-				if cur_entry.is_expanded then
-					cur_state:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up")
-					cur_state:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down")
-					cur_state:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight")
-					cur_state:SetDisabledTexture("Interface\\Buttons\\UI-MinusButton-Disabled")
-				else
-					cur_state:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
-					cur_state:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down")
-					cur_state:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight")
-					cur_state:SetDisabledTexture("Interface\\Buttons\\UI-PlusButton-Disabled")
-				end
-				cur_state.string_index = string_index
-				cur_state:SetScript("OnEnter", Button_OnEnter)
-				cur_state:SetScript("OnLeave", Button_OnLeave)
-				cur_state:Enable()
-			else
-				cur_state:Hide()
-				cur_state:Disable()
-			end
-			local cur_container = cur_state.container
-			local cur_button = self.entry_buttons[button_index]
-
-			if cur_entry.type == "header" or cur_entry.type == "entry" then
-				cur_state:SetPoint("TOPLEFT", cur_container, "TOPLEFT", 0, 0)
-			elseif cur_entry.type == "subheader" or cur_entry.type == "subentry" then
-				cur_state:SetPoint("TOPLEFT", cur_container, "TOPLEFT", 15, 0)
-				cur_button:SetWidth(LISTFRAME_WIDTH - 15)
-			end
-			cur_button.string_index = string_index
-			cur_button:SetText(cur_entry.text)
-			cur_button:SetScript("OnEnter", Bar_OnEnter)
-			cur_button:SetScript("OnLeave", Bar_OnLeave)
-			cur_button:Enable()
-
-			button_index = button_index + 1
-			string_index = string_index + 1
-		end
-		button_index = 1
-		string_index = button_index + offset
-
-		-- This function could possibly have been called from a mouse click or by scrolling.
-		-- Since, in those cases, the list entries have changed, the mouse is likely over a different entry - a tooltip should be generated for it.
-		while button_index <= NUM_RECIPE_LINES and string_index <= num_entries do
-			local cur_state = self.state_buttons[button_index]
-			local cur_button = self.entry_buttons[button_index]
-
-			if cur_state:IsMouseOver() then
-				Button_OnEnter(cur_state)
-				break
-			elseif cur_button:IsMouseOver() then
-				Bar_OnEnter(cur_button)
-				break
-			end
-			button_index = button_index + 1
-			string_index = string_index + 1
-		end
-	end
-	-------------------------------------------------------------------------------
-	-- Functions and data pertaining to individual list entries.
-	-------------------------------------------------------------------------------
-	local faction_strings
-
-	local function CanDisplayFaction(faction)
-		if addon.db.profile.filters.general.faction then
-			return true
-		end
-		return (not faction or faction == BFAC[Player.faction] or faction == FACTION_NEUTRAL)
-	end
-
-	-- Padding for list entries/subentries
-	local PADDING = "    "
-
-	-- Changes the color of "name" based on faction type.
-	local function ColorNameByFaction(name, faction)
-		if faction == FACTION_NEUTRAL then
-			name = SetTextColor(private.reputation_colors["neutral"], name)
-		elseif faction == BFAC[Player.faction] then
-			name = SetTextColor(private.reputation_colors["exalted"], name)
-		else
-			name = SetTextColor(private.reputation_colors["hated"], name)
-		end
-		return name
-	end
-
-	local function ExpandTrainerData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-		local trainer = private.trainer_list[id_num]
-
-		if not CanDisplayFaction(trainer.faction) then
-			return entry_index
-		end
-
-		local name = ColorNameByFaction(trainer.name, trainer.faction)
-		local coord_text = ""
-
-		if trainer.coord_x ~= 0 and trainer.coord_y ~= 0 then
-			coord_text = SetTextColor(CATEGORY_COLORS["coords"], "(" .. trainer.coord_x .. ", " .. trainer.coord_y .. ")")
-		end
-		local t = AcquireTable()
-
-		t.text = string.format("%s%s %s", PADDING, hide_type and "" or SetTextColor(CATEGORY_COLORS["trainer"], L["Trainer"])..":", name)
-		t.recipe_id = recipe_id
-		t.npc_id = id_num
-
-		entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-
-		if coord_text == "" and hide_location then
-			return entry_index
-		end
-		t = AcquireTable()
-		t.text = string.format("%s%s%s %s", PADDING, PADDING, hide_location and "" or SetTextColor(CATEGORY_COLORS["location"], trainer.location), coord_text)
-		t.recipe_id = recipe_id
-		t.npc_id = id_num
-
-		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-	end
-
-	-- Right now PVP obtained items are located on vendors so they have the vendor and PVP flag.
-	-- We need to display the vendor in the drop down if we want to see vendors or if we want to see PVP
-	-- This allows us to select PVP only and to see just the PVP recipes
-	local function ExpandVendorData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-		local vendor = private.vendor_list[id_num]
-
-		if not CanDisplayFaction(vendor.faction) then
-			return entry_index
-		end
-
-		local name = ColorNameByFaction(vendor.name, vendor.faction)
-		local coord_text = ""
-
-		if vendor.coord_x ~= 0 and vendor.coord_y ~= 0 then
-			coord_text = SetTextColor(CATEGORY_COLORS["coords"], "(" .. vendor.coord_x .. ", " .. vendor.coord_y .. ")")
-		end
-		local t = AcquireTable()
-		local quantity = vendor.item_list[recipe_id]
-
-		t.text = string.format("%s%s %s%s", PADDING,
-				       hide_type and "" or SetTextColor(CATEGORY_COLORS["vendor"], L["Vendor"])..":", name,
-				       type(quantity) == "number" and SetTextColor(BASIC_COLORS["white"], string.format(" (%d)", quantity)) or "")
-		t.recipe_id = recipe_id
-		t.npc_id = id_num
-
-		entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-
-		if coord_text == "" and hide_location then
-			return entry_index
-		end
-		t = AcquireTable()
-		t.text = string.format("%s%s%s %s", PADDING, PADDING, hide_location and "" or SetTextColor(CATEGORY_COLORS["location"], vendor.location), coord_text)
-		t.recipe_id = recipe_id
-		t.npc_id = id_num
-
-		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-	end
-
-	-- Mobs can be in instances, raids, or specific mob related drops.
-	local function ExpandMobData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-		local mob = private.mob_list[id_num]
-		local coord_text = ""
-
-		if mob.coord_x ~= 0 and mob.coord_y ~= 0 then
-			coord_text = SetTextColor(CATEGORY_COLORS["coords"], "(" .. mob.coord_x .. ", " .. mob.coord_y .. ")")
-		end
-		local t = AcquireTable()
-
-		t.text = string.format("%s%s %s", PADDING, hide_type and "" or SetTextColor(CATEGORY_COLORS["mobdrop"], L["Mob Drop"])..":", SetTextColor(private.reputation_colors["hostile"], mob.name))
-		t.recipe_id = recipe_id
-		t.npc_id = id_num
-
-		entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-
-		if coord_text == "" and hide_location then
-			return entry_index
-		end
-		t = AcquireTable()
-		t.text = string.format("%s%s%s %s", PADDING, PADDING, hide_location and "" or SetTextColor(CATEGORY_COLORS["location"], mob.location), coord_text)
-		t.recipe_id = recipe_id
-		t.npc_id = id_num
-
-		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-	end
-
-	local function ExpandQuestData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-		local quest = private.quest_list[id_num]
-
-		if not CanDisplayFaction(quest.faction) then
-			return entry_index
-		end
-
-		local name = ColorNameByFaction(private.quest_names[id_num], quest.faction)
-		local coord_text = ""
-
-		if quest.coord_x ~= 0 and quest.coord_y ~= 0 then
-			coord_text = SetTextColor(CATEGORY_COLORS["coords"], "(" .. quest.coord_x .. ", " .. quest.coord_y .. ")")
-		end
-		local t = AcquireTable()
-
-		t.text = string.format("%s%s %s", PADDING, hide_type and "" or SetTextColor(CATEGORY_COLORS["quest"], L["Quest"])..":", name)
-		t.recipe_id = recipe_id
-
-		entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-
-		if coord_text == "" and hide_location then
-			return entry_index
-		end
-		t = AcquireTable()
-		t.text = string.format("%s%s%s %s", PADDING, PADDING, hide_location and "" or SetTextColor(CATEGORY_COLORS["location"], quest.location), coord_text)
-		t.recipe_id = recipe_id
-
-		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-	end
-
-	local function ExpandSeasonalData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-		local t = AcquireTable()
-
-		t.text = string.format("%s%s %s", PADDING, hide_type and "" or SetTextColor(CATEGORY_COLORS["seasonal"], private.acquire_names[A.SEASONAL])..":",
-				       SetTextColor(CATEGORY_COLORS["seasonal"], private.seasonal_list[id_num].name))
-		t.recipe_id = recipe_id
-
-		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-	end
-
-	local function ExpandReputationData(entry_index, entry_type, parent_entry, vendor_id, rep_id, rep_level, recipe_id, hide_location, hide_type)
-		local rep_vendor = private.vendor_list[vendor_id]
-
-		if not CanDisplayFaction(rep_vendor.faction) then
-			return entry_index
-		end
-
-		if not faction_strings then
-			local rep_color = private.reputation_colors
-
-			faction_strings = {
-				[0] = SetTextColor(rep_color["neutral"], FACTION_NEUTRAL .. " : "),
-				[1] = SetTextColor(rep_color["friendly"], BFAC["Friendly"] .. " : "),
-				[2] = SetTextColor(rep_color["honored"], BFAC["Honored"] .. " : "),
-				[3] = SetTextColor(rep_color["revered"], BFAC["Revered"] .. " : "),
-				[4] = SetTextColor(rep_color["exalted"], BFAC["Exalted"] .. " : ")
-			}
-		end
-
-		local name = ColorNameByFaction(rep_vendor.name, rep_vendor.faction)
-		local t = AcquireTable()
-
-		t.text = string.format("%s%s %s", PADDING, hide_type and "" or SetTextColor(CATEGORY_COLORS["reputation"], _G.REPUTATION)..":",
-				       SetTextColor(CATEGORY_COLORS["repname"], private.reputation_list[rep_id].name))
-		t.recipe_id = recipe_id
-		t.npc_id = vendor_id
-
-		entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-
-		t = AcquireTable()
-		t.text = PADDING .. PADDING .. faction_strings[rep_level] .. name
-		t.recipe_id = recipe_id
-		t.npc_id = vendor_id
-
-		entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-
-		local coord_text = ""
-
-		if rep_vendor.coord_x ~= 0 and rep_vendor.coord_y ~= 0 then
-			coord_text = SetTextColor(CATEGORY_COLORS["coords"], "(" .. rep_vendor.coord_x .. ", " .. rep_vendor.coord_y .. ")")
-		end
-
-		if coord_text == "" and hide_location then
-			return entry_index
-		end
-		t = AcquireTable()
-		t.text = string.format("%s%s%s%s %s", PADDING, PADDING, PADDING, hide_location and "" or SetTextColor(CATEGORY_COLORS["location"], rep_vendor.location), coord_text)
-		t.recipe_id = recipe_id
-		t.npc_id = vendor_id
-
-		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-	end
-
-	local function ExpandWorldDropData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-		local _, _, _, hex_color = GetItemQualityColor(private.recipe_list[recipe_id].quality)
-		local drop_location = type(id_num) == "string" and BZ[id_num] or nil
-
-		if drop_location then
-			drop_location = string.format(": %s", SetTextColor(CATEGORY_COLORS["location"], drop_location))
-		else
-			drop_location = ""
-		end
-		local t = AcquireTable()
-
-		t.text = string.format("%s%s%s|r%s", PADDING, hex_color, L["World Drop"], drop_location)
-		t.recipe_id = recipe_id
-
-		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-	end
-
-	local function ExpandCustomData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-		local t = AcquireTable()
-
-		t.text = PADDING .. SetTextColor(CATEGORY_COLORS["custom"], private.custom_list[id_num].name)
-		t.recipe_id = recipe_id
-
-		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-	end
-
-	function ListFrame:ExpandAcquireData(entry_index, entry_type, parent_entry, acquire_type, acquire_data, recipe_id, hide_location, hide_type)
-		local obtain_filters = addon.db.profile.filters.obtain
-
-		for id_num, info in pairs(acquire_data) do
-			if acquire_type == A.TRAINER and obtain_filters.trainer then
-				entry_index = ExpandTrainerData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-			elseif acquire_type == A.VENDOR and (obtain_filters.vendor or obtain_filters.pvp) then
-				entry_index = ExpandVendorData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-			elseif acquire_type == A.MOB_DROP and (obtain_filters.mobdrop or obtain_filters.instance or obtain_filters.raid) then
-				entry_index = ExpandMobData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-			elseif acquire_type == A.QUEST and obtain_filters.quest then
-				entry_index = ExpandQuestData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-			elseif acquire_type == A.SEASONAL and obtain_filters.seasonal then
-				entry_index = ExpandSeasonalData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-			elseif acquire_type == A.REPUTATION then
-				for rep_level, level_info in pairs(info) do
-					for vendor_id in pairs(level_info) do
-						entry_index =  ExpandReputationData(entry_index, entry_type, parent_entry, vendor_id, id_num, rep_level, recipe_id, hide_location, hide_type)
-					end
-				end
-			elseif acquire_type == A.WORLD_DROP and obtain_filters.worlddrop then
-				if not hide_type then
-					entry_index = ExpandWorldDropData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-				end
-			elseif acquire_type == A.CUSTOM then
-				if not hide_type then
-					entry_index = ExpandCustomData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
-				end
-				--@alpha@
-			elseif acquire_type > A_MAX then
-				local t = AcquireTable()
-
-				t.text = "Unhandled Acquire Case - Type: " .. acquire_type
-				t.recipe_id = recipe_id
-
-				entry_index = self:InsertEntry(t, parent_entry, entry_index, entry_type, true)
-				--@end-alpha@
-			end
-		end	-- for
-		return entry_index
-	end
-
-	-- This function is called when an un-expanded entry in the list has been clicked.
-	function ListFrame:ExpandEntry(entry_index, expand_mode)
-		local orig_index = entry_index
-		local current_entry = self.entries[orig_index]
-		local expand_all = expand_mode == "deep"
-		local search_box = MainPanel.search_editbox
-		local current_tab = MainPanel.tabs[MainPanel.current_tab]
-		local prof_name = ORDERED_PROFESSIONS[MainPanel.profession]
-
-		-- Entry_index is the position in self.entries that we want to expand. Since we are expanding the current entry, the return
-		-- value should be the index of the next button after the expansion occurs
-		entry_index = entry_index + 1
-
-		current_tab:ModifyEntry(current_entry, true)
-
-		-- This entry was generated using sorting based on Acquisition.
-		if current_entry.acquire_id then
-			local acquire_id = current_entry.acquire_id
-
-			if current_entry.type == "header" then
-				local recipe_list = private.acquire_list[acquire_id].recipes
-				local sorted_recipes = addon.sorted_recipes
-
-				private.SortRecipeList(recipe_list)
-
-				for index = 1, #sorted_recipes do
-					local spell_id = sorted_recipes[index]
-					local recipe_entry = private.recipe_list[spell_id]
-
-					if recipe_entry:HasState("VISIBLE") and search_box:MatchesRecipe(recipe_entry) then
-						local t = AcquireTable()
-						local expand = false
-						local type = "subheader"
-
-						if acquire_id == A.WORLD_DROP or acquire_id == A.CUSTOM then
-							expand = true
-							type = "entry"
-						end
-						local is_expanded = current_tab[prof_name.." expanded"][spell_id] and current_tab[prof_name.." expanded"][private.acquire_names[acquire_id]]
-
-						t.text = recipe_entry:GetDisplayName()
-						t.recipe_id = spell_id
-						t.acquire_id = acquire_id
-
-						entry_index = self:InsertEntry(t, current_entry, entry_index, type, expand or is_expanded, expand_all or is_expanded)
-					end
-				end
-			elseif current_entry.type == "subheader" then
-				for acquire_type, acquire_data in pairs(private.recipe_list[current_entry.recipe_id].acquire_data) do
-					if acquire_type == acquire_id then
-						entry_index = self:ExpandAcquireData(entry_index, "subentry", current_entry, acquire_type, acquire_data, current_entry.recipe_id, false, true)
-					end
-				end
-			end
-			return entry_index
-		end
-
-		-- This entry was generated using sorting based on Location.
-		if current_entry.location_id then
-			local location_id = current_entry.location_id
-
-			if current_entry.type == "header" then
-				local recipe_list = private.location_list[location_id].recipes
-				local sorted_recipes = addon.sorted_recipes
-
-				private.SortRecipeList(recipe_list)
-
-				for index = 1, #sorted_recipes do
-					local spell_id = sorted_recipes[index]
-					local recipe_entry = private.recipe_list[spell_id]
-
-					if recipe_entry:HasState("VISIBLE") and search_box:MatchesRecipe(recipe_entry) then
-						local expand = false
-						local type = "subheader"
-						local t = AcquireTable()
-
-						-- Add World Drop entries as normal entries.
-						if recipe_list[spell_id] == "world_drop" then
-							expand = true
-							type = "entry"
-						end
-						local is_expanded = current_tab[prof_name.." expanded"][spell_id] and current_tab[prof_name.." expanded"][location_id]
-
-						t.text = recipe_entry:GetDisplayName()
-						t.recipe_id = spell_id
-						t.location_id = location_id
-
-						entry_index = self:InsertEntry(t, current_entry, entry_index, type, expand or is_expanded, expand_all or is_expanded)
-					end
-				end
-			elseif current_entry.type == "subheader" then
-				local recipe_entry = private.recipe_list[current_entry.recipe_id]
-
-				-- World Drops are not handled here because they are of type "entry".
-				for acquire_type, acquire_data in pairs(recipe_entry.acquire_data) do
-					for id_num, info in pairs(acquire_data) do
-						-- Only expand an acquisition entry if it is from this location.
-						if acquire_type == A.TRAINER and private.trainer_list[id_num].location == location_id then
-							entry_index = ExpandTrainerData(entry_index, "subentry", current_entry,
-											id_num, current_entry.recipe_id, true)
-						elseif acquire_type == A.VENDOR and private.vendor_list[id_num].location == location_id then
-							entry_index = ExpandVendorData(entry_index, "subentry", current_entry,
-										       id_num, current_entry.recipe_id, true)
-						elseif acquire_type == A.MOB_DROP and private.mob_list[id_num].location == location_id then
-							entry_index = ExpandMobData(entry_index, "subentry", current_entry,
-										    id_num, current_entry.recipe_id, true)
-						elseif acquire_type == A.QUEST and private.quest_list[id_num].location == location_id then
-							entry_index = ExpandQuestData(entry_index, "subentry", current_entry,
-										      id_num, current_entry.recipe_id, true)
-						elseif acquire_type == A.SEASONAL and private.seasonal_list[id_num].location == location_id then
-							-- Hide the acquire type for this - it will already show up in the location list as "World Events".
-							entry_index = ExpandSeasonalData(entry_index, "subentry", current_entry,
-											 id_num, current_entry.recipe_id, true, true)
-						elseif acquire_type == A.CUSTOM and private.custom_list[id_num].location == location_id then
-							entry_index = ExpandCustomData(entry_index, "subentry", current_entry,
-										       id_num, current_entry.recipe_id, true, true)
-						elseif acquire_type == A.REPUTATION then
-							for rep_level, level_info in pairs(info) do
-								for vendor_id in pairs(level_info) do
-									if private.vendor_list[vendor_id].location == location_id then
-										entry_index =  ExpandReputationData(entry_index, "subentry", current_entry,
-														    vendor_id, id_num, rep_level, current_entry.recipe_id, true)
-									end
-								end
-							end
-						end
-					end
-				end
-			end
-			return entry_index
-		end
-
-		-- Normal entry - expand all acquire types.
-		local recipe_id = self.entries[orig_index].recipe_id
-
-		for acquire_type, acquire_data in pairs(private.recipe_list[recipe_id].acquire_data) do
-			entry_index = self:ExpandAcquireData(entry_index, "entry", current_entry,
-							     acquire_type, acquire_data, recipe_id)
-		end
-		return entry_index
-	end
-end	-- do
-
--------------------------------------------------------------------------------
 -- Create MainPanel.progress_bar and set its scripts
 -------------------------------------------------------------------------------
 do
@@ -2933,6 +1402,7 @@ local function InitializeFrame()
 	-------------------------------------------------------------------------------
 	-- Initialize components defined in other files.
 	-------------------------------------------------------------------------------
+	private.InitializeListFrame()
 	private.InitializeTabs()

 	-------------------------------------------------------------------------------
@@ -3153,7 +1623,7 @@ local function InitializeFrame()
 						       general_frame[class]:SetChecked(true)
 					       end
 					       MainPanel:UpdateTitle()
-					       ListFrame:Update(nil, false)
+					       MainPanel.list_frame:Update(nil, false)
 				       end)

 		general_frame.class_toggle = class_toggle
@@ -3244,7 +1714,7 @@ local function InitializeFrame()
 						       item_frame[armor]:SetChecked(toggle)
 					       end
 					       MainPanel:UpdateTitle()
-					       ListFrame:Update(nil, false)
+					       MainPanel.list_frame:Update(nil, false)
 				       end)

 		item_frame.armor_toggle = armor_toggle
@@ -3284,7 +1754,7 @@ local function InitializeFrame()
 							end
 						end
 						MainPanel:UpdateTitle()
-						ListFrame:Update(nil, false)
+						MainPanel.list_frame:Update(nil, false)
 					end)

 		item_frame.weapon_toggle = weapon_toggle
@@ -3520,7 +1990,7 @@ local function InitializeFrame()
 							    expansion0_frame[reputation]:SetChecked(filterdb[reputation])
 						    end
 						    MainPanel:UpdateTitle()
-						    ListFrame:Update(nil, false)
+						    MainPanel.list_frame:Update(nil, false)
 					    end)
 	end	-- do-block

@@ -3583,7 +2053,7 @@ local function InitializeFrame()
 							    expansion1_frame[reputation]:SetChecked(filterdb[reputation])
 						    end
 						    MainPanel:UpdateTitle()
-						    ListFrame:Update(nil, false)
+						    MainPanel.list_frame:Update(nil, false)
 					    end)
 	end	-- do-block

@@ -3656,7 +2126,7 @@ local function InitializeFrame()
 							    expansion2_frame[reputation]:SetChecked(filterdb[reputation])
 						    end
 						    MainPanel:UpdateTitle()
-						    ListFrame:Update(nil, false)
+						    MainPanel.list_frame:Update(nil, false)
 					   end)
 	end	-- do-block

@@ -3902,7 +2372,7 @@ do
 		editbox:SetText(editbox.prev_search or _G.SEARCH)

 		-- If there is no current tab, this is the first time the panel has been
-		-- shown so things must be initialized. In this case, ListFrame:Update()
+		-- shown so things must be initialized. In this case, MainPanel.list_frame:Update()
 		-- will be called by the tab's OnClick handler.
 		if not self.current_tab then
 			local current_tab = self.tabs[addon.db.profile.current_tab]
@@ -3912,7 +2382,7 @@ do

 			self.current_tab = addon.db.profile.current_tab
 		else
-			ListFrame:Update(nil, false)
+			MainPanel.list_frame:Update(nil, false)
 		end
 		self.sort_button:SetTextures()
 		self.filter_toggle:SetTextures()
diff --git a/Interface/List.lua b/Interface/List.lua
new file mode 100644
index 0000000..da652ab
--- /dev/null
+++ b/Interface/List.lua
@@ -0,0 +1,1566 @@
+-------------------------------------------------------------------------------
+-- Localized Lua globals.
+-------------------------------------------------------------------------------
+local _G = getfenv(0)
+
+local string = _G.string
+local table = _G.table
+
+-------------------------------------------------------------------------------
+-- AddOn namespace.
+-------------------------------------------------------------------------------
+local LibStub = LibStub
+
+local MODNAME	= "Ackis Recipe List"
+local addon	= LibStub("AceAddon-3.0"):GetAddon(MODNAME)
+
+local BFAC	= LibStub("LibBabble-Faction-3.0"):GetLookupTable()
+local BZ	= LibStub("LibBabble-Zone-3.0"):GetLookupTable()
+local L		= LibStub("AceLocale-3.0"):GetLocale(MODNAME)
+local QTip	= LibStub("LibQTip-1.0")
+
+-- Set up the private intra-file namespace.
+local private	= select(2, ...)
+
+local Player	= private.Player
+
+-------------------------------------------------------------------------------
+-- Constants
+-------------------------------------------------------------------------------
+local NUM_RECIPE_LINES	= 25
+local SCROLL_DEPTH	= 5
+local LISTFRAME_WIDTH	= 295
+
+local CATEGORY_COLORS	= private.category_colors
+local BASIC_COLORS	= private.basic_colors
+
+local SF		= private.recipe_state_flags
+local COMMON_FLAGS_1	= private.common_flags_word1
+
+local A			= private.acquire_types
+local A_MAX		= 9
+
+local FACTION_NEUTRAL	= BFAC["Neutral"]
+
+-------------------------------------------------------------------------------
+-- Upvalues
+-------------------------------------------------------------------------------
+local ListItem_ShowTooltip
+
+local acquire_tip
+local spell_tip
+
+local AcquireTable = private.AcquireTable
+local ReleaseTable = private.ReleaseTable
+local SetTextColor = private.SetTextColor
+local GenericCreateButton = private.GenericCreateButton
+
+-------------------------------------------------------------------------------
+-- Frame creation and anchoring
+-------------------------------------------------------------------------------
+function private.InitializeListFrame()
+	local MainPanel	= addon.Frame
+	local ListFrame = CreateFrame("Frame", nil, MainPanel)
+
+	MainPanel.list_frame = ListFrame
+
+	ListFrame:SetHeight(335)
+	ListFrame:SetWidth(LISTFRAME_WIDTH)
+	ListFrame:SetPoint("TOPLEFT", MainPanel, "TOPLEFT", 22, -75)
+	ListFrame:SetBackdrop({
+				      bgFile = [[Interface\DialogFrame\UI-DialogBox-Background-Dark]],
+				      tile = true,
+				      tileSize = 16,
+			      })
+	ListFrame:SetBackdropColor(1, 1, 1)
+	ListFrame:EnableMouse(true)
+	ListFrame:EnableMouseWheel(true)
+
+	-------------------------------------------------------------------------------
+	-- Scroll bar.
+	-------------------------------------------------------------------------------
+	local ScrollBar = CreateFrame("Slider", nil, ListFrame)
+
+	ScrollBar:SetPoint("TOPLEFT", ListFrame, "TOPRIGHT", 5, -11)
+	ScrollBar:SetPoint("BOTTOMLEFT", ListFrame, "BOTTOMRIGHT", 5, 12)
+	ScrollBar:SetWidth(24)
+
+	ScrollBar:EnableMouseWheel(true)
+	ScrollBar:SetOrientation("VERTICAL")
+
+	ScrollBar:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob")
+	ScrollBar:SetMinMaxValues(0, 1)
+	ScrollBar:SetValueStep(1)
+
+	ListFrame.scroll_bar = ScrollBar
+
+	local ScrollUpButton = CreateFrame("Button", nil, ScrollBar, "UIPanelScrollUpButtonTemplate")
+
+	ScrollUpButton:SetHeight(16)
+	ScrollUpButton:SetWidth(18)
+	ScrollUpButton:SetPoint("BOTTOM", ScrollBar, "TOP", 0, -4)
+
+	local ScrollDownButton = CreateFrame("Button", nil, ScrollBar,"UIPanelScrollDownButtonTemplate")
+
+	ScrollDownButton:SetHeight(16)
+	ScrollDownButton:SetWidth(18)
+	ScrollDownButton:SetPoint("TOP", ScrollBar, "BOTTOM", 0, 4)
+
+	local function ScrollBar_Scroll(delta)
+		if not ScrollBar:IsShown() then
+			return
+		end
+		local cur_val = ScrollBar:GetValue()
+		local min_val, max_val = ScrollBar:GetMinMaxValues()
+
+		if delta < 0 and cur_val < max_val then
+			cur_val = math.min(max_val, cur_val + SCROLL_DEPTH)
+			ScrollBar:SetValue(cur_val)
+		elseif delta > 0 and cur_val > min_val then
+			cur_val = math.max(min_val, cur_val - SCROLL_DEPTH)
+			ScrollBar:SetValue(cur_val)
+		end
+	end
+
+	ScrollUpButton:SetScript("OnClick",
+				 function(self, button, down)
+					 if _G.IsAltKeyDown() then
+						 local min_val = ScrollBar:GetMinMaxValues()
+						 ScrollBar:SetValue(min_val)
+					 else
+						 ScrollBar_Scroll(1)
+					 end
+				 end)
+
+	ScrollDownButton:SetScript("OnClick",
+				   function(self, button, down)
+					   if _G.IsAltKeyDown() then
+						   local _, max_val = ScrollBar:GetMinMaxValues()
+						   ScrollBar:SetValue(max_val)
+					   else
+						   ScrollBar_Scroll(-1)
+					   end
+				   end)
+
+	ScrollBar:SetScript("OnMouseWheel",
+			    function(self, delta)
+				    ScrollBar_Scroll(delta)
+			    end)
+
+	ListFrame:SetScript("OnMouseWheel",
+			    function(self, delta)
+				    ScrollBar_Scroll(delta)
+			    end)
+
+	-- This can be called either from ListFrame's OnMouseWheel script, manually
+	-- sliding the thumb, or from clicking the up/down buttons.
+	ScrollBar:SetScript("OnValueChanged",
+			    function(self, value)
+				    local min_val, max_val = self:GetMinMaxValues()
+				    local current_tab = MainPanel.tabs[MainPanel.current_tab]
+				    local member = "profession_"..MainPanel.profession.."_scroll_value"
+
+				    current_tab[member] = value
+
+				    if value == min_val then
+					    ScrollUpButton:Disable()
+					    ScrollDownButton:Enable()
+				    elseif value == max_val then
+					    ScrollUpButton:Enable()
+					    ScrollDownButton:Disable()
+				    else
+					    ScrollUpButton:Enable()
+					    ScrollDownButton:Enable()
+				    end
+				    ListFrame:Update(nil, true)
+			    end)
+
+	local function Button_OnEnter(self)
+		ListItem_ShowTooltip(self, ListFrame.entries[self.string_index])
+	end
+
+	local function Button_OnLeave()
+		QTip:Release(acquire_tip)
+		spell_tip:Hide()
+	end
+
+	local function Bar_OnEnter(self)
+		ListItem_ShowTooltip(self, ListFrame.entries[self.string_index])
+	end
+
+	local function Bar_OnLeave()
+		QTip:Release(acquire_tip)
+		spell_tip:Hide()
+	end
+
+	local function ListItem_OnClick(self, button, down)
+		local clickedIndex = self.string_index
+
+		-- Don't do anything if they've clicked on an empty button
+		if not clickedIndex or clickedIndex == 0 then
+			return
+		end
+		local clicked_line = ListFrame.entries[clickedIndex]
+		local traverseIndex = 0
+
+		if not clicked_line then
+			return
+		end
+		-- First, check if this is a "modified" click, and react appropriately
+		if clicked_line.recipe_id and _G.IsModifierKeyDown() then
+			if _G.IsControlKeyDown() and _G.IsShiftKeyDown() then
+				addon:AddWaypoint(clicked_line.recipe_id, clicked_line.acquire_id, clicked_line.location_id, clicked_line.npc_id)
+			elseif _G.IsShiftKeyDown() then
+				local itemID = private.recipe_list[clicked_line.recipe_id].item_id
+
+				if itemID then
+					local _, itemLink = _G.GetItemInfo(itemID)
+
+					if itemLink then
+						local edit_box = _G.ChatEdit_ChooseBoxForSend()
+
+						_G.ChatEdit_ActivateChat(edit_box)
+						edit_box:Insert(itemLink)
+					else
+						addon:Print(L["NoItemLink"])
+					end
+				else
+					addon:Print(L["NoItemLink"])
+				end
+			elseif _G.IsControlKeyDown() then
+				local edit_box = _G.ChatEdit_ChooseBoxForSend()
+
+				_G.ChatEdit_ActivateChat(edit_box)
+				edit_box:Insert(GetSpellLink(private.recipe_list[clicked_line.recipe_id].spell_id))
+			elseif _G.IsAltKeyDown() then
+				local exclusion_list = addon.db.profile.exclusionlist
+				local recipe_id = clicked_line.recipe_id
+
+				exclusion_list[recipe_id] = (not exclusion_list[recipe_id] and true or nil)
+				ListFrame:Update(nil, false)
+			end
+		elseif clicked_line.type == "header" or clicked_line.type == "subheader" then
+			-- three possibilities here (all with no modifiers)
+			-- 1) We clicked on the recipe button on a closed recipe
+			-- 2) We clicked on the recipe button of an open recipe
+			-- 3) we clicked on the expanded text of an open recipe
+			if clicked_line.is_expanded then
+				traverseIndex = clickedIndex + 1
+
+				local check_type = clicked_line.type
+				local entry = ListFrame.entries[traverseIndex]
+				local current_tab = MainPanel.tabs[MainPanel.current_tab]
+
+				-- get rid of our expanded lines
+				while entry and entry.type ~= check_type do
+					-- Headers are never removed.
+					if entry.type == "header" then
+						break
+					end
+					current_tab:ModifyEntry(entry, false)
+					ReleaseTable(table.remove(ListFrame.entries, traverseIndex))
+					entry = ListFrame.entries[traverseIndex]
+
+					if not entry then
+						break
+					end
+				end
+				current_tab:ModifyEntry(clicked_line, false)
+				clicked_line.is_expanded = false
+			else
+				ListFrame:ExpandEntry(clickedIndex)
+				clicked_line.is_expanded = true
+			end
+		else
+			-- clicked_line is an expanded entry - find the index for its parent, and remove all of the parent's child entries.
+			local parent = clicked_line.parent
+
+			if parent then
+				local parent_index
+				local entries = ListFrame.entries
+
+				for index = 1, #entries do
+					if entries[index] == parent then
+						parent_index = index
+						break
+					end
+				end
+
+				if not parent_index then
+					addon:Debug("clicked_line (%s): parent wasn't found in ListFrame.entries", clicked_line.text)
+					return
+				end
+				local current_tab = MainPanel.tabs[MainPanel.current_tab]
+
+				parent.is_expanded = false
+				current_tab:ModifyEntry(parent, false)
+
+				local child_index = parent_index + 1
+
+				while entries[child_index] and entries[child_index].parent == parent do
+					ReleaseTable(table.remove(entries, child_index))
+				end
+			else
+				addon:Debug("Error: clicked_line has no parent.")
+			end
+		end
+		QTip:Release(acquire_tip)
+		spell_tip:Hide()
+
+		ListFrame:Update(nil, true)
+	end
+
+	-------------------------------------------------------------------------------
+	-- The state and entry buttons and the container frames which hold them.
+	-------------------------------------------------------------------------------
+	ListFrame.entries = {}
+	ListFrame.button_containers = {}
+	ListFrame.state_buttons = {}
+	ListFrame.entry_buttons = {}
+
+	for i = 1, NUM_RECIPE_LINES do
+		local cur_container = CreateFrame("Frame", nil, ListFrame)
+
+		cur_container:SetHeight(16)
+		cur_container:SetWidth(LISTFRAME_WIDTH)
+
+		local cur_state = GenericCreateButton(nil, ListFrame, 16, 16, nil, nil, nil, nil, 2)
+		local cur_entry = GenericCreateButton(nil, ListFrame, 16, LISTFRAME_WIDTH, "GameFontNormalSmall", "Blort", "LEFT", nil, 0)
+
+		if i == 1 then
+			cur_container:SetPoint("TOPLEFT", ListFrame, "TOPLEFT", 0, 0)
+			cur_state:SetPoint("TOPLEFT", cur_container, "TOPLEFT", 0, 0)
+			cur_entry:SetPoint("TOPLEFT", cur_state, "TOPRIGHT", -3, 0)
+		else
+			local prev_container = ListFrame.button_containers[i - 1]
+
+			cur_container:SetPoint("TOPLEFT", prev_container, "BOTTOMLEFT", 0, 3)
+			cur_state:SetPoint("TOPLEFT", cur_container, "TOPLEFT", 0, 0)
+			cur_entry:SetPoint("TOPLEFT", cur_state, "TOPRIGHT", -3, 0)
+		end
+		cur_state.container = cur_container
+
+		cur_state:SetScript("OnClick", ListItem_OnClick)
+		cur_entry:SetScript("OnClick", ListItem_OnClick)
+
+		ListFrame.button_containers[i] = cur_container
+		ListFrame.state_buttons[i] = cur_state
+		ListFrame.entry_buttons[i] = cur_entry
+	end
+
+	function ListFrame:InsertEntry(entry, parent_entry, entry_index, entry_type, entry_expanded, expand_mode)
+		entry.type = entry_type
+
+		if parent_entry then
+			if parent_entry ~= entry then
+				entry.parent = parent_entry
+
+				local recipe_id = parent_entry.recipe_id
+				local acquire_id = parent_entry.acquire_id
+				local location_id = parent_entry.location_id
+				local npc_id = parent_entry.npc_id
+
+				if recipe_id then
+					entry.recipe_id = recipe_id
+				end
+
+				if acquire_id then
+					entry.acquire_id = acquire_id
+				end
+
+				if location_id then
+					entry.location_id = location_id
+				end
+
+				if npc_id then
+					entry.npc_id = npc_id
+				end
+			else
+				addon:Debug("Attempting to parent an entry to itself.")
+			end
+		elseif entry.type ~= "header" then
+			addon:Debug("Non-header entry without a parent: %s - %s", entry.type, entry.text)
+		end
+		local insert_index = entry_index
+
+		-- If we have acquire information for this entry, push the data table into the list
+		-- and start processing the acquires.
+		if expand_mode then
+			local current_tab = MainPanel.tabs[MainPanel.current_tab]
+
+			entry.is_expanded = true
+			table.insert(self.entries, insert_index, entry)
+
+			current_tab:ModifyEntry(entry, entry_expanded)
+
+			if entry_type == "header" or entry_type == "subheader" then
+				insert_index = self:ExpandEntry(insert_index, expand_mode)
+			else
+				insert_index = insert_index + 1
+			end
+		else
+			entry.is_expanded = entry_expanded
+			table.insert(self.entries, insert_index, entry)
+
+			insert_index = insert_index + 1
+		end
+		return insert_index
+	end
+
+	function ListFrame:Initialize(expand_mode)
+		for i = 1, #self.entries do
+			ReleaseTable(self.entries[i])
+		end
+		table.wipe(self.entries)
+
+		addon:UpdateFilters(MainPanel.is_linked)
+
+		-------------------------------------------------------------------------------
+		-- Mark all exclusions in the recipe database to not be displayed, and update
+		-- the player's known and unknown counts.
+		-------------------------------------------------------------------------------
+		local exclusion_list = addon.db.profile.exclusionlist
+		local ignored = not addon.db.profile.ignoreexclusionlist
+		local recipe_list = private.recipe_list
+		local current_prof = private.ordered_professions[MainPanel.profession]
+		local known_count = 0
+		local unknown_count = 0
+
+		for spell_id in pairs(exclusion_list) do
+			local recipe = recipe_list[spell_id]
+
+			if recipe then
+				if recipe:HasState("KNOWN") and recipe.profession == current_prof then
+					known_count = known_count + 1
+				elseif recipe_profession == current_prof then
+					unknown_count = unknown_count + 1
+				end
+			end
+		end
+		Player.excluded_recipes_known = known_count
+		Player.excluded_recipes_unknown = unknown_count
+
+		-------------------------------------------------------------------------------
+		-- Initialize the expand button and entries for the current tab.
+		-------------------------------------------------------------------------------
+		local current_tab = MainPanel.tabs[addon.db.profile.current_tab]
+		local expanded_button = current_tab["expand_button_"..MainPanel.profession]
+
+		if expanded_button then
+			MainPanel.expand_button:Expand(current_tab)
+		else
+			MainPanel.expand_button:Contract(current_tab)
+		end
+		local recipe_count = current_tab:Initialize(expand_mode)
+
+		-------------------------------------------------------------------------------
+		-- Update the progress bar display.
+		-------------------------------------------------------------------------------
+		local profile = addon.db.profile
+		local max_value = profile.includefiltered and Player.recipes_total or Player.recipes_total_filtered
+		local cur_value = profile.includefiltered and Player.recipes_known or Player.recipes_known_filtered
+
+		if not profile.includeexcluded and not profile.ignoreexclusionlist then
+			max_value = max_value - Player.excluded_recipes_known
+		end
+		local progress_bar = MainPanel.progress_bar
+
+		progress_bar:SetMinMaxValues(0, max_value)
+		progress_bar:SetValue(cur_value)
+
+		local percentage = cur_value / max_value * 100
+
+		if (floor(percentage) < 101) and cur_value >= 0 and max_value >= 0 then
+			local results = string.format(_G.SINGLE_PAGE_RESULTS_TEMPLATE, recipe_count)
+			progress_bar.text:SetFormattedText("%d/%d - %1.2f%% (%s)", cur_value, max_value, percentage, results)
+		else
+			progress_bar.text:SetFormattedText("%s", L["NOT_YET_SCANNED"])
+		end
+	end
+
+	-- Reset the current buttons/lines
+	function ListFrame:ClearLines()
+		local font_object = addon.db.profile.frameopts.small_list_font and "GameFontNormalSmall" or "GameFontNormal"
+
+		for i = 1, NUM_RECIPE_LINES do
+			local entry = self.entry_buttons[i]
+			local state = self.state_buttons[i]
+
+			entry.string_index = 0
+			entry.text:SetFontObject(font_object)
+
+			entry:SetText("")
+			entry:SetScript("OnEnter", nil)
+			entry:SetScript("OnLeave", nil)
+			entry:SetWidth(LISTFRAME_WIDTH)
+			entry:Disable()
+
+			state.string_index = 0
+
+			state:Hide()
+			state:SetScript("OnEnter", nil)
+			state:SetScript("OnLeave", nil)
+			state:Disable()
+
+			state:ClearAllPoints()
+		end
+	end
+
+	function ListFrame:Update(expand_mode, refresh)
+		if not refresh then
+			self:Initialize(expand_mode)
+		end
+
+		local num_entries = #self.entries
+
+		if num_entries == 0 then
+			self:ClearLines()
+
+			-- disable expand button, it's useless here and would spam the same error again
+			MainPanel.expand_button:SetNormalFontObject("GameFontDisableSmall")
+			MainPanel.expand_button:Disable()
+			self.scroll_bar:Hide()
+
+			local showpopup = false
+
+			if not addon.db.profile.hidepopup then
+				showpopup = true
+			end
+
+			-- If we haven't run this before we'll show pop-ups for the first time.
+			if addon.db.profile.addonversion ~= addon.version then
+				addon.db.profile.addonversion = addon.version
+				showpopup = true
+			end
+			local editbox_text = SearchBox:GetText()
+
+			if Player.recipes_total == 0 then
+				if showpopup then
+					_G.StaticPopup_Show("ARL_NOTSCANNED")
+				end
+			elseif Player.recipes_known == Player.recipes_total then
+				if showpopup then
+					_G.StaticPopup_Show("ARL_ALLKNOWN")
+				end
+			elseif (Player.recipes_total_filtered - Player.recipes_known_filtered) == 0 then
+				if showpopup then
+					_G.StaticPopup_Show("ARL_ALLFILTERED")
+				end
+			elseif Player.excluded_recipes_unknown ~= 0 then
+				if showpopup then
+					_G.StaticPopup_Show("ARL_ALLEXCLUDED")
+				end
+			elseif editbox_text ~= "" and editbox_text ~= _G.SEARCH then
+				_G.StaticPopup_Show("ARL_SEARCHFILTERED")
+			else
+				addon:Print(L["NO_DISPLAY"])
+				addon:Debug("Current tab is %s", tostring(addon.db.profile.current_tab))
+				addon:Debug("recipes_total check for 0")
+				addon:Debug("recipes_total: " .. Player.recipes_total)
+				addon:Debug("recipes_total check for equal to recipes_total")
+				addon:Debug("recipes_known: " .. Player.recipes_known)
+				addon:Debug("recipes_total: " .. Player.recipes_total)
+				addon:Debug("recipes_total_filtered - recipes_known_filtered = 0")
+				addon:Debug("recipes_total_filtered: " .. Player.recipes_total_filtered)
+				addon:Debug("recipes_known_filtered: " .. Player.recipes_known_filtered)
+				addon:Debug("excluded_recipes_unknown ~= 0")
+				addon:Debug("excluded_recipes_unknown: " .. Player.excluded_recipes_unknown)
+			end
+			return
+		end
+		local offset = 0
+
+		addon:ClosePopups()
+
+		MainPanel.expand_button:SetNormalFontObject("GameFontNormalSmall")
+		MainPanel.expand_button:Enable()
+
+		if num_entries <= NUM_RECIPE_LINES then
+			self.scroll_bar:Hide()
+		else
+			local max_val = num_entries - NUM_RECIPE_LINES
+			local current_tab = MainPanel.tabs[MainPanel.current_tab]
+			local scroll_value = current_tab["profession_"..MainPanel.profession.."_scroll_value"] or 0
+
+			scroll_value = math.max(0, math.min(scroll_value, max_val))
+			offset = scroll_value
+
+			self.scroll_bar:SetMinMaxValues(0, math.max(0, max_val))
+			self.scroll_bar:SetValue(scroll_value)
+			self.scroll_bar:Show()
+		end
+		self:ClearLines()
+
+		local button_index = 1
+		local string_index = button_index + offset
+
+		-- Populate the buttons with new values
+		while button_index <= NUM_RECIPE_LINES and string_index <= num_entries do
+			local cur_state = self.state_buttons[button_index]
+			local cur_entry = self.entries[string_index]
+
+			if cur_entry.type == "header" or cur_entry.type == "subheader" then
+				cur_state:Show()
+
+				if cur_entry.is_expanded then
+					cur_state:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up")
+					cur_state:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down")
+					cur_state:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight")
+					cur_state:SetDisabledTexture("Interface\\Buttons\\UI-MinusButton-Disabled")
+				else
+					cur_state:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
+					cur_state:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down")
+					cur_state:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight")
+					cur_state:SetDisabledTexture("Interface\\Buttons\\UI-PlusButton-Disabled")
+				end
+				cur_state.string_index = string_index
+				cur_state:SetScript("OnEnter", Button_OnEnter)
+				cur_state:SetScript("OnLeave", Button_OnLeave)
+				cur_state:Enable()
+			else
+				cur_state:Hide()
+				cur_state:Disable()
+			end
+			local cur_container = cur_state.container
+			local cur_button = self.entry_buttons[button_index]
+
+			if cur_entry.type == "header" or cur_entry.type == "entry" then
+				cur_state:SetPoint("TOPLEFT", cur_container, "TOPLEFT", 0, 0)
+			elseif cur_entry.type == "subheader" or cur_entry.type == "subentry" then
+				cur_state:SetPoint("TOPLEFT", cur_container, "TOPLEFT", 15, 0)
+				cur_button:SetWidth(LISTFRAME_WIDTH - 15)
+			end
+			cur_button.string_index = string_index
+			cur_button:SetText(cur_entry.text)
+			cur_button:SetScript("OnEnter", Bar_OnEnter)
+			cur_button:SetScript("OnLeave", Bar_OnLeave)
+			cur_button:Enable()
+
+			button_index = button_index + 1
+			string_index = string_index + 1
+		end
+		button_index = 1
+		string_index = button_index + offset
+
+		-- This function could possibly have been called from a mouse click or by scrolling.
+		-- Since, in those cases, the list entries have changed, the mouse is likely over a different entry - a tooltip should be generated for it.
+		while button_index <= NUM_RECIPE_LINES and string_index <= num_entries do
+			local cur_state = self.state_buttons[button_index]
+			local cur_button = self.entry_buttons[button_index]
+
+			if cur_state:IsMouseOver() then
+				Button_OnEnter(cur_state)
+				break
+			elseif cur_button:IsMouseOver() then
+				Bar_OnEnter(cur_button)
+				break
+			end
+			button_index = button_index + 1
+			string_index = string_index + 1
+		end
+	end
+
+	-------------------------------------------------------------------------------
+	-- Functions and data pertaining to individual list entries.
+	-------------------------------------------------------------------------------
+	local faction_strings
+
+	local function CanDisplayFaction(faction)
+		if addon.db.profile.filters.general.faction then
+			return true
+		end
+		return (not faction or faction == BFAC[Player.faction] or faction == FACTION_NEUTRAL)
+	end
+
+	-- Padding for list entries/subentries
+	local PADDING = "    "
+
+	-- Changes the color of "name" based on faction type.
+	local function ColorNameByFaction(name, faction)
+		if faction == FACTION_NEUTRAL then
+			name = SetTextColor(private.reputation_colors["neutral"], name)
+		elseif faction == BFAC[Player.faction] then
+			name = SetTextColor(private.reputation_colors["exalted"], name)
+		else
+			name = SetTextColor(private.reputation_colors["hated"], name)
+		end
+		return name
+	end
+
+	local function ExpandTrainerData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+		local trainer = private.trainer_list[id_num]
+
+		if not CanDisplayFaction(trainer.faction) then
+			return entry_index
+		end
+
+		local name = ColorNameByFaction(trainer.name, trainer.faction)
+		local coord_text = ""
+
+		if trainer.coord_x ~= 0 and trainer.coord_y ~= 0 then
+			coord_text = SetTextColor(CATEGORY_COLORS["coords"], "(" .. trainer.coord_x .. ", " .. trainer.coord_y .. ")")
+		end
+		local t = AcquireTable()
+
+		t.text = string.format("%s%s %s", PADDING, hide_type and "" or SetTextColor(CATEGORY_COLORS["trainer"], L["Trainer"])..":", name)
+		t.recipe_id = recipe_id
+		t.npc_id = id_num
+
+		entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+
+		if coord_text == "" and hide_location then
+			return entry_index
+		end
+		t = AcquireTable()
+		t.text = string.format("%s%s%s %s", PADDING, PADDING, hide_location and "" or SetTextColor(CATEGORY_COLORS["location"], trainer.location), coord_text)
+		t.recipe_id = recipe_id
+		t.npc_id = id_num
+
+		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+	end
+
+	-- Right now PVP obtained items are located on vendors so they have the vendor and PVP flag.
+	-- We need to display the vendor in the drop down if we want to see vendors or if we want to see PVP
+	-- This allows us to select PVP only and to see just the PVP recipes
+	local function ExpandVendorData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+		local vendor = private.vendor_list[id_num]
+
+		if not CanDisplayFaction(vendor.faction) then
+			return entry_index
+		end
+
+		local name = ColorNameByFaction(vendor.name, vendor.faction)
+		local coord_text = ""
+
+		if vendor.coord_x ~= 0 and vendor.coord_y ~= 0 then
+			coord_text = SetTextColor(CATEGORY_COLORS["coords"], "(" .. vendor.coord_x .. ", " .. vendor.coord_y .. ")")
+		end
+		local t = AcquireTable()
+		local quantity = vendor.item_list[recipe_id]
+
+		t.text = string.format("%s%s %s%s", PADDING,
+				       hide_type and "" or SetTextColor(CATEGORY_COLORS["vendor"], L["Vendor"])..":", name,
+				       type(quantity) == "number" and SetTextColor(BASIC_COLORS["white"], string.format(" (%d)", quantity)) or "")
+		t.recipe_id = recipe_id
+		t.npc_id = id_num
+
+		entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+
+		if coord_text == "" and hide_location then
+			return entry_index
+		end
+		t = AcquireTable()
+		t.text = string.format("%s%s%s %s", PADDING, PADDING, hide_location and "" or SetTextColor(CATEGORY_COLORS["location"], vendor.location), coord_text)
+		t.recipe_id = recipe_id
+		t.npc_id = id_num
+
+		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+	end
+
+	-- Mobs can be in instances, raids, or specific mob related drops.
+	local function ExpandMobData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+		local mob = private.mob_list[id_num]
+		local coord_text = ""
+
+		if mob.coord_x ~= 0 and mob.coord_y ~= 0 then
+			coord_text = SetTextColor(CATEGORY_COLORS["coords"], "(" .. mob.coord_x .. ", " .. mob.coord_y .. ")")
+		end
+		local t = AcquireTable()
+
+		t.text = string.format("%s%s %s", PADDING, hide_type and "" or SetTextColor(CATEGORY_COLORS["mobdrop"], L["Mob Drop"])..":", SetTextColor(private.reputation_colors["hostile"], mob.name))
+		t.recipe_id = recipe_id
+		t.npc_id = id_num
+
+		entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+
+		if coord_text == "" and hide_location then
+			return entry_index
+		end
+		t = AcquireTable()
+		t.text = string.format("%s%s%s %s", PADDING, PADDING, hide_location and "" or SetTextColor(CATEGORY_COLORS["location"], mob.location), coord_text)
+		t.recipe_id = recipe_id
+		t.npc_id = id_num
+
+		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+	end
+
+	local function ExpandQuestData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+		local quest = private.quest_list[id_num]
+
+		if not CanDisplayFaction(quest.faction) then
+			return entry_index
+		end
+
+		local name = ColorNameByFaction(private.quest_names[id_num], quest.faction)
+		local coord_text = ""
+
+		if quest.coord_x ~= 0 and quest.coord_y ~= 0 then
+			coord_text = SetTextColor(CATEGORY_COLORS["coords"], "(" .. quest.coord_x .. ", " .. quest.coord_y .. ")")
+		end
+		local t = AcquireTable()
+
+		t.text = string.format("%s%s %s", PADDING, hide_type and "" or SetTextColor(CATEGORY_COLORS["quest"], L["Quest"])..":", name)
+		t.recipe_id = recipe_id
+
+		entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+
+		if coord_text == "" and hide_location then
+			return entry_index
+		end
+		t = AcquireTable()
+		t.text = string.format("%s%s%s %s", PADDING, PADDING, hide_location and "" or SetTextColor(CATEGORY_COLORS["location"], quest.location), coord_text)
+		t.recipe_id = recipe_id
+
+		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+	end
+
+	local function ExpandSeasonalData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+		local t = AcquireTable()
+
+		t.text = string.format("%s%s %s", PADDING, hide_type and "" or SetTextColor(CATEGORY_COLORS["seasonal"], private.acquire_names[A.SEASONAL])..":",
+				       SetTextColor(CATEGORY_COLORS["seasonal"], private.seasonal_list[id_num].name))
+		t.recipe_id = recipe_id
+
+		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+	end
+
+	local function ExpandReputationData(entry_index, entry_type, parent_entry, vendor_id, rep_id, rep_level, recipe_id, hide_location, hide_type)
+		local rep_vendor = private.vendor_list[vendor_id]
+
+		if not CanDisplayFaction(rep_vendor.faction) then
+			return entry_index
+		end
+
+		if not faction_strings then
+			local rep_color = private.reputation_colors
+
+			faction_strings = {
+				[0] = SetTextColor(rep_color["neutral"], FACTION_NEUTRAL .. " : "),
+				[1] = SetTextColor(rep_color["friendly"], BFAC["Friendly"] .. " : "),
+				[2] = SetTextColor(rep_color["honored"], BFAC["Honored"] .. " : "),
+				[3] = SetTextColor(rep_color["revered"], BFAC["Revered"] .. " : "),
+				[4] = SetTextColor(rep_color["exalted"], BFAC["Exalted"] .. " : ")
+			}
+		end
+
+		local name = ColorNameByFaction(rep_vendor.name, rep_vendor.faction)
+		local t = AcquireTable()
+
+		t.text = string.format("%s%s %s", PADDING, hide_type and "" or SetTextColor(CATEGORY_COLORS["reputation"], _G.REPUTATION)..":",
+				       SetTextColor(CATEGORY_COLORS["repname"], private.reputation_list[rep_id].name))
+		t.recipe_id = recipe_id
+		t.npc_id = vendor_id
+
+		entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+
+		t = AcquireTable()
+		t.text = PADDING .. PADDING .. faction_strings[rep_level] .. name
+		t.recipe_id = recipe_id
+		t.npc_id = vendor_id
+
+		entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+
+		local coord_text = ""
+
+		if rep_vendor.coord_x ~= 0 and rep_vendor.coord_y ~= 0 then
+			coord_text = SetTextColor(CATEGORY_COLORS["coords"], "(" .. rep_vendor.coord_x .. ", " .. rep_vendor.coord_y .. ")")
+		end
+
+		if coord_text == "" and hide_location then
+			return entry_index
+		end
+		t = AcquireTable()
+		t.text = string.format("%s%s%s%s %s", PADDING, PADDING, PADDING, hide_location and "" or SetTextColor(CATEGORY_COLORS["location"], rep_vendor.location), coord_text)
+		t.recipe_id = recipe_id
+		t.npc_id = vendor_id
+
+		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+	end
+
+	local function ExpandWorldDropData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+		local _, _, _, hex_color = GetItemQualityColor(private.recipe_list[recipe_id].quality)
+		local drop_location = type(id_num) == "string" and BZ[id_num] or nil
+
+		if drop_location then
+			drop_location = string.format(": %s", SetTextColor(CATEGORY_COLORS["location"], drop_location))
+		else
+			drop_location = ""
+		end
+		local t = AcquireTable()
+
+		t.text = string.format("%s%s%s|r%s", PADDING, hex_color, L["World Drop"], drop_location)
+		t.recipe_id = recipe_id
+
+		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+	end
+
+	local function ExpandCustomData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+		local t = AcquireTable()
+
+		t.text = PADDING .. SetTextColor(CATEGORY_COLORS["custom"], private.custom_list[id_num].name)
+		t.recipe_id = recipe_id
+
+		return ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+	end
+
+	local function ExpandAcquireData(entry_index, entry_type, parent_entry, acquire_type, acquire_data, recipe_id, hide_location, hide_type)
+		local obtain_filters = addon.db.profile.filters.obtain
+
+		for id_num, info in pairs(acquire_data) do
+			if acquire_type == A.TRAINER and obtain_filters.trainer then
+				entry_index = ExpandTrainerData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+			elseif acquire_type == A.VENDOR and (obtain_filters.vendor or obtain_filters.pvp) then
+				entry_index = ExpandVendorData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+			elseif acquire_type == A.MOB_DROP and (obtain_filters.mobdrop or obtain_filters.instance or obtain_filters.raid) then
+				entry_index = ExpandMobData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+			elseif acquire_type == A.QUEST and obtain_filters.quest then
+				entry_index = ExpandQuestData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+			elseif acquire_type == A.SEASONAL and obtain_filters.seasonal then
+				entry_index = ExpandSeasonalData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+			elseif acquire_type == A.REPUTATION then
+				for rep_level, level_info in pairs(info) do
+					for vendor_id in pairs(level_info) do
+						entry_index =  ExpandReputationData(entry_index, entry_type, parent_entry, vendor_id, id_num, rep_level, recipe_id, hide_location, hide_type)
+					end
+				end
+			elseif acquire_type == A.WORLD_DROP and obtain_filters.worlddrop then
+				if not hide_type then
+					entry_index = ExpandWorldDropData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+				end
+			elseif acquire_type == A.CUSTOM then
+				if not hide_type then
+					entry_index = ExpandCustomData(entry_index, entry_type, parent_entry, id_num, recipe_id, hide_location, hide_type)
+				end
+				--@alpha@
+			elseif acquire_type > A_MAX then
+				local t = AcquireTable()
+
+				t.text = "Unhandled Acquire Case - Type: " .. acquire_type
+				t.recipe_id = recipe_id
+
+				entry_index = ListFrame:InsertEntry(t, parent_entry, entry_index, entry_type, true)
+				--@end-alpha@
+			end
+		end	-- for
+		return entry_index
+	end
+
+	-- This function is called when an un-expanded entry in the list has been clicked.
+	function ListFrame:ExpandEntry(entry_index, expand_mode)
+		local orig_index = entry_index
+		local current_entry = self.entries[orig_index]
+		local expand_all = expand_mode == "deep"
+		local search_box = MainPanel.search_editbox
+		local current_tab = MainPanel.tabs[MainPanel.current_tab]
+		local prof_name = private.ordered_professions[MainPanel.profession]
+
+		-- Entry_index is the position in self.entries that we want to expand. Since we are expanding the current entry, the return
+		-- value should be the index of the next button after the expansion occurs
+		entry_index = entry_index + 1
+
+		current_tab:ModifyEntry(current_entry, true)
+
+		-- This entry was generated using sorting based on Acquisition.
+		if current_entry.acquire_id then
+			local acquire_id = current_entry.acquire_id
+
+			if current_entry.type == "header" then
+				local recipe_list = private.acquire_list[acquire_id].recipes
+				local sorted_recipes = addon.sorted_recipes
+
+				private.SortRecipeList(recipe_list)
+
+				for index = 1, #sorted_recipes do
+					local spell_id = sorted_recipes[index]
+					local recipe_entry = private.recipe_list[spell_id]
+
+					if recipe_entry:HasState("VISIBLE") and search_box:MatchesRecipe(recipe_entry) then
+						local t = AcquireTable()
+						local expand = false
+						local type = "subheader"
+
+						if acquire_id == A.WORLD_DROP or acquire_id == A.CUSTOM then
+							expand = true
+							type = "entry"
+						end
+						local is_expanded = current_tab[prof_name.." expanded"][spell_id] and current_tab[prof_name.." expanded"][private.acquire_names[acquire_id]]
+
+						t.text = recipe_entry:GetDisplayName()
+						t.recipe_id = spell_id
+						t.acquire_id = acquire_id
+
+						entry_index = self:InsertEntry(t, current_entry, entry_index, type, expand or is_expanded, expand_all or is_expanded)
+					end
+				end
+			elseif current_entry.type == "subheader" then
+				for acquire_type, acquire_data in pairs(private.recipe_list[current_entry.recipe_id].acquire_data) do
+					if acquire_type == acquire_id then
+						entry_index = ExpandAcquireData(entry_index, "subentry", current_entry, acquire_type, acquire_data, current_entry.recipe_id, false, true)
+					end
+				end
+			end
+			return entry_index
+		end
+
+		-- This entry was generated using sorting based on Location.
+		if current_entry.location_id then
+			local location_id = current_entry.location_id
+
+			if current_entry.type == "header" then
+				local recipe_list = private.location_list[location_id].recipes
+				local sorted_recipes = addon.sorted_recipes
+
+				private.SortRecipeList(recipe_list)
+
+				for index = 1, #sorted_recipes do
+					local spell_id = sorted_recipes[index]
+					local recipe_entry = private.recipe_list[spell_id]
+
+					if recipe_entry:HasState("VISIBLE") and search_box:MatchesRecipe(recipe_entry) then
+						local expand = false
+						local type = "subheader"
+						local t = AcquireTable()
+
+						-- Add World Drop entries as normal entries.
+						if recipe_list[spell_id] == "world_drop" then
+							expand = true
+							type = "entry"
+						end
+						local is_expanded = current_tab[prof_name.." expanded"][spell_id] and current_tab[prof_name.." expanded"][location_id]
+
+						t.text = recipe_entry:GetDisplayName()
+						t.recipe_id = spell_id
+						t.location_id = location_id
+
+						entry_index = self:InsertEntry(t, current_entry, entry_index, type, expand or is_expanded, expand_all or is_expanded)
+					end
+				end
+			elseif current_entry.type == "subheader" then
+				local recipe_entry = private.recipe_list[current_entry.recipe_id]
+
+				-- World Drops are not handled here because they are of type "entry".
+				for acquire_type, acquire_data in pairs(recipe_entry.acquire_data) do
+					for id_num, info in pairs(acquire_data) do
+						-- Only expand an acquisition entry if it is from this location.
+						if acquire_type == A.TRAINER and private.trainer_list[id_num].location == location_id then
+							entry_index = ExpandTrainerData(entry_index, "subentry", current_entry,
+											id_num, current_entry.recipe_id, true)
+						elseif acquire_type == A.VENDOR and private.vendor_list[id_num].location == location_id then
+							entry_index = ExpandVendorData(entry_index, "subentry", current_entry,
+										       id_num, current_entry.recipe_id, true)
+						elseif acquire_type == A.MOB_DROP and private.mob_list[id_num].location == location_id then
+							entry_index = ExpandMobData(entry_index, "subentry", current_entry,
+										    id_num, current_entry.recipe_id, true)
+						elseif acquire_type == A.QUEST and private.quest_list[id_num].location == location_id then
+							entry_index = ExpandQuestData(entry_index, "subentry", current_entry,
+										      id_num, current_entry.recipe_id, true)
+						elseif acquire_type == A.SEASONAL and private.seasonal_list[id_num].location == location_id then
+							-- Hide the acquire type for this - it will already show up in the location list as "World Events".
+							entry_index = ExpandSeasonalData(entry_index, "subentry", current_entry,
+											 id_num, current_entry.recipe_id, true, true)
+						elseif acquire_type == A.CUSTOM and private.custom_list[id_num].location == location_id then
+							entry_index = ExpandCustomData(entry_index, "subentry", current_entry,
+										       id_num, current_entry.recipe_id, true, true)
+						elseif acquire_type == A.REPUTATION then
+							for rep_level, level_info in pairs(info) do
+								for vendor_id in pairs(level_info) do
+									if private.vendor_list[vendor_id].location == location_id then
+										entry_index =  ExpandReputationData(entry_index, "subentry", current_entry,
+														    vendor_id, id_num, rep_level, current_entry.recipe_id, true)
+									end
+								end
+							end
+						end
+					end
+				end
+			end
+			return entry_index
+		end
+
+		-- Normal entry - expand all acquire types.
+		local recipe_id = self.entries[orig_index].recipe_id
+
+		for acquire_type, acquire_data in pairs(private.recipe_list[recipe_id].acquire_data) do
+			entry_index = ExpandAcquireData(entry_index, "entry", current_entry, acquire_type, acquire_data, recipe_id)
+		end
+		return entry_index
+	end
+end	-- InitializeListFrame()
+
+-------------------------------------------------------------------------------
+-- Tooltip functions and data.
+-------------------------------------------------------------------------------
+spell_tip = CreateFrame("GameTooltip", "AckisRecipeList_SpellTooltip", UIParent, "GameTooltipTemplate")
+
+-- Font Objects needed for acquire_tip
+local narrowFont
+local normalFont
+
+do
+	-- Fallback in case the user doesn't have LSM-3.0 installed
+	if not LibStub:GetLibrary("LibSharedMedia-3.0", true) then
+
+		local locale = GetLocale()
+		-- Fix for font issues on koKR
+		if locale == "koKR" then
+			narrowFont = "Fonts\\2002.TTF"
+			normalFont = "Fonts\\2002.TTF"
+		else
+			narrowFont = "Fonts\\ARIALN.TTF"
+			normalFont = "Fonts\\FRIZQT__.TTF"
+		end
+	else
+		-- Register LSM 3.0
+		local LSM3 = LibStub("LibSharedMedia-3.0")
+
+		narrowFont = LSM3:Fetch(LSM3.MediaType.FONT, "Arial Narrow")
+		normalFont = LSM3:Fetch(LSM3.MediaType.FONT, "Friz Quadrata TT")
+	end
+	local narrowFontObj = CreateFont(MODNAME.."narrowFontObj")
+	local normalFontObj = CreateFont(MODNAME.."normalFontObj")
+
+	-- I want to do a bit more comprehensive tooltip processing. Things like changing font sizes,
+	-- adding padding to the left hand side, and using better color handling. So... this function
+	-- will do that for me.
+	local function ttAdd(
+			leftPad,		-- number of times to pad two spaces on left side
+			textSize,		-- add to or subtract from addon.db.profile.tooltip.acquire_fontsize to get fontsize
+			narrow,			-- if 1, use ARIALN instead of FRITZQ
+			str1,			-- left-hand string
+			hexcolor1,		-- hex color code for left-hand side
+			str2,			-- if present, this is the right-hand string
+			hexcolor2)		-- if present, hex color code for right-hand side
+
+		-- are we changing fontsize or narrow?
+		local fontSize
+
+		if narrow or textSize ~= 0 then
+			local font = narrow and narrowFont or normalFont
+			local fontObj = narrow and narrowFontObj or normalFontObj
+
+			fontSize = addon.db.profile.tooltip.acquire_fontsize + textSize
+
+			fontObj:SetFont(font, fontSize)
+			acquire_tip:SetFont(fontObj)
+		end
+
+		-- Add in our left hand padding
+		local loopPad = leftPad
+		local leftStr = str1
+
+		while loopPad > 0 do
+			leftStr = "    " .. leftStr
+			loopPad = loopPad - 1
+		end
+		-- Set maximum width to match fontSize to maintain uniform tooltip size. -Torhal
+		local width = math.ceil(fontSize * 37.5)
+		local line = acquire_tip:AddLine()
+
+		if str2 then
+			width = width / 2
+
+			acquire_tip:SetCell(line, 1, "|cff"..hexcolor1..leftStr.."|r", "LEFT", nil, nil, 0, 0, width, width)
+			acquire_tip:SetCell(line, 2, "|cff"..hexcolor2..str2.."|r", "RIGHT", nil, nil, 0, 0, width, width)
+		else
+			acquire_tip:SetCell(line, 1, "|cff"..hexcolor1..leftStr.."|r", nil, "LEFT", 2, nil, 0, 0, width, width)
+		end
+	end
+
+	local function SetSpellTooltip(owner, loc, link)
+		spell_tip:SetOwner(owner, "ANCHOR_NONE")
+		spell_tip:ClearAllPoints()
+
+		if loc == "Top" then
+			spell_tip:SetPoint("BOTTOMLEFT", owner, "TOPLEFT")
+		elseif loc == "Bottom" then
+			spell_tip:SetPoint("TOPLEFT", owner, "BOTTOMLEFT")
+		elseif loc == "Left" then
+			spell_tip:SetPoint("TOPRIGHT", owner, "TOPLEFT")
+		elseif loc == "Right" then
+			spell_tip:SetPoint("TOPLEFT", owner, "TOPRIGHT")
+		end
+
+		-- Add TipTac Support
+		if _G.TipTac and _G.TipTac.AddModifiedTip and not spell_tip.tiptac then
+			_G.TipTac:AddModifiedTip(spell_tip)
+			spell_tip.tiptac = true
+		end
+
+		-- Set the spell tooltip's scale, and copy its other values from GameTooltip so AddOns which modify it will work.
+		spell_tip:SetBackdrop(GameTooltip:GetBackdrop())
+		spell_tip:SetBackdropColor(GameTooltip:GetBackdropColor())
+		spell_tip:SetBackdropBorderColor(GameTooltip:GetBackdropBorderColor())
+		spell_tip:SetScale(addon.db.profile.tooltip.scale)
+		spell_tip:SetClampedToScreen(true)
+		spell_tip:SetHyperlink(link)
+		spell_tip:Show()
+	end
+
+	local function GetTipFactionInfo(comp_faction)
+		local display_tip
+		local color
+
+		if comp_faction == FACTION_NEUTRAL then
+			color = private.reputation_colors["neutral"]
+			display_tip = true
+		elseif comp_faction == BFAC[Player.faction] then
+			color = private.reputation_colors["exalted"]
+			display_tip = true
+		else
+			color = private.reputation_colors["hated"]
+			display_tip = addon.db.profile.filters.general.faction
+		end
+		return display_tip, color
+	end
+
+	-------------------------------------------------------------------------------
+	-- Functions for adding individual acquire type data to the tooltip.
+	-------------------------------------------------------------------------------
+	local function Tooltip_AddTrainer(id_num, location, addline_func)
+		local trainer = private.trainer_list[id_num]
+
+		if location and trainer.location ~= location then
+			return
+		end
+		local display_tip, name_color = GetTipFactionInfo(trainer.faction)
+
+		if display_tip then
+			local coord_text = ""
+
+			if trainer.coord_x ~= 0 and trainer.coord_y ~= 0 then
+				coord_text = "(" .. trainer.coord_x .. ", " .. trainer.coord_y .. ")"
+			end
+			addline_func(0, -2, false, L["Trainer"], CATEGORY_COLORS["trainer"], trainer.name, name_color)
+			addline_func(1, -2, true, trainer.location, CATEGORY_COLORS["location"], coord_text, CATEGORY_COLORS["coords"])
+		end
+	end
+
+	local function Tooltip_AddVendor(recipe_id, id_num, location, addline_func)
+		local vendor = private.vendor_list[id_num]
+
+		if location and vendor.location ~= location then
+			return
+		end
+		local type_color = CATEGORY_COLORS["vendor"]
+		local display_tip, name_color = GetTipFactionInfo(vendor.faction)
+
+		if display_tip then
+			local coord_text = ""
+
+			if vendor.coord_x ~= 0 and vendor.coord_y ~= 0 then
+				coord_text = "(" .. vendor.coord_x .. ", " .. vendor.coord_y .. ")"
+			end
+			addline_func(0, -1, false, L["Vendor"], type_color, vendor.name, name_color)
+			addline_func(1, -2, true, vendor.location, CATEGORY_COLORS["location"], coord_text, CATEGORY_COLORS["coords"])
+
+			local quantity = vendor.item_list[recipe_id]
+
+			if type(quantity) == "number" then
+				addline_func(2, -2, true, L["LIMITED_SUPPLY"], type_color, string.format("(%d)", quantity), BASIC_COLORS["white"])
+			end
+		end
+	end
+
+	local function Tooltip_AddMobDrop(id_num, location, addline_func)
+		local mob = private.mob_list[id_num]
+
+		if location and mob.location ~= location then
+			return
+		end
+		local coord_text = ""
+
+		if mob.coord_x ~= 0 and mob.coord_y ~= 0 then
+			coord_text = "(" .. mob.coord_x .. ", " .. mob.coord_y .. ")"
+		end
+		addline_func(0, -1, false, L["Mob Drop"], CATEGORY_COLORS["mobdrop"], mob.name, private.reputation_colors["hostile"])
+		addline_func(1, -2, true, mob.location, CATEGORY_COLORS["location"], coord_text, CATEGORY_COLORS["coords"])
+	end
+
+	local function Tooltip_AddQuest(id_num, location, addline_func)
+		local quest = private.quest_list[id_num]
+
+		if location and quest.location ~= location then
+			return
+		end
+		local type_color = CATEGORY_COLORS["quest"]
+		local display_tip, name_color = GetTipFactionInfo(quest.faction)
+
+		if display_tip then
+			local coord_text = ""
+
+			if quest.coord_x ~= 0 and quest.coord_y ~= 0 then
+				coord_text = "(" .. quest.coord_x .. ", " .. quest.coord_y .. ")"
+			end
+			addline_func(0, -1, false, L["Quest"], type_color, private.quest_names[id_num], name_color)
+			addline_func(1, -2, true, quest.location, CATEGORY_COLORS["location"], coord_text, CATEGORY_COLORS["coords"])
+		end
+	end
+
+	local function Tooltip_AddRepVendor(id_num, location, rep_id, rep_level, vendor_id, addline_func)
+		local rep_vendor = private.vendor_list[vendor_id]
+
+		if location and rep_vendor.location ~= location then
+			return
+		end
+		local display_tip, name_color = GetTipFactionInfo(rep_vendor.faction)
+
+		if display_tip then
+			local rep_color = private.reputation_colors
+			local rep_str = ""
+			local type_color
+
+			if rep_level == 0 then
+				rep_str = FACTION_NEUTRAL
+				type_color = rep_color["neutral"]
+			elseif rep_level == 1 then
+				rep_str = BFAC["Friendly"]
+				type_color = rep_color["friendly"]
+			elseif rep_level == 2 then
+				rep_str = BFAC["Honored"]
+				type_color = rep_color["honored"]
+			elseif rep_level == 3 then
+				rep_str = BFAC["Revered"]
+				type_color = rep_color["revered"]
+			else
+				rep_str = BFAC["Exalted"]
+				type_color = rep_color["exalted"]
+			end
+			addline_func(0, -1, false, _G.REPUTATION, CATEGORY_COLORS["reputation"], private.reputation_list[id_num].name, CATEGORY_COLORS["repname"])
+			addline_func(1, -2, false, rep_str, type_color, rep_vendor.name, name_color)
+
+			local coord_text = ""
+
+			if rep_vendor.coord_x ~= 0 and rep_vendor.coord_y ~= 0 then
+				coord_text = "(" .. rep_vendor.coord_x .. ", " .. rep_vendor.coord_y .. ")"
+			end
+			addline_func(2, -2, true, rep_vendor.location, CATEGORY_COLORS["location"], coord_text, CATEGORY_COLORS["coords"])
+		end
+	end
+
+	local function Tooltip_AddWorldDrop(recipe_id, id_num, location, addline_func)
+		local drop_location = type(id_num) == "string" and BZ[id_num] or nil
+
+		if location and drop_location ~= location then
+			return
+		end
+		local item_id = private.spell_to_recipe_map[recipe_id]
+		local _, item_level
+
+		if item_id then
+			_, _, _, item_level = GetItemInfo(item_id)
+		end
+		local _, _, _, quality_color = GetItemQualityColor(private.recipe_list[recipe_id].quality)
+		local type_color = string.gsub(quality_color, "|cff", "")
+
+		if type(id_num) == "string" then
+			local location_text = item_level and string.format("%s (%d - %d)", drop_location, item_level - 5, item_level + 5) or drop_location
+
+			addline_func(0, -1, false, L["World Drop"], type_color, location_text, CATEGORY_COLORS["location"])
+		else
+			local location_text = item_level and string.format("%s (%d - %d)", _G.UNKNOWN, item_level - 5, item_level + 5) or _G.UNKNOWN
+
+			addline_func(0, -1, false, L["World Drop"], type_color, location_text, CATEGORY_COLORS["location"])
+		end
+	end
+
+	-------------------------------------------------------------------------------
+	-- Public API function for displaying a recipe's acquire data.
+	-- * The addline_func paramater must be a function which accepts the same
+	-- * arguments as ARL's ttAdd function.
+	-------------------------------------------------------------------------------
+	function addon:DisplayAcquireData(recipe_id, acquire_id, location, addline_func)
+		local recipe = private.recipe_list[recipe_id]
+
+		if not recipe then
+			return
+		end
+
+		for acquire_type, acquire_data in pairs(recipe.acquire_data) do
+			local can_display = (not acquire_id or acquire_type == acquire_id)
+
+			if can_display then
+				for id_num, info in pairs(acquire_data) do
+					if acquire_type == A.TRAINER then
+						Tooltip_AddTrainer(id_num, location, addline_func)
+					elseif acquire_type == A.VENDOR then
+						Tooltip_AddVendor(recipe_id, id_num, location, addline_func)
+					elseif acquire_type == A.MOB_DROP then
+						Tooltip_AddMobDrop(id_num, location, addline_func)
+					elseif acquire_type == A.QUEST then
+						Tooltip_AddQuest(id_num, location, addline_func)
+					elseif acquire_type == A.SEASONAL then
+						color_1 = CATEGORY_COLORS["seasonal"]
+						addline_func(0, -1, 0, private.acquire_names[A.SEASONAL], color_1, private.seasonal_list[id_num].name, color_1)
+					elseif acquire_type == A.REPUTATION then
+						for rep_level, level_info in pairs(info) do
+							for vendor_id in pairs(level_info) do
+								Tooltip_AddRepVendor(id_num, location, rep_id, rep_level, vendor_id, addline_func)
+							end
+						end
+					elseif acquire_type == A.WORLD_DROP then
+						Tooltip_AddWorldDrop(recipe_id, id_num, location, addline_func)
+					elseif acquire_type == A.CUSTOM then
+						addline_func(0, -1, false, private.custom_list[id_num].name, CATEGORY_COLORS["custom"])
+						--@alpha@
+					elseif can_display then
+						-- Unhandled
+						addline_func(0, -1, 0, L["Unhandled Recipe"], BASIC_COLORS["normal"])
+						--@end-alpha@
+					end
+				end	-- for id_num
+			end	-- if can_display
+		end	-- for acquire_type
+	end
+
+	-------------------------------------------------------------------------------
+	-- Main tooltip-generating function.
+	-------------------------------------------------------------------------------
+	local BINDING_FLAGS = {
+		[COMMON_FLAGS_1.IBOE] = L["BOEFilter"],
+		[COMMON_FLAGS_1.IBOP] = L["BOPFilter"],
+		[COMMON_FLAGS_1.IBOA] = L["BOAFilter"],
+		[COMMON_FLAGS_1.RBOE] = L["RecipeBOEFilter"],
+		[COMMON_FLAGS_1.RBOP] = L["RecipeBOPFilter"],
+		[COMMON_FLAGS_1.RBOA] = L["RecipeBOAFilter"]
+	}
+
+	function ListItem_ShowTooltip(owner, list_entry)
+		if not list_entry then
+			return
+		end
+		local recipe_id = list_entry.recipe_id
+		local recipe = private.recipe_list[recipe_id]
+
+		if not recipe then
+			return
+		end
+		local spell_tip_anchor = addon.db.profile.spelltooltiplocation
+		local acquire_tip_anchor = addon.db.profile.acquiretooltiplocation
+		local spell_link = GetSpellLink(recipe.spell_id)
+		local MainPanel = addon.Frame
+
+		if acquire_tip_anchor == _G.OFF then
+			QTip:Release(acquire_tip)
+
+			-- If we have the spell link tooltip, anchor it to MainPanel instead so it shows
+			if spell_tip_anchor ~= _G.OFF and spell_link then
+				SetSpellTooltip(MainPanel, spell_tip_anchor, spell_link)
+			else
+				spell_tip:Hide()
+			end
+			return
+		end
+		acquire_tip = QTip:Acquire(MODNAME.." Tooltip", 2, "LEFT", "LEFT")
+		acquire_tip:ClearAllPoints()
+
+		if acquire_tip_anchor == "Right" then
+			acquire_tip:SetPoint("TOPLEFT", MainPanel, "TOPRIGHT", MainPanel.is_expanded and -90 or -35, 0)
+		elseif acquire_tip_anchor == "Left" then
+			acquire_tip:SetPoint("TOPRIGHT", MainPanel, "TOPLEFT")
+		elseif acquire_tip_anchor == "Top" then
+			acquire_tip:SetPoint("BOTTOMLEFT", MainPanel, "TOPLEFT")
+		elseif acquire_tip_anchor == "Bottom" then
+			acquire_tip:SetPoint("TOPLEFT", MainPanel, "BOTTOMLEFT", 0, 55)
+		elseif acquire_tip_anchor == "Mouse" then
+			local x, y = GetCursorPosition()
+			local uiscale = UIParent:GetEffectiveScale()
+
+			acquire_tip:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", x / uiscale, y / uiscale)
+		end
+		acquire_tip:SetClampedToScreen(true)
+
+		if _G.TipTac and _G.TipTac.AddModifiedTip then
+			-- Pass true as second parameter because hooking OnHide causes C stack overflows -Torhal
+			_G.TipTac:AddModifiedTip(acquire_tip, true)
+		end
+		local _, _, _, quality_color = GetItemQualityColor(recipe.quality)
+
+		acquire_tip:Clear()
+		acquire_tip:SetScale(addon.db.profile.tooltip.scale)
+		acquire_tip:AddHeader()
+		acquire_tip:SetCell(1, 1, quality_color..recipe.name, "CENTER", 2)
+
+		-- check if the recipe is excluded
+		if addon.db.profile.exclusionlist[recipe_id] then
+			ttAdd(0, -1, true, L["RECIPE_EXCLUDED"], "ff0000")
+		end
+
+		-- Add in skill level requirement, colored correctly
+		local color_1 = BASIC_COLORS["normal"]
+		local color_2
+
+		local skill_level = Player["ProfessionLevel"]
+		local recipe_level = recipe.skill_level
+		local optimal_level = recipe.optimal_level
+		local medium_level = recipe.medium_level
+		local easy_level = recipe.easy_level
+		local trivial_level = recipe.trivial_level
+		local difficulty = private.difficulty_colors
+
+		if recipe_level > skill_level then
+			color_2 = difficulty["impossible"]
+		elseif skill_level >= trivial_level then
+			color_2 = difficulty["trivial"]
+		elseif skill_level >= easy_level then
+			color_2 = difficulty["easy"]
+		elseif skill_level >= medium_level then
+			color_2 = difficulty["medium"]
+		elseif skill_level >= optimal_level then
+			color_2 = difficulty["optimal"]
+		else
+			color_2 = difficulty["trivial"]
+		end
+		ttAdd(0, -1, false, string.format("%s:", _G.SKILL_LEVEL), color_1, recipe.skill_level, color_2)
+
+		-- Binding info
+		acquire_tip:AddSeparator()
+		color_1 = BASIC_COLORS["normal"]
+
+		for flag, label in pairs(BINDING_FLAGS) do
+			if bit.band(recipe.flags.common1, flag) == flag then
+				ttAdd(0, -1, true, label, color_1)
+			end
+		end
+		acquire_tip:AddSeparator()
+
+		if recipe.specialty then
+			local spec = recipe.specialty
+			local spec_name = GetSpellInfo(spec)
+			local known = (spec == Player["Specialty"])
+
+			ttAdd(0, -1, false, string.format(_G.ITEM_REQ_SKILL, spec_name), known and BASIC_COLORS["white"] or difficulty["impossible"])
+			acquire_tip:AddSeparator()
+		end
+
+		ttAdd(0, -1, false, L["Obtained From"] .. " : ", BASIC_COLORS["normal"])
+
+		local acquire_id = list_entry.acquire_id
+		local location = list_entry.location_id
+
+		addon:DisplayAcquireData(recipe_id, acquire_id, location, ttAdd)
+
+		if not addon.db.profile.hide_tooltip_hint then
+			-- Give the tooltip hint a unique color.
+			color_1 = "c9c781"
+
+			acquire_tip:AddSeparator()
+			acquire_tip:AddSeparator()
+
+			ttAdd(0, -1, 0, L["ALT_CLICK"], color_1)
+			ttAdd(0, -1, 0, L["CTRL_CLICK"], color_1)
+			ttAdd(0, -1, 0, L["SHIFT_CLICK"], color_1)
+
+			if acquire_id ~= A.WORLD_DROP and acquire_id ~= A.CUSTOM and (_G.TomTom or _G.Cartographer_Waypoints) and (addon.db.profile.worldmap or addon.db.profile.minimap) then
+				ttAdd(0, -1, 0, L["CTRL_SHIFT_CLICK"], color_1)
+			end
+		end
+		acquire_tip:Show()
+
+		-- If we have the spell link tooltip, link it to the acquire tooltip.
+		if spell_tip_anchor ~= _G.OFF and spell_link then
+			SetSpellTooltip(acquire_tip, spell_tip_anchor, spell_link)
+		else
+			spell_tip:Hide()
+		end
+	end
+end	-- do
diff --git a/interface.xml b/interface.xml
index 84fe4d4..3dd469a 100644
--- a/interface.xml
+++ b/interface.xml
@@ -4,5 +4,6 @@

 <Include file="Interface\Common.lua"/>
 <Include file="Interface\Tabs.lua"/>
+<Include file="Interface\List.lua"/>

 </Ui>