------------------------------------------------------------------------------- -- Util.lua -- -- General utility functions. ------------------------------------------------------------------------------- local _ -- Make a printable string for a time in seconds. function AuctionLite:PrintTime(sec) local min = math.floor(sec / 60); sec = sec % 60; local hr = math.floor(min / 60); min = min % 60; local result = ""; if hr > 0 then result = tostring(hr) .. ":" .. string.format("%02d", min) .. ":"; else result = tostring(min) .. ":"; end result = result .. string.format("%02d", sec); return result; end -- Make a printable string for a given amount of money (in copper). function AuctionLite:PrintMoney(money) money = math.floor(money); local copper = money % 100; money = math.floor(money / 100); local silver = money % 100; money = math.floor(money / 100); local gold = money; local result = ""; local append = function(s) if result ~= "" then result = result .. " "; end result = result .. s; end if gold > 0 then append("|cffd3c63a" .. gold .. "|cffffffffg|r"); end if silver > 0 then append("|cffb0b0b0" .. silver .. "|cffffffffs|r"); end if copper > 0 then append("|cffb2734a" .. copper .. "|cffffffffc|r"); end if result == "" then result = "0"; end return result; end -- Parse a string representing an amount of money. function AuctionLite:ParseMoney(str) -- Remove colors. str = str:gsub("|c........", ""); str = str:gsub("|r", ""); -- Extract gold, silver, and copper portions. -- This could be a single regexp if Lua regexps worked properly... local _, _, gold = str:find("^ *(%d+)g"); str = str:gsub("^ *(%d+)g", ""); local _, _, silver = str:find("^ *(%d+)s"); str = str:gsub("^ *(%d+)s", ""); local _, _, copper = str:find("^ *(%d+)c"); str = str:gsub("^ *(%d+)c", ""); -- Make sure there's nothing left but whitespace, and compute the result. local money = 0; if str:find("^ *$") ~= nil then if gold ~= nil then money = money + gold * 10000 end if silver ~= nil then money = money + silver * 100 end if copper ~= nil then money = money + copper end end return money; end -- Regular expression for parsing links. local LinkRegexp = "|c(.*)|H(.*)|h%[(.*)%]"; -- Is this string a link? function AuctionLite:IsLink(str) return (str:find(LinkRegexp) ~= nil); end -- Dissect an item link or item string. function AuctionLite:SplitLink(link) -- Parse the link. local _, _, color, str, name = link:find(LinkRegexp); -- If we failed, then assume it's actually an item string. if str == nil then str = link; end -- Split the item string. local _, id, enchant, jewel1, jewel2, jewel3, jewel4, suffix, unique = strsplit(":", str); return name, color, tonumber(id) or 0, tonumber(suffix) or 0, tonumber(enchant) or 0, tonumber(jewel1) or 0, tonumber(jewel2) or 0, tonumber(jewel3) or 0, tonumber(jewel4) or 0, tonumber(unique) or 0; end -- Zero out the uniqueId field from an item link. function AuctionLite:RemoveUniqueId(link) if link ~= nil then return link:gsub( -- Fields: itemID, enchant, gem1, gem2, gem3, gem4, suffixID. "(Hitem:%-?%d*:%-?%d*:%-?%d*:%-?%d*:%-?%d*:%-?%d*:%-?%d*)" .. -- Fields: uniqueID, level. ":%-?%d*:%-?%d*", -- Set uniqueID and level to 0. "%1::") else return nil; end end -- Zero out the suffixId field from an item link, and also replace the name with the un-suffixed name. function AuctionLite:RemoveSuffix(link) if link ~= nil then local name, _, id = AuctionLite:SplitLink(link) local baseName = GetItemInfo(id) return link:gsub( -- Fields: itemID, enchant, gem1, gem2, gem3, gem4. "(Hitem:%-?%d*:%-?%d*:%-?%d*:%-?%d*:%-?%d*:%-?%d*)" .. -- Fields: suffixID. ":%-?%d*", -- Set suffixID to 0. "%1:"):gsub(name, baseName) else return nil; end end -- Get auction sell item info as well as a link to the item and the -- location of the item in the player's bags. We use the fact that it -- must be locked (since it's in the auction slot). Returns nil if the -- item is not found or if we can't pinpoint the exact bag slot. function AuctionLite:GetAuctionSellItemInfoAndLink() local name, texture, count, quality, canUse, price = GetAuctionSellItemInfo(); local link = nil; local container = nil; local slot = nil; if name ~= nil then local i, j; -- Look through the bags to find a matching item. for i = 0, 4 do local numItems = C_Container.GetContainerNumSlots(i); for j = 1, numItems do if C_Container.GetContainerItemInfo(i, j) ~= nil then local slotInfo = C_Container.GetContainerItemInfo(i, j); local curCount, locked = slotInfo.stackCount, slotInfo.isLocked; if count == curCount and locked then -- We've found a partial match. Now check the name... local curLink = C_Container.GetContainerItemLink(i, j); local curName = self:SplitLink(curLink); if name == curName then if link == nil then -- It's our first match--make a note of it. link = self:RemoveUniqueId(curLink); container = i; slot = j; else -- Ambiguous result. Bail! return; end end end end end end end -- Return all the original item info plus our three results. return name, texture, count, quality, canUse, price, link, container, slot; end -- Get the maximum stack size for an item. function AuctionLite:GetMaxStackSize(link) local _, _, _, _, _, _, _, maxSize = GetItemInfo(link); if maxSize == nil then maxSize = 1; end return maxSize; end -- Make a money frame value negative. function AuctionLite:MakeNegative(frameName) local adjust = function(button) local current = button:GetText(); if button:IsShown() then button:SetText("-" .. current); return true; else return false; end end local goldButton = getglobal(frameName .. "GoldButton"); if not adjust(goldButton) then local silverButton = getglobal(frameName .. "SilverButton"); if not adjust(silverButton) then local copperButton = getglobal(frameName .. "CopperButton"); adjust(copperButton); end end end -- Truncates a UTF-8 string to a fixed number of bytes. function AuctionLite:Truncate(str, bytes) -- We need to make sure that we don't truncate mid-character, and in -- UTF-8, all mid-character bytes start with bits 10. So, reduce bytes -- until the first character we're dropping does not start with 10. if str:len() > bytes then while bytes > 0 and bit.band(str:byte(bytes + 1), 0xc0) == 0x80 do bytes = bytes - 1; end end return str:sub(1, bytes); end -- Get a listing from the auction house. function AuctionLite:GetListing(kind, i) -- There has *got* to be a better way to do this... local link = self:RemoveUniqueId(GetAuctionItemLink(kind, i)); local name, texture, count, quality, canUse, level, levelColHeader, minBid, minIncrement, buyout, bidAmount, highBidder, bidderFullName, owner, ownerFullName, sold = GetAuctionItemInfo(kind, i); -- Figure out the true minimum bid. local bid; if bidAmount <= 0 then bid = minBid; else bid = bidAmount + minIncrement; if bid > buyout and buyout > 0 then bid = buyout; end end -- Create a listing object with all this data. local listing = { link = link, name = name, texture = texture, count = count, quality = quality, canUse = canUse, level = level, bid = bid, minBid = minBid, minIncrement = minIncrement, buyout = buyout, bidAmount = bidAmount, highBidder = highBidder, owner = owner, sold = sold }; return listing; end -- Does a target from the "Buy" frame match an auction listing? -- TODO: Get rid of the targetName hackery. function AuctionLite:MatchListing(targetName, target, listing) return targetName == listing.name and target.count == listing.count and target.bid == listing.bid and target.buyout == listing.buyout and (target.owner == nil or listing.owner == nil or target.owner == listing.owner); end -- Do two pages from an AH scan match exactly? function AuctionLite:MatchPages(data, page1, page2) local i; for i = 1, NUM_AUCTION_ITEMS_PER_PAGE do local listing1 = data[page1 * NUM_AUCTION_ITEMS_PER_PAGE + i]; local listing2 = data[page2 * NUM_AUCTION_ITEMS_PER_PAGE + i]; if listing1 == nil or listing2 == nil or not self:MatchListing(listing1.name, listing1, listing2) then return false; end end return true; end -- Get the names of all my auctions. function AuctionLite:GetMyAuctionLinks() local batch = GetNumAuctionItems("owner"); local links = {}; -- Find all the auctions to cancel. local i; for i = 1, batch do local listing = self:GetListing("owner", i); if listing.sold ~= 1 then links[listing.link] = true; end end return links; end -- Is the specified item (name or link) a favorite, optionally in a specific -- favorites list? function AuctionLite:IsFavorite(item, list) local name = self:SplitLink(item); local link; if name == nil then name = item; link = nil; else link = item; end name = strlower(name); local searchList = function(list) if list[link] ~= nil then return link; else for item, _ in pairs(list) do local itemName = self:SplitLink(item); if itemName == nil then itemName = item; end if strlower(itemName) == name then return item; end end return nil; end end if list ~= nil then return searchList(list); else for _, list in pairs(self.db.profile.favorites) do local result = searchList(list); if result then return result; end end return nil; end end -- If there's exactly one favorites list, get it. Otherwise, return nil. function AuctionLite:GetSingleFavoritesList() local found = nil; for _, list in pairs(self.db.profile.favorites) do if found == nil then found = list; else return nil; end end -- Sanity check: If there are no favorites lists, make one. if found == nil then found = {}; self.db.profile.favorites = { [L["Favorites"]] = found }; end return found; end -- Sort the columns by the designated sort type. function AuctionLite:ApplySort(info, data, cmp) local fn = function(a, b) if cmp(a, b) == cmp(b, a) then if info.justFlipped then return a.orig < b.orig; else return b.orig < a.orig; end else if info.flipped then return cmp(b, a); else return cmp(a, b); end end end local i = 1; local item; for _, item in ipairs(data) do item.orig = i; i = i + 1; end table.sort(data, fn); local item; for _, item in ipairs(data) do item.orig = nil; end info.sorted = true; info.justFlipped = false; end -- Update the current sort for a click. function AuctionLite:SortButton_OnClick(info, sort) if info.sort == sort then info.flipped = not info.flipped; else info.sort = sort; info.flipped = false; info.justFlipped = true; end info.sorted = false; end -- Update sortable header buttons. function AuctionLite:UpdateSortButton(prefix, buttonName, text) local button = _G[prefix .. buttonName .. "Button"]; local arrow = _G[prefix .. buttonName .. "ButtonArrow"]; button:SetText(text); local offset = button:GetTextWidth(); if offset > button:GetWidth() - 10 then offset = button:GetWidth() - 10; end arrow:SetPoint("RIGHT", button, "RIGHT", -offset - 1, -1); end -- Update sort arrows. function AuctionLite:UpdateSortArrow(prefix, buttonSort, sort, flipped) local arrow = _G[prefix .. buttonSort .. "ButtonArrow"]; if buttonSort == sort then arrow:Show(); if flipped then arrow:SetTexCoord(0, 0.5625, 1.0, 0); else arrow:SetTexCoord(0, 0.5625, 0, 1.0); end else arrow:Hide(); end end -- Table of ignored chat messages (msg -> count). local IgnoreTable = {}; -- Filter out "ignored" messages. function AuctionLite:MessageEventFilter(frame, event, arg1, ...) local count = IgnoreTable[arg1]; if count ~= nil then IgnoreTable[arg1] = (count > 1) and (count - 1) or nil; return true; else return false, arg1, ...; end end -- Ignore "count" instances of "msg" in the chat window. function AuctionLite:IgnoreMessage(msg, count) -- Find out how many windows listen for this message. local mult = 0; for i = 1, NUM_CHAT_WINDOWS do local messages = { GetChatWindowMessages(i) }; for _, message in ipairs(messages) do if message == "SYSTEM" then mult = mult + 1; break; end end end -- Increment the ignore table entry by an appropriate amount. count = count or 1; cur = IgnoreTable[msg] or 0; IgnoreTable[msg] = cur + (count * mult); end