-- ------------------------------------------------------------------------------ -- -- TradeSkillMaster -- -- https://tradeskillmaster.com -- -- All Rights Reserved - Detailed license information included with addon. -- -- ------------------------------------------------------------------------------ -- local _, TSM = ... local Shopping = TSM.UI.AuctionUI:NewPackage("Shopping") local ItemClass = TSM.Include("Data.ItemClass") local L = TSM.Include("Locale").GetTable() local FSM = TSM.Include("Util.FSM") local Event = TSM.Include("Util.Event") local TempTable = TSM.Include("Util.TempTable") local Table = TSM.Include("Util.Table") local Money = TSM.Include("Util.Money") local Log = TSM.Include("Util.Log") local Math = TSM.Include("Util.Math") local ItemString = TSM.Include("Util.ItemString") local ItemInfo = TSM.Include("Service.ItemInfo") local CustomPrice = TSM.Include("Service.CustomPrice") local AuctionTracking = TSM.Include("Service.AuctionTracking") local BagTracking = TSM.Include("Service.BagTracking") local AuctionHouseWrapper = TSM.Include("Service.AuctionHouseWrapper") local AuctionScan = TSM.Include("Service.AuctionScan") local MailTracking = TSM.Include("Service.MailTracking") local Settings = TSM.Include("Service.Settings") local PlayerInfo = TSM.Include("Service.PlayerInfo") local UIElements = TSM.Include("UI.UIElements") local private = { settings = nil, fsm = nil, rarityList = nil, frame = nil, hasLastScan = false, contentPath = "selection", selectedGroups = {}, groupSearch = "", filterText = "", searchName = "", postContext = { itemString = nil, seller = nil, stackSize = nil, displayedBid = nil, itemDisplayedBid = nil, buyout = nil, }, itemString = nil, postStack = nil, postQuantity = nil, postTimeStr = nil, perItem = true, updateCallbacks = {}, itemLocation = ItemLocation:CreateEmpty(), } local MAX_ITEM_LEVEL = 500 local PLAYER_NAME = UnitName("player") local ARMOR_TYPES = { [GetItemSubClassInfo(LE_ITEM_CLASS_ARMOR, LE_ITEM_ARMOR_PLATE)] = true, [GetItemSubClassInfo(LE_ITEM_CLASS_ARMOR, LE_ITEM_ARMOR_MAIL)] = true, [GetItemSubClassInfo(LE_ITEM_CLASS_ARMOR, LE_ITEM_ARMOR_LEATHER)] = true, [GetItemSubClassInfo(LE_ITEM_CLASS_ARMOR, LE_ITEM_ARMOR_CLOTH)] = true, } local INVENTORY_TYPES = { GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexHeadType or LE_INVENTORY_TYPE_HEAD_TYPE), GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexShoulderType or LE_INVENTORY_TYPE_SHOULDER_TYPE), GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexChestType or LE_INVENTORY_TYPE_CHEST_TYPE), GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexWaistType or LE_INVENTORY_TYPE_WAIST_TYPE), GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexLegsType or LE_INVENTORY_TYPE_LEGS_TYPE), GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexFeetType or LE_INVENTORY_TYPE_FEET_TYPE), GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexWristType or LE_INVENTORY_TYPE_WRIST_TYPE), GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexHandType or LE_INVENTORY_TYPE_HAND_TYPE), } local GENERIC_TYPES = { GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexNeckType or LE_INVENTORY_TYPE_NECK_TYPE), GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexCloakType or LE_INVENTORY_TYPE_CLOAK_TYPE), GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexFingerType or LE_INVENTORY_TYPE_FINGER_TYPE), GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexTrinketType or LE_INVENTORY_TYPE_TRINKET_TYPE), GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexHoldableType or LE_INVENTORY_TYPE_HOLDABLE_TYPE), GetItemInventorySlotInfo(TSM.IsShadowlands() and Enum.InventoryType.IndexBodyType or LE_INVENTORY_TYPE_BODY_TYPE), } local MAX_LEVEL = nil do if TSM.IsShadowlands() then MAX_LEVEL = 60 else for _, v in pairs(MAX_PLAYER_LEVEL_TABLE) do MAX_LEVEL = max(MAX_LEVEL or 0, v) end end end -- ============================================================================ -- Module Functions -- ============================================================================ function Shopping.OnInitialize() private.settings = Settings.NewView() :AddKey("global", "auctionUIContext", "shoppingSelectionDividedContainer") :AddKey("global", "auctionUIContext", "shoppingAuctionScrollingTable") :AddKey("global", "auctionUIContext", "shoppingSearchesTabGroup") :AddKey("global", "shoppingOptions", "searchAutoFocus") :AddKey("char", "auctionUIContext", "shoppingGroupTree") private.postTimeStr = TSM.CONST.AUCTION_DURATIONS[2] TSM.UI.AuctionUI.RegisterTopLevelPage(L["Browse"], private.GetShoppingFrame, private.OnItemLinked) private.FSMCreate() end function Shopping.StartGatheringSearch(items, stateCallback, buyCallback, mode) assert(Shopping.IsVisible()) private.frame:SetPath("selection") private.StartGatheringSearchHelper(private.frame, items, stateCallback, buyCallback, mode) end function Shopping.StartItemSearch(item) private.OnItemLinked(ItemInfo.GetName(item), item, true) end function Shopping.IsVisible() return TSM.UI.AuctionUI.IsPageOpen(L["Browse"]) end function Shopping.RegisterUpdateCallback(callback) tinsert(private.updateCallbacks, callback) end -- ============================================================================ -- Shopping UI -- ============================================================================ function private.GetShoppingFrame() TSM.UI.AnalyticsRecordPathChange("auction", "shopping") if not private.hasLastScan then private.contentPath = "selection" end local frame = UIElements.New("ViewContainer", "shopping") :SetNavCallback(private.GetShoppingContentFrame) :AddPath("selection") :AddPath("scan") :SetPath(private.contentPath) :SetScript("OnHide", private.FrameOnHide) private.frame = frame for _, callback in ipairs(private.updateCallbacks) do callback() end return frame end function private.GetShoppingContentFrame(viewContainer, path) private.contentPath = path if path == "selection" then return private.GetSelectionFrame() elseif path == "scan" then return private.GetScanFrame() else error("Unexpected path: "..tostring(path)) end end function private.GetSelectionFrame() TSM.UI.AnalyticsRecordPathChange("auction", "shopping", "selection") local frame = UIElements.New("DividedContainer", "selection") :SetSettingsContext(private.settings, "shoppingSelectionDividedContainer") :SetMinWidth(220, 350) :SetBackgroundColor("PRIMARY_BG") :SetLeftChild(UIElements.New("Frame", "groupSelection") :SetLayout("VERTICAL") :SetPadding(0, 0, 8, 0) :AddChild(UIElements.New("Frame", "title") :SetLayout("HORIZONTAL") :SetMargin(8, 8, 0, 8) :SetHeight(24) :AddChild(UIElements.New("Input", "search") :SetIconTexture("iconPack.18x18/Search") :AllowItemInsert(true) :SetClearButtonEnabled(true) :SetValue(private.groupSearch) :SetHintText(L["Search Groups"]) :SetScript("OnValueChanged", private.GroupSearchOnValueChanged) ) :AddChild(UIElements.New("Button", "expandAllBtn") :SetSize(24, 24) :SetMargin(8, 4, 0, 0) :SetBackground("iconPack.18x18/Expand All") :SetScript("OnClick", private.ExpandAllGroupsOnClick) :SetTooltip(L["Expand / Collapse All Groups"]) ) :AddChild(UIElements.New("Button", "selectAllBtn") :SetSize(24, 24) :SetBackground("iconPack.18x18/Select All") :SetScript("OnClick", private.SelectAllGroupsOnClick) :SetTooltip(L["Select / Deselect All Groups"]) ) ) :AddChild(UIElements.New("Texture", "line") :SetHeight(2) :SetTexture("ACTIVE_BG") ) :AddChild(UIElements.New("ApplicationGroupTree", "groupTree") :SetSettingsContext(private.settings, "shoppingGroupTree") :SetQuery(TSM.Groups.CreateQuery(), "Shopping") :SetSearchString(private.groupSearch) :SetScript("OnGroupSelectionChanged", private.GroupTreeOnGroupSelectionChanged) ) :AddChild(UIElements.New("Texture", "line") :SetHeight(2) :SetTexture("ACTIVE_BG") ) :AddChild(UIElements.New("Frame", "bottom") :SetLayout("VERTICAL") :SetHeight(40) :SetBackgroundColor("PRIMARY_BG_ALT") :AddChild(UIElements.New("ActionButton", "scanBtn") :SetHeight(24) :SetMargin(8) :SetText(L["Run Shopping Scan"]) :SetDisabled(true) :SetScript("OnClick", private.ScanButtonOnClick) ) ) ) :SetRightChild(UIElements.New("ViewContainer", "content") :SetNavCallback(private.GetSelectionContent) :AddPath("search", true) :AddPath("advanced") ) :SetScript("OnUpdate", private.SelectionFrameOnUpdate) return frame end function private.SelectionFrameOnUpdate(frame) frame:SetScript("OnUpdate", nil) frame:GetElement("groupSelection.bottom.scanBtn"):SetDisabled(frame:GetElement("groupSelection.groupTree"):IsSelectionCleared(true)):Draw() end function private.GetSelectionContent(viewContainer, path) if path == "search" then return private.GetSelectionSearchFrame() elseif path == "advanced" then return private.GetAdvancedFrame() else error("Unexpected path: "..tostring(path)) end end function private.GetSelectionSearchFrame() return UIElements.New("Frame", "search") :SetLayout("VERTICAL") :SetPadding(8, 8, 8, 0) :SetBackgroundColor("PRIMARY_BG_ALT") :AddChild(UIElements.New("Frame", "header") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 0, 20) :AddChild(UIElements.New("Input", "filterInput") :SetIconTexture("iconPack.18x18/Search") :SetClearButtonEnabled(true) :SetFocused(private.settings.searchAutoFocus) :SetHintText(L["Search the auction house"]) :SetScript("OnValueChanged", private.FilterInputOnValueChanged) :SetScript("OnEnterPressed", private.FilterInputOnEnterPressed) ) :AddChild(UIElements.New("ActionButton", "search") :SetWidth(90) :SetMargin(8, 0, 0, 0) :SetDisabled(TSM.IsWowClassic()) :SetText(L["Search"]) :SetScript("OnClick", private.SearchButtonOnClick) ) ) :AddChild(UIElements.New("Frame", "buttonsLine1") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 0, 10) :AddChild(UIElements.New("ActionButton", "advSearchBtn") :SetMargin(0, 8, 0, 0) :SetText(L["Advanced Item Search"]) :SetScript("OnClick", private.AdvancedButtonOnClick) ) :AddChild(UIElements.New("ActionButton", "vendorBtn") :SetText(L["Vendor Search"]) :SetScript("OnClick", private.VendorButtonOnClick) ) ) :AddChild(UIElements.New("Frame", "buttonsLine2") :SetLayout("HORIZONTAL") :SetHeight(24) :AddChild(UIElements.New("ActionButton", "disenchantBtn") :SetMargin(0, 8, 0, 0) :SetText(L["Disenchant Search"]) :SetScript("OnClick", private.DisenchantButtonOnClick) ) :AddChild(UIElements.New("ActionButton", "dealsBtn") :SetText(L["Great Deals Search"]) :SetDisabled(not TSM.Shopping.GreatDealsSearch.GetFilter()) :SetScript("OnClick", private.DealsButtonOnClick) ) ) :AddChild(UIElements.New("TabGroup", "buttons") :SetMargin(-8, -8, 21, 0) :SetNavCallback(private.GetSearchesElement) :SetSettingsContext(private.settings, "shoppingSearchesTabGroup") :AddPath(L["Recent Searches"]) :AddPath(L["Favorite Searches"]) ) end function private.GetAdvancedFrame() if not private.rarityList then private.rarityList = {} for i = 1, 7 do tinsert(private.rarityList, _G["ITEM_QUALITY"..i.."_DESC"]) end end return UIElements.New("Frame", "advanced") :SetLayout("VERTICAL") :SetBackgroundColor("PRIMARY_BG_ALT") :AddChild(UIElements.New("ScrollFrame", "search") :AddChild(UIElements.New("Frame", "header") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(8) :AddChild(UIElements.New("ActionButton", "backBtn") :SetWidth(64) :SetIcon("iconPack.14x14/Chevron/Right@180") :SetText(BACK) :SetScript("OnClick", private.AdvancedBackButtonOnClick) ) :AddChild(UIElements.New("Input", "keyword") :SetMargin(8, 0, 0, 0) :SetIconTexture("iconPack.18x18/Search") :SetClearButtonEnabled(true) :SetHintText(L["Filter by Keyword"]) ) ) :AddChild(UIElements.New("Frame", "body") :SetLayout("VERTICAL") :SetPadding(8, 8, 0, 0) :AddChild(UIElements.New("Frame", "classAndSubClassLabels") :SetLayout("HORIZONTAL") :SetHeight(20) :SetMargin(0, 0, 16, 0) :AddChild(UIElements.New("Text", "classLabel") :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Item Class"]) ) :AddChild(UIElements.New("Text", "subClassLabel") :SetMargin(20, 0, 0, 0) :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Item Subclass"]) ) ) :AddChild(UIElements.New("Frame", "classAndSubClass") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 4, 0) :AddChild(UIElements.New("SelectionDropdown", "classDropdown") :SetMargin(0, 20, 0, 0) :SetItems(ItemClass.GetClasses()) :SetScript("OnSelectionChanged", private.ClassDropdownOnSelectionChanged) :SetHintText(L["All Item Classes"]) ) :AddChild(UIElements.New("SelectionDropdown", "subClassDropdown") :SetDisabled(true) :SetScript("OnSelectionChanged", private.SubClassDropdownOnSelectionChanged) :SetHintText(L["All Subclasses"]) ) ) :AddChild(UIElements.New("Frame", "itemSlot") :SetLayout("VERTICAL") :SetMargin(0, 0, 16, 0) :AddChild(UIElements.New("Text", "label") :SetHeight(20) :SetMargin(0, 0, 0, 4) :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Item Slot"]) ) :AddChild(UIElements.New("Frame", "frame") :SetLayout("HORIZONTAL") :SetHeight(24) :AddChild(UIElements.New("SelectionDropdown", "dropdown") :SetWidth(238) :SetDisabled(true) :SetHintText(L["All Slots"]) ) :AddChild(UIElements.New("Spacer", "spacer")) ) ) :AddChild(UIElements.New("Frame", "frame") :SetLayout("HORIZONTAL") :SetHeight(20) :SetMargin(0, 0, 16, 0) :AddChild(UIElements.New("Text", "label") :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Required Level Range"]) ) ) :AddChild(UIElements.New("Frame", "level") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 2, 0) :AddChild(UIElements.New("Slider", "slider") :SetRange(0, MAX_LEVEL) ) ) :AddChild(UIElements.New("Frame", "frame") :SetLayout("HORIZONTAL") :SetHeight(20) :SetMargin(0, 0, 18, 0) :AddChild(UIElements.New("Text", "label") :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Item Level Range"]) ) ) :AddChild(UIElements.New("Frame", "itemLevel") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 2, 0) :AddChild(UIElements.New("Slider", "slider") :SetRange(0, MAX_ITEM_LEVEL) ) ) :AddChild(UIElements.New("Frame", "content") :SetLayout("HORIZONTAL") :SetHeight(48) :SetMargin(0, 0, 18, 0) :AddChild(UIElements.New("Frame", "frame") :SetLayout("VERTICAL") :SetWidth(254) :AddChild(UIElements.New("Text", "label") :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Maximum Quantity to Buy"]) ) :AddChild(UIElements.New("Frame", "maxQty") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 4, 0) :AddChild(UIElements.New("Input", "input") :SetWidth(178) :SetMargin(0, 4, 0, 0) :SetBackgroundColor("PRIMARY_BG_ALT") :SetValidateFunc("NUMBER", "0:2000") :SetValue(0) ) :AddChild(UIElements.New("Text", "label") :SetWidth(100) :SetFont("BODY_BODY3_MEDIUM") :SetFormattedText("(%d - %d)", 0, 2000) ) ) ) :AddChild(UIElements.New("Frame", "minRarity") :SetLayout("VERTICAL") :AddChild(UIElements.New("Text", "label") :SetFont("BODY_BODY2_MEDIUM") :SetText(L["Minimum Rarity"]) ) :AddChild(UIElements.New("SelectionDropdown", "dropdown") :SetHeight(24) :SetMargin(0, 0, 4, 0) :SetItems(private.rarityList) :SetHintText(L["All"]) ) ) ) :AddChild(UIElements.New("Frame", "filters") :SetLayout("HORIZONTAL") :SetMargin(0, 0, 16, 8) :AddChildIf(not TSM.IsWowClassic(), UIElements.New("Frame", "uncollected") :SetLayout("HORIZONTAL") :SetHeight(20) :AddChild(UIElements.New("Checkbox", "checkbox") :SetCheckboxPosition("LEFT") :SetText(L["Uncollected Only"]) ) ) :AddChildIf(not TSM.IsWowClassic(), UIElements.New("Frame", "upgrades") :SetLayout("HORIZONTAL") :SetHeight(20) :AddChild(UIElements.New("Checkbox", "checkbox") :SetCheckboxPosition("LEFT") :SetText(L["Upgrades Only"]) ) ) :AddChild(UIElements.New("Frame", "usable") :SetLayout("HORIZONTAL") :SetHeight(20) :AddChild(UIElements.New("Checkbox", "checkbox") :SetCheckboxPosition("LEFT") :SetText(L["Usable Only"]) ) ) ) :AddChild(UIElements.New("Frame", "filters2") :SetLayout("HORIZONTAL") :SetHeight(20) :AddChild(UIElements.New("Checkbox", "exact") :SetCheckboxPosition("LEFT") :SetText(L["Exact Match"]) ) :AddChild(UIElements.New("Checkbox", "crafting") :SetCheckboxPosition("LEFT") :SetText(L["Crafting Mode"]) ) :AddChild(UIElements.New("Spacer", "spacer")) ) ) ) :AddChild(UIElements.New("Texture", "line") :SetHeight(2) :SetTexture("ACTIVE_BG") ) :AddChild(UIElements.New("Frame", "buttons") :SetLayout("HORIZONTAL") :SetHeight(40) :SetPadding(8) :SetBackgroundColor("PRIMARY_BG_ALT") :AddChild(UIElements.New("ActionButton", "startBtn") :SetHeight(24) :SetMargin(0, 8, 0, 0) :SetText(L["Run Advanced Item Search"]) :SetScript("OnClick", private.AdvancedStartOnClick) ) :AddChild(UIElements.New("Button", "resetBtn") :SetSize("AUTO", 24) :SetFont("BODY_BODY3_MEDIUM") :SetText(L["Reset All Filters"]) :SetScript("OnClick", private.ResetButtonOnClick) ) ) end function private.GetSearchesElement(self, button) if button == L["Recent Searches"] then return UIElements.New("SearchList", "list") :SetQuery(TSM.Shopping.SavedSearches.CreateRecentSearchesQuery()) :SetEditButtonHidden(true) :SetScript("OnFavoriteChanged", private.SearchListOnFavoriteChanged) :SetScript("OnDelete", private.SearchListOnDelete) :SetScript("OnRowClick", private.SearchListOnRowClick) elseif button == L["Favorite Searches"] then return UIElements.New("SearchList", "list") :SetQuery(TSM.Shopping.SavedSearches.CreateFavoriteSearchesQuery()) :SetScript("OnFavoriteChanged", private.SearchListOnFavoriteChanged) :SetScript("OnEditClick", private.SearchListOnEditClick) :SetScript("OnDelete", private.SearchListOnDelete) :SetScript("OnRowClick", private.SearchListOnRowClick) else error("Unexpected button: "..tostring(button)) end end function private.GetScanFrame() TSM.UI.AnalyticsRecordPathChange("auction", "shopping", "scan") local frame = UIElements.New("Frame", "scan") :SetLayout("VERTICAL") :SetBackgroundColor("PRIMARY_BG_ALT") :AddChild(UIElements.New("Frame", "searchFrame") :SetLayout("HORIZONTAL") :SetHeight(40) :SetPadding(8) :AddChild(UIElements.New("Frame", "back") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 8, 0, 0) :AddChild(UIElements.New("ActionButton", "button") :SetWidth(64) :SetIcon("iconPack.14x14/Chevron/Right@180") :SetText(BACK) :SetScript("OnClick", private.ScanBackButtonOnClick) ) ) :AddChild(UIElements.New("Input", "filterInput") :SetIconTexture("iconPack.18x18/Search") :SetClearButtonEnabled(true) :SetHintText(L["Enter Filter"]) :SetValue(private.searchName) :SetScript("OnEnterPressed", private.ScanFilterInputOnEnterPressed) ) :AddChild(UIElements.New("ActionButton", "rescanBtn") :SetWidth(140) :SetMargin(8, 0, 0, 0) :SetText(L["Rescan"]) :SetScript("OnClick", private.RescanBtnOnClick) ) ) :AddChild(UIElements.New("AuctionScrollingTable", "auctions") :SetSettingsContext(private.settings, "shoppingAuctionScrollingTable") :SetBrowseResultsVisible(true) :SetScript("OnSelectionChanged", private.AuctionsOnSelectionChanged) ) :AddChild(UIElements.New("Texture", "line") :SetHeight(2) :SetTexture("ACTIVE_BG") ) :AddChild(UIElements.New("Frame", "bottom") :SetLayout("HORIZONTAL") :SetHeight(40) :SetPadding(8) :SetBackgroundColor("PRIMARY_BG_ALT") :AddChild(UIElements.New("ActionButton", "pauseResumeBtn") :SetSize(24, 24) :SetMargin(0, 8, 0, 0) :SetIcon("iconPack.18x18/PlayPause") :SetScript("OnClick", private.PauseResumeOnClick) ) :AddChild(UIElements.New("ProgressBar", "progressBar") :SetHeight(24) :SetMargin(0, 8, 0, 0) :SetProgress(0) :SetProgressIconHidden(false) :SetText(L["Starting Scan..."]) ) :AddChild(UIElements.New("ActionButton", "postBtn") :SetSize(107, 24) :SetMargin(0, 8, 0, 0) :SetText(L["Post"]) :SetDisabled(true) :SetScript("OnClick", private.AuctionsOnPostButtonClick) ) :AddChild(UIElements.New("Texture", "line") :SetSize(2, 24) :SetMargin(0, 8, 0, 0) :SetTexture("ACTIVE_BG") ) :AddChild(UIElements.New("ActionButton", "bidBtn") :SetSize(107, 24) :SetMargin(0, 8, 0, 0) :SetText(BID) :SetDisabled(true) :SetScript("OnClick", private.BidBtnOnClick) ) :AddChild(UIElements.NewNamed("ActionButton", "buyoutBtn", "TSMShoppingBuyoutBtn") :SetSize(107, 24) :SetText(BUYOUT) :SetDisabled(true) :DisableClickCooldown(true) :SetScript("OnClick", private.BuyoutBtnOnClick) ) :AddChild(UIElements.New("ActionButton", "cancelBtn") :SetSize(107, 24) :SetText(CANCEL) :SetDisabled(true) :DisableClickCooldown(true) :SetScript("OnClick", private.CancelBtnOnClick) ) ) :SetScript("OnUpdate", private.ScanFrameOnUpdate) :SetScript("OnHide", private.ScanFrameOnHide) frame:GetElement("bottom.cancelBtn"):Hide() return frame end function private.BidBtnOnClick(button) private.fsm:ProcessEvent("EV_BID_CLICKED") end function private.BuyoutBtnOnClick(button) private.fsm:ProcessEvent("EV_BUYOUT_CLICKED") end function private.CancelBtnOnClick(button) private.fsm:ProcessEvent("EV_CANCEL_CLICKED") end function private.PostDialogShow(baseFrame, row) baseFrame:ShowDialogFrame(UIElements.New("Frame", "frame") :SetLayout("VERTICAL") :SetSize(326, TSM.IsWowClassic() and 380 or 344) :SetPadding(12) :AddAnchor("CENTER") :SetBackgroundColor("FRAME_BG", true) :SetMouseEnabled(true) :AddChild(UIElements.New("ViewContainer", "view") :SetNavCallback(private.GetViewContentFrame) :AddPath("posting", true) :AddPath("selection") :SetContext(row) ) :SetScript("OnHide", private.PostDialogOnHide) ) end function private.PostDialogOnHide(frame) private.itemString = nil end function private.GetViewContentFrame(viewContainer, path) if path == "posting" then return private.GetPostingFrame() elseif path == "selection" then return private.GetPostSelectionFrame() else error("Unexpected path: "..tostring(path)) end end function private.GetPostingFrame() return UIElements.New("Frame", "posting") :SetLayout("VERTICAL") :AddChild(UIElements.New("Frame", "header") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, -4, 10) :AddChild(UIElements.New("Spacer", "spacer") :SetWidth(24) ) :AddChild(UIElements.New("Text", "title") :SetFont("BODY_BODY2_MEDIUM") :SetJustifyH("CENTER") :SetText(L["Post from Shopping Scan"]) ) :AddChild(UIElements.New("Button", "closeBtn") :SetMargin(0, -4, 0, 0) :SetBackgroundAndSize("iconPack.24x24/Close/Default") :SetScript("OnClick", private.PostDialogCloseBtnOnClick) ) ) :AddChild(UIElements.New("Frame", "item") :SetLayout("HORIZONTAL") :SetPadding(6) :SetMargin(0, 0, 0, 16) :SetBackgroundColor("PRIMARY_BG_ALT", true) :AddChild(UIElements.New("Button", "icon") :SetSize(36, 36) :SetMargin(0, 8, 0, 0) ) :AddChild(UIElements.New("Text", "name") :SetHeight(36) :SetFont("ITEM_BODY1") ) :AddChild(UIElements.New("Button", "editBtn") :SetMargin(8, 0, 0, 0) :SetBackgroundAndSize("iconPack.18x18/Edit") :SetScript("OnClick", private.ItemBtnOnClick) ) ) :AddChildIf(TSM.IsWowClassic(), UIElements.New("Frame", "numStacks") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 0, 12) :AddChild(UIElements.New("Text", "desc") :SetFont("BODY_BODY2") :SetText(L["Stack(s)"]..":") ) :AddChild(UIElements.New("Input", "input") :SetWidth(140) :SetMargin(0, 8, 0, 0) :SetBackgroundColor("PRIMARY_BG_ALT") :SetJustifyH("RIGHT") :SetValidateFunc("NUMBER", "0:5000") :SetValue(1) :SetScript("OnValueChanged", private.StackNumInputOnValueChanged) ) :AddChild(UIElements.New("ActionButton", "maxBtn") :SetWidth(64) :SetText(L["Max"]) :SetScript("OnClick", private.MaxStackNumBtnOnClick) ) ) :AddChild(UIElements.New("Frame", "quantity") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 0, 12) :AddChild(UIElements.New("Text", "desc") :SetFont("BODY_BODY2") :SetText(L["Quantity"]..":") ) :AddChild(UIElements.New("Input", "input") :SetWidth(140) :SetMargin(0, 8, 0, 0) :SetBackgroundColor("PRIMARY_BG_ALT") :SetJustifyH("RIGHT") :SetValidateFunc("NUMBER", "0:5000") :SetScript("OnValueChanged", private.QuantityInputOnValueChanged) ) :AddChild(UIElements.New("ActionButton", "maxBtn") :SetWidth(64) :SetText(L["Max"]) :SetScript("OnClick", private.MaxQuantityBtnOnClick) ) ) :AddChild(UIElements.New("Frame", "duration") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 0, 8) :AddChild(UIElements.New("Text", "desc") :SetWidth("AUTO") :SetFont("BODY_BODY2") :SetText(L["Duration"]..":") ) :AddChild(UIElements.New("Toggle", "toggle") :SetMargin(0, 48, 0, 0) :AddOption(TSM.CONST.AUCTION_DURATIONS[1]) :AddOption(TSM.CONST.AUCTION_DURATIONS[2]) :AddOption(TSM.CONST.AUCTION_DURATIONS[3]) :SetOption(private.postTimeStr, true) :SetScript("OnValueChanged", private.DurationOnValueChanged) ) ) :AddChild(UIElements.New("Spacer", "spacer")) :AddChild(UIElements.New("Frame", "per") :SetLayout("HORIZONTAL") :SetHeight(20) :SetMargin(0, 0, 0, 8) :AddChild(UIElements.New("Spacer", "spacer")) :AddChild(UIElements.New("Button", "item") :SetWidth("AUTO") :SetMargin(0, 8, 0, 0) :SetFont("BODY_BODY2_MEDIUM") :SetJustifyH("RIGHT") :SetTextColor("INDICATOR") :SetText(L["Per Item"]) :SetScript("OnClick", TSM.IsWowClassic() and private.PerItemOnClick) ) :AddChildIf(TSM.IsWowClassic(), UIElements.New("Button", "stack") :SetWidth("AUTO") :SetJustifyH("RIGHT") :SetFont("BODY_BODY2_MEDIUM") :SetTextColor("TEXT") :SetText(L["Per Stack"]) :SetScript("OnClick", TSM.IsWowClassic() and private.PerStackOnClick) ) ) :AddChild(UIElements.New("Frame", "bid") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 0, 10) :AddChild(UIElements.New("Text", "desc") :SetWidth("AUTO") :SetFont("BODY_BODY2") :SetText(L["Bid Price"]..":") ) :AddChild(UIElements.New("Spacer", "spacer")) :AddChild(UIElements.New("Input", "input") :SetWidth(132) :SetBackgroundColor("PRIMARY_BG_ALT") :SetFont("TABLE_TABLE1") :SetValidateFunc(private.BidBuyoutValidateFunc) :SetJustifyH("RIGHT") :SetTabPaths("__parent.__parent.quantity.input", "__parent.__parent.buyout.input") :SetScript("OnValidationChanged", private.BidBuyoutOnValidationChanged) :SetScript("OnValueChanged", private.BidBuyoutInputOnValueChanged) :SetScript("OnEnterPressed", private.BidBuyoutInputOnEnterPressed) ) ) :AddChild(UIElements.New("Frame", "buyout") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 0, 10) :AddChild(UIElements.New("Text", "desc") :SetWidth("AUTO") :SetFont("BODY_BODY2") :SetText(L["Buyout Price"]..":") ) :AddChild(UIElements.New("Spacer", "spacer")) :AddChild(UIElements.New("Input", "input") :SetWidth(132) :SetBackgroundColor("PRIMARY_BG_ALT") :SetFont("TABLE_TABLE1") :SetValidateFunc(private.BidBuyoutValidateFunc) :SetJustifyH("RIGHT") :SetTabPaths("__parent.__parent.bid.input", "__parent.__parent.quantity.input") :SetScript("OnValidationChanged", private.BidBuyoutOnValidationChanged) :SetScript("OnValueChanged", private.BidBuyoutInputOnValueChanged) :SetScript("OnEnterPressed", private.BidBuyoutInputOnEnterPressed) ) ) :AddChild(UIElements.New("Frame", "deposit") :SetLayout("HORIZONTAL") :SetHeight(20) :SetMargin(0, 0, 0, 15) :AddChild(UIElements.New("Text", "desc") :SetWidth("AUTO") :SetFont("BODY_BODY2") :SetText(L["Deposit Cost"]..":") ) :AddChild(UIElements.New("Text", "text") :SetFont("TABLE_TABLE1") :SetJustifyH("RIGHT") ) ) :AddChild(UIElements.New("ActionButton", "confirmBtn") :SetHeight(26) :SetText(L["Post Auction"]) :SetScript("OnClick", private.PostButtonOnClick) ) :SetScript("OnUpdate", private.PostingFrameOnUpdate) end function private.GetPostSelectionFrame() local query = BagTracking.CreateQueryBagsItemAuctionable(ItemString.GetBase(private.itemString)) local frame = UIElements.New("Frame", "selection") :SetLayout("VERTICAL") :AddChild(UIElements.New("Frame", "header") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, -4, 10) :AddChild(UIElements.New("Spacer", "spacer") :SetWidth(24) ) :AddChild(UIElements.New("Text", "title") :SetFont("BODY_BODY2_MEDIUM") :SetJustifyH("CENTER") :SetText(L["Item Selection"]) ) :AddChild(UIElements.New("Button", "closeBtn") :SetMargin(0, -4, 0, 0) :SetBackgroundAndSize("iconPack.24x24/Close/Default") :SetScript("OnClick", private.PostDialogCloseBtnOnClick) ) ) :AddChild(UIElements.New("QueryScrollingTable", "items") :SetHeaderHidden(true) :GetScrollingTableInfo() :NewColumn("item") :SetTitle(L["Item"]) :SetFont("ITEM_BODY3") :SetJustifyH("LEFT") :SetIconSize(14) :SetTextInfo("itemString", TSM.UI.GetColoredItemName) :SetIconInfo("itemString", ItemInfo.GetTexture) :SetTooltipInfo("itemString") :DisableHiding() :Commit() :Commit() :SetQuery(query) :SetAutoReleaseQuery(true) :SetScript("OnRowClick", private.ItemQueryOnRowClick) ) :AddChild(UIElements.New("ActionButton", "backBtn") :SetMargin(0, 0, 9, 0) :SetHeight(26) :SetText(BACK) :SetScript("OnClick", private.ViewBackButtonOnClick) ) return frame end function private.PostingFrameOnUpdate(frame) frame:SetScript("OnUpdate", nil) local postContext = frame:GetParentElement():GetContext() if not private.itemString then assert(postContext.itemString) local foundItem = false local backupItemString = nil local query = BagTracking.CreateQueryBagsAuctionable() :OrderBy("slotId", true) :Select("itemString") for _, itemString in query:Iterator() do if itemString == postContext.itemString then foundItem = true elseif not backupItemString and ItemString.GetBase(itemString) == postContext.baseItemString then backupItemString = itemString end end query:Release() private.itemString = foundItem and postContext.itemString or backupItemString if not private.itemString then frame:GetBaseElement():HideDialog() Log.PrintfUser(L["Failed to post %sx%d as the item no longer exists in your bags."], ItemInfo.GetName(postContext.itemString), postContext.quantity) private.frame:GetElement("scan.bottom.postBtn") :SetDisabled(true) :Draw() return end end local undercut = PlayerInfo.IsPlayer(postContext.ownerStr, true, true, true) and 0 or 1 local bid = postContext.itemDisplayedBid - undercut if not TSM.IsWowClassic() then bid = Math.Round(bid, COPPER_PER_SILVER) end if bid <= 0 then bid = 1 elseif bid > MAXIMUM_BID_PRICE then bid = MAXIMUM_BID_PRICE end local buyout = nil if TSM.IsWowClassic() then buyout = postContext.itemBuyout - undercut else buyout = Math.Round(postContext.itemBuyout - undercut, COPPER_PER_SILVER) end if buyout < 0 then buyout = 0 elseif buyout > MAXIMUM_BID_PRICE then buyout = MAXIMUM_BID_PRICE end private.perItem = true private.postStack = nil private.postQuantity = nil frame:GetElement("item.icon") :SetBackground(ItemInfo.GetTexture(private.itemString)) :SetTooltip(private.itemString) frame:GetElement("item.name") :SetText(TSM.UI.GetColoredItemName(private.itemString)) local maxPostStack = private.GetMaxPostStack(private.itemString) local isCommodity = ItemInfo.IsCommodity(private.itemString) frame:GetElement("quantity.input") :SetValidateFunc("NUMBER", "0:"..maxPostStack) :SetValue(min(postContext.quantity, maxPostStack, 5000)) frame:GetElement("bid.input") :SetDisabled(isCommodity) :SetValue(Money.ToString(bid, nil, "OPT_83_NO_COPPER", isCommodity and "OPT_DISABLE" or nil)) frame:GetElement("buyout.input") :SetValue(Money.ToString(buyout, nil, "OPT_83_NO_COPPER")) frame:GetElement("confirmBtn") :SetContext(private.itemString) frame:Draw() private.UpdateDepositCostAndPostButton(frame) end function private.ItemQueryOnRowClick(scrollingtable, row) private.itemString = row:GetField("itemString") scrollingtable:GetElement("__parent.__parent"):SetPath("posting", true) end function private.ViewBackButtonOnClick(button) button:GetElement("__parent.__parent"):SetPath("posting", true) end -- ============================================================================ -- Local Script Handlers -- ============================================================================ function private.OnItemLinked(name, itemLink, forceSearch) local itemString = ItemString.Get(itemLink) local baseItemString = ItemString.GetBase(itemString) local baseName = ItemInfo.GetName(baseItemString) if itemString == baseItemString then baseName = baseName.."/exact" end if not forceSearch and private.frame:GetPath() == "selection" and private.frame:GetElement("selection.content"):GetPath() == "advanced" then -- they are on the advanced search UI, so just populate the filter dialog instead of starting a search private.frame:GetElement("selection.content.advanced.search.header.keyword") :SetValue(baseName) :Draw() return end private.frame:SetPath("selection") local price = CustomPrice.GetValue("first(dbmarket, 100g)", itemString) local postContext = private.postContext wipe(postContext) postContext.baseItemString = baseItemString postContext.itemString = itemString postContext.ownerStr = PLAYER_NAME postContext.currentBid = 0 postContext.displayedBid = price postContext.itemDisplayedBid = price postContext.buyout = price postContext.itemBuyout = price postContext.quantity = 1 private.frame:GetBaseElement():HideDialog() private.StartFilterSearchHelper(private.frame, baseName, nil, postContext) return true end function private.GroupSearchOnValueChanged(input) private.groupSearch = strlower(input:GetValue()) input:GetElement("__parent.__parent.groupTree") :SetSearchString(private.groupSearch) :Draw() end function private.ExpandAllGroupsOnClick(button) button:GetElement("__parent.__parent.groupTree") :ToggleExpandAll() end function private.SelectAllGroupsOnClick(button) button:GetElement("__parent.__parent.groupTree") :ToggleSelectAll() end function private.GroupTreeOnGroupSelectionChanged(groupTree) local scanBtn = groupTree:GetElement("__parent.bottom.scanBtn") scanBtn:SetDisabled(groupTree:IsSelectionCleared()) scanBtn:Draw() end function private.FrameOnHide(frame) assert(frame == private.frame) private.frame = nil for _, callback in ipairs(private.updateCallbacks) do callback() end end function private.ScanButtonOnClick(button) wipe(private.selectedGroups) for _, groupPath in button:GetElement("__parent.__parent.groupTree"):SelectedGroupsIterator() do if groupPath ~= "" and not strmatch(groupPath, "^`") then tinsert(private.selectedGroups, groupPath) end end local viewContainer = button:GetParentElement():GetParentElement():GetParentElement():GetParentElement() local searchContext = TSM.Shopping.GroupSearch.GetSearchContext(private.selectedGroups) assert(searchContext) private.StartSearchHelper(viewContainer, searchContext) end function private.SearchListOnFavoriteChanged(_, dbRow, isFavorite) TSM.Shopping.SavedSearches.SetSearchIsFavorite(dbRow, isFavorite) end function private.SearchListOnEditClick(searchList, dbRow) local dialog = UIElements.New("Frame", "frame") :SetLayout("VERTICAL") :SetSize(600, 187) :AddAnchor("CENTER") :SetBackgroundColor("FRAME_BG") :SetBorderColor("ACTIVE_BG") :AddChild(UIElements.New("Text", "title") :SetHeight(44) :SetMargin(16, 16, 24, 16) :SetFont("BODY_BODY1_BOLD") :SetJustifyH("CENTER") :SetText(L["Rename Search"]) ) :AddChild(UIElements.New("Input", "nameInput") :SetHeight(26) :SetMargin(16, 16, 0, 25) :SetBackgroundColor("PRIMARY_BG_ALT") :AllowItemInsert(true) :SetContext(dbRow) :SetValue(dbRow:GetField("name")) :SetScript("OnEnterPressed", private.RenameInputOnEnterPressed) ) :AddChild(UIElements.New("Frame", "buttons") :SetLayout("HORIZONTAL") :SetMargin(16, 16, 0, 16) :AddChild(UIElements.New("Spacer", "spacer")) :AddChild(UIElements.New("ActionButton", "closeBtn") :SetSize(126, 26) :SetText(CLOSE) :SetScript("OnClick", private.DialogCloseBtnOnClick) ) ) searchList:GetBaseElement():ShowDialogFrame(dialog) dialog:GetElement("nameInput"):SetFocused(true) end function private.RenameInputOnEnterPressed(input) local name = input:GetValue() if name == "" then return end local dbRow = input:GetContext() local baseElement = input:GetBaseElement() baseElement:HideDialog() TSM.Shopping.SavedSearches.RenameSearch(dbRow, name) end function private.DialogCloseBtnOnClick(button) private.RenameInputOnEnterPressed(button:GetElement("__parent.__parent.nameInput")) end function private.SearchListOnDelete(_, dbRow) TSM.Shopping.SavedSearches.DeleteSearch(dbRow) end function private.SearchListOnRowClick(searchList, dbRow) local viewContainer = searchList:GetParentElement():GetParentElement():GetParentElement():GetParentElement():GetParentElement() private.StartFilterSearchHelper(viewContainer, dbRow:GetField("filter")) end function private.AdvancedButtonOnClick(button) button:GetParentElement():GetParentElement():GetParentElement():SetPath("advanced", true) end function private.AdvancedBackButtonOnClick(button) button:GetParentElement():GetParentElement():GetParentElement():GetParentElement():SetPath("search", true) end function private.ClassDropdownOnSelectionChanged(dropdown) local subClassDropdown = dropdown:GetElement("__parent.subClassDropdown") local selection = dropdown:GetSelectedItem() if selection then local subClasses = TempTable.Acquire() for _, v in pairs(ItemClass.GetSubClasses(selection)) do tinsert(subClasses, v) end if dropdown:GetSelectedItem() == GetItemClassInfo(LE_ITEM_CLASS_ARMOR) then for _, v in pairs(GENERIC_TYPES) do tinsert(subClasses, v) end end subClassDropdown:SetItems(subClasses) subClassDropdown:SetDisabled(false) subClassDropdown:SetSelectedItem(nil) :Draw() TempTable.Release(subClasses) else subClassDropdown:SetDisabled(true) subClassDropdown:SetSelectedItem(nil) :Draw() end end function private.SubClassDropdownOnSelectionChanged(dropdown) local classDropdown = dropdown:GetElement("__parent.classDropdown") local itemSlotDropdown = dropdown:GetElement("__parent.__parent.itemSlot.frame.dropdown") local selection = dropdown:GetSelectedItem() if selection and classDropdown:GetSelectedItem() == GetItemClassInfo(LE_ITEM_CLASS_ARMOR) and ARMOR_TYPES[selection] then itemSlotDropdown:SetItems(INVENTORY_TYPES) itemSlotDropdown:SetDisabled(false) itemSlotDropdown:SetSelectedItem(nil) :Draw() else itemSlotDropdown:SetDisabled(true) itemSlotDropdown:SetSelectedItem(nil) :Draw() end end function private.ResetButtonOnClick(button) local headerFrame = button:GetElement("__parent.__parent.search.header") headerFrame:GetElement("keyword"):SetText("") headerFrame:Draw() local searchFrame = button:GetElement("__parent.__parent.search.body") searchFrame:GetElement("level.slider"):SetValue(0, MAX_LEVEL) searchFrame:GetElement("itemLevel.slider"):SetValue(0, MAX_ITEM_LEVEL) searchFrame:GetElement("classAndSubClass.classDropdown"):SetSelectedItem(nil) searchFrame:GetElement("classAndSubClass.subClassDropdown"):SetSelectedItem(nil):SetDisabled(true) searchFrame:GetElement("itemSlot.frame.dropdown"):SetSelectedItem(nil):SetDisabled(true) searchFrame:GetElement("content.minRarity.dropdown"):SetSelectedItem(nil) searchFrame:GetElement("content.frame.maxQty.input"):SetValue(0) searchFrame:GetElement("filters.uncollected.checkbox"):SetChecked(false) searchFrame:GetElement("filters.upgrades.checkbox"):SetChecked(false) searchFrame:GetElement("filters.usable.checkbox"):SetChecked(false) searchFrame:GetElement("filters2.exact"):SetChecked(false) searchFrame:GetElement("filters2.crafting"):SetChecked(false) searchFrame:Draw() end function private.AdvancedStartOnClick(button) local headerFrame = button:GetElement("__parent.__parent.search.header") local searchFrame = button:GetElement("__parent.__parent.search.body") local filterParts = TempTable.Acquire() tinsert(filterParts, strtrim(headerFrame:GetElement("keyword"):GetValue())) local levelMin, levelMax = searchFrame:GetElement("level.slider"):GetValue() if levelMin ~= 0 or levelMax ~= MAX_LEVEL then tinsert(filterParts, levelMin) tinsert(filterParts, levelMax) end local itemLevelMin, itemLevelMax = searchFrame:GetElement("itemLevel.slider"):GetValue() if itemLevelMin ~= 0 or itemLevelMax ~= MAX_ITEM_LEVEL then tinsert(filterParts, "i"..itemLevelMin) tinsert(filterParts, "i"..itemLevelMax) end local class = searchFrame:GetElement("classAndSubClass.classDropdown"):GetSelectedItem() if class then tinsert(filterParts, class) end local subClass = searchFrame:GetElement("classAndSubClass.subClassDropdown"):GetSelectedItem() if subClass then tinsert(filterParts, subClass) end local itemSlot = searchFrame:GetElement("itemSlot.frame.dropdown"):GetSelectedItem() if itemSlot then tinsert(filterParts, itemSlot) end local rarity = searchFrame:GetElement("content.minRarity.dropdown"):GetSelectedItem() if rarity then tinsert(filterParts, rarity) end local quantity = tonumber(searchFrame:GetElement("content.frame.maxQty.input"):GetValue()) if quantity > 0 then tinsert(filterParts, "x"..quantity) end if not TSM.IsWowClassic() and searchFrame:GetElement("filters.uncollected.checkbox"):IsChecked() then tinsert(filterParts, "uncollected") end if not TSM.IsWowClassic() and searchFrame:GetElement("filters.upgrades.checkbox"):IsChecked() then tinsert(filterParts, "upgrades") end if searchFrame:GetElement("filters.usable.checkbox"):IsChecked() then tinsert(filterParts, "usable") end if searchFrame:GetElement("filters2.exact"):IsChecked() then tinsert(filterParts, "exact") end if searchFrame:GetElement("filters2.crafting"):IsChecked() then tinsert(filterParts, "crafting") end local filter = table.concat(filterParts, "/") TempTable.Release(filterParts) local viewContainer = searchFrame:GetParentElement():GetParentElement():GetParentElement():GetParentElement():GetParentElement() private.StartFilterSearchHelper(viewContainer, filter) end function private.FilterInputOnValueChanged(input) local text = input:GetValue() if text == private.filterText then return end private.filterText = text input:GetElement("__parent.search"):SetDisabled(TSM.IsWowClassic() and text == "") :Draw() end function private.FilterInputOnEnterPressed(input) local filter = input:GetValue() if TSM.IsWowClassic() and filter == "" then return end local viewContainer = input:GetElement("__parent.__parent.__parent.__parent.__parent") private.StartFilterSearchHelper(viewContainer, filter) end function private.SearchButtonOnClick(button) private.FilterInputOnEnterPressed(button:GetElement("__parent.filterInput")) end function private.StartSearchHelper(viewContainer, searchContext, filter, errMsg) if not TSM.UI.AuctionUI.StartingScan(L["Browse"]) then return end if searchContext then viewContainer:SetPath("scan", true) local name = searchContext:GetName() assert(name) private.searchName = name viewContainer:GetElement("scan.searchFrame.filterInput") :SetValue(name) viewContainer:GetElement("scan.searchFrame.rescanBtn") :SetDisabled(name == L["Gathering Search"]) private.fsm:ProcessEvent("EV_START_SCAN", searchContext) else viewContainer:SetPath("selection", true) if type(filter) == "string" then Log.PrintUser(format(L["Invalid search filter (%s)."], filter).." "..errMsg) end end end function private.StartFilterSearchHelper(viewContainer, filter, isGreatDeals, postContext) local searchContext, errMsg = nil, nil if isGreatDeals then searchContext = TSM.Shopping.FilterSearch.GetGreatDealsSearchContext(filter) else searchContext, errMsg = TSM.Shopping.FilterSearch.GetSearchContext(filter, postContext) end private.StartSearchHelper(viewContainer, searchContext, filter, errMsg) end function private.StartGatheringSearchHelper(viewContainer, items, stateCallback, buyCallback, mode) local filterList = TempTable.Acquire() for itemString, quantity in pairs(items) do tinsert(filterList, itemString.."/x"..quantity) end local filter = table.concat(filterList, ";") TempTable.Release(filterList) local searchContext = TSM.Shopping.FilterSearch.GetGatheringSearchContext(filter, mode) assert(searchContext) searchContext:SetCallbacks(buyCallback, stateCallback) private.StartSearchHelper(viewContainer, searchContext, filter) end function private.DealsButtonOnClick(button) local viewContainer = button:GetParentElement():GetParentElement():GetParentElement():GetParentElement():GetParentElement() private.StartFilterSearchHelper(viewContainer, TSM.Shopping.GreatDealsSearch.GetFilter(), true) end function private.VendorButtonOnClick(button) local viewContainer = button:GetParentElement():GetParentElement():GetParentElement():GetParentElement():GetParentElement() local searchContext = TSM.Shopping.VendorSearch.GetSearchContext() assert(searchContext) private.StartSearchHelper(viewContainer, searchContext) end function private.DisenchantButtonOnClick(button) local viewContainer = button:GetParentElement():GetParentElement():GetParentElement():GetParentElement():GetParentElement() local searchContext = TSM.Shopping.DisenchantSearch.GetSearchContext() assert(searchContext) private.StartSearchHelper(viewContainer, searchContext) end function private.ScanBackButtonOnClick(button) private.searchName = "" button:GetElement("__parent.__parent.__parent.__parent"):SetPath("selection", true) private.fsm:ProcessEvent("EV_SCAN_BACK_BUTTON_CLICKED") end function private.AuctionsOnSelectionChanged() private.fsm:ProcessEvent("EV_AUCTION_SELECTION_CHANGED") end function private.PauseResumeOnClick() private.fsm:ProcessEvent("EV_PAUSE_RESUME_CLICKED") end function private.AuctionsOnPostButtonClick() private.fsm:ProcessEvent("EV_POST_BUTTON_CLICK") end function private.ScanFrameOnUpdate(frame) frame:SetScript("OnUpdate", nil) private.fsm:ProcessEvent("EV_SCAN_FRAME_SHOWN", frame) end function private.ScanFrameOnHide(frame) private.fsm:ProcessEvent("EV_SCAN_FRAME_HIDDEN") end function private.PerItemOnClick(button) if private.perItem then return end private.perItem = true button:GetElement("__parent.stack") :SetTextColor("TEXT") :Draw() button:SetTextColor("INDICATOR") :Draw() local frame = button:GetElement("__parent.__parent") local postContext = frame:GetElement("__parent"):GetContext() local undercut = (not TSM.IsWowClassic() or PlayerInfo.IsPlayer(postContext.ownerStr, true, true, true)) and 0 or 1 local bidInput = frame:GetElement("bid.input") local buyoutInput = frame:GetElement("buyout.input") buyoutInput:SetFocused(false) bidInput:SetFocused(false) local numStacksInput = frame:GetElement("numStacks.input") numStacksInput:SetFocused(false) local quantityInput = frame:GetElement("quantity.input") quantityInput:SetFocused(false) local stackSizeEdit = tonumber(quantityInput:GetValue()) local isCommodity = ItemInfo.IsCommodity(private.itemString) if postContext.quantity == stackSizeEdit then local newBid = private.ParseBidBuyout(bidInput:GetValue()) newBid = newBid + undercut == postContext.displayedBid and floor(postContext.displayedBid / postContext.quantity) - undercut or floor(newBid / postContext.quantity) local newBuyout = private.ParseBidBuyout(buyoutInput:GetValue()) newBuyout = newBuyout + undercut == postContext.buyout and postContext.itemBuyout - undercut or floor(newBuyout / postContext.quantity) buyoutInput:SetValue(Money.ToString(newBuyout, nil, "OPT_83_NO_COPPER")) bidInput:SetValue(Money.ToString(newBid, nil, "OPT_83_NO_COPPER", isCommodity and "OPT_DISABLE" or nil)) else local newBid = private.ParseBidBuyout(bidInput:GetValue()) newBid = newBid + undercut == postContext.displayedBid and floor(postContext.displayedBid / stackSizeEdit) - undercut or floor(newBid / stackSizeEdit) local newBuyout = private.ParseBidBuyout(buyoutInput:GetValue()) newBuyout = newBuyout + undercut == postContext.buyout and postContext.itemBuyout - undercut or floor(newBuyout / stackSizeEdit) buyoutInput:SetValue(Money.ToString(newBuyout, nil, "OPT_83_NO_COPPER")) bidInput:SetValue(Money.ToString(newBid, nil, "OPT_83_NO_COPPER", isCommodity and "OPT_DISABLE" or nil)) end frame:Draw() end function private.PerStackOnClick(button) if not private.perItem then return end private.perItem = false button:GetElement("__parent.item") :SetTextColor("TEXT") :Draw() button:SetTextColor("INDICATOR") :Draw() local frame = button:GetElement("__parent.__parent") local postContext = frame:GetElement("__parent"):GetContext() local undercut = (not TSM.IsWowClassic() or PlayerInfo.IsPlayer(postContext.ownerStr, true, true, true)) and 0 or 1 local bidInput = frame:GetElement("bid.input") local buyoutInput = frame:GetElement("buyout.input") buyoutInput:SetFocused(false) bidInput:SetFocused(false) local numStacksInput = frame:GetElement("numStacks.input") numStacksInput:SetFocused(false) local quantityInput = frame:GetElement("quantity.input") quantityInput:SetFocused(false) local stackSizeEdit = tonumber(quantityInput:GetValue()) local newBuyout, newBid = nil, nil local isCommodity = ItemInfo.IsCommodity(private.itemString) if postContext.quantity == stackSizeEdit then newBid = private.ParseBidBuyout(bidInput:GetValue()) newBid = ((newBid + undercut) * postContext.quantity) == postContext.displayedBid and (postContext.displayedBid - undercut) or (newBid * postContext.quantity) newBuyout = private.ParseBidBuyout(buyoutInput:GetValue()) newBuyout = ((newBuyout + undercut) * postContext.quantity) == postContext.buyout and (postContext.buyout - undercut) or (newBuyout * postContext.quantity) else newBid = private.ParseBidBuyout(bidInput:GetValue()) newBid = ((newBid + undercut) * postContext.quantity) == postContext.displayedBid and floor(postContext.displayedBid / postContext.quantity) * stackSizeEdit - undercut or newBid * stackSizeEdit newBuyout = private.ParseBidBuyout(buyoutInput:GetValue()) newBuyout = ((newBuyout + undercut) * postContext.quantity) == postContext.buyout and postContext.itemBuyout * stackSizeEdit - undercut or newBuyout * stackSizeEdit end buyoutInput:SetValue(Money.ToString(newBuyout, nil, "OPT_83_NO_COPPER")) bidInput:SetValue(Money.ToString(newBid, nil, "OPT_83_NO_COPPER", isCommodity and "OPT_DISABLE" or nil)) frame:Draw() end function private.ParseBidBuyout(value) value = Money.FromString(value) or tonumber(value) if not value then return nil end if not TSM.IsWowClassic() and value % COPPER_PER_SILVER ~= 0 then return nil end return (value or 0) > 0 and value <= MAXIMUM_BID_PRICE and value or nil end function private.BidBuyoutValidateFunc(input, value) value = private.ParseBidBuyout(value) if not value then return false, L["Invalid price."] end return true end function private.BidBuyoutOnValidationChanged(input) private.UpdateDepositCostAndPostButton(input:GetElement("__parent.__parent")) end function private.BidBuyoutInputOnValueChanged(input) local frame = input:GetElement("__parent.__parent") local itemString = frame:GetElement("confirmBtn"):GetContext() local bidInput = frame:GetElement("bid.input") local buyoutInput = frame:GetElement("buyout.input") local bid = private.ParseBidBuyout(bidInput:GetValue()) local buyout = private.ParseBidBuyout(buyoutInput:GetValue()) if input == buyoutInput and not TSM.IsWowClassic() and ItemInfo.IsCommodity(itemString) then -- update the bid to match bidInput:SetValue(Money.ToString(buyout, nil, "OPT_83_NO_COPPER", "OPT_DISABLE")) :Draw() elseif input == bidInput and private.ParseBidBuyout(input:GetValue()) > private.ParseBidBuyout(buyoutInput:GetValue()) then -- update the buyout to match buyoutInput:SetValue(Money.ToString(bid, nil, "OPT_83_NO_COPPER")) :Draw() end private.UpdateDepositCostAndPostButton(frame) end function private.BidBuyoutInputOnEnterPressed(input) local frame = input:GetElement("__parent.__parent") local bidInput = frame:GetElement("bid.input") local buyoutInput = frame:GetElement("buyout.input") local value = private.ParseBidBuyout(input:GetValue()) input:SetValue(Money.ToString(value, nil, "OPT_83_NO_COPPER")) input:Draw() if input == buyoutInput and private.ParseBidBuyout(buyoutInput:GetValue()) < private.ParseBidBuyout(bidInput:GetValue()) then -- update the bid to match bidInput:SetValue(Money.ToString(value, nil, "OPT_83_NO_COPPER")) :Draw() end private.UpdateDepositCostAndPostButton(frame) end function private.ItemBtnOnClick(button) button:GetElement("__parent.__parent.__parent"):SetPath("selection", true) end function private.StackNumInputOnValueChanged(input) local value = tonumber(input:GetValue()) assert(value) if value == private.postStack then return end private.postStack = value private.UpdateDepositCostAndPostButton(input:GetParentElement():GetParentElement()) end function private.QuantityInputOnValueChanged(input) local value = tonumber(input:GetValue()) if value == private.postQuantity then return end private.postQuantity = value private.UpdateDepositCostAndPostButton(input:GetParentElement():GetParentElement()) if private.perItem then return end local frame = input:GetElement("__parent.__parent") local postContext = frame:GetElement("__parent"):GetContext() local undercut = (not TSM.IsWowClassic() or PlayerInfo.IsPlayer(postContext.ownerStr, true, true, true)) and 0 or 1 local bidInput = frame:GetElement("bid.input") local buyoutInput = frame:GetElement("buyout.input") local stackSizeEdit = tonumber(frame:GetElement("quantity.input"):GetValue()) stackSizeEdit = tonumber(stackSizeEdit) local newBuyout, newBid = nil, nil if postContext.quantity == stackSizeEdit then newBuyout = postContext.buyout - undercut newBid = postContext.displayedBid - undercut else newBuyout = postContext.itemBuyout * stackSizeEdit - undercut newBid = floor(postContext.displayedBid / postContext.quantity) * stackSizeEdit - undercut end buyoutInput:SetValue(Money.ToString(newBuyout, nil, "OPT_83_NO_COPPER")) bidInput:SetValue(Money.ToString(newBid, nil, "OPT_83_NO_COPPER", ItemInfo.IsCommodity(private.itemString) and "OPT_DISABLE" or nil)) frame:Draw() end function private.GetBagQuantity(itemString, useSpecificItem) return BagTracking.CreateQueryBagsItemAuctionable(useSpecificItem and itemString or ItemString.GetBase(itemString)) :SumAndRelease("quantity") or 0 end function private.GetMaxPostStack(itemString) local numHave = private.GetBagQuantity(itemString, not TSM.IsWowClassic()) if TSM.IsWowClassic() then return min(ItemInfo.GetMaxStack(itemString), numHave) else return numHave end end function private.MaxStackNumBtnOnClick(button) button:GetElement("__parent.__parent.quantity.input"):SetFocused(false) button:GetElement("__parent.input"):SetFocused(false) local itemString = button:GetElement("__parent.__parent.confirmBtn"):GetContext() local stackSize = tonumber(button:GetElement("__parent.__parent.quantity.input"):GetValue()) local num = min(floor(private.GetBagQuantity(itemString) / stackSize), 5000) if num == 0 then return end button:GetElement("__parent.input") :SetValue(num) :Draw() private.StackNumInputOnValueChanged(button:GetElement("__parent.input")) end function private.MaxQuantityBtnOnClick(button) if TSM.IsWowClassic() then button:GetElement("__parent.__parent.numStacks.input"):SetFocused(false) end button:GetElement("__parent.input"):SetFocused(false) local itemString = button:GetElement("__parent.__parent.confirmBtn"):GetContext() local numHave = private.GetBagQuantity(itemString) local stackSize = min(private.GetMaxPostStack(itemString), 5000) assert(stackSize > 0) button:GetElement("__parent.input") :SetValue(stackSize) :Draw() if TSM.IsWowClassic() then local numStacks = tonumber(button:GetElement("__parent.__parent.numStacks.input"):GetValue()) local newStackSize = min(floor(numHave / stackSize), 5000) if numStacks > newStackSize then button:GetElement("__parent.__parent.numStacks.input") :SetValue(newStackSize) :Draw() private.StackNumInputOnValueChanged(button:GetElement("__parent.__parent.numStacks.input")) end end private.QuantityInputOnValueChanged(button:GetElement("__parent.input")) end function private.DurationOnValueChanged(toggle) private.UpdateDepositCostAndPostButton(toggle:GetParentElement():GetParentElement()) end function private.UpdateDepositCostAndPostButton(frame) local itemString = frame:GetElement("confirmBtn"):GetContext() if not itemString then return end local bidInput = frame:GetElement("bid.input") local buyoutInput = frame:GetElement("buyout.input") local bid = private.ParseBidBuyout(bidInput:GetValue()) local buyout = private.ParseBidBuyout(buyoutInput:GetValue()) local stackSize = tonumber(frame:GetElement("quantity.input"):GetValue()) local numAuctions = TSM.IsWowClassic() and tonumber(frame:GetElement("numStacks.input"):GetValue()) or 1 if bid > buyout or not bidInput:IsValid() or not buyoutInput:IsValid() or (stackSize * numAuctions) > private.GetBagQuantity(itemString) then frame:GetElement("deposit.text") :SetText(Money.ToString(0, nil, "OPT_83_NO_COPPER")) :Draw() frame:GetElement("confirmBtn") :SetDisabled(true) :Draw() return end local postBag, postSlot = BagTracking.CreateQueryBagsAuctionable() :OrderBy("slotId", true) :Select("bag", "slot") :Equal("itemString", itemString) :GetFirstResultAndRelease() if not postBag or not postSlot then frame:GetElement("deposit.text") :SetText(Money.ToString(0, nil, "OPT_83_NO_COPPER")) :Draw() frame:GetElement("confirmBtn") :SetDisabled(true) :Draw() return end private.postTimeStr = frame:GetElement("duration.toggle"):GetValue() local postTime = Table.GetDistinctKey(TSM.CONST.AUCTION_DURATIONS, private.postTimeStr) local depositCost = nil if not TSM.IsWowClassic() then local isCommodity = ItemInfo.IsCommodity(itemString) depositCost = max(floor(0.15 * (ItemInfo.GetVendorSell(itemString) or 0) * (isCommodity and stackSize or 1) * (postTime == 3 and 4 or postTime)), 100) * (isCommodity and 1 or stackSize) else if private.perItem then bid = bid * stackSize buyout = buyout * stackSize end ClearCursor() PickupContainerItem(postBag, postSlot) ClickAuctionSellItemButton(AuctionsItemButton, "LeftButton") ClearCursor() depositCost = GetAuctionDeposit(postTime, bid, buyout, stackSize, numAuctions) ClearCursor() ClickAuctionSellItemButton(AuctionsItemButton, "LeftButton") ClearCursor() end local noMoney = depositCost > GetMoney() frame:GetElement("deposit.text") :SetText(Money.ToString(depositCost)) :Draw() frame:GetElement("confirmBtn") :SetText(noMoney and L["Not Enough Money"] or L["Post Auction"]) :SetDisabled(noMoney) :Draw() end function private.PostButtonOnClick(button) local frame = button:GetParentElement() local stackSize = tonumber(frame:GetElement("quantity.input"):GetValue()) local bid = private.ParseBidBuyout(frame:GetElement("bid.input"):GetValue()) local buyout = private.ParseBidBuyout(frame:GetElement("buyout.input"):GetValue()) local itemString = button:GetContext() local postBag, postSlot = BagTracking.CreateQueryBagsAuctionable() :OrderBy("slotId", true) :Select("bag", "slot") :Equal("itemString", itemString) :GetFirstResultAndRelease() if postBag and postSlot then local postTime = Table.GetDistinctKey(TSM.CONST.AUCTION_DURATIONS, frame:GetElement("duration.toggle"):GetValue()) if not TSM.IsWowClassic() then bid = Math.Round(bid, COPPER_PER_SILVER) buyout = Math.Round(buyout, COPPER_PER_SILVER) private.itemLocation:Clear() private.itemLocation:SetBagAndSlot(postBag, postSlot) local commodityStatus = C_AuctionHouse.GetItemCommodityStatus(private.itemLocation) local future = nil if commodityStatus == Enum.ItemCommodityStatus.Item then future = AuctionHouseWrapper.PostItem(private.itemLocation, postTime, stackSize, bid < buyout and bid or nil, buyout) elseif commodityStatus == Enum.ItemCommodityStatus.Commodity then future = AuctionHouseWrapper.PostCommodity(private.itemLocation, postTime, stackSize, buyout) else error("Unknown commodity status: "..tostring(itemString)) end if future then -- TODO: wait for the future future:Cancel() AuctionTracking.QueryOwnedAuctions() end else local num = tonumber(frame:GetElement("numStacks.input"):GetValue()) if strfind(button:GetContext(), "^p") then stackSize = 1 num = 1 end if private.perItem then bid = bid * stackSize buyout = buyout * stackSize end -- need to set the duration in the default UI to avoid Blizzard errors AuctionFrameAuctions.duration = postTime ClearCursor() PickupContainerItem(postBag, postSlot) ClickAuctionSellItemButton(AuctionsItemButton, "LeftButton") PostAuction(bid, buyout, postTime, stackSize, num) ClearCursor() end end frame:GetBaseElement():HideDialog() end function private.PostDialogCloseBtnOnClick(button) button:GetBaseElement():HideDialog() end function private.ScanFilterInputOnEnterPressed(input) local filter = input:GetValue() if TSM.IsWowClassic() and filter == "" then return end local viewContainer = input:GetParentElement():GetParentElement():GetParentElement() viewContainer:SetPath("selection") private.StartFilterSearchHelper(viewContainer, filter) end function private.RescanBtnOnClick(button) if not TSM.UI.AuctionUI.StartingScan(L["Browse"]) then return end private.fsm:ProcessEvent("EV_RESCAN_CLICKED") end -- ============================================================================ -- FSM -- ============================================================================ function private.FSMCreate() local fsmContext = { scanFrame = nil, auctionScan = nil, progress = 0, progressText = L["Starting Scan..."], progressPaused = false, postDisabled = true, bidDisabled = true, buyoutDisabled = true, cancelShown = false, findHash = nil, findAuction = nil, findResult = nil, numFound = 0, maxQuantity = 0, defaultBuyQuantity = 0, numBought = 0, lastBuyQuantity = 0, numBid = 0, numConfirmed = 0, searchContext = nil, postContextTemp = {}, pausePending = nil, cancelFuture = nil, } Event.Register("CHAT_MSG_SYSTEM", private.FSMMessageEventHandler) Event.Register("UI_ERROR_MESSAGE", private.FSMMessageEventHandler) if not TSM.IsWowClassic() then Event.Register("COMMODITY_PURCHASE_SUCCEEDED", private.FSMBuyoutSuccess) end Event.Register("AUCTION_HOUSE_CLOSED", function() private.fsm:ProcessEvent("EV_AUCTION_HOUSE_CLOSED") end) Event.Register("BAG_UPDATE_DELAYED", function() private.fsm:ProcessEvent("EV_BAG_UPDATE_DELAYED") end) AuctionHouseWrapper.RegisterAuctionIdUpdateCallback(function(...) private.fsm:ProcessEvent("EV_AUCTION_ID_UPDATE", ...) end) local function UpdateScanFrame(context) if not context.scanFrame then return end local isCanceling = context.cancelFuture and true or false local bottom = context.scanFrame:GetElement("bottom") bottom:GetElement("postBtn"):SetDisabled(isCanceling or context.postDisabled) bottom:GetElement("bidBtn"):SetDisabled(isCanceling or context.bidDisabled) bottom:GetElement("buyoutBtn"):SetDisabled(isCanceling or context.buyoutDisabled) if context.cancelShown then assert(context.buyoutDisabled) bottom:GetElement("buyoutBtn"):Hide() bottom:GetElement("cancelBtn") :SetDisabled(isCanceling) :Show() else bottom:GetElement("buyoutBtn"):Show() bottom:GetElement("cancelBtn") :SetDisabled(true) :Hide() end local progress, isPaused = context.auctionScan:GetProgress() bottom:GetElement("pauseResumeBtn") :SetDisabled((not isPaused and progress == 1) or context.pausePending ~= nil) :SetHighlightLocked(context.pausePending ~= nil) bottom:GetElement("progressBar"):SetProgress(context.progress) :SetText(isCanceling and L["Cancelling..."] or context.progressText or "") :SetProgressIconHidden(context.progress == 1 or (context.findResult and context.numBought + context.numBid == context.numConfirmed) or context.progressPaused) local auctionList = context.scanFrame:GetElement("auctions") :SetContext(context.auctionScan) :SetAuctionScan(context.auctionScan) :SetMarketValueFunction(context.searchContext:GetMarketValueFunc()) :SetSelectionDisabled(context.numBought + context.numBid ~= context.numConfirmed) :SetPctTooltip(context.searchContext:GetPctTooltip()) if context.findAuction and not auctionList:GetSelection() then auctionList:SetSelection(context.findAuction) end context.scanFrame:Draw() end private.fsm = FSM.New("SHOPPING") :AddState(FSM.NewState("ST_INIT") :SetOnEnter(function(context, searchContext) private.hasLastScan = false if context.searchContext then context.searchContext:KillThread() context.searchContext:OnStateChanged("DONE") context.searchContext = nil end if context.cancelFuture then context.cancelFuture:Cancel() context.cancelFuture = nil end context.progress = 0 context.progressText = L["Starting Scan..."] context.progressPaused = false context.pausePending = nil context.postDisabled = true context.bidDisabled = true context.buyoutDisabled = true context.cancelShown = false context.findHash = nil context.findAuction = nil context.findResult = nil context.numFound = 0 context.maxQuantity = 0 context.defaultBuyQuantity = 0 context.numBought = 0 context.lastBuyQuantity = 0 context.numBid = 0 context.numConfirmed = 0 if context.auctionScan then context.auctionScan:Release() context.auctionScan = nil end if searchContext then return "ST_STARTING_SCAN", searchContext elseif context.scanFrame then context.scanFrame:GetParentElement():SetPath("selection", true) context.scanFrame = nil end TSM.UI.AuctionUI.EndedScan(L["Browse"]) end) :AddTransition("ST_INIT") :AddTransition("ST_STARTING_SCAN") ) :AddState(FSM.NewState("ST_STARTING_SCAN") :SetOnEnter(function(context, searchContext) context.searchContext = searchContext private.hasLastScan = true context.auctionScan = AuctionScan.GetManager() :SetResolveSellers(true) :SetScript("OnProgressUpdate", private.FSMAuctionScanOnProgressUpdate) UpdateScanFrame(context) context.searchContext:StartThread(private.FSMScanCallback, context.auctionScan) context.searchContext:OnStateChanged("SCANNING") return "ST_SCANNING" end) :AddTransition("ST_SCANNING") ) :AddState(FSM.NewState("ST_SCANNING") :SetOnEnter(function(context) UpdateScanFrame(context) local selection = context.scanFrame and context.scanFrame:GetElement("auctions"):GetSelection() if context.pausePending == nil and selection and selection:IsSubRow() then -- pause the scan so the selected auction can be bought context.pausePending = true context.auctionScan:SetPaused(true) return "ST_UPDATING_SCAN_PROGRESS" end end) :AddTransition("ST_UPDATING_SCAN_PROGRESS") :AddTransition("ST_RESULTS") :AddTransition("ST_INIT") :AddEventTransition("EV_SCAN_PROGRESS_UPDATE", "ST_UPDATING_SCAN_PROGRESS") :AddEvent("EV_SCAN_COMPLETE", function(context) TSM.UI.AuctionUI.EndedScan(L["Browse"]) if context.scanFrame then context.scanFrame:GetElement("auctions"):ExpandSingleResult() end context.searchContext:OnStateChanged("RESULTS") return "ST_RESULTS" end) :AddEvent("EV_SCAN_FAILED", function(context) context.searchContext:OnStateChanged("RESULTS") return "ST_RESULTS" end) :AddEvent("EV_RESCAN_CLICKED", function(context) if context.scanFrame then local viewContainer = context.scanFrame:GetParentElement() viewContainer:SetPath("selection", true) viewContainer:SetPath("scan", true) context.scanFrame = viewContainer:GetElement("scan") local name = context.searchContext:GetName() assert(name) context.scanFrame:GetElement("searchFrame.filterInput") :SetValue(name) context.scanFrame:GetElement("searchFrame.rescanBtn") :SetDisabled(name == L["Gathering Search"]) end return "ST_INIT", context.searchContext end) :AddEvent("EV_PAUSE_RESUME_CLICKED", function(context) assert(context.pausePending == nil) context.pausePending = true context.auctionScan:SetPaused(true) return "ST_UPDATING_SCAN_PROGRESS" end) :AddEvent("EV_AUCTION_SELECTION_CHANGED", function(context) if context.pausePending ~= nil then return end local selection = context.scanFrame:GetElement("auctions"):GetSelection() if selection and selection:IsSubRow() then -- pause the scan so the selected auction can be bought assert(context.pausePending == nil) context.pausePending = true context.auctionScan:SetPaused(true) return "ST_UPDATING_SCAN_PROGRESS" end end) ) :AddState(FSM.NewState("ST_UPDATING_SCAN_PROGRESS") :SetOnEnter(function(context) local progress, isPaused = context.auctionScan:GetProgress() local text, progressPaused = nil, false if context.pausePending ~= nil and context.pausePending == isPaused then context.pausePending = nil end if context.pausePending == true then text = L["Pausing Scan..."] elseif context.pausePending == false then text = L["Resuming Scan..."] elseif isPaused then text = L["Scan Paused"] progressPaused = true elseif progress == 1 then text = L["Done Scanning"] else local numItems = context.auctionScan:GetNumItems() text = numItems and format(L["Scanning (%d Items)"], numItems) or L["Scanning"] end context.progress = progress context.progressText = text context.progressPaused = progressPaused UpdateScanFrame(context) if isPaused then return "ST_RESULTS" else return "ST_SCANNING" end end) :AddTransition("ST_SCANNING") :AddTransition("ST_RESULTS") ) :AddState(FSM.NewState("ST_RESULTS") :SetOnEnter(function(context, didBuy) TSM.UI.AuctionUI.EndedScan(L["Browse"]) local _, isPaused = context.auctionScan:GetProgress() if not isPaused then context.searchContext:KillThread() context.progress = 1 end context.progressText = isPaused and L["Scan Paused"] or L["Done Scanning"] context.progressPaused = isPaused context.findAuction = nil context.findResult = nil context.numFound = 0 context.defaultBuyQuantity = 0 context.numBought = 0 context.lastBuyQuantity = 0 context.numBid = 0 context.numConfirmed = 0 local postContext = context.searchContext:GetPostContext() if postContext then for _, query in context.auctionScan:QueryIterator() do for _, subRow in query:ItemSubRowIterator(postContext.itemString) do local buyout, itemBuyout = subRow:GetBuyouts() if itemBuyout > 0 and itemBuyout < postContext.itemBuyout then postContext.ownerStr = subRow:GetOwnerInfo() local _, _, currentBid = subRow:GetBidInfo() postContext.currentBid = currentBid postContext.displayedBid, postContext.itemDisplayedBid = subRow:GetDisplayedBids() postContext.buyout = buyout postContext.itemBuyout = itemBuyout end end end end context.postDisabled = not postContext or private.GetBagQuantity(postContext.itemString) == 0 context.bidDisabled = true context.buyoutDisabled = true context.cancelShown = false UpdateScanFrame(context) local selection = context.scanFrame and context.scanFrame:GetElement("auctions"):GetSelection() or nil if selection and selection:IsSubRow() then if TSM.UI.AuctionUI.StartingScan(L["Browse"]) then return "ST_FINDING_AUCTION" end elseif selection and isPaused and context.pausePending == nil then -- resume the scan to search for the item context.pausePending = false context.auctionScan:SetPaused(false) return "ST_UPDATING_SCAN_PROGRESS" elseif didBuy and not selection and isPaused and context.pausePending == nil then -- we bought something and should now resume the scan context.pausePending = false context.auctionScan:SetPaused(false) return "ST_UPDATING_SCAN_PROGRESS" end end) :AddTransition("ST_UPDATING_SCAN_PROGRESS") :AddTransition("ST_FINDING_AUCTION") :AddTransition("ST_INIT") :AddEventTransition("EV_SCAN_PROGRESS_UPDATE", "ST_UPDATING_SCAN_PROGRESS") :AddEvent("EV_AUCTION_SELECTION_CHANGED", function(context) assert(context.scanFrame) local selection = context.scanFrame:GetElement("auctions"):GetSelection() if not selection then return end if selection:IsSubRow() then if TSM.UI.AuctionUI.StartingScan(L["Browse"]) then -- find the auction return "ST_FINDING_AUCTION" end elseif select(2, context.auctionScan:GetProgress()) then -- resume the scan to search for the item assert(context.pausePending == nil) context.pausePending = false context.auctionScan:SetPaused(false) return "ST_UPDATING_SCAN_PROGRESS" end end) :AddEvent("EV_POST_BUTTON_CLICK", function(context) local postContext = nil local selection = context.scanFrame:GetElement("auctions"):GetSelection() if selection then wipe(context.postContextTemp) private.PopulatePostContextFromRow(context.postContextTemp, selection) postContext = context.postContextTemp else postContext = context.searchContext:GetPostContext() end private.PostDialogShow(context.scanFrame:GetBaseElement(), postContext) end) :AddEvent("EV_BAG_UPDATE_DELAYED", function(context) if not context.scanFrame then return end local postContext = context.searchContext:GetPostContext() context.postDisabled = not postContext or private.GetBagQuantity(postContext.itemString) == 0 context.scanFrame:GetElement("bottom.postBtn") :SetDisabled(context.postDisabled) :Draw() end) :AddEvent("EV_RESCAN_CLICKED", function(context) if context.scanFrame then local viewContainer = context.scanFrame:GetParentElement() viewContainer:SetPath("selection", true) viewContainer:SetPath("scan", true) context.scanFrame = viewContainer:GetElement("scan") local name = context.searchContext:GetName() assert(name) context.scanFrame:GetElement("searchFrame.filterInput") :SetValue(name) context.scanFrame:GetElement("searchFrame.rescanBtn") :SetDisabled(name == L["Gathering Search"]) end return "ST_INIT", context.searchContext end) :AddEvent("EV_PAUSE_RESUME_CLICKED", function(context) assert(context.pausePending == nil) context.pausePending = false context.auctionScan:SetPaused(false) return "ST_UPDATING_SCAN_PROGRESS" end) ) :AddState(FSM.NewState("ST_FINDING_AUCTION") :SetOnEnter(function(context) assert(context.scanFrame) context.findAuction = context.scanFrame:GetElement("auctions"):GetSelection() assert(context.findAuction and context.findAuction:IsSubRow()) context.findHash = context.findAuction:GetHashes() context.progress = 0 context.progressText = L["Finding Selected Auction"] context.progressPaused = false context.postDisabled = true context.bidDisabled = true context.buyoutDisabled = true context.cancelShown = false UpdateScanFrame(context) TSM.Shopping.SearchCommon.StartFindAuction(context.auctionScan, context.findAuction, private.FSMFindAuctionCallback, false) end) :SetOnExit(function(context) context.auctionScan:Cancel() TSM.Shopping.SearchCommon.StopFindAuction(true) end) :AddTransition("ST_FINDING_AUCTION") :AddTransition("ST_RESULTS") :AddTransition("ST_UPDATING_SCAN_PROGRESS") :AddTransition("ST_AUCTION_FOUND") :AddTransition("ST_AUCTION_NOT_FOUND") :AddTransition("ST_INIT") :AddEventTransition("EV_AUCTION_FOUND", "ST_AUCTION_FOUND") :AddEventTransition("EV_AUCTION_NOT_FOUND", "ST_AUCTION_NOT_FOUND") :AddEvent("EV_AUCTION_SELECTION_CHANGED", function(context) assert(context.scanFrame) local selection = context.scanFrame:GetElement("auctions"):GetSelection() if not selection then return end if selection:IsSubRow() then if TSM.UI.AuctionUI.StartingScan(L["Browse"]) then return "ST_FINDING_AUCTION" end elseif select(2, context.auctionScan:GetProgress()) then -- resume the scan to search for the item assert(context.pausePending == nil) context.pausePending = false context.auctionScan:SetPaused(false) return "ST_UPDATING_SCAN_PROGRESS" else return "ST_RESULTS" end end) :AddEvent("EV_POST_BUTTON_CLICK", function(context) wipe(context.postContextTemp) private.PopulatePostContextFromRow(context.postContextTemp, context.scanFrame:GetElement("auctions"):GetSelection()) private.PostDialogShow(context.scanFrame:GetBaseElement(), context.postContextTemp) end) :AddEvent("EV_RESCAN_CLICKED", function(context) if context.scanFrame then local viewContainer = context.scanFrame:GetParentElement() viewContainer:SetPath("selection", true) viewContainer:SetPath("scan", true) context.scanFrame = viewContainer:GetElement("scan") local name = context.searchContext:GetName() assert(name) context.scanFrame:GetElement("searchFrame.filterInput") :SetValue(name) context.scanFrame:GetElement("searchFrame.rescanBtn") :SetDisabled(name == L["Gathering Search"]) end return "ST_INIT", context.searchContext end) :AddEvent("EV_SCAN_FRAME_HIDDEN", function(context) context.scanFrame = nil context.findAuction = nil return "ST_RESULTS" end) :AddEvent("EV_PAUSE_RESUME_CLICKED", function(context) context.findAuction = nil context.scanFrame:GetElement("auctions"):SetSelection(nil) return "ST_RESULTS" end) ) :AddState(FSM.NewState("ST_AUCTION_FOUND") :SetOnEnter(function(context, result) TSM.UI.AuctionUI.EndedScan(L["Browse"]) local selection = context.scanFrame:GetElement("auctions"):GetSelection() -- update the selection in case the result rows changed if context.findHash == selection:GetHashes() then context.findAuction = selection end local itemString = context.findAuction:GetItemString() local maxQuantity = context.searchContext:GetMaxCanBuy(itemString) if TSM.IsWowClassic() then if maxQuantity then maxQuantity = maxQuantity / context.findAuction:GetQuantities() end context.findResult = result context.numFound = min(#result, maxQuantity or math.huge) context.maxQuantity = maxQuantity or 1 context.defaultBuyQuantity = context.numFound else local maxCommodity = context.findAuction:IsCommodity() and context.findAuction:GetResultRow():GetMaxQuantities() local numCanBuy = min(maxCommodity or result, maxQuantity or math.huge) context.findResult = numCanBuy > 0 context.numFound = numCanBuy context.maxQuantity = maxCommodity or 1 context.defaultBuyQuantity = maxQuantity and min(numCanBuy, maxQuantity) or 1 end assert(context.numBought == 0 and context.numBid == 0 and context.numConfirmed == 0) return "ST_BUYING" end) :AddTransition("ST_BUYING") ) :AddState(FSM.NewState("ST_AUCTION_NOT_FOUND") :SetOnEnter(function(context) context.scanFrame:GetBaseElement():HideDialog() TSM.UI.AuctionUI.EndedScan(L["Browse"]) local selection = context.scanFrame:GetElement("auctions"):GetSelection() if not selection or not selection:IsSubRow() then return "ST_RESULTS" end -- update the selection in case the result rows changed if context.findHash == selection:GetHashes() then context.findAuction = selection end local _, rawLink = context.findAuction:GetLinks() context.findAuction:GetResultRow():RemoveSubRow(context.findAuction) context.scanFrame:GetElement("auctions"):UpdateData() Log.PrintfUser(L["Failed to find auction for %s, so removing it from the results."], rawLink) return "ST_RESULTS" end) :AddTransition("ST_RESULTS") ) :AddState(FSM.NewState("ST_BUYING") :SetOnEnter(function(context, numToRemove) if numToRemove then -- remove the one we just bought local itemString = context.findAuction:GetItemString() assert(itemString) context.findAuction:DecrementQuantity(numToRemove) context.searchContext:OnBuy(itemString, context.lastBuyQuantity) context.scanFrame:GetElement("auctions"):UpdateData() context.findAuction = context.scanFrame and context.scanFrame:GetElement("auctions"):GetSelection() if context.findAuction and not context.findAuction:IsSubRow() then context.findAuction = nil else local maxQuantity = context.searchContext:GetMaxCanBuy(itemString) if maxQuantity then if TSM.IsWowClassic() and context.findAuction then maxQuantity = maxQuantity / context.findAuction:GetQuantities() end context.defaultBuyQuantity = min(context.defaultBuyQuantity, maxQuantity) end end end local selection = context.scanFrame and context.scanFrame:GetElement("auctions"):GetSelection() if selection and not selection:IsSubRow() then selection = nil end local itemString, isPlayer = nil, false if selection then assert(selection:IsSubRow()) itemString = selection:GetItemString() local ownerStr = selection and selection:GetOwnerInfo() or nil isPlayer = PlayerInfo.IsPlayer(ownerStr, true, true, true) end local auctionSelected = selection and context.findHash == selection:GetHashes() local numCanBuy = not auctionSelected and 0 or (context.numFound - context.numBought - context.numBid) local numConfirming = context.numBought + context.numBid - context.numConfirmed local canPost = selection and private.GetBagQuantity(itemString) > 0 and numConfirming == 0 local progressText = nil if numConfirming == 0 and (numCanBuy == 0 and (not isPlayer or context.scanFrame:GetElement("auctions"):GetSelection() ~= context.findAuction)) then -- we're done buying and confirming this batch return "ST_RESULTS", true elseif isPlayer and canPost then progressText = TSM.IsWowClassic() and L["Post"] or L["Cancel or Post"] elseif isPlayer then progressText = TSM.IsWowClassic() and L["Select an Auction to Buy"] or L["Cancel Auction"] elseif numConfirming == 0 then -- we can still buy more progressText = format(isPlayer and not TSM.IsWowClassic() and L["Cancel %d / %d"] or L["Buy %d / %d"], context.numBought + context.numBid + 1, context.numFound) elseif numCanBuy == 0 then -- we're just confirming progressText = format(L["Confirming %d / %d"], context.numConfirmed + 1, context.numFound) else -- we can buy more while confirming progressText = format(L["Buy %d / %d (Confirming %d / %d)"], context.numBought + context.numBid + 1, context.numFound, context.numConfirmed + 1, context.numFound) end local _, isPaused = context.auctionScan:GetProgress() if isPaused then progressText = L["Scan Paused"].." | "..progressText end context.progress = context.numConfirmed / context.numFound context.progressText = progressText context.progressPaused = false context.postDisabled = not canPost if numCanBuy == 0 or isPlayer or (not TSM.IsWowClassic() and numConfirming > 0) then context.bidDisabled = true context.buyoutDisabled = true context.cancelShown = isPlayer and not TSM.IsWowClassic() if context.cancelShown then AuctionTracking.QueryOwnedAuctions() end else context.bidDisabled = not selection or not AuctionScan.CanBid(selection) context.buyoutDisabled = not selection or not AuctionScan.CanBuyout(selection, context.auctionScan) context.cancelShown = false end UpdateScanFrame(context) end) :AddTransition("ST_BUYING") :AddTransition("ST_UPDATING_SCAN_PROGRESS") :AddTransition("ST_BUY_CONFIRMATION") :AddTransition("ST_BID_CONFIRMATION") :AddTransition("ST_CANCELING") :AddTransition("ST_PLACING_BUY") :AddTransition("ST_PLACING_BID") :AddTransition("ST_CONFIRMING_BUY") :AddTransition("ST_RESULTS") :AddTransition("ST_INIT") :AddEventTransition("EV_AUCTION_SELECTION_CHANGED", "ST_BUYING") :AddEventTransition("EV_BUYOUT_CLICKED", "ST_BUY_CONFIRMATION") :AddEventTransition("EV_BID_CLICKED", "ST_BID_CONFIRMATION") :AddEventTransition("EV_CANCEL_CLICKED", "ST_CANCELING") :AddEvent("EV_CONFIRMED", function(context, isBuy, quantity) return isBuy and "ST_PLACING_BUY" or "ST_PLACING_BID", quantity end) :AddEvent("EV_MSG", function(context, msg) if not context.findAuction then return end local _, rawLink = context.findAuction:GetLinks() if msg == LE_GAME_ERR_AUCTION_HIGHER_BID or msg == LE_GAME_ERR_ITEM_NOT_FOUND or msg == LE_GAME_ERR_AUCTION_BID_OWN or msg == LE_GAME_ERR_NOT_ENOUGH_MONEY or msg == LE_GAME_ERR_ITEM_MAX_COUNT then -- failed to buy an auction return "ST_CONFIRMING_BUY", false elseif msg == format(ERR_AUCTION_WON_S, ItemInfo.GetName(rawLink)) or (context.numBid > 0 and msg == ERR_AUCTION_BID_PLACED) then -- bought an auction return "ST_CONFIRMING_BUY", true end end) :AddEvent("EV_BUYOUT_SUCCESS", function(context) if not context.findAuction then return end return "ST_CONFIRMING_BUY", true end) :AddEvent("EV_POST_BUTTON_CLICK", function(context) wipe(context.postContextTemp) private.PopulatePostContextFromRow(context.postContextTemp, context.scanFrame:GetElement("auctions"):GetSelection()) private.PostDialogShow(context.scanFrame:GetBaseElement(), context.postContextTemp) end) :AddEvent("EV_RESCAN_CLICKED", function(context) if context.scanFrame then local viewContainer = context.scanFrame:GetParentElement() viewContainer:SetPath("selection", true) viewContainer:SetPath("scan", true) context.scanFrame = viewContainer:GetElement("scan") local name = context.searchContext:GetName() assert(name) context.scanFrame:GetElement("searchFrame.filterInput") :SetValue(name) context.scanFrame:GetElement("searchFrame.rescanBtn") :SetDisabled(name == L["Gathering Search"]) end return "ST_INIT", context.searchContext end) :AddEvent("EV_SCAN_FRAME_HIDDEN", function(context) context.scanFrame = nil context.findAuction = nil return "ST_RESULTS" end) :AddEvent("EV_PAUSE_RESUME_CLICKED", function(context) context.scanFrame:GetElement("auctions"):SetSelection(nil) context.findAuction = nil assert(context.pausePending == nil) context.pausePending = false context.auctionScan:SetPaused(false) return "ST_UPDATING_SCAN_PROGRESS" end) :AddEvent("EV_AUCTION_ID_UPDATE", function(context, oldAuctionId, newAuctionId, newResultInfo) if not context.findAuction or select(2, context.findAuction:GetListingInfo()) ~= oldAuctionId then return end context.findAuction:UpdateResultInfo(newAuctionId, newResultInfo) context.findHash = context.findAuction:GetHashes() end) :AddEvent("EV_BAG_UPDATE_DELAYED", function(context) local postContext = context.searchContext:GetPostContext() local prevDisabled = context.postDisabled context.postDisabled = not postContext or private.GetBagQuantity(postContext.itemString) == 0 context.scanFrame:GetElement("bottom.postBtn") :SetDisabled(context.postDisabled) :Draw() if not prevDisabled and context.postDisabled then -- hide any visible dialog in case the post dialog is visible context.scanFrame:GetBaseElement():HideDialog() end end) ) :AddState(FSM.NewState("ST_BUY_CONFIRMATION") :SetOnEnter(function(context) local selection = context.scanFrame:GetElement("auctions"):GetSelection() local index = TSM.IsWowClassic() and context.findResult[#context.findResult] or nil if TSM.UI.AuctionUI.BuyUtil.ShowConfirmation(context.scanFrame, selection, true, context.numConfirmed + 1, context.defaultBuyQuantity, context.maxQuantity, private.FSMConfirmationCallback, context.auctionScan, index, false, context.searchContext:GetMarketValueFunc()) then return "ST_BUYING" else return "ST_PLACING_BUY", selection:GetQuantities() end end) :AddTransition("ST_PLACING_BUY") :AddTransition("ST_BUYING") ) :AddState(FSM.NewState("ST_BID_CONFIRMATION") :SetOnEnter(function(context) local selection = context.scanFrame:GetElement("auctions"):GetSelection() local index = TSM.IsWowClassic() and context.findResult[#context.findResult] or nil if TSM.UI.AuctionUI.BuyUtil.ShowConfirmation(context.scanFrame, selection, false, context.numConfirmed + 1, context.defaultBuyQuantity, context.maxQuantity, private.FSMConfirmationCallback, context.auctionScan, index, false, context.searchContext:GetMarketValueFunc()) then return "ST_BUYING" else local quantity = selection:GetQuantities() return "ST_PLACING_BID", quantity end end) :AddTransition("ST_PLACING_BID") :AddTransition("ST_BUYING") ) :AddState(FSM.NewState("ST_PLACING_BUY") :SetOnEnter(function(context, quantity) local index = TSM.IsWowClassic() and tremove(context.findResult, #context.findResult) or nil assert(not TSM.IsWowClassic() or index) -- buy the auction local buyout = context.findAuction:GetBuyouts() local result = context.auctionScan:PlaceBidOrBuyout(index, buyout, context.findAuction, quantity) if result then MailTracking.RecordAuctionBuyout(ItemString.GetBaseFast(context.findAuction:GetItemString()), quantity) context.numBought = context.numBought + (TSM.IsWowClassic() and 1 or quantity) context.lastBuyQuantity = quantity else local _, rawLink = context.findAuction:GetLinks() Log.PrintfUser(L["Failed to buy auction of %s."], rawLink) end return "ST_BUYING" end) :AddTransition("ST_BUYING") ) :AddState(FSM.NewState("ST_CONFIRMING_BUY") :SetOnEnter(function(context, success) if not success then local _, rawLink = context.findAuction:GetLinks() Log.PrintfUser(L["Failed to buy auction of %s."], rawLink) end context.numConfirmed = context.numConfirmed + (TSM.IsWowClassic() and 1 or context.lastBuyQuantity) return "ST_BUYING", context.lastBuyQuantity end) :AddTransition("ST_BUYING") ) :AddState(FSM.NewState("ST_PLACING_BID") :SetOnEnter(function(context, quantity) local index = TSM.IsWowClassic() and tremove(context.findResult, #context.findResult) or nil assert(not TSM.IsWowClassic() or index) -- bid on the auction local result, future = context.auctionScan:PrepareForBidOrBuyout(index, context.findAuction, false, quantity) assert(not future) result = result and context.auctionScan:PlaceBidOrBuyout(index, context.findAuction:GetRequiredBid(), context.findAuction, quantity) if result then MailTracking.RecordAuctionBuyout(ItemString.GetBaseFast(context.findAuction:GetItemString()), quantity) context.numBid = context.numBid + (TSM.IsWowClassic() and 1 or quantity) context.lastBuyQuantity = quantity else local _, rawLink = context.findAuction:GetLinks() Log.PrintfUser(L["Failed to bid on auction of %s."], rawLink) end return "ST_BUYING" end) :AddTransition("ST_BUYING") ) :AddState(FSM.NewState("ST_CANCELING") :SetOnEnter(function(context) assert(not TSM.IsWowClassic() and context.findAuction and context.findAuction:IsSubRow()) local _, auctionId = context.findAuction:GetListingInfo() Log.Info("Canceling (auctionId=%d)", auctionId) local future = AuctionHouseWrapper.CancelAuction(auctionId) if future then future:SetScript("OnDone", private.FSMCancelFutureOnDone) context.cancelFuture = future UpdateScanFrame(context) else Log.PrintUser(L["Failed to cancel auction due to the auction house being busy. Ensure no other addons are scanning the AH and try again."]) return "ST_BUYING" end end) :AddTransition("ST_BUYING") :AddTransition("ST_INIT") :AddEvent("EV_CANCEL_DONE", function(context) assert(context.cancelFuture) local result = context.cancelFuture:GetValue() context.cancelFuture = nil if result then context.findAuction:GetResultRow():RemoveSubRow(context.findAuction) context.scanFrame:GetElement("auctions"):UpdateData() else Log.PrintUser(L["Failed to cancel auction due to the auction house being busy. Ensure no other addons are scanning the AH and try again."]) end return "ST_BUYING" end) ) :AddDefaultEvent("EV_START_SCAN", function(context, searchContext) return "ST_INIT", searchContext end) :AddDefaultEvent("EV_SCAN_FRAME_SHOWN", function(context, scanFrame) context.scanFrame = scanFrame UpdateScanFrame(context) context.scanFrame:GetElement("auctions") :UpdateData(true) :ExpandSingleResult() end) :AddDefaultEvent("EV_SCAN_FRAME_HIDDEN", function(context) context.scanFrame = nil context.findAuction = nil end) :AddDefaultEventTransition("EV_AUCTION_HOUSE_CLOSED", "ST_INIT") :AddDefaultEventTransition("EV_SCAN_BACK_BUTTON_CLICKED", "ST_INIT") :Init("ST_INIT", fsmContext) end function private.FSMMessageEventHandler(_, msg) private.fsm:SetLoggingEnabled(false) private.fsm:ProcessEvent("EV_MSG", msg) private.fsm:SetLoggingEnabled(true) end function private.FSMBuyoutSuccess() private.fsm:ProcessEvent("EV_BUYOUT_SUCCESS") end function private.FSMAuctionScanOnProgressUpdate(auctionScan) private.fsm:ProcessEvent("EV_SCAN_PROGRESS_UPDATE") end function private.FSMScanCallback(success) if success then private.fsm:ProcessEvent("EV_SCAN_COMPLETE") else private.fsm:ProcessEvent("EV_SCAN_FAILED") end end function private.FSMFindAuctionCallback(result) if result then private.fsm:ProcessEvent("EV_AUCTION_FOUND", result) else private.fsm:ProcessEvent("EV_AUCTION_NOT_FOUND") end end function private.FSMConfirmationCallback(isBuy, quantity) private.fsm:ProcessEvent("EV_CONFIRMED", isBuy, quantity) end function private.PopulatePostContextFromRow(postContext, row) postContext.baseItemString = row:GetBaseItemString() postContext.itemString = row:GetItemString() postContext.ownerStr = row:GetOwnerInfo() local _, _, currentBid = row:GetBidInfo() postContext.currentBid = currentBid postContext.displayedBid, postContext.itemDisplayedBid = row:GetDisplayedBids() postContext.buyout, postContext.itemBuyout = row:GetBuyouts() postContext.quantity = row:GetQuantities() end function private.FSMCancelFutureOnDone() private.fsm:ProcessEvent("EV_CANCEL_DONE") end