-- ------------------------------------------------------------------------------ -- -- TradeSkillMaster -- -- http://www.curse.com/addons/wow/tradeskill-master -- -- -- -- A TradeSkillMaster Addon (http://tradeskillmaster.com) -- -- All Rights Reserved* - Detailed license information included with addon. -- -- ------------------------------------------------------------------------------ -- local TSM = select(2, ...) local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table local RT_COUNT = 1 local HEAD_HEIGHT = 27 local HEAD_SPACE = 2 local purchaseCache = {} local resultsTables = {} local function OnSizeChanged(rt, width) for i, col in ipairs(rt.headCols) do col:SetWidth(col.info.width*width) end for _, row in ipairs(rt.rows) do for i, col in ipairs(row.cols) do col:SetWidth(rt.headCols[i].info.width*width) end end end local rowTextFunctions = { GetPriceText = function(buyout, displayBid) local bidLine = TSMAPI:FormatTextMoney(displayBid, "|cff999999", true) or "|cff999999---|r" local buyoutLine = buyout and buyout > 0 and TSMAPI:FormatTextMoney(buyout, nil, true) or "---" if TSM.db.profile.showBids then return bidLine.."\n"..buyoutLine else return buyoutLine end end, GetTimeLeftText = function(timeLeft) return _G["AUCTION_TIME_LEFT"..(timeLeft or "")] or "" end, GetNameText = function(_, link) return gsub(gsub(link, "%[", ""), "%]", "") end, GetAuctionsText = function(num, player, isExpandable, totalNum) num = totalNum or num local playerText = player and (" |cffffff00("..player..")|r") or "" if isExpandable then return TSMAPI.Design:GetInlineColor("link2")..num.."|r"..playerText else return num..playerText end end, GetSellerText = function(seller) if TSMAPI:IsPlayer(seller) then return "|cffffff00"..seller.."|r" else return seller or "" end end, GetPercentText = function(pct) if not pct then return "---" end return TSMAPI:GetAuctionPercentColor(pct)..floor(pct+0.5).."%|r" end } local function GetRowTable(rt, auction, isExpandable) if not auction then return end local bid, buyout if TSM.db.profile.pricePerUnit then bid = auction:GetItemDisplayedBid() buyout = auction:GetItemBuyout() else bid = auction:GetDisplayedBid() buyout = auction.buyout end local auctionsData, rowTable local itemString = auction.parent:GetItemString() if rt.expanded[itemString] then auctionsData = {#auction.parent.records, nil, nil, auction.numAuctions} else auctionsData = {#auction.parent.records, auction.parent.records[1].playerAuctions, isExpandable} end local name, iLvl -- if strmatch(auction.parent.itemLink, "battlepet") then -- local _, speciesID, itemLvl = strsplit(":", auction.parent.itemLink) -- local itemName = C_PetJournal.GetPetInfoBySpeciesID(speciesID) -- name, iLvl = itemName, itemLvl -- else local itemName, _, _, itemLvl = GetItemInfo(auction.parent.itemLink) name, iLvl = itemName, itemLvl -- end local pct = auction:GetPercent() if not pct or pct < 0 or pct == math.huge then pct = nil end if rt.isDestroying then local destroyingBid = auction:GetItemDestroyingDisplayedBid() local destroyingBuyout = auction:GetItemDestroyingBuyout() rowTable = { {value=rowTextFunctions.GetNameText, args={name, auction.parent.itemLink}}, {value=rowTextFunctions.GetAuctionsText, args=auctionsData}, {value=auction.count, args={auction.count}}, {value=rowTextFunctions.GetSellerText, args={auction.seller}}, {value=rowTextFunctions.GetPriceText, args={destroyingBuyout, destroyingBid}}, {value=rowTextFunctions.GetPriceText, args={buyout, bid}}, {value=rowTextFunctions.GetPercentText, args={pct}}, } else rowTable = { {value=rowTextFunctions.GetNameText, args={name, auction.parent.itemLink}}, {value=(iLvl or "---"), args={iLvl or 0}}, {value=rowTextFunctions.GetAuctionsText, args=auctionsData}, {value=auction.count, args={auction.count}}, {value=rowTextFunctions.GetTimeLeftText, args={auction.timeLeft}}, {value=rowTextFunctions.GetSellerText, args={auction.seller}}, {value=rowTextFunctions.GetPriceText, args={buyout, bid}}, {value=rowTextFunctions.GetPercentText, args={pct}}, } end rowTable.itemString = itemString rowTable.auctionRecord = auction rowTable.expandable = isExpandable rowTable.texture = auction.parent:GetTexture() rowTable.link = auction.parent.itemLink return rowTable end local function GetTableIndex(tbl, value) for i, v in pairs(tbl) do if value == v then return i end end end local function OnColumnClick(self, ...) local button = ... local rt = self.rt local column = GetTableIndex(rt.headCols, self) if button == "RightButton" and column == #rt.headCols-1 then TSM.db.profile.pricePerUnit = not TSM.db.profile.pricePerUnit local priceColName = TSM.db.profile.pricePerUnit and L["Price Per Item"] or L["Price Per Stack"] self:SetText(priceColName) rt:RefreshRowData() return end local ascending = rt.sortInfo.ascending rt:SetSort(column, rt.sortInfo.column ~= column or not ascending) local handler = self.rt.handlers.OnColumnClick if handler then handler(self.rt, self.row.data, self, ...) end end local methods = { DrawRows = function(rt) if not rt.auctionData then return end for i=1, rt.NUM_ROWS do rt.rows[i]:Hide() end wipe(rt.displayRows) local itemsUsed = {} for i, data in ipairs(rt.data) do local itemString = data.itemString if not itemsUsed[itemString] or rt.expanded[itemString] then tinsert(rt.displayRows, data) itemsUsed[itemString] = true elseif i == rt.selected then rt.selected = nil end end FauxScrollFrame_Update(rt.scrollFrame, #rt.displayRows, rt.NUM_ROWS, rt.ROW_HEIGHT) local offset = FauxScrollFrame_GetOffset(rt.scrollFrame) rt.offset = offset for i=1, min(rt.NUM_ROWS, #rt.displayRows) do rt.rows[i]:Show() local data = rt.displayRows[i+offset] local cols = rt.rows[i].cols rt.rows[i].data = data if rt.selected == GetTableIndex(rt.data, data) then rt.rows[i].highlight:Show() else rt.rows[i].highlight:Hide() end for j, col in ipairs(rt.rows[i].cols) do local colData = data[j] if j == 1 then col.icon:SetTexture(data.texture) if data.indented then col.spacer:SetWidth(10) col.icon:SetAlpha(0.5) col:GetFontString():SetAlpha(0.7) else col.spacer:SetWidth(1) col.icon:SetAlpha(1) col:GetFontString():SetAlpha(1) end end if type(colData.value) == "function" then col:SetText(colData.value(unpack(colData.args))) else col:SetText(colData.value) end end end rt:UpdateActiveRows() end, RefreshRowData = function(rt) if not rt.auctionData then return end wipe(rt.data) wipe(rt.displayRows) local function RowSort(a, b) if a[rt.sortInfo.column].args[1] == nil then return end local aVal local bVal if getn(a[rt.sortInfo.column].args) == 4 then aVal = a[rt.sortInfo.column].args[4] bVal = b[rt.sortInfo.column].args[4] else aVal = a[rt.sortInfo.column].args[1] bVal = b[rt.sortInfo.column].args[1] end -- local aVal = a[rt.sortInfo.column].args[1] -- local bVal = b[rt.sortInfo.column].args[1] if type(aVal) ~= "string" or type(bVal) ~= "string" then aVal = tonumber(aVal) or 0 bVal = tonumber(bVal) or 0 end if aVal == bVal then -- make this a stable sort (abitrarily) by using table reference strings return tostring(a) < tostring(b) end if rt.sortInfo.ascending then return aVal < bVal else return aVal > bVal end end local tmp = {} for _, auction in ipairs(rt.auctionData) do local itemString = auction:GetItemString() local itemRowData = {} for i, data in ipairs(auction.compactRecords) do local rowTbl = GetRowTable(rt, data, #auction.compactRecords > 1) rowTbl.indented = true tinsert(itemRowData, rowTbl) end sort(itemRowData, RowSort) if itemRowData[1] then itemRowData[1].indented = false end tinsert(tmp, itemRowData) end sort(tmp, function(a, b) return RowSort(a[1], b[1]) end) for _, itemRows in ipairs(tmp) do for _, row in ipairs(itemRows) do tinsert(rt.data, row) end end rt:DrawRows() end, SetData = function(rt, auctionData) rt.auctionData = auctionData rt:RefreshRowData() end, ClearSelection = function(rt) rt.selected = nil rt:DrawRows() end, SetSelectedAuction = function(rt, auction) rt.selected = nil for i, data in ipairs(rt.data) do if type(auction) == "table" then if data.auctionRecord == auction or data.auctionRecord:Equals(auction) then rt.selected = i break end elseif type(auction) == "string" then if data.itemString == auction then rt.selected = i break end end end rt:DrawRows() end, GetSelectedAuction = function(rt) if not rt.selected or not rt.data[rt.selected] then return end return rt.data[rt.selected].auctionRecord end, SetExpanded = function(rt, itemString, expanded) rt.expanded[itemString] = expanded rt:RefreshRowData() end, ToggleExpanded = function(rt, itemString) rt.expanded[itemString] = not rt.expanded[itemString] rt:RefreshRowData() end, SetSort = function(rt, column, ascending) if not rt.headCols[column or 0] then return end rt.sortInfo.column = column rt.sortInfo.ascending = ascending for _, col in ipairs(rt.headCols) do local tex = col:GetNormalTexture() tex:SetTexture("Interface\\WorldStateFrame\\WorldStateFinalScore-Highlight") tex:SetTexCoord(0.017, 1, 0.083, 0.909) tex:SetAlpha(0.5) end if ascending then rt.headCols[column]:GetNormalTexture():SetTexture(0.6, 0.8, 1, 0.8) else rt.headCols[column]:GetNormalTexture():SetTexture(0.8, 0.6, 1, 0.8) end rt:RefreshRowData() end, SetDisabled = function(rt, disabled) rt.disabled = disabled end, SetColHeadText = function(rt, column, text) rt.headCols[column]:SetText(text) end, UpdateActiveRows = function(rt) if not rt.quickBuyout then return end for _, row in ipairs(rt.rows) do row:HideActiveBorder() if row.data then local rowRecord = row.data.auctionRecord for i=1, GetNumAuctionItems("list") do local itemString = TSMAPI:GetItemString(GetAuctionItemLink("list", i)) -- local _, _, count, _, _, _, _, _, _, buyout, _, _, _, seller = GetAuctionItemInfo("list", i) local _, _, count, _, _, _, _, _, buyout, _, _, seller = GetAuctionItemInfo("list", i) if itemString == row.data.itemString and rowRecord.count == count and rowRecord.buyout == buyout and rowRecord.seller == seller then row:ShowActiveBorder() break end end end end wipe(purchaseCache) end, } local defaultColScripts = { OnEnter = function(self, ...) if self.rt.disabled then return end if self ~= self.row.cols[1] or not self.rt.isShowingItemTooltip then GameTooltip:SetOwner(self, "ANCHOR_NONE") GameTooltip:SetPoint("BOTTOMLEFT", self, "TOPLEFT") local data = self.row.data local extra = "" if self.row.isActive then extra = TSMAPI.Design:GetInlineColor("link").."\n\n"..L["Alt-Click to immediately buyout this auction."].."|r" end if self.rt.expanded[data.itemString] then GameTooltip:AddLine(L["Double-click to collapse this item and show only the cheapest auction."]..extra, 1, 1, 1, true) elseif data.expandable then GameTooltip:AddLine(L["Double-click to expand this item and show all the auctions."]..extra, 1, 1, 1, true) else GameTooltip:AddLine(L["There is only one price level and seller for this item."]..extra, 1, 1, 1, true) end GameTooltip:Show() end self.row.highlight:Show() local handler = self.rt.handlers.OnEnter if handler then handler(self.rt, self.row.data, self, ...) end end, OnLeave = function(self, ...) if self.rt.disabled then return end if self ~= self.row.cols[1] or not self.rt.isShowingItemTooltip then GameTooltip:Hide() end if not self.rt.selected or self.rt.selected ~= GetTableIndex(self.rt.data, self.row.data) then self.row.highlight:Hide() end local handler = self.rt.handlers.OnLeave if handler then handler(self.rt, self.row.data, self, ...) end end, OnClick = function(self, button, ...) if self.rt.disabled then return end self.rt:ClearSelection() self.rt.selected = GetTableIndex(self.rt.data, self.row.data) self.row.highlight:Show() if self.rt.quickBuyout and IsAltKeyDown() then local rowRecord = self.row.data.auctionRecord for i=GetNumAuctionItems("list"), 1, -1 do local link = GetAuctionItemLink("list", i) if not purchaseCache[link] then local itemString = TSMAPI:GetItemString(link) -- local _, _, count, _, _, _, _, _, _, buyout, _, _, _, seller = GetAuctionItemInfo("list", i) local _, _, count, _, _, _, _, _, buyout, _, _, seller = GetAuctionItemInfo("list", i) if itemString == self.row.data.itemString and rowRecord.count == count and rowRecord.buyout == buyout and rowRecord.seller == seller then PlaceAuctionBid("list", i, rowRecord.buyout) TSM:AuctionControlCallback("OnBuyout", {itemString=TSMAPI:GetItemString(rowRecord.parent.itemLink), link=rowRecord.parent.itemLink, count=rowRecord.count, seller=rowRecord.seller, buyout=rowRecord.buyout, destroyingNum=rowRecord.parent.destroyingNum}) purchaseCache[link] = true return end end end end local handler = self.rt.handlers.OnClick if handler then handler(self.rt, self.row.data, self, button, ...) end end, OnDoubleClick = function(self, ...) if self.rt.disabled or IsAltKeyDown() then return end local data = self.row.data if data.expandable then self.rt:ToggleExpanded(data.itemString) end local handler = self.rt.handlers.OnDoubleClick if handler then handler(self.rt, self.row.data, self, ...) end end, } function TSMAPI:CreateAuctionResultsTable(parent, handlers, quickBuyout, isDestroying) local priceColName = TSM.db.profile.pricePerUnit and "Price Per Item" or "Price Per Stack" local colInfo = isDestroying and { {name=L["Item"], width=0.43}, {name=L["Auctions"], width=0.07, align="CENTER"}, {name=L["Stack Size"], width=0.05, align="CENTER"}, {name=L["Seller"], width=0.11, align="CENTER"}, {name=L["Price Per Target Item"], width=0.13, align="RIGHT", isPrice=true}, {name=priceColName, width=0.13, align="RIGHT", isPrice=true}, {name=L["% Market Value"], width=0.08, align="CENTER"}, } or { {name=L["Item"], width=0.42}, {name=L["Item Level"], width=0.05, align="CENTER"}, {name=L["Auctions"], width=0.07, align="CENTER"}, {name=L["Stack Size"], width=0.05, align="CENTER"}, {name=L["Time Left"], width=0.09, align="CENTER"}, {name=L["Seller"], width=0.11, align="CENTER"}, {name=priceColName, width=0.13, align="RIGHT", isPrice=true}, {name=L["% Market Value"], width=0.08, align="CENTER"}, } local rtName = "TSMAuctionResultsTable"..RT_COUNT RT_COUNT = RT_COUNT + 1 local rt = CreateFrame("Frame", rtName, parent) rt.NUM_ROWS = TSM.db.profile.auctionResultRows rt.ROW_HEIGHT = (parent:GetHeight()-HEAD_HEIGHT-HEAD_SPACE)/rt.NUM_ROWS rt:SetScript("OnShow", function() local priceColName = TSM.db.profile.pricePerUnit and L["Price Per Item"] or L["Price Per Stack"] rt:SetColHeadText(#rt.headCols-1, priceColName) rt:RefreshRowData() end) local contentFrame = CreateFrame("Frame", rtName.."Content", rt) contentFrame:SetPoint("TOPLEFT") contentFrame:SetPoint("BOTTOMRIGHT", -15, 0) contentFrame:SetScript("OnSizeChanged", function(_, width) OnSizeChanged(rt, width) end) rt.contentFrame = contentFrame -- frame to hold the header columns and the rows local scrollFrame = CreateFrame("ScrollFrame", rtName.."ScrollFrame", rt, "FauxScrollFrameTemplate") scrollFrame:SetScript("OnVerticalScroll", function(self, offset) FauxScrollFrame_OnVerticalScroll(self, offset, rt.ROW_HEIGHT, function() rt:DrawRows() end) end) scrollFrame:SetAllPoints(contentFrame) rt.scrollFrame = scrollFrame -- make the scroll bar consistent with the TSM theme local scrollBar = _G[scrollFrame:GetName().."ScrollBar"] scrollBar:ClearAllPoints() scrollBar:SetPoint("BOTTOMRIGHT", rt, -1, 1) scrollBar:SetPoint("TOPRIGHT", rt, -1, -HEAD_HEIGHT) scrollBar:SetWidth(12) local thumbTex = scrollBar:GetThumbTexture() thumbTex:SetPoint("CENTER") TSMAPI.Design:SetFrameColor(thumbTex) thumbTex:SetHeight(150) thumbTex:SetWidth(scrollBar:GetWidth()) _G[scrollBar:GetName().."ScrollUpButton"]:Hide() _G[scrollBar:GetName().."ScrollDownButton"]:Hide() -- create the header columns rt.headCols = {} for i, info in ipairs(colInfo) do local col = CreateFrame("Button", rtName.."HeadCol"..i, rt.contentFrame) col:SetHeight(HEAD_HEIGHT) if i == 1 then col:SetPoint("TOPLEFT") else col:SetPoint("TOPLEFT", rt.headCols[i-1], "TOPRIGHT") end col.info = info col.rt = rt col:RegisterForClicks("AnyUp") col:SetScript("OnClick", OnColumnClick) local text = col:CreateFontString() text:SetJustifyH("CENTER") text:SetJustifyV("CENTER") text:SetFont(TSMAPI.Design:GetContentFont("small")) TSMAPI.Design:SetWidgetTextColor(text) col:SetFontString(text) col:SetText(info.name or "") text:SetAllPoints() local tex = col:CreateTexture() tex:SetAllPoints() tex:SetTexture("Interface\\WorldStateFrame\\WorldStateFinalScore-Highlight") tex:SetTexCoord(0.017, 1, 0.083, 0.909) tex:SetAlpha(0.5) col:SetNormalTexture(tex) local tex = col:CreateTexture() tex:SetAllPoints() tex:SetTexture("Interface\\Buttons\\UI-Listbox-Highlight") tex:SetTexCoord(0.025, 0.957, 0.087, 0.931) tex:SetAlpha(0.2) col:SetHighlightTexture(tex) tinsert(rt.headCols, col) end -- create the rows rt.rows = {} for i=1, rt.NUM_ROWS do local row = CreateFrame("Frame", rtName.."Row"..i, rt.contentFrame) row:SetHeight(rt.ROW_HEIGHT) if i == 1 then row:SetPoint("TOPLEFT", 0, -(HEAD_HEIGHT+HEAD_SPACE)) row:SetPoint("TOPRIGHT", 0, -(HEAD_HEIGHT+HEAD_SPACE)) else row:SetPoint("TOPLEFT", rt.rows[i-1], "BOTTOMLEFT") row:SetPoint("TOPRIGHT", rt.rows[i-1], "BOTTOMRIGHT") end local highlight = row:CreateTexture() highlight:SetAllPoints() highlight:SetTexture(1, .9, 0, .5) highlight:Hide() row.highlight = highlight row.rt = rt row.cols = {} for j=1, #colInfo do local col = CreateFrame("Button", nil, row) local text = col:CreateFontString() if TSM.db.profile.showBids and colInfo[j].isPrice then text:SetFont(TSMAPI.Design:GetContentFont(), min(13, rt.ROW_HEIGHT/2 - 2)) else text:SetFont(TSMAPI.Design:GetContentFont(), min(14, rt.ROW_HEIGHT)) end text:SetJustifyH(colInfo[j].align or "LEFT") text:SetJustifyV("CENTER") text:SetPoint("TOPLEFT", 1, -1) text:SetPoint("BOTTOMRIGHT", -1, 1) col:SetFontString(text) col:SetHeight(rt.ROW_HEIGHT) col:RegisterForClicks("AnyUp") for name, func in pairs(defaultColScripts) do col:SetScript(name, func) end col.rt = rt col.row = row col.rowNum = i if j == 1 then col:SetPoint("TOPLEFT") else col:SetPoint("TOPLEFT", row.cols[j-1], "TOPRIGHT") end if j%2 == 1 then local tex = col:CreateTexture() tex:SetAllPoints() tex:SetTexture(1, 1, 1, .03) col:SetNormalTexture(tex) end -- special first column to hold spacer / item name / item icon if j == 1 then local spacer = CreateFrame("Frame", nil, col) spacer:SetPoint("TOPLEFT") spacer:SetHeight(rt.ROW_HEIGHT) spacer:SetWidth(1) col.spacer = spacer local iconBtn = CreateFrame("Button", nil, col) iconBtn:SetBackdrop({edgeFile="Interface\\Buttons\\WHITE8X8", edgeSize=1.5}) iconBtn:SetBackdropBorderColor(0, 1, 0, 0) iconBtn:SetPoint("TOPLEFT", spacer, "TOPRIGHT") iconBtn:SetHeight(rt.ROW_HEIGHT) iconBtn:SetWidth(rt.ROW_HEIGHT) iconBtn:SetScript("OnEnter", function(self) if row.data.link then GameTooltip:SetOwner(self, "ANCHOR_RIGHT") TSMAPI:SafeTooltipLink(row.data.link) GameTooltip:Show() rt.isShowingItemTooltip = true end end) iconBtn:SetScript("OnLeave", function(self) -- BattlePetTooltip:Hide() GameTooltip:ClearLines() GameTooltip:Hide() rt.isShowingItemTooltip = false end) iconBtn:SetScript("OnClick", function(_, ...) if IsModifiedClick() then HandleModifiedItemClick(row.data.auctionRecord.parent.itemLink) else col:GetScript("OnClick")(col, ...) end end) iconBtn:SetScript("OnDoubleClick", function(_, ...) col:GetScript("OnDoubleClick")(col, ...) end) local icon = iconBtn:CreateTexture(nil, "ARTWORK") icon:SetPoint("TOPLEFT", 2, -2) icon:SetPoint("BOTTOMRIGHT", -2, 2) col.iconBtn = iconBtn col.icon = icon row.ShowActiveBorder = function() if rt.quickBuyout then row.isActive = true iconBtn:SetBackdropBorderColor(0, 1, 0, .7) end end row.HideActiveBorder = function() row.isActive = nil iconBtn:SetBackdropBorderColor(0, 0, 0, 0) end text:ClearAllPoints() text:SetPoint("TOPLEFT", iconBtn, "TOPRIGHT", 2, 0) text:SetPoint("BOTTOMRIGHT") end tinsert(row.cols, col) end if i%2 == 0 then local tex = row:CreateTexture() tex:SetAllPoints() tex:SetTexture("Interface\\WorldStateFrame\\WorldStateFinalScore-Highlight") tex:SetTexCoord(0.017, 1, 0.083, 0.909) tex:SetAlpha(0.3) end tinsert(rt.rows, row) end rt:SetAllPoints() rt.data = {} rt.expanded = {} rt.displayRows = {} rt.handlers = handlers or {} rt.sortInfo = {} rt.quickBuyout = quickBuyout rt.isDestroying = isDestroying tinsert(resultsTables, rt) for name, func in pairs(methods) do rt[name] = func end LibStub("AceEvent-3.0").RegisterEvent(rt, "AUCTION_ITEM_LIST_UPDATE", "UpdateActiveRows") return rt end