AuctionLite-classic/BuyFrame.lua

2141 lignes
61 KiB
Lua

-------------------------------------------------------------------------------
-- BuyFrame.lua
--
-- Implements the "Buy" tab.
-------------------------------------------------------------------------------
local _
local L = LibStub("AceLocale-3.0"):GetLocale("AuctionLite", false)
-- Constants for display elements.
local BUY_DISPLAY_SIZE = 15;
local ROW_HEIGHT = 21;
local EXPAND_ROWS = 4;
-- Various states for the buy frame.
local BUY_MODE_NONE = 1;
local BUY_MODE_INTRO = 2;
local BUY_MODE_SEARCH = 3;
local BUY_MODE_SCAN = 4;
local BUY_MODE_DEALS = 5;
local BUY_MODE_FAVORITES = 6;
local BUY_MODE_MY_AUCTIONS = 7;
-- Current state of the buy frame.
local BuyMode = BUY_MODE_NONE;
-- Current sorting state.
local SummarySort = {
sort = "ItemSummary",
flipped = false,
justFlipped = false,
sorted = false,
};
local DetailSort = {
sort = "BuyoutEach",
flipped = false,
justFlipped = false,
sorted = false,
};
-- Height of expandable frame.
local ExpandHeight = 0;
-- Data to be shown in detail view.
local DetailLink = nil;
local DetailData = {};
local SavedOffset = 0;
-- Save the last selected item.
local DetailLinkPrev = nil;
-- Selected item in detail view and index of last item clicked.
local SelectedItems = {};
local LastClick = nil;
-- Data to be shown in summary view.
local SummaryData = {};
local SummaryDataByLink = {};
local NoResults = false;
-- Info about current purchase for display in expandable frame.
local PurchaseOrder = nil;
-- Information about current search progress.
local StartTime = nil;
local LastTime = nil;
local LastRemaining = nil;
local Progress = nil;
local GetAll = nil;
local Scanning = nil;
-- Stored scan data from the latest full scan.
local ScanData = nil;
-- Links and data for multi-item scan.
local MultiScanCurrent = nil;
local MultiScanOriginal = {};
local MultiScanItems = {};
local MultiScanData = {};
-- Static popup advertising AL's fast scan.
StaticPopupDialogs["AL_FAST_SCAN"] = {
text = L["FAST_SCAN_AD"],
button1 = L["Enable"],
button2 = L["Disable"],
OnAccept = function(self)
AuctionLite:Print(L["Fast auction scan enabled."]);
AuctionLite.db.profile.fastScanAd2 = true;
AuctionLite.db.profile.getAll = true;
AuctionLite:StartFullScan();
end,
OnCancel = function(self)
AuctionLite:Print(L["Fast auction scan disabled."]);
AuctionLite.db.profile.fastScanAd2 = true;
AuctionLite.db.profile.getAll = false;
AuctionLite:StartFullScan();
end,
showAlert = 1,
timeout = 0,
exclusive = 1,
hideOnEscape = 1,
preferredIndex = 3
};
-- Static popup explaining cancel clicks.
StaticPopupDialogs["AL_CANCEL_NOTE"] = {
text = L["CANCEL_NOTE"],
button1 = OKAY,
OnAccept = function(self)
AuctionLite.db.profile.cancelNote = true;
end,
showAlert = 1,
timeout = 0,
exclusive = 1,
hideOnEscape = 1,
preferredIndex = 3
};
-- Set current item to be shown in detail view, and update dependent data.
function AuctionLite:SetDetailLink(link)
-- If there's no data to show, then just set the link to nil.
if link ~= nil and table.getn(SummaryDataByLink[link].data) == 0 then
link = nil;
end
-- If we're leaving the summary view, save our offset.
if DetailLink == nil then
SavedOffset = FauxScrollFrame_GetOffset(BuyScrollFrame);
end
-- Set the new detail link, if any.
DetailLink = link;
local offset;
if DetailLink ~= nil then
DetailData = SummaryDataByLink[DetailLink].data;
offset = 0;
else
DetailData = {};
offset = SavedOffset;
end
DetailSort.sorted = false;
-- Return to our saved offset.
self:SetScrollFrameOffset(offset);
SelectedItems = {};
LastClick = nil;
end
-- Set the data for the scrolling frame.
function AuctionLite:SetBuyData(results)
SummaryData = {};
local count = 0;
local last = nil;
local foundPrev = false;
-- Sort everything and assemble the summary data.
for link, result in pairs(results) do
table.insert(SummaryData, result);
result.link = link;
result.name = self:SplitLink(link);
local hist = self:GetHistoricalPrice(link);
if hist ~= nil then
result.histPrice = hist.price;
else
result.histPrice = 0;
end
count = count + 1;
last = link;
if DetailLinkPrev == link then
foundPrev = true;
end
end
SummaryDataByLink = results;
-- Sort our data by name/profit.
if BuyMode == BUY_MODE_DEALS or
BuyMode == BUY_MODE_SCAN then
SummarySort.sort = "Historical";
SummarySort.flipped = true;
else
SummarySort.sort = "ItemSummary";
SummarySort.flipped = false;
end
SummarySort.sorted = false;
SummarySort.justFlipped = false;
-- Set up warning markers for undercuts.
self:SetWarnings();
-- If we found our last-selected item, then select it again.
-- If we found only one item, select it. Otherwise, select nothing.
local newLink = nil;
if foundPrev then
newLink = DetailLinkPrev;
elseif count == 1 then
newLink = last;
end
DetailLinkPrev = nil;
-- Reset current query info.
self:ResetSearch();
-- Save our data and set our detail link, if we only got one kind of item.
SummaryDataByLink = results;
NoResults = (count == 0);
self:SetDetailLink(newLink);
-- Clean up the display.
BuyMessageText:Hide();
BuyStatus:Hide();
-- Start a mass buyout, if necessary.
self:StartMassBuyout();
-- Repaint.
self:AuctionFrameBuy_Update();
end
-- Handle results for a full scan. Make a list of the deals.
function AuctionLite:SetScanData(results)
ScanData = {};
-- Search through all scanned items.
for link, result in pairs(results) do
local hist = self:GetHistoricalPrice(link);
-- Find the lowest buyout.
local min = 0;
for _, listing in ipairs(result.data) do
if min == 0 or (0 < listing.buyout and listing.buyout < min) then
min = listing.buyout;
end
end
-- If it meets a bunch of conditions below, it's considered a deal.
if min > 0 and hist ~= nil and
min < hist.price - (10000 * self.db.profile.minProfit) and
min < hist.price * (1 - self.db.profile.minDiscount) and
hist.listings / hist.scans > 1.2 then
result.profit = hist.price - min;
ScanData[link] = result;
end
end
-- Display our list of deals.
DetailLinkPrev = nil;
self:SetBuyData(ScanData, true);
end
-- Helper that finds user's listings. Return value indicates whether we've
-- been undercut. (If that's all you care about, omit the last three args.)
function AuctionLite:FindMyListings(listings, list, name, onlyUndercut)
local listingsCopy = {};
-- Make a copy of listings.
for a, b in pairs(listings) do
listingsCopy[a] = b;
end
-- Sort by per-item buyout price.
table.sort(listingsCopy,
function(a, b) return a.buyout / a.count < b.buyout / b.count end);
-- Iterate over listings.
local undercut = false;
local foundOther = false;
local listing;
for _, listing in ipairs(listingsCopy) do
if listing.buyout > 0 then
if listing.owner == UnitName("player") then
if foundOther then
undercut = true;
if list == nil then
break;
end
end
if list ~= nil then
if foundOther or not onlyUndercut then
listing.name = name;
table.insert(list, listing);
end
end
else
foundOther = true;
end
end
end
return undercut;
end
-- Set undercut warnings for all auctions.
function AuctionLite:SetWarnings()
if BuyMode == BUY_MODE_MY_AUCTIONS then
for link, result in pairs(SummaryDataByLink) do
-- Sort by per-item buyout price.
table.sort(result.data,
function(a, b) return a.buyout / a.count < b.buyout / b.count end);
-- Invalidate the current sort.
DetailSort.sorted = false;
-- Find the first listing with non-zero buyout. If it's not ours,
-- we've been undercut.
result.warning = self:FindMyListings(result.data);
end
end
end
-- Determine whether the selected items are biddable/buyable.
function AuctionLite:GetSelectionStatus()
local biddable = true;
local buyable = true;
local cancellable = true;
local found = false;
-- Helper to determine whether we have any listings.
local findMine = function(data)
for _, listing in ipairs(data) do
if listing.owner == UnitName("player") then
return true;
end
end
return false;
end
if DetailLink ~= nil then
-- Check all selected items.
for listing, _ in pairs(SelectedItems) do
-- We can't bid/buy our own auctions.
if listing.owner == UnitName("player") then
biddable = false;
buyable = false;
else
cancellable = false;
end
-- To buy, we must have a buyout price listed.
if listing.buyout == 0 then
buyable = false;
end
-- We must find at least one selected item.
found = true;
end
-- Nothing's selected, so check all items.
if not found then
cancellable = findMine(DetailData);
end
elseif SummaryDataByLink ~= nil then
-- We're at the summary screen, so check all items.
cancellable = false;
for link, result in pairs(SummaryDataByLink) do
if findMine(result.data) then
cancellable = true;
break;
end
end
else
cancellable = false;
end
return (found and biddable), (found and buyable), cancellable;
end
-- Cancel the selected items.
function AuctionLite:CancelItems(onlyUndercut, link)
-- List of items to be cancelled.
local list = {};
-- If we didn't specify data, try using DetailData.
if link == nil then
link = DetailLink;
end
-- Now find the items to cancel.
if link ~= nil then
local data = SummaryDataByLink[link].data;
local name = self:SplitLink(link);
-- Add information about each selected item.
local i;
for listing, _ in pairs(SelectedItems) do
assert(listing.owner == UnitName("player"));
listing.name = name;
table.insert(list, listing);
end
-- If we don't have any selected items, cancel all.
if table.getn(list) == 0 then
self:FindMyListings(data, list, name, onlyUndercut);
end
else
-- We're at the summary screen, so do this for all items.
for link, result in pairs(SummaryDataByLink) do
local name = self:SplitLink(link);
self:FindMyListings(result.data, list, name, onlyUndercut);
end
end
-- If we found some items to cancel, do it.
if table.getn(list) > 0 then
self:CancelAuctions(list);
end
end
-- Update the display now that we've finished cancelling.
function AuctionLite:CancelComplete()
-- Go through all listings and remove the ones that were cancelled.
for link, summary in pairs(SummaryDataByLink) do
local data = summary.data;
local i = table.getn(data);
while i > 0 do
local listing = data[i];
if listing.cancelled then
table.remove(data, i);
summary.itemsAll = summary.itemsAll - listing.count;
summary.itemsMine = summary.itemsMine - listing.count;
summary.listingsAll = summary.listingsAll - 1;
summary.listingsMine = summary.listingsMine - 1;
SelectedItems[listing] = nil;
end
i = i - 1;
end
end
-- Reevaluate "warning" status.
self:SetWarnings();
-- Cleanup.
if table.getn(DetailData) == 0 then
self:SetDetailLink(nil);
end
-- Update the display.
self:AuctionFrameBuy_Update();
end
-- Called after a search query ends in order to start a mass buyout.
function AuctionLite:StartMassBuyout()
-- See if the user requested a specific quantity.
local requested = BuyQuantity:GetNumber();
if DetailLink ~= nil and table.getn(DetailData) > 0 and requested > 0 then
-- Clear our selected items. (Should already be done, but what the hey.)
SelectedItems = {};
-- Make a list of all items from other sellers with nonzero buyout.
-- Also figure out how many items we have available in all.
local available = {};
local cheapest = nil;
local cheapestOwned = nil;
local total = 0;
local listing;
for _, listing in ipairs(DetailData) do
if listing.buyout > 0 then
local price = listing.buyout / listing.count;
if listing.owner == UnitName("player") then
-- Remember the cheapest auction that we own.
if cheapestOwned == nil or price < cheapestOwned then
cheapestOwned = price;
end
else
-- Remember the cheapest auction that we don't own.
if cheapest == nil or price < cheapest then
cheapest = price;
end
-- Keep track of all other auctions with buyouts.
table.insert(available, listing);
total = total + listing.count;
end
end
end
-- Are there enough items for sale to satisfy the request?
if total <= requested then
-- Nope. Just take everything we can.
local listing;
for _, listing in ipairs(available) do
SelectedItems[listing] = true;
end
else
-- Yep. So now we have to figure out what to buy. We use an
-- adaptation of the standard dynamic programming algorithm for
-- 0-1 knapsack.
-- The state for this algorithm is as follows:
-- results: For any n, what's the best price we can get for exactly
-- n items, and what listings do we have to buy to do so?
-- indices: A list of valid indices for results, in descending order.
-- We initialize these with 0, our base case.
local results = { [0] = { price = 0 } };
local indices = { 0 };
-- Determine the maximum number of items we want to consider buying.
-- This is the number requested plus the maximum stack size available
-- in the AH. We don't need to consider anything large than this,
-- because if we could fill the user's order for a larger count, then
-- we must be able to remove one listing to fill the user's order for
-- less money overall.
local maxCount = 0;
local listing;
for _, listing in ipairs(available) do
if listing.count > maxCount then
maxCount = listing.count;
end
end
maxCount = maxCount + requested;
-- Sort by count, which improves the speed of the algorithm below.
table.sort(available, function(a, b) return a.count > b.count end);
-- Save our start time.
local start = time();
-- Consider each listing in turn. At each step, the results table
-- will have the best results for the set of listings we've considered
-- so far.
local timeout = false;
local listing;
for _, listing in ipairs(available) do
-- If we've taken more than 1-2 seconds, bail.
if start + 1 < time() then
timeout = true;
break;
end
-- Iterate over all counts where we have results so far, in
-- descending order so that we can update in place.
local count;
for _, count in ipairs(indices) do
local info = results[count];
local newCount = count + listing.count;
assert(info ~= nil);
-- Add this listing to the best result at this stack size.
-- Do we care about the resulting set of items?
if newCount < maxCount then
-- If this is the first option at this new stack size, or if it's
-- cheaper than our previous best effort, update the data.
local newInfo = results[newCount];
local newPrice = info.price + listing.buyout;
if newInfo == nil or newPrice < newInfo.price then
-- The new data consists of the current result's listings plus
-- the listing we just added.
results[newCount] = {
price = newPrice,
listing = listing,
info = info,
};
end
-- If we added a new index to the results table, add it to our
-- list of indices too, and make sure it's still sorted.
if newInfo == nil then
table.insert(indices, newCount);
table.sort(indices, function(a, b) return a > b end);
end
end
end
end
if timeout then
-- We hit a timeout, so just do things the fast way.
-- First sort by per-item buyout.
local sort = function(a, b)
return a.buyout / a.count < b.buyout / b.count;
end
table.sort(available, sort);
-- Get the cheapest items until the order is filled.
local selected = {};
local count = 0;
local i = 1;
while i < table.getn(available) and count < requested do
local listing = available[i];
table.insert(selected, listing);
count = count + listing.count;
i = i + 1;
end
-- Go over the selected items in reverse order and remove any
-- that aren't actually necessary.
local i = table.getn(selected);
while i > 0 do
local listing = selected[i];
if count - listing.count >= requested then
table.remove(selected, i);
count = count - listing.count;
end
i = i - 1;
end
-- Update the selected items list as appropriate.
local listing;
for _, listing in ipairs(selected) do
SelectedItems[listing] = true;
end
else
-- We've computed our best options for all stack sizes.
-- Figure out the resale value for this item. We use the lesser
-- of the cheapest price and the historical price.
local resale = cheapest - 1;
local hist = self:GetHistoricalPrice(DetailLink);
if hist ~= nil and hist.price > 0 and hist.price < resale then
resale = hist.price;
end
-- Now find the cheapest way to buy the requested number of items.
local bestCount = nil;
local bestInfo = nil;
local count;
local info;
for count, info in pairs(results) do
if count >= requested then
-- If the user wants us to, adjust for resale price.
if self.db.profile.considerResale then
info.price = info.price - (resale * (count - requested));
end
-- Is this the best option we've seen?
if bestInfo == nil or info.price < bestInfo.price or
(info.price == bestInfo.price and count > bestCount) then
bestCount = count;
bestInfo = info;
end
end
end
-- Update SelectedItems with the final results. We should always
-- find a result here, but we'll be defensive anyway.
while bestInfo ~= nil and bestInfo.listing ~= nil do
SelectedItems[bestInfo.listing] = true;
bestInfo = bestInfo.info;
end
end
-- Sort by selectedness in order to pull selected items to the
-- top, and mark the list as unsorted so that they will be sorted
-- properly (and stably) on the next update.
local sort = function(a, b)
return SelectedItems[b] and not SelectedItems[a];
end
table.sort(DetailData, sort);
DetailSort.sorted = false;
end
-- Do we have any listings for this item?
if cheapestOwned ~= nil then
-- Find the worst price we're paying.
local worstBought = nil;
local listing;
for listing, _ in pairs(SelectedItems) do
local price = listing.buyout / listing.count;
if worstBought == nil or worstBought < price then
worstBought = price;
end
end
-- If it's above the price for our cheapest listing, warn the user.
if cheapestOwned < worstBought then
self:Print(L["|cffff0000[Warning]|r Skipping your own auctions. " ..
"You might want to cancel them instead."]);
end
end
-- Now create our buyout order.
self:CreateOrder(true, requested);
end
end
-- Create a purchase order based on the current selection. The first
-- argument indicates whether we're bidding or buying, and the second
-- argument (optional) indicates the actual number of items the user wants.
function AuctionLite:CreateOrder(isBuyout, requested)
if DetailLink ~= nil then
-- Create purchase order object to be filled out.
local order = { list = {}, name = self:SplitLink(DetailLink),
price = 0, spent = 0, count = 0, isBuyout = isBuyout,
itemsBought = 0, listingsBought = 0,
itemsNotFound = 0, listingsNotFound = 0 };
-- Add information about each selected item.
local i;
for listing, _ in pairs(SelectedItems) do
assert(listing.owner ~= UnitName("player"));
local price;
if isBuyout then
price = listing.buyout;
else
price = listing.bid;
end
table.insert(order.list, listing);
order.count = order.count + listing.count;
order.price = order.price + price;
end
-- If we found any selected items and we have enough money, proceed.
if order.price > GetMoney() then
self:Print(L["|cffff0000[Error]|r Insufficient funds."]);
elseif order.count > 0 then
-- If the second argument wasn't specified, the user wants exactly
-- the number of items selected.
if requested == nil then
requested = order.count;
end
-- If we overshot, figure out how much we can resell the excess for.
if order.count > requested then
order.resell = order.count - requested;
local price = SummaryDataByLink[DetailLink].price;
order.resellPrice = math.floor(order.resell * price);
order.netPrice = order.price - order.resellPrice;
end
-- Get a historical comparison.
local hist = self:GetHistoricalPrice(DetailLink);
if hist ~= nil then
order.histPrice = math.floor(requested * hist.price);
else
order.histPrice = 0;
end
-- Save the purchase order and start buying.
PurchaseOrder = order;
self:ContinuePurchase();
end
end
end
-- Place an order for the next item in line.
function AuctionLite:ContinuePurchase()
local order = PurchaseOrder;
if order ~= nil then
assert(order.current == nil);
local n = table.getn(order.list);
if n > 0 then
-- Find the first item to purchase in the current detail list.
-- TODO: Make this more efficient by just sorting order.list.
self:ApplyDetailSort();
local index = nil;
local i;
local displayListing;
for _, displayListing in ipairs(DetailData) do
local purchaseListing;
for i, purchaseListing in ipairs(order.list) do
if displayListing == purchaseListing then
index = i;
break;
end
end
if index ~= nil then
break;
end
end
-- The purchase items should be a subset of the main listing.
assert(index ~= nil);
order.current = table.remove(order.list, index);
local query = {
name = order.name,
wait = true,
exact = true,
listing = order.current,
isBuyout = order.isBuyout,
finish = function(cancelled)
AuctionLite:PurchaseComplete(cancelled);
end,
};
-- TODO: Make this work.
-- self:ScrollToListing(order.current);
-- TODO: What if this returns false?
self:StartQuery(query);
else
self:EndPurchase();
end
end
end
-- Finish a purchase; show the result and clean up.
function AuctionLite:EndPurchase(cancelled)
assert(PurchaseOrder ~= nil);
assert(PurchaseOrder.current == nil);
local order = PurchaseOrder;
-- Tally up remaining items.
local listing;
for _, listing in ipairs(order.list) do
self:UpdatePurchaseStats(listing);
end
-- Print a summary.
if not cancelled or order.listingsBought > 0 then
if order.isBuyout then
self:Print(L["Bought %dx %s (%d |4listing:listings; at %s)."]:
format(order.itemsBought, order.name, order.listingsBought,
self:PrintMoney(order.spent)));
else
self:Print(L["Bid on %dx %s (%d |4listing:listings; at %s)."]:
format(order.itemsBought, order.name, order.listingsBought,
self:PrintMoney(order.spent)));
end
if order.itemsNotFound > 0 then
self:Print(L["Note: %d |4listing:listings; of %d |4item was:items were; not purchased."]:
format(order.listingsNotFound, order.itemsNotFound));
end
end
PurchaseOrder = nil;
end
-- The query system needs us to approve purchases.
function AuctionLite:RequestApproval()
-- Mark ourselves as waiting for approval.
if PurchaseOrder ~= nil and PurchaseOrder.current ~= nil then
PurchaseOrder.needsApproval = true;
end
-- Update the display.
self:AuctionFrameBuy_Update();
end
-- The query system no longer needs approval.
function AuctionLite:CancelRequestApproval()
-- Mark ourselves as not waiting for approval.
if PurchaseOrder ~= nil then
PurchaseOrder.needsApproval = nil;
end
-- Update the display.
self:AuctionFrameBuy_Update();
end
-- Update purchase stats for an item.
function AuctionLite:UpdatePurchaseStats(listing)
local order = PurchaseOrder;
if order ~= nil then
if listing.purchased then
order.itemsBought = order.itemsBought + listing.count;
order.listingsBought = order.listingsBought + 1;
if order.isBuyout then
order.spent = order.spent + listing.buyout;
else
order.spent = order.spent + listing.bid;
end
else
order.itemsNotFound = order.itemsNotFound + listing.count;
order.listingsNotFound = order.listingsNotFound + 1;
end
end
end
-- Notification that a purchase has completed.
function AuctionLite:PurchaseComplete(cancelled)
local order = PurchaseOrder;
-- Update the purchase order.
if order ~= nil then
-- Update our current purchase.
if order.current ~= nil then
self:UpdatePurchaseStats(order.current);
order.current = nil;
end
-- Update our display according to the purchase.
-- TODO: Make this more efficient, now that we only buy one item at a time.
if DetailLink ~= nil then
local summary = SummaryDataByLink[DetailLink];
local i = table.getn(DetailData);
while i > 0 do
local listing = DetailData[i];
if listing.purchased then
if PurchaseOrder.isBuyout then
-- If we bought an item, remove it.
table.remove(DetailData, i);
summary.itemsAll = summary.itemsAll - listing.count;
summary.listingsAll = summary.listingsAll - 1;
SelectedItems[listing] = nil;
else
-- If we bid on an item, update the minimum bid.
local increment = math.floor(listing.bid / 100) * 5;
listing.purchased = false;
listing.bidder = 1;
listing.bid = listing.bid + increment;
if listing.bid > listing.buyout and listing.buyout > 0 then
listing.bid = listing.buyout;
end
end
end
i = i - 1;
end
if table.getn(DetailData) == 0 then
self:SetDetailLink(nil);
end
end
-- Continue with further purchases if relevant.
if not cancelled then
self:ContinuePurchase();
else
self:EndPurchase(true);
end
end
-- Update the display.
self:AuctionFrameBuy_Update();
end
-- Update search progress display.
function AuctionLite:UpdateProgressSearch(pct, getAll, scan)
-- If we got a progress update, record it.
if pct ~= nil then
Progress = pct;
end
-- Record a start time if we don't have one already.
if StartTime == nil then
StartTime = math.floor(time());
end
-- Figure out whether we're actually doing getAll and/or scanning.
if GetAll == nil and getAll ~= nil then
GetAll = getAll;
end
if Scanning == nil and scan ~= nil then
Scanning = scan;
end
-- Update the display every second.
local currentTime = math.floor(time());
if LastTime == nil or currentTime > LastTime then
LastTime = currentTime;
-- If we have some data, compute the time remaining.
local elapsed = currentTime - StartTime;
if elapsed > 0 and Progress > 0 then
-- In order to reduce jitter in the estimate, do the following:
-- 1. Update once every two seconds at first.
-- 2. Average each estimate with the previous one.
-- 3. Ignore estimates that exceed the previous by less than 10%.
if Progress > 15 or (elapsed % 2) == 0 then
local remaining = math.floor((100 * elapsed / Progress) - elapsed);
if LastRemaining ~= nil then
remaining = math.floor((remaining + LastRemaining) / 2);
if remaining > LastRemaining and remaining < LastRemaining * 1.1 then
remaining = LastRemaining;
end
end
LastRemaining = remaining;
BuyRemainingData:SetText(self:PrintTime(remaining));
end
else
BuyRemainingData:SetText("---");
end
-- Update the percentage.
if GetAll then
BuyStatusText:SetText(L["Scanning..."]);
BuyStatusData:SetText("");
else
if Scanning then
BuyStatusText:SetText(L["Scanning:"]);
else
BuyStatusText:SetText(L["Searching:"]);
end
BuyStatusData:SetText(tostring(Progress) .. "%");
end
-- Update the elapsed time and show the whole pane.
BuyElapsedData:SetText(self:PrintTime(elapsed));
BuyStatus:Show();
end
end
-- Show our progress for the favorites scan. We assume each scan takes
-- roughly the same amount of time, and then split each segment accordingly.
function AuctionLite:UpdateProgressMulti(pct)
local numDone = 0;
for _, _ in pairs(MultiScanData) do
numDone = numDone + 1;
end
local numItems = 0;
for _, _ in pairs(MultiScanItems) do
numItems = numItems + 1;
end
local overall = math.floor(((100 * numDone) + pct) / numItems);
self:UpdateProgressSearch(overall);
end
-- Take the next step in a multi-item scan. If we need to scan for another
-- item, do it; if there are no items left, display our results.
function AuctionLite:MultiScan(items)
-- Are we starting a new multi-item scan?
local first = false;
if items ~= nil then
MultiScanOriginal = items;
MultiScanItems = {};
MultiScanData = {};
for item, _ in pairs(items) do
MultiScanItems[item] = true;
end
first = true;
end
-- Find an unscanned favorite.
MultiScanCurrent = nil;
local item;
for item, _ in pairs(MultiScanItems) do
if MultiScanItems[item] then
MultiScanCurrent = item;
break;
end
end
if MultiScanCurrent ~= nil then
-- Start the scan.
local query = {
wait = true,
exact = true,
update = function(pct) AuctionLite:UpdateProgressMulti(pct) end,
finish = function(data) AuctionLite:SetMultiScanData(data) end,
};
if self:IsLink(MultiScanCurrent) then
query.link = MultiScanCurrent;
else
query.name = MultiScanCurrent;
end
if self:StartQuery(query) and first then
DetailLinkPrev = nil;
self:ClearBuyFrame(true);
end
else
-- Show our results.
self:SetBuyData(MultiScanData);
MultiScanData = {};
end
end
-- Get the results for a multi-item scan.
function AuctionLite:SetMultiScanData(data)
-- Gather all relevant results returned by the search.
local found = false;
for link, results in pairs(data) do
local item = self:IsFavorite(link, MultiScanItems);
if item then
-- We were looking for this one. Save the data and mark it as found.
MultiScanItems[link] = false;
MultiScanItems[item] = false;
MultiScanData[link] = results;
-- We now have a link to use, so swap it in if we just had a name before.
if MultiScanOriginal[item] then
MultiScanOriginal[item] = nil;
MultiScanOriginal[link] = true;
end
end
-- Did we find the actual search term?
if MultiScanCurrent == link or MultiScanCurrent == item then
found = true;
end
end
-- We didn't find the item we were looking for, so add a fake entry to
-- the list so that it shows up with zero results.
if not found then
local link;
if self:IsLink(MultiScanCurrent) then
link = MultiScanCurrent;
else
link = "|cffffffff|Hitem:0:0:0:0:0:0:0:0|h[" ..
MultiScanCurrent ..
"]|h|r";
end
MultiScanItems[MultiScanCurrent] = false;
MultiScanData[link] = {
link = link,
data = {},
price = 0,
itemsAll = 0,
itemsMine = 0,
listingsAll = 0,
listingsMine = 0,
};
end
-- Continue the scan.
self:MultiScan();
end
-- Sort the detail view.
function AuctionLite:ApplyDetailSort()
local info = DetailSort;
local data = DetailData;
local cmp;
if info.sort == "Item" then
cmp = function(a, b) return a.count < b.count end;
elseif info.sort == "BidEach" then
cmp = function(a, b) return a.bid / a.count < b.bid / b.count end;
elseif info.sort == "BidAll" then
cmp = function(a, b) return a.bid < b.bid end;
elseif info.sort == "BuyoutEach" then
cmp = function(a, b) return a.buyout / a.count < b.buyout / b.count end;
elseif info.sort == "BuyoutAll" then
cmp = function(a, b) return a.buyout < b.buyout end;
else
assert(false);
end
self:ApplySort(info, data, cmp);
end
-- Sort the summary view.
function AuctionLite:ApplySummarySort()
local info = SummarySort;
local data = SummaryData;
local cmp;
if info.sort == "ItemSummary" then
cmp = function(a, b)
local aFav = (self:IsFavorite(a.link) ~= nil);
local bFav = (self:IsFavorite(b.link) ~= nil);
if aFav == bFav then
return a.name < b.name;
else
return aFav;
end
end
elseif info.sort == "Listings" then
cmp = function(a, b) return a.listingsAll < b.listingsAll end;
elseif info.sort == "Items" then
cmp = function(a, b) return a.itemsAll < b.itemsAll end;
elseif info.sort == "Market" then
cmp = function(a, b) return a.price < b.price end;
elseif info.sort == "Historical" then
if BuyMode == BUY_MODE_DEALS or
BuyMode == BUY_MODE_SCAN then
cmp = function(a, b) return a.profit < b.profit end;
else
cmp = function(a, b) return a.histPrice < b.histPrice end;
end
else
assert(false);
end
self:ApplySort(info, data, cmp);
end
-- Set a new sort type for the detail view.
function AuctionLite:DetailSortButton_OnClick(sort)
assert(sort == "Item" or sort == "BidEach" or sort == "BidAll" or
sort == "BuyoutEach" or sort == "BuyoutAll");
self:SortButton_OnClick(DetailSort, sort);
self:AuctionFrameBuy_Update();
end
-- Set a new sort type for the summary view.
function AuctionLite:SummarySortButton_OnClick(sort)
assert(sort == "ItemSummary" or sort == "Listings" or sort == "Items" or
sort == "Market" or sort == "Historical");
self:SortButton_OnClick(SummarySort, sort);
self:AuctionFrameBuy_Update();
end
-- Return a sorted list of favorites list names.
function AuctionLite:GetFavoritesLists()
local names = {};
for name, list in pairs(self.db.profile.favorites) do
table.insert(names, name);
end
table.sort(names);
return names;
end
-- Toggle the favorites flag for this item.
function AuctionLite:FavoritesButton_OnClick(id)
local offset = FauxScrollFrame_GetOffset(BuyScrollFrame);
local link = SummaryData[offset + id].link;
local toggle = function(list)
-- Pop open any fake links.
local item;
local name, _, id = self:SplitLink(link);
if id == 0 then
item = name;
else
item = link;
end
-- Toggle the favorite.
if list[item] == nil then
list[item] = true;
else
list[item] = nil;
end
self:AuctionFrameBuy_Update();
end
local single = self:GetSingleFavoritesList();
if single ~= nil then
-- Just toggle the list.
toggle(single);
else
-- Make a more detailed menu showing the available lists.
if BuyFavoritesDropDown == nil then
CreateFrame("Frame", "BuyFavoritesDropDown", UIParent,
"UIDropDownMenuTemplate");
end
BuyFavoritesDropDown.displayMode = "MENU";
BuyFavoritesDropDown.initialize = function(menu)
local info = UIDropDownMenu_CreateInfo();
info.text = L["Member Of"];
info.isTitle = true;
UIDropDownMenu_AddButton(info);
for _, name in ipairs(self:GetFavoritesLists()) do
local list = self.db.profile.favorites[name];
local info = UIDropDownMenu_CreateInfo();
info.text = name;
info.func = function() toggle(list) end;
info.checked = (self:IsFavorite(link, list) ~= nil);
UIDropDownMenu_AddButton(info);
end
end
ToggleDropDownMenu(1, nil, BuyFavoritesDropDown, "cursor");
end
end
-- Handles clicks on the buttons in the "Buy" scroll frame.
function AuctionLite:BuyButton_OnClick(id, button)
if button == "LeftButton" then
local offset = FauxScrollFrame_GetOffset(BuyScrollFrame);
if DetailLink ~= nil then
-- We're in detail view, so select the item.
-- Unless we're holding control, this is a new selection.
if not IsControlKeyDown() then
SelectedItems = {};
end
if LastClick ~= nil and IsShiftKeyDown() then
-- Shift is down and we have a previous click.
-- Add all items in this range to the selection.
local lower = offset + id;
local upper = offset + id;
if LastClick < offset + id then
lower = LastClick;
else
upper = LastClick;
end
local i;
for i = lower, upper do
SelectedItems[DetailData[i]] = true;
end
else
-- No shift, or first click; add only the current item to the selection.
-- If control is down, toggle the item.
LastClick = offset + id;
local listing = DetailData[offset + id];
if IsControlKeyDown() and SelectedItems[listing] then
SelectedItems[listing] = nil;
else
SelectedItems[listing] = true;
end
end
else
-- We're in summary view, so switch to detail view.
self:SetDetailLink(SummaryData[offset + id].link);
self:StartMassBuyout();
end
self:AuctionFrameBuy_Update();
elseif button == "RightButton" then
local offset = FauxScrollFrame_GetOffset(BuyScrollFrame);
if DetailLink == nil then
local link = SummaryData[offset + id].link;
local menuList = {
{
text = L["Cancel Undercut Auctions"],
func = function() AuctionLite:CancelItems(true, link) end,
},
{
text = L["Cancel All Auctions"],
func = function() AuctionLite:CancelItems(false, link) end,
},
};
if BuyContextDropDown == nil then
CreateFrame("Frame", "BuyContextDropDown", UIParent,
"UIDropDownMenuTemplate");
end
EasyMenu(menuList, BuyContextDropDown, "cursor", 0, 0, "MENU");
end
end
end
-- Mouse has entered a row in the scrolling frame.
function AuctionLite:BuyButton_OnEnter(widget)
-- Get our index into the current display data.
local offset = FauxScrollFrame_GetOffset(BuyScrollFrame);
local id = widget:GetID();
-- Get a link and count for the item we're currently hovering over.
-- The "shift" is used to move the tooltip to the right in detail view
-- so that it doesn't obscure item quantities.
local link = nil;
local count = 1;
local shift = 0;
if DetailLink ~= nil then
local item = DetailData[offset + id];
if item ~= nil then
link = DetailLink;
count = item.count;
shift = BuyButton1DetailName:GetLeft() - BuyButton1DetailCount:GetLeft();
end
shift = shift + 200;
else
link = SummaryData[offset + id].link;
shift = shift + 250;
end
-- If we have an item, show the tooltip.
self:SetAuctionLiteTooltip(widget, shift, link, count);
end
-- Mouse has left a row in the scrolling frame.
function AuctionLite:BuyButton_OnLeave(widget)
GameTooltip:Hide();
end
-- Click the advanced menu button.
function AuctionLite:BuyAdvancedButton_OnClick()
BuyAdvancedDropDown.displayMode = "MENU";
BuyAdvancedDropDown.initialize = function(menu, level)
UIDropDownMenu_SetWidth(BuyAdvancedDropDown, 140);
if level == 1 then
local info = UIDropDownMenu_CreateInfo();
info.text = L["Show Deals"];
info.func = function() AuctionLite:AuctionFrameBuy_Deals() end;
UIDropDownMenu_AddButton(info, level);
local info = UIDropDownMenu_CreateInfo();
info.text = L["Show Favorites"];
local single = self:GetSingleFavoritesList();
if single == nil then
info.hasArrow = true;
else
info.func = function()
BuyMode = BUY_MODE_FAVORITES;
self:MultiScan(single);
end
end
UIDropDownMenu_AddButton(info, level);
local info = UIDropDownMenu_CreateInfo();
info.text = L["Show My Auctions"];
info.func = function()
BuyMode = BUY_MODE_MY_AUCTIONS;
self:MultiScan(self:GetMyAuctionLinks());
end
UIDropDownMenu_AddButton(info, level);
local info = UIDropDownMenu_CreateInfo();
info.text = L["Configure AuctionLite"];
info.func = function()
InterfaceOptionsFrame_OpenToCategory(AuctionLite.optionFrames.tooltips);
InterfaceOptionsFrame_OpenToCategory(self.optionFrames.main);
end
UIDropDownMenu_AddButton(info, level);
elseif level == 2 then
for _, name in ipairs(self:GetFavoritesLists()) do
local favs = self.db.profile.favorites[name];
local info = UIDropDownMenu_CreateInfo();
info.text = name;
info.func = function()
BuyMode = BUY_MODE_FAVORITES;
self:MultiScan(favs);
CloseDropDownMenus();
end
UIDropDownMenu_AddButton(info, level);
end
end
end
ToggleDropDownMenu(1, nil, BuyAdvancedDropDown);
end
-- Returns to the summary page.
function AuctionLite:BuySummaryButton_OnClick()
if PurchaseOrder ~= nil then
self:CancelQuery();
self:ResetSearch();
end
self:SetDetailLink(nil);
self:AuctionFrameBuy_Update();
end
-- Approve a pending purchase.
function AuctionLite:BuyApproveButton_OnClick()
PurchaseOrder.needsApproval = nil;
self:QueryApprove();
self:AuctionFrameBuy_Update();
end
-- Cancel a pending purchase.
function AuctionLite:BuyCancelPurchaseButton_OnClick()
PurchaseOrder.needsApproval = nil;
self:CancelQuery();
self:ResetSearch();
self:AuctionFrameBuy_Update();
end
-- Cancel an in-progress search.
function AuctionLite:BuyCancelSearchButton_OnClick()
self:CancelQuery();
self:ResetSearch();
self:AuctionFrameBuy_Update();
end
-- Bid on the currently-selected item.
function AuctionLite:BuyBidButton_OnClick()
if PurchaseOrder ~= nil then
self:CancelQuery();
end
self:CreateOrder(false);
self:AuctionFrameBuy_Update();
end
-- Buy out the currently-selected item.
function AuctionLite:BuyBuyoutButton_OnClick()
if PurchaseOrder ~= nil then
self:CancelQuery();
end
self:CreateOrder(true);
self:AuctionFrameBuy_Update();
end
-- Cancel the currently-selected item.
function AuctionLite:BuyCancelAuctionButton_OnClick()
self:CancelItems(IsControlKeyDown());
self:AuctionFrameBuy_Update();
end
-- Show the cancel tooltip.
function AuctionLite:BuyCancelAuctionButton_OnEnter(widget)
GameTooltip:SetOwner(widget, "ANCHOR_TOPLEFT");
GameTooltip:SetText(L["CANCEL_TOOLTIP"]);
end
-- Hide the cancel tooltip.
function AuctionLite:BuyCancelAuctionButton_OnLeave(widget)
GameTooltip:Hide();
end
-- Starts a full scan of the auction house.
function AuctionLite:StartFullScan()
BuyMode = BUY_MODE_SCAN;
if not self.db.profile.fastScanAd2 then
StaticPopup_Show("AL_FAST_SCAN");
else
local query = {
name = "",
getAll = self.db.profile.getAll,
update = function(pct, all)
AuctionLite:UpdateProgressSearch(pct, all, true);
end,
finish = function(data, link)
AuctionLite:SetScanData(data);
end,
};
if self:StartQuery(query) then
DetailLinkPrev = nil;
self:ClearBuyFrame(true);
end
end
end
-- List current deals. If we haven't done a full scan, do it now.
function AuctionLite:AuctionFrameBuy_Deals()
BuyMode = BUY_MODE_DEALS;
if ScanData ~= nil then
DetailLinkPrev = nil;
self:SetScanData(ScanData);
else
self:StartFullScan();
end
end
-- Submit a search query.
function AuctionLite:AuctionFrameBuy_Search()
BuyMode = BUY_MODE_SEARCH;
local query = {
name = BuyName:GetText(),
wait = true,
exact = IsShiftKeyDown(),
update = function(pct) AuctionLite:UpdateProgressSearch(pct) end,
finish = function(data) AuctionLite:SetBuyData(data) end,
};
if self:StartQuery(query) then
DetailLinkPrev = DetailLink;
self:ClearBuyFrame(true);
end
end
-- Adjust frame buttons for repaint.
function AuctionLite:AuctionFrameBuy_OnUpdate()
local canSend = CanSendAuctionQuery("list") and not self:QueryInProgress();
local biddable, buyable, cancellable = self:GetSelectionStatus();
if canSend and BuyName:GetText() ~= "" then
BuySearchButton:Enable();
else
BuySearchButton:Disable();
end
if canSend then
BuyScanButton:Enable();
else
BuyScanButton:Disable();
end
if canSend and biddable then
BuyBidButton:Enable();
else
BuyBidButton:Disable();
end
if canSend and buyable then
BuyBuyoutButton:Enable();
else
BuyBuyoutButton:Disable();
end
if cancellable then
BuyCancelAuctionButton:Enable();
else
BuyCancelAuctionButton:Disable();
end
if StartTime ~= nil then
self:UpdateProgressSearch();
end
end
-- Adjust the scroll bar to a specified offset.
function AuctionLite:SetScrollFrameOffset(offset)
-- We have to set the inner scroll bar manually, because otherwise the
-- two values will become inconsistent. We also set the max values to
-- make sure that the value we're setting is not ignored.
FauxScrollFrame_SetOffset(BuyScrollFrame, offset);
BuyScrollFrameScrollBar:SetMinMaxValues(0, offset * BuyButton1:GetHeight());
BuyScrollFrameScrollBar:SetValue(offset * BuyButton1:GetHeight());
end
-- Scroll the frame to a particular listing.
function AuctionLite:ScrollToListing(listing)
if DetailLink ~= nil then
-- Get the index of the item we're looking for.
local i;
local current;
local found = nil;
for i, current in ipairs(DetailData) do
if current == listing then
found = i;
break;
end
end
if found ~= nil then
local offset = FauxScrollFrame_GetOffset(BuyScrollFrame);
local displaySize = BUY_DISPLAY_SIZE - 2;
if found < offset or found >= displaySize then
local newOffset = math.min(0, found - (displaySize / 2));
self:SetScrollFrameOffset(newOffset);
end
end
end
end
-- Update the scroll frame with either the detail view or summary view.
function AuctionLite:AuctionFrameBuy_Update()
-- First clear everything.
local i;
for i = 1, BUY_DISPLAY_SIZE do
local buttonName = "BuyButton" .. i;
local button = _G[buttonName];
local buttonDetail = _G[buttonName .. "Detail"];
local buttonSummary = _G[buttonName .. "Summary"];
button:Hide();
buttonDetail:Hide();
buttonSummary:Hide();
end
BuyHeader:Hide();
BuySummaryHeader:Hide();
BuyStatus:Hide();
-- If we have no items, say so.
BuyMessageText:Show();
if BuyMode == BUY_MODE_INTRO then
BuyMessageText:SetText(L["Enter item name and click \"Search\""]);
elseif BuyMode == BUY_MODE_SEARCH and NoResults then
BuyMessageText:SetText(L["No items found"]);
elseif BuyMode == BUY_MODE_SCAN and NoResults then
BuyMessageText:SetText(L["Scan complete. Try again later to find deals!"]);
elseif BuyMode == BUY_MODE_DEALS and NoResults then
BuyMessageText:SetText(L["No deals found"]);
elseif BuyMode == BUY_MODE_FAVORITES and NoResults then
BuyMessageText:SetText(L["No items found"]);
elseif BuyMode == BUY_MODE_MY_AUCTIONS and NoResults then
BuyMessageText:SetText(L["No current auctions"]);
else
BuyMessageText:Hide();
end
-- Update the expandable header.
self:AuctionFrameBuy_UpdateExpand();
-- Use detail view if we've chosen an item, or summary view otherwise.
if DetailLink ~= nil then
self:AuctionFrameBuy_UpdateDetail();
else
self:AuctionFrameBuy_UpdateSummary();
end
end
-- Update the expandable frame at the top of the scroll frame.
function AuctionLite:AuctionFrameBuy_UpdateExpand()
-- Figure out how big a window to make.
local order = PurchaseOrder;
if order == nil then
ExpandHeight = 0;
elseif order.resell == nil then
ExpandHeight = 2;
else
ExpandHeight = 4;
end
-- Set the height of the expandable window. It's always one higher than
-- the number of rows because WoW gets confused if it goes to zero;
-- other frame offsets are computed appropriately.
BuyExpand:SetHeight((ExpandHeight + 1) * ROW_HEIGHT);
-- Show rows as appropriate.
local i;
for i = 1, EXPAND_ROWS do
local prefix = "BuyExpand" .. i;
local text = _G[prefix .. "Text"];
local money = _G[prefix .. "MoneyFrame"];
if i <= ExpandHeight then
text:Show();
money:Show();
else
text:Hide();
money:Hide();
end
end
-- Populate the expandable frame with appropriate data from the order.
local order = PurchaseOrder;
if order ~= nil then
if order.resell == nil then
if order.isBuyout then
BuyExpand1Text:SetText(L["Buyout cost for %d:"]:format(order.count));
else
BuyExpand1Text:SetText(L["Bid cost for %d:"]:format(order.count));
end
MoneyFrame_Update(BuyExpand1MoneyFrame, order.price);
BuyExpand2Text:SetText(L["Historical price for %d:"]:format(order.count));
MoneyFrame_Update(BuyExpand2MoneyFrame, order.histPrice);
else
if order.isBuyout then
BuyExpand1Text:SetText(L["Buyout cost for %d:"]:format(order.count));
else
BuyExpand1Text:SetText(L["Bid cost for %d:"]:format(order.count));
end
MoneyFrame_Update(BuyExpand1MoneyFrame, order.price);
BuyExpand2Text:SetText(L["Resell %d:"]:format(order.resell));
MoneyFrame_Update(BuyExpand2MoneyFrame, order.resellPrice);
self:MakeNegative("BuyExpand2MoneyFrame");
BuyExpand3Text:SetText(
L["Net cost for %d:"]:format(order.count - order.resell));
if order.netPrice < 0 then
MoneyFrame_Update(BuyExpand3MoneyFrame, - order.netPrice);
self:MakeNegative("BuyExpand3MoneyFrame");
else
MoneyFrame_Update(BuyExpand3MoneyFrame, order.netPrice);
end
BuyExpand4Text:SetText(
L["Historical price for %d:"]:format(order.count - order.resell));
MoneyFrame_Update(BuyExpand4MoneyFrame, order.histPrice);
end
local current = order.listingsBought + order.listingsNotFound + 1;
local total = current + table.getn(order.list);
BuyBatchText:SetText(L["Listing %d of %d"]:format(current, total));
else
BuyBatchText:SetText("");
end
-- Show/hide and enable/disable approval buttons.
if ExpandHeight > 0 then
BuyApproveButton:Show();
BuyCancelPurchaseButton:Show();
if PurchaseOrder ~= nil and PurchaseOrder.needsApproval then
BuyApproveButton:Enable();
BuyCancelPurchaseButton:Enable();
else
BuyApproveButton:Disable();
BuyCancelPurchaseButton:Disable();
end
else
BuyApproveButton:Hide();
BuyCancelPurchaseButton:Hide();
end
end
-- Update the scroll frame with the detail view.
function AuctionLite:AuctionFrameBuy_UpdateDetail()
if not DetailSort.sorted then
self:ApplyDetailSort();
end
local sort;
for _, sort in ipairs({ "Item", "BidEach", "BidAll",
"BuyoutEach", "BuyoutAll" }) do
self:UpdateSortArrow("Buy", sort, DetailSort.sort, DetailSort.flipped);
end
local offset = FauxScrollFrame_GetOffset(BuyScrollFrame);
local displaySize = BUY_DISPLAY_SIZE - ExpandHeight;
local _, _, _, _, enchant, jewel1, jewel2, jewel3, jewel4 =
self:SplitLink(DetailLink);
local showPlus = enchant ~= 0 or
jewel1 ~= 0 or jewel2 ~= 0 or
jewel3 ~= 0 or jewel4 ~= 0;
local i;
for i = 1, displaySize do
local item = DetailData[offset + i];
if item ~= nil then
local buttonName = "BuyButton" .. i;
local button = _G[buttonName];
local warning = _G[buttonName .. "Warning"];
local buttonDetailName = buttonName .. "Detail";
local buttonDetail = _G[buttonDetailName];
local countText = _G[buttonDetailName .. "Count"];
local nameText = _G[buttonDetailName .. "Name"];
local plusText = _G[buttonDetailName .. "Plus"];
local bidEachFrame = _G[buttonDetailName .. "BidEachFrame"];
local bidFrame = _G[buttonDetailName .. "BidFrame"];
local buyoutEachFrame = _G[buttonDetailName .. "BuyoutEachFrame"];
local buyoutFrame = _G[buttonDetailName .. "BuyoutFrame"];
local name, color = self:SplitLink(DetailLink);
local countColor;
local nameColor;
if item.owner == UnitName("player") then
countColor = "ffffff00";
nameColor = "ffffff00";
else
countColor = "ffffffff";
nameColor = color;
end
countText:SetText("|c" .. countColor .. item.count .. "x|r");
nameText:SetText("|c" .. nameColor .. name .. "|r");
if showPlus then
plusText:SetPoint("LEFT", nameText, "LEFT",
nameText:GetStringWidth(), 0);
plusText:Show();
else
plusText:Hide();
end
MoneyFrame_Update(bidEachFrame, math.floor(item.bid / item.count));
bidEachFrame:SetAlpha(0.5);
if item.bidder then
SetMoneyFrameColor(buttonDetailName .. "BidEachFrame", "yellow");
else
SetMoneyFrameColor(buttonDetailName .. "BidEachFrame", "white");
end
MoneyFrame_Update(bidFrame, math.floor(item.bid));
bidFrame:SetAlpha(0.5);
if item.bidder then
SetMoneyFrameColor(buttonDetailName .. "BidFrame", "yellow");
else
SetMoneyFrameColor(buttonDetailName .. "BidFrame", "white");
end
if item.buyout > 0 then
MoneyFrame_Update(buyoutEachFrame, math.floor(item.buyout / item.count));
buyoutEachFrame:Show();
MoneyFrame_Update(buyoutFrame, math.floor(item.buyout));
buyoutFrame:Show();
else
buyoutEachFrame:Hide();
buyoutFrame:Hide();
end
if SelectedItems[item] then
button:LockHighlight();
else
button:UnlockHighlight();
end
if PurchaseOrder ~= nil and PurchaseOrder.current == item then
warning:SetAlpha(0.4);
else
warning:SetAlpha(0);
end
buttonDetail:Show();
button:Show();
end
end
FauxScrollFrame_Update(BuyScrollFrame, table.getn(DetailData),
displaySize, ROW_HEIGHT);
if table.getn(DetailData) > 0 then
BuyHeader:Show();
end
end
-- Update the scroll frame with the summary view.
function AuctionLite:AuctionFrameBuy_UpdateSummary()
if not SummarySort.sorted then
self:ApplySummarySort();
end
local sort;
for _, sort in ipairs({ "ItemSummary", "Listings", "Items",
"Market", "Historical" }) do
self:UpdateSortArrow("Buy", sort, SummarySort.sort, SummarySort.flipped);
end
local offset = FauxScrollFrame_GetOffset(BuyScrollFrame);
local displaySize = BUY_DISPLAY_SIZE - ExpandHeight;
local i;
for i = 1, displaySize do
local item = SummaryData[offset + i];
if item ~= nil then
local buttonName = "BuyButton" .. i;
local button = _G[buttonName];
local warning = _G[buttonName .. "Warning"];
local buttonSummaryName = buttonName .. "Summary";
local buttonSummary = _G[buttonSummaryName];
local starButton = _G[buttonSummaryName .. "StarButton"];
local nameText = _G[buttonSummaryName .. "Name"];
local plusText = _G[buttonSummaryName .. "Plus"];
local listingsText = _G[buttonSummaryName .. "Listings"];
local itemsText = _G[buttonSummaryName .. "Items"];
local marketFrame = _G[buttonSummaryName .. "MarketPriceFrame"];
local histFrame = _G[buttonSummaryName .. "HistPriceFrame"];
if self:IsFavorite(item.link) then
starButton:GetNormalTexture():SetAlpha(1.0);
else
starButton:GetNormalTexture():SetAlpha(0.1);
end
local name, color, _, _, enchant, jewel1, jewel2, jewel3, jewel4 =
self:SplitLink(item.link);
local countStr = function(mine, all)
local prefix;
if mine > 0 and self.db.profile.countMyListings then
prefix = "(" .. mine .. ") ";
else
prefix = "";
end
return prefix .. "|cffffffff" .. all .. "|r";
end
nameText:SetText("|c" .. color .. name .. "|r");
listingsText:SetText(countStr(item.listingsMine, item.listingsAll));
itemsText:SetText(countStr(item.itemsMine, item.itemsAll));
if item.price ~= nil and item.price > 0 then
MoneyFrame_Update(marketFrame, math.floor(item.price));
marketFrame:Show();
else
marketFrame:Hide();
end
local moreData;
if BuyMode == BUY_MODE_DEALS or
BuyMode == BUY_MODE_SCAN then
moreData = item.profit;
else
moreData = item.histPrice;
end
if moreData ~= nil and moreData > 0 then
MoneyFrame_Update(histFrame, math.floor(moreData));
histFrame:Show();
else
histFrame:Hide();
end
if enchant ~= 0 or
jewel1 ~= 0 or jewel2 ~= 0 or
jewel3 ~= 0 or jewel4 ~= 0 then
plusText:SetPoint("LEFT", nameText, "LEFT",
nameText:GetStringWidth(), 0);
plusText:Show();
else
plusText:Hide();
end
button:UnlockHighlight();
if item.warning then
warning:SetAlpha(0.4);
else
warning:SetAlpha(0);
end
buttonSummary:Show();
button:Show();
end
end
FauxScrollFrame_Update(BuyScrollFrame, table.getn(SummaryData),
displaySize, ROW_HEIGHT);
if table.getn(SummaryData) > 0 then
BuySummaryHeader:Show();
if BuyMode == BUY_MODE_DEALS or
BuyMode == BUY_MODE_SCAN then
self:UpdateSortButton("Buy", "Historical", L["Potential Profit"]);
else
self:UpdateSortButton("Buy", "Historical", L["Historical Price"]);
end
end
end
-- Loads a name into the input field and searches.
function AuctionLite:NameClickBuy(name)
BuyName:SetText(name);
BuyQuantity:SetFocus();
AuctionLite:AuctionFrameBuy_Search();
end
-- Handle bag item clicks by searching for the item.
function AuctionLite:BagClickBuy(container, slot)
local location = ItemLocation:CreateFromBagAndSlot(container, slot)
local link = C_Item.GetItemLink(location);
if link ~= nil then
local name = self:SplitLink(link);
self:NameClickBuy(name);
end
end
-- Resets all info about an in-progress search.
function AuctionLite:ResetSearch()
StartTime = nil;
LastTime = nil;
LastRemaining = nil;
Progress = nil;
GetAll = nil;
Scanning = nil;
end
-- Clean up the "Buy" tab.
function AuctionLite:ClearBuyFrame(partial)
ExpandHeight = 0;
if not partial then
BuyMode = BUY_MODE_INTRO;
SummarySort = {
sort = "ItemSummary",
flipped = false,
justFlipped = false,
sorted = false,
};
DetailSort = {
sort = "BuyoutEach",
flipped = false,
justFlipped = false,
sorted = false,
};
end
DetailLink = nil;
DetailData = {};
if not partial then
DetailLinkPrev = nil;
end
SelectedItems = {};
LastClick = nil;
SummaryData = {};
SummaryDataByLink = {};
NoResults = false;
PurchaseOrder = nil;
self:ResetSearch();
if not partial then
MultiScanCurrent = nil;
MultiScanOriginal = {};
MultiScanItems = {};
end
MultiScanData = {};
if not partial then
BuyName:SetText("");
BuyQuantity:SetText("");
BuyName:SetFocus();
end
BuyMessageText:Hide();
BuyStatus:Hide();
FauxScrollFrame_SetOffset(BuyScrollFrame, 0);
BuyScrollFrameScrollBar:SetValue(0);
self:AuctionFrameBuy_Update();
end
-- Create the "Buy" tab.
function AuctionLite:CreateBuyFrame()
-- Create our tab.
local index = self:CreateTab(L["AuctionLite - Buy"], AuctionFrameBuy);
-- Set all localizable strings in the UI.
BuyTitle:SetText(L["AuctionLite - Buy"]);
BuyNameText:SetText(L["Name"]);
BuyQuantityText:SetText(L["Qty"]);
BuyStatusText:SetText(L["Searching:"]);
BuyElapsedText:SetText(L["Time Elapsed:"]);
BuyRemainingText:SetText(L["Time Remaining:"]);
BuyCancelSearchButton:SetText(L["Cancel"]);
BuyApproveButton:SetText(L["Approve"]);
BuyCancelPurchaseButton:SetText(L["Cancel"]);
BuyAdvancedText:SetText(L["Advanced"]);
BuyScanButton:SetText(L["Full Scan"]);
BuySearchButton:SetText(L["Search"]);
BuyCancelAuctionButton:SetText(L["Cancel"]);
-- Set button text and adjust arrow icons accordingly.
BuyItemButton:SetText(L["Item"]);
BuyItemSummaryButton:SetText(L["Item Summary"]);
self:UpdateSortButton("Buy", "BidEach", L["Bid Per Item"]);
self:UpdateSortButton("Buy", "BidAll", L["Bid Total"]);
self:UpdateSortButton("Buy", "BuyoutEach", L["Buyout Per Item"]);
self:UpdateSortButton("Buy", "BuyoutAll", L["Buyout Total"]);
self:UpdateSortButton("Buy", "Historical", L["Historical Price"]);
self:UpdateSortButton("Buy", "Market", L["Market Price"]);
self:UpdateSortButton("Buy", "Items", L["Items"]);
self:UpdateSortButton("Buy", "Listings", L["Listings"]);
-- Make sure it's pristine.
self:ClearBuyFrame();
return index;
end