-- ------------------------------------------------------------------------------ -- -- TradeSkillMaster -- -- https://tradeskillmaster.com -- -- All Rights Reserved - Detailed license information included with addon. -- -- ------------------------------------------------------------------------------ -- local _, TSM = ... local Auctioning = TSM.UI.AuctionUI:NewPackage("Auctioning") local L = TSM.Include("Locale").GetTable() local FSM = TSM.Include("Util.FSM") local Event = TSM.Include("Util.Event") local Table = TSM.Include("Util.Table") local Sound = TSM.Include("Util.Sound") local Money = TSM.Include("Util.Money") local Log = TSM.Include("Util.Log") local Theme = TSM.Include("Util.Theme") local ItemString = TSM.Include("Util.ItemString") local Threading = TSM.Include("Service.Threading") local ItemInfo = TSM.Include("Service.ItemInfo") local CustomPrice = TSM.Include("Service.CustomPrice") local BagTracking = TSM.Include("Service.BagTracking") local AuctionTracking = TSM.Include("Service.AuctionTracking") local AuctionScan = TSM.Include("Service.AuctionScan") local Settings = TSM.Include("Service.Settings") local UIElements = TSM.Include("UI.UIElements") local private = { settings = nil, contentPath = "selection", hasLastScan = false, fsm = nil, scanContext = {}, auctionScan = nil, groupSearch = "", selectionFrame = nil, logQuery = nil, perItem = true, canStartNewScan = false, } local SECONDS_PER_MIN = 60 local SECONDS_PER_HOUR = 60 * SECONDS_PER_MIN local SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR -- ============================================================================ -- Module Functions -- ============================================================================ function Auctioning.OnInitialize() private.settings = Settings.NewView() :AddKey("global", "auctionUIContext", "auctioningSelectionDividedContainer") :AddKey("global", "auctionUIContext", "auctioningSelectionVerticalDividedContainer") :AddKey("global", "auctionUIContext", "auctioningBagScrollingTable") :AddKey("global", "auctionUIContext", "auctioningLogScrollingTable") :AddKey("global", "auctionUIContext", "auctioningAuctionScrollingTable") :AddKey("global", "auctionUIContext", "auctioningTabGroup") :AddKey("char", "auctionUIContext", "auctioningGroupTree") :AddKey("global", "auctioningOptions", "scanCompleteSound") :AddKey("global", "auctioningOptions", "confirmCompleteSound") TSM.UI.AuctionUI.RegisterTopLevelPage(L["Auctioning"], private.GetAuctioningFrame, private.OnItemLinked) private.FSMCreate() end -- ============================================================================ -- Auctioning UI -- ============================================================================ function private.GetAuctioningFrame() TSM.UI.AnalyticsRecordPathChange("auction", "auctioning") if not private.hasLastScan then private.contentPath = "selection" end return UIElements.New("ViewContainer", "auctioning") :SetNavCallback(private.GetAuctioningContentFrame) :AddPath("selection") :AddPath("scan") :SetPath(private.contentPath) end function private.GetAuctioningContentFrame(_, path) private.contentPath = path if path == "selection" then return private.GetAuctioningSelectionFrame() elseif path == "scan" then return private.GetAuctioningScanFrame() else error("Unexpected path: "..tostring(path)) end end function private.GetAuctioningSelectionFrame() TSM.UI.AnalyticsRecordPathChange("auction", "auctioning", "selection") local frame = UIElements.New("DividedContainer", "selection") :SetSettingsContext(private.settings, "auctioningSelectionDividedContainer") :SetMinWidth(220, 250) :SetBackgroundColor("PRIMARY_BG") :SetLeftChild(UIElements.New("Frame", "groupSelection") :SetLayout("VERTICAL") :SetPadding(0, 0, 8, 0) :AddChild(UIElements.New("Frame", "title") :SetLayout("HORIZONTAL") :SetMargin(0, 0, 0, 8) :SetHeight(24) :AddChild(UIElements.New("Input", "search") :SetMargin(8, 8, 0, 0) :SetIconTexture("iconPack.18x18/Search") :SetClearButtonEnabled(true) :AllowItemInsert(true) :SetHintText(L["Search Groups"]) :SetValue(private.groupSearch) :SetScript("OnValueChanged", private.GroupSearchOnValueChanged) ) :AddChild(UIElements.New("Button", "expandAllBtn") :SetSize(24, 24) :SetMargin(0, 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) :SetMargin(0, 8, 0, 0) :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, "auctioningGroupTree") :SetQuery(TSM.Groups.CreateQuery(), "Auctioning") :SetSearchString(private.groupSearch) :SetScript("OnGroupSelectionChanged", private.GroupTreeOnGroupSelectionChanged) ) :AddChild(UIElements.New("Frame", "bottom") :SetLayout("VERTICAL") :SetHeight(74) :SetBackgroundColor("PRIMARY_BG_ALT") :AddChild(UIElements.New("Texture", "line") :SetHeight(2) :SetTexture("ACTIVE_BG") ) :AddChild(UIElements.New("ActionButton", "postScanBtn") :SetHeight(24) :SetMargin(8) :SetText(L["Run Post Scan"]) :SetScript("OnClick", private.RunPostButtonOnclick) ) :AddChild(UIElements.New("ActionButton", "cancelScanBtn") :SetHeight(24) :SetMargin(8, 8, 0, 8) :SetText(L["Run Cancel Scan"]) :SetScript("OnClick", private.RunCancelButtonOnclick) ) ) ) :SetRightChild(UIElements.New("DividedContainer", "content") :SetVertical() :SetMargin(0, 0, 6, 0) :SetSettingsContext(private.settings, "auctioningSelectionVerticalDividedContainer") :SetMinWidth(50, 100) :SetBackgroundColor("PRIMARY_BG") :SetTopChild(UIElements.New("Frame", "content") :SetLayout("VERTICAL") :AddChild(UIElements.New("TabGroup", "buttons") :SetNavCallback(private.GetScansElement) :SetSettingsContext(private.settings, "auctioningTabGroup") :AddPath(L["Recent Scans"]) :AddPath(L["Favorite Scans"]) ) ) :SetBottomChild(UIElements.New("Frame", "content") :SetLayout("VERTICAL") :AddChild(UIElements.New("Frame", "bottom") :SetLayout("VERTICAL") :SetHeight(37) :SetBackgroundColor("PRIMARY_BG_ALT") :AddChild(UIElements.New("Text", "label") :SetHeight(24) :SetMargin(4, 0, 6, 6) :SetFont("BODY_BODY1_BOLD") :SetText(L["Post Items from Bags"]) ) ) :AddChild(UIElements.New("SelectionScrollingTable", "bagScrollingTable") :SetSettingsContext(private.settings, "auctioningBagScrollingTable") :GetScrollingTableInfo() :NewColumn("item") :SetTitle(L["Item"]) :SetHeaderIndent(18) :SetFont("ITEM_BODY3") :SetJustifyH("LEFT") :SetIconSize(12) :SetTextInfo("autoBaseItemString", TSM.UI.GetColoredItemName) :SetIconInfo("itemTexture") :SetTooltipInfo("autoBaseItemString") :SetSortInfo("name") :DisableHiding() :Commit() :NewColumn("operation") :SetTitle(L["Auctioning Operation"]) :SetFont("BODY_BODY3_MEDIUM") :SetJustifyH("LEFT") :SetTextInfo("firstOperation", private.BagGetOperationText) :SetSortInfo("firstOperation") :Commit() :Commit() :SetQuery(TSM.Auctioning.PostScan.CreateBagsQuery()) :SetAutoReleaseQuery(true) :SetSelectionValidator(private.BagScrollingTableIsSelectionEnabled) :SetScript("OnSelectionChanged", private.BagOnSelectionChanged) ) :AddChild(UIElements.New("Texture", "line") :SetHeight(2) :SetTexture("ACTIVE_BG") ) :AddChild(UIElements.New("Frame", "button") :SetLayout("HORIZONTAL") :SetHeight(40) :SetPadding(8) :SetBackgroundColor("PRIMARY_BG_ALT") :AddChild(UIElements.New("ActionButton", "postSelected") :SetHeight(24) :SetMargin(0, 8, 0, 0) :SetDisabled(true) :SetText(L["Post Selected"]) :SetScript("OnClick", private.RunPostBagsButtonOnClick) ) :AddChild(UIElements.New("Button", "selectAll") :SetSize("AUTO", 20) :SetMargin(0, 8, 0, 0) :SetFont("BODY_BODY3_MEDIUM") :SetText(L["Select All"]) :SetScript("OnClick", private.SelectAllOnClick) ) :AddChild(UIElements.New("Texture", "line") :SetSize(2, 20) :SetMargin(0, 8, 0, 0) :SetTexture("ACTIVE_BG") ) :AddChild(UIElements.New("Button", "clearAll") :SetSize("AUTO", 20) :SetFont("BODY_BODY3_MEDIUM") :SetText(L["Clear All"]) :SetDisabled(true) :SetScript("OnClick", private.ClearAllOnClick) ) ) ) ) :SetScript("OnHide", private.SelectionOnHide) local noGroupSelected = frame:GetElement("groupSelection.groupTree"):IsSelectionCleared(true) frame:GetElement("groupSelection.bottom.postScanBtn"):SetDisabled(noGroupSelected) frame:GetElement("groupSelection.bottom.cancelScanBtn"):SetDisabled(noGroupSelected) private.selectionFrame = frame return frame end function private.GetScansElement(_, button) if button == L["Recent Scans"] then return UIElements.New("SearchList", "list") :SetQuery(TSM.Auctioning.SavedSearches.CreateRecentSearchesQuery()) :SetEditButtonHidden(true) :SetScript("OnFavoriteChanged", private.SearchListOnFavoriteChanged) :SetScript("OnDelete", private.SearchListOnDelete) :SetScript("OnRowClick", private.SearchListOnRowClick) elseif button == L["Favorite Scans"] then return UIElements.New("SearchList", "list") :SetQuery(TSM.Auctioning.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.GetAuctioningScanFrame() TSM.UI.AnalyticsRecordPathChange("auction", "auctioning", "scan") return UIElements.New("Frame", "scan") :SetLayout("VERTICAL") :SetBackgroundColor("PRIMARY_BG") :AddChild(UIElements.New("Frame", "header") :SetLayout("HORIZONTAL") :SetPadding(8) :SetHeight(92) :AddChild(UIElements.New("Frame", "back") :SetLayout("VERTICAL") :SetWidth(54) :SetHeight(76) :SetMargin(0, 8, 0, 0) :SetPadding(0, 0, 4, 0) :SetBackgroundColor("PRIMARY_BG_ALT", true) :AddChild(UIElements.New("Button", "backBtn") :SetMargin(0, 0, 2, 0) :SetSize(28, 28) :SetBackground("iconPack.24x24/Close/Default") :SetScript("OnEnter", private.ExitScanButtonOnEnter) :SetScript("OnLeave", private.ExitScanButtonOnLeave) :SetScript("OnClick", private.ExitScanButtonOnClick) ) :AddChild(UIElements.New("Text", "text") :SetHeight(18) :SetFont("BODY_BODY3_MEDIUM") :SetJustifyH("CENTER") :SetText(L["Exit"]) ) :AddChild(UIElements.New("Text", "text") :SetHeight(18) :SetFont("BODY_BODY3_MEDIUM") :SetJustifyH("CENTER") :SetText(L["Scan"]) ) :SetScript("OnEnter", private.ExitScanFrameOnEnter) :SetScript("OnLeave", private.ExitScanFrameOnLeave) :SetScript("OnMouseUp", private.ExitScanButtonOnClick) ) :AddChild(UIElements.New("Frame", "content") :SetLayout("HORIZONTAL") :SetPadding(8, 8, 4, 4) :SetBackgroundColor("PRIMARY_BG_ALT", true) :AddChild(UIElements.New("Frame", "item") :SetLayout("VERTICAL") :AddChild(UIElements.New("Frame", "content") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 0, 4) :AddChild(UIElements.New("Button", "icon") :SetSize(18, 18) ) :AddChild(UIElements.New("Button", "text") :SetMargin(4, 0, 0, 0) :SetFont("ITEM_BODY1") :SetJustifyH("LEFT") ) ) :AddChild(UIElements.New("Frame", "cost") :SetLayout("HORIZONTAL") :SetHeight(20) :AddChild(UIElements.New("Text", "desc") :SetWidth("AUTO") :SetFont("BODY_BODY3_MEDIUM") :SetText(L["Deposit Cost"]..":") ) :AddChild(UIElements.New("Text", "text") :SetMargin(4, 0, 0, 0) :SetFont("TABLE_TABLE1") ) ) :AddChild(UIElements.New("Frame", "operation") :SetLayout("HORIZONTAL") :SetHeight(20) :AddChild(UIElements.New("Text", "desc") :SetWidth("AUTO") :SetFont("BODY_BODY3_MEDIUM") :SetText(L["Operation"]..":") ) :AddChild(UIElements.New("Text", "text") :SetMargin(4, 0, 0, 0) :SetFont("BODY_BODY3_MEDIUM") ) ) ) :AddChild(UIElements.New("Frame", "details") :SetLayout("VERTICAL") :SetWidth(371) :SetMargin(16, 0, 0, 0) :AddChild(UIElements.New("Frame", "header") :SetLayout("HORIZONTAL") :SetHeight(24) :AddChild(UIElements.New("Text", "text") :SetMargin(0, 10, 0, 0) :SetSize("AUTO", 16) :SetFont("BODY_BODY1") :SetText(L["Auctioning Details"]) ) :AddChild(UIElements.New("ActionButton", "editBtn") :SetHeight(16) :SetFont("BODY_BODY3_MEDIUM") :SetText(L["Edit Post"]) :SetScript("OnClick", private.EditButtonOnClick) ) ) :AddChild(UIElements.New("Frame", "details") :SetLayout("HORIZONTAL") :SetMargin(0, 0, 4, 0) :AddChild(UIElements.New("Frame", "details1") :SetLayout("VERTICAL") :SetWidth(200) :SetMargin(0, 8, 0, 0) :AddChild(UIElements.New("Frame", "bid") :SetLayout("HORIZONTAL") :SetHeight(20) :AddChild(UIElements.New("Text", "desc") :SetWidth("AUTO") :SetFont("BODY_BODY3_MEDIUM") :SetText(L["Bid Price"]..":") ) :AddChild(UIElements.New("Text", "text") :SetMargin(4, 0, 0, 0) :SetFont("TABLE_TABLE1") ) ) :AddChild(UIElements.New("Frame", "buyout") :SetLayout("HORIZONTAL") :SetHeight(20) :AddChild(UIElements.New("Text", "desc") :SetWidth("AUTO") :SetFont("BODY_BODY3_MEDIUM") :SetText(L["Buyout Price"]..":") ) :AddChild(UIElements.New("Text", "text") :SetMargin(4, 0, 0, 0) :SetFont("TABLE_TABLE1") ) ) ) :AddChild(UIElements.New("Frame", "details2") :SetLayout("VERTICAL") :AddChild(UIElements.New("Frame", "quantity") :SetLayout("HORIZONTAL") :SetHeight(20) :AddChild(UIElements.New("Text", "desc") :SetWidth("AUTO") :SetFont("BODY_BODY3_MEDIUM") :SetText((TSM.IsWowClassic() and L["Stack / Quantity"] or L["Quantity"])..":") ) :AddChild(UIElements.New("Text", "text") :SetMargin(4, 0, 0, 0) :SetFont("TABLE_TABLE1") ) ) :AddChild(UIElements.New("Frame", "duration") :SetLayout("HORIZONTAL") :SetHeight(20) :AddChild(UIElements.New("Text", "desc") :SetWidth("AUTO") :SetFont("BODY_BODY3_MEDIUM") :SetText(L["Duration"]..":") ) :AddChild(UIElements.New("Text", "text") :SetMargin(4, 0, 0, 0) :SetFont("TABLE_TABLE1") ) ) ) ) ) ) ) :AddChild(UIElements.New("SimpleTabGroup", "tabs") :SetNavCallback(private.ScanNavCallback) :AddPath(L["Auctioning Log"], true) :AddPath(L["All Auctions"]) ) :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.PauseResumeBtnOnClick) ) :AddChild(UIElements.New("ProgressBar", "progressBar") :SetHeight(24) :SetMargin(0, 8, 0, 0) :SetProgress(0) :SetProgressIconHidden(false) :SetText(L["Starting Scan..."]) ) :AddChild(UIElements.NewNamed("ActionButton", "processBtn", "TSMAuctioningBtn") :SetSize(160, 24) :SetMargin(0, 8, 0, 0) :SetText(L["Post"]) :SetDisabled(true) :DisableClickCooldown(true) :SetScript("OnClick", private.ProcessButtonOnClick) ) :AddChild(UIElements.New("ActionButton", "skipBtn") :SetSize(160, 24) :SetText(L["Skip"]) :SetDisabled(true) :DisableClickCooldown(true) :SetScript("OnClick", private.SkipButtonOnClick) ) ) :SetScript("OnUpdate", private.ScanFrameOnUpdate) :SetScript("OnHide", private.ScanFrameOnHide) end function private.ScanNavCallback(_, path) if path == L["Auctioning Log"] then TSM.UI.AnalyticsRecordPathChange("auction", "auctioning", "scan", "log") private.logQuery = private.logQuery or TSM.Auctioning.Log.CreateQuery() return UIElements.New("Frame", "logFrame") :SetLayout("VERTICAL") :AddChild(UIElements.New("QueryScrollingTable", "log") :SetSettingsContext(private.settings, "auctioningLogScrollingTable") :GetScrollingTableInfo() :NewColumn("index") :SetTitleIcon("iconPack.14x14/Attention") :SetIconSize(12) :SetFont("ITEM_BODY3") :SetJustifyH("CENTER") :SetIconInfo(nil, private.LogGetIndexIcon) :SetSortInfo("index") :Commit() :NewColumn("item") :SetTitle(L["Item"]) :SetFont("ITEM_BODY3") :SetJustifyH("LEFT") :SetIconSize(12) :SetTextInfo("itemString", TSM.UI.GetColoredItemName) :SetIconInfo("itemString", ItemInfo.GetTexture) :SetTooltipInfo("itemString") :SetSortInfo("name") :Commit() :NewColumn("buyout") :SetTitle(L["Your Buyout"]) :SetFont("TABLE_TABLE1") :SetJustifyH("RIGHT") :SetTextInfo("buyout", private.LogGetBuyoutText) :SetSortInfo("buyout") :Commit() :NewColumn("operation") :SetTitle(L["Operation"]) :SetFont("TABLE_TABLE1") :SetJustifyH("LEFT") :SetTextInfo("operation") :SetSortInfo("operation") :Commit() :NewColumn("seller") :SetTitle(L["Seller"]) :SetFont("TABLE_TABLE1") :SetJustifyH("LEFT") :SetTextInfo("seller") :SetSortInfo("seller") :Commit() :NewColumn("info") :SetTitle(INFO) :SetFont("TABLE_TABLE1") :SetJustifyH("LEFT") :SetTextInfo(nil, TSM.Auctioning.Log.GetInfoStr) :SetSortInfo("reasonStr") :Commit() :Commit() :SetQuery(private.logQuery) :SetSelectionDisabled(true) ) elseif path == L["All Auctions"] then TSM.UI.AnalyticsRecordPathChange("auction", "auctioning", "scan", "auctions") return UIElements.New("Frame", "auctionsFrame") :SetLayout("VERTICAL") :AddChild(UIElements.New("AuctionScrollingTable", "auctions") :SetSettingsContext(private.settings, "auctioningAuctionScrollingTable") :SetBrowseResultsVisible(true) :SetMarketValueFunction(private.MarketValueFunction) :SetAuctionScan(private.auctionScan) ) else error("Unexpected path: "..tostring(path)) end end -- ============================================================================ -- Local Script Handlers -- ============================================================================ function private.OnItemLinked(_, itemLink) if private.selectionFrame then if not TSM.UI.AuctionUI.StartingScan(L["Auctioning"]) then return false end wipe(private.scanContext) private.scanContext.isItems = true tinsert(private.scanContext, TSM.Groups.TranslateItemString(ItemString.Get(itemLink))) private.selectionFrame:GetParentElement():SetPath("scan", true) private.fsm:ProcessEvent("EV_START_SCAN", "POST", private.scanContext) return true else if not private.canStartNewScan then return false end wipe(private.scanContext) private.scanContext.isItems = true tinsert(private.scanContext, TSM.Groups.TranslateItemString(ItemString.Get(itemLink))) private.fsm:ProcessEvent("EV_START_SCAN", "POST", private.scanContext) return true end end function private.SelectionOnHide(frame) assert(frame == private.selectionFrame) private.selectionFrame = nil 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 postScanBtn = groupTree:GetElement("__parent.bottom.postScanBtn") postScanBtn:SetDisabled(groupTree:IsSelectionCleared()) postScanBtn:Draw() local cancelScanBtn = groupTree:GetElement("__parent.bottom.cancelScanBtn") cancelScanBtn:SetDisabled(groupTree:IsSelectionCleared()) cancelScanBtn:Draw() end function private.RunPostButtonOnclick(button) if not TSM.UI.AuctionUI.StartingScan(L["Auctioning"]) then return end wipe(private.scanContext) for _, groupPath in button:GetElement("__parent.__parent.groupTree"):SelectedGroupsIterator() do tinsert(private.scanContext, groupPath) end button:GetElement("__parent.__parent.__parent.__parent"):SetPath("scan", true) private.fsm:ProcessEvent("EV_START_SCAN", "POST", private.scanContext) end function private.RunCancelButtonOnclick(button) if not TSM.UI.AuctionUI.StartingScan(L["Auctioning"]) then return end wipe(private.scanContext) for _, groupPath in button:GetElement("__parent.__parent.groupTree"):SelectedGroupsIterator() do tinsert(private.scanContext, groupPath) end button:GetElement("__parent.__parent.__parent.__parent"):SetPath("scan", true) private.fsm:ProcessEvent("EV_START_SCAN", "CANCEL", private.scanContext) end function private.SearchListOnFavoriteChanged(_, dbRow, isFavorite) TSM.Auctioning.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.Auctioning.SavedSearches.RenameSearch(dbRow, name) end function private.DialogCloseBtnOnClick(button) button:GetBaseElement():HideDialog() end function private.SearchListOnDelete(_, dbRow) TSM.Auctioning.SavedSearches.DeleteSearch(dbRow) end function private.SearchListOnRowClick(searchList, dbRow) if not TSM.UI.AuctionUI.StartingScan(L["Auctioning"]) then return end local scanType = dbRow:GetField("searchType") wipe(private.scanContext) private.scanContext.isItems = scanType == "postItems" or nil TSM.Auctioning.SavedSearches.FiltersToTable(dbRow, private.scanContext) searchList:GetParentElement():GetParentElement():GetParentElement():GetParentElement():GetParentElement():SetPath("scan", true) private.fsm:ProcessEvent("EV_START_SCAN", scanType == "cancelGroups" and "CANCEL" or "POST", private.scanContext) end function private.PauseResumeBtnOnClick(button) private.fsm:ProcessEvent("EV_PAUSE_RESUME_CLICKED") end function private.ProcessButtonOnClick(button) private.fsm:ProcessEvent("EV_PROCESS_CLICKED") end function private.SkipButtonOnClick(button) private.fsm:ProcessEvent("EV_SKIP_CLICKED") 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.BagOnSelectionChanged(scrollingTable) local selectionCleared = scrollingTable:IsSelectionCleared() scrollingTable:GetElement("__parent.button.postSelected"):SetDisabled(selectionCleared) :Draw() scrollingTable:GetElement("__parent.button.selectAll"):SetDisabled(scrollingTable:IsAllSelected()) :Draw() scrollingTable:GetElement("__parent.button.clearAll"):SetDisabled(selectionCleared) :Draw() end function private.SelectAllOnClick(button) button:GetElement("__parent.__parent.bagScrollingTable"):SelectAll() end function private.ClearAllOnClick(button) button:GetElement("__parent.__parent.bagScrollingTable"):ClearSelection() end function private.RunPostBagsButtonOnClick(button) if not TSM.UI.AuctionUI.StartingScan(L["Auctioning"]) then return end wipe(private.scanContext) private.scanContext.isItems = true for _, row in button:GetElement("__parent.__parent.bagScrollingTable"):SelectionIterator() do local autoBaseItemString, operation = row:GetFields("autoBaseItemString", "firstOperation") if operation then tinsert(private.scanContext, autoBaseItemString) end end button:GetParentElement():GetParentElement():GetParentElement():GetParentElement():GetParentElement():SetPath("scan", true) private.fsm:ProcessEvent("EV_START_SCAN", "POST", private.scanContext) end function private.ExitScanButtonOnEnter(button) private.ExitScanFrameOnEnter(button:GetParentElement()) end function private.ExitScanButtonOnLeave(button) private.ExitScanFrameOnLeave(button:GetParentElement()) end function private.ExitScanFrameOnEnter(frame) frame:SetBackgroundColor("PRIMARY_BG_ALT+HOVER", true) :Draw() end function private.ExitScanFrameOnLeave(frame) frame:SetBackgroundColor("PRIMARY_BG_ALT", true) :Draw() end function private.ExitScanButtonOnClick() if TSM.IsWowClassic() then ClearCursor() ClickAuctionSellItemButton(AuctionsItemButton, "LeftButton") ClearCursor() end private.fsm:ProcessEvent("EV_BACK_BUTTON_CLICKED") end function private.EditButtonOnClick(button) private.fsm:ProcessEvent("EV_EDIT_BUTTON_CLICKED") end -- ============================================================================ -- FSM -- ============================================================================ function private.FSMCreate() local fsmContext = { itemString = nil, scanFrame = nil, scanThreadId = nil, scanType = nil, auctionScan = nil, pausePending = nil, isScanning = false, pendingFuture = nil, } Event.Register("AUCTION_HOUSE_CLOSED", function() private.fsm:ProcessEvent("EV_AUCTION_HOUSE_CLOSED") end) if TSM.IsWowClassic() then Event.Register("CHAT_MSG_SYSTEM", function(_, msg) if msg == ERR_AUCTION_STARTED then private.fsm:SetLoggingEnabled(false) private.fsm:ProcessEvent("EV_AUCTION_POST_CONFIRM", true) private.fsm:SetLoggingEnabled(true) elseif msg == ERR_AUCTION_REMOVED then private.fsm:SetLoggingEnabled(false) private.fsm:ProcessEvent("EV_AUCTION_CANCEL_CONFIRM", true) private.fsm:SetLoggingEnabled(true) end end) local POST_ERR_MSGS = { -- errors where we can retry [ERR_ITEM_NOT_FOUND] = true, [ERR_AUCTION_DATABASE_ERROR] = true, -- errors where we can't retry [ERR_AUCTION_REPAIR_ITEM] = false, [ERR_AUCTION_LIMITED_DURATION_ITEM] = false, [ERR_AUCTION_USED_CHARGES] = false, [ERR_AUCTION_WRAPPED_ITEM] = false, [ERR_AUCTION_BAG] = false, [ERR_NOT_ENOUGH_MONEY] = false, } Event.Register("UI_ERROR_MESSAGE", function(_, _, msg) if POST_ERR_MSGS[msg] ~= nil then private.fsm:ProcessEvent("EV_AUCTION_POST_CONFIRM", false, POST_ERR_MSGS[msg]) end if msg == ERR_ITEM_NOT_FOUND then private.fsm:ProcessEvent("EV_AUCTION_CANCEL_CONFIRM", false, true) end end) end local fsmPrivate = {} function fsmPrivate.UpdateDepositCost(context) if context.scanType ~= "POST" then return end local header = context.scanFrame:GetElement("header") local detailsHeader1 = header:GetElement("content.details.details.details1") local currentRow = TSM.Auctioning.PostScan.GetCurrentRow() if not currentRow then return end local itemString = currentRow:GetField("itemString") local postBag, postSlot = BagTracking.CreateQueryBagsAuctionable() :OrderBy("slotId", true) :Select("bag", "slot") :Equal("baseItemString", ItemString.GetBaseFast(itemString)) :VirtualField("autoBaseItemString", "string", TSM.Groups.TranslateItemString, "itemString") :Equal("autoBaseItemString", itemString) :GetFirstResultAndRelease() local postTime = currentRow:GetField("postTime") local stackSize = tonumber(currentRow:GetField("stackSize")) local depositCost = 0 if postBag and postSlot then if TSM.IsWowClassic() then ClearCursor() PickupContainerItem(postBag, postSlot) ClickAuctionSellItemButton(AuctionsItemButton, "LeftButton") ClearCursor() local bid = Money.FromString(detailsHeader1:GetElement("bid.text"):GetText()) local buyout = Money.FromString(detailsHeader1:GetElement("buyout.text"):GetText()) depositCost = GetAuctionDeposit(postTime, bid, buyout, stackSize, 1) ClearCursor() ClickAuctionSellItemButton(AuctionsItemButton, "LeftButton") ClearCursor() else 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) end end header:GetElement("content.item.cost.text"):SetText(Money.ToString(depositCost)) :Draw() end function fsmPrivate.UpdateScanFrame(context) if not context.scanFrame then return end local header = context.scanFrame:GetElement("header") local currentRow, numProcessed, numConfirmed, _, totalNum = nil, nil, nil, nil, nil if context.scanType == "POST" then currentRow = TSM.Auctioning.PostScan.GetCurrentRow() numProcessed, numConfirmed, _, totalNum = TSM.Auctioning.PostScan.GetStatus() header:GetElement("content.item.cost") :Show() :Draw() elseif context.scanType == "CANCEL" then currentRow = TSM.Auctioning.CancelScan.GetCurrentRow() numProcessed, numConfirmed, _, totalNum = TSM.Auctioning.CancelScan.GetStatus() header:GetElement("content.item.cost") :Hide() :Draw() else error("Invalid scan type: "..tostring(context.scanType)) end local itemContent = header:GetElement("content.item.content") local detailsHeader1 = header:GetElement("content.details.details.details1") local detailsHeader2 = header:GetElement("content.details.details.details2") if currentRow then local selectedRow = nil for _, row in private.logQuery:Iterator() do if currentRow:GetField("auctionId") == row:GetField("index") then selectedRow = row end end if selectedRow and context.scanFrame:GetElement("tabs"):GetPath() == L["Auctioning Log"] then context.scanFrame:GetElement("tabs.logFrame.log") :SetSelection(selectedRow:GetUUID()) :Draw() end local itemString = currentRow:GetField("itemString") local rowStacksRemaining = currentRow:GetField("numStacks") - currentRow:GetField("numProcessed") itemContent:GetElement("icon") :SetBackground(ItemInfo.GetTexture(itemString)) :SetTooltip(itemString) :Draw() itemContent:GetElement("text") :SetText(TSM.UI.GetColoredItemName(itemString)) :SetTooltip(itemString) :Draw() header:GetElement("content.item.operation.text") :SetText(currentRow:GetField("operationName")) :Draw() detailsHeader1:GetElement("bid.text") :SetText(Money.ToString(currentRow:GetField(ItemInfo.IsCommodity(itemString) and "itemBuyout" or "bid"), nil, "OPT_83_NO_COPPER")) :Draw() detailsHeader1:GetElement("buyout.text") :SetText(Money.ToString(currentRow:GetField(ItemInfo.IsCommodity(itemString) and "itemBuyout" or "buyout"), nil, "OPT_83_NO_COPPER")) :Draw() detailsHeader2:GetElement("quantity.text") :SetText(TSM.IsWowClassic() and format(L["%d of %d"], rowStacksRemaining, currentRow:GetField("stackSize")) or currentRow:GetField("stackSize")) :Draw() local duration = nil if context.scanType == "POST" then duration = TSM.CONST.AUCTION_DURATIONS[currentRow:GetField("postTime")] elseif context.scanType == "CANCEL" then if TSM.IsWowClassic() then duration = _G["AUCTION_TIME_LEFT"..currentRow:GetField("duration")] else duration = currentRow:GetField("duration") - time() if duration < SECONDS_PER_MIN then duration = duration.."s" elseif duration < SECONDS_PER_HOUR then duration = floor(duration / SECONDS_PER_MIN).."m" elseif duration < SECONDS_PER_DAY then duration = floor(duration / SECONDS_PER_HOUR).."h" else duration = floor(duration / SECONDS_PER_DAY).."d" end end else error("Invalid scanType: "..tostring(context.scanType)) end detailsHeader2:GetElement("duration.text") :SetText(duration) :Draw() if context.scanType == "POST" and context.itemString ~= itemString then fsmPrivate.UpdateDepositCost(context) context.itemString = itemString end header:GetElement("content.details.header.editBtn") :SetDisabled(context.scanType ~= "POST") :Draw() else itemContent:GetElement("icon") :SetBackground(nil) :SetTooltip(nil) :Draw() itemContent:GetElement("text") :SetText("-") :SetTooltip(nil) :Draw() header:GetElement("content.item.cost.text") :SetText("-") :Draw() header:GetElement("content.item.operation.text") :SetText("-") :Draw() detailsHeader1:GetElement("bid.text") :SetText("-") :Draw() detailsHeader1:GetElement("buyout.text") :SetText("-") :Draw() detailsHeader2:GetElement("quantity.text") :SetText("-") :Draw() detailsHeader2:GetElement("duration.text") :SetText("-") :Draw() if context.scanFrame:GetElement("tabs"):GetPath() == L["Auctioning Log"] then context.scanFrame:GetElement("tabs.logFrame.log") :SetSelection(nil) :Draw() end header:GetElement("content.details.header.editBtn") :SetDisabled(true) :Draw() end local processText = nil if context.scanType == "POST" then processText = L["Post"] elseif context.scanType == "CANCEL" then processText = CANCEL else error("Invalid scan type: "..tostring(context.scanType)) end local bottom = context.scanFrame:GetElement("bottom") bottom:GetElement("processBtn") :SetText(processText) local progress, isPaused = context.auctionScan:GetProgress() local scanDone = progress == 1 and not context.isScanning local isPausePending = context.pausePending ~= nil if not isPausePending and (scanDone or isPaused) then -- we're done (or paused) scanning so start Posting/Canceling local doneStr, progressFmtStr = nil, nil if context.scanType == "POST" then doneStr = L["Done Posting"] progressFmtStr = (isPaused and (L["Scan Paused"].." | ") or "")..L["Posting %d / %d"] elseif context.scanType == "CANCEL" then doneStr = L["Done Canceling"] progressFmtStr = (isPaused and (L["Scan Paused"].." | ") or "")..L["Canceling %d / %d"] else error("Invalid scan type: "..tostring(context.scanType)) end local progressText, iconHidden = nil, false if numConfirmed == totalNum then progressText = doneStr iconHidden = true elseif numProcessed == totalNum then progressText = format(L["Confirming %d / %d"], numConfirmed + 1, totalNum) elseif numProcessed == numConfirmed then progressText = format(progressFmtStr, numProcessed + 1, totalNum) iconHidden = true else progressText = format(progressFmtStr.." ("..L["Confirming %d / %d"]..")", numProcessed + 1, totalNum, numConfirmed + 1, totalNum) end bottom:GetElement("progressBar") :SetProgress(totalNum > 0 and (numProcessed / totalNum) or 1) :SetProgressIconHidden(iconHidden) :SetText(progressText) local deposit = context.scanType == "POST" and Money.FromString(header:GetElement("content.item.cost.text"):GetText()) bottom:GetElement("processBtn"):SetDisabled(numProcessed == totalNum or (deposit and deposit > GetMoney()) or (not TSM.IsWowClassic() and context.pendingFuture)) bottom:GetElement("skipBtn"):SetDisabled(numProcessed == totalNum) bottom:GetElement("pauseResumeBtn") :SetDisabled(numProcessed ~= numConfirmed or scanDone) :SetHighlightLocked(false) else -- we're scanning or pausing local text = nil if isPausePending then text = isPaused and L["Resuming Scan..."] or L["Pausing Scan..."] else local numItems = context.auctionScan:GetNumItems() text = numItems and format(L["Scanning (%d Items)"], numItems) or L["Scanning"] end bottom:GetElement("progressBar") :SetProgress(progress) :SetProgressIconHidden(false) :SetText(text) bottom:GetElement("processBtn"):SetDisabled(true) bottom:GetElement("skipBtn"):SetDisabled(true) bottom:GetElement("pauseResumeBtn") :SetDisabled(isPausePending or numProcessed == totalNum) :SetHighlightLocked(isPausePending) end bottom:Draw() end function fsmPrivate.ShowEditDialog(context) if context.scanType ~= "POST" then return end local currentRow = TSM.Auctioning.PostScan.GetCurrentRow() local itemString = currentRow:GetField("itemString") local isCommodity = ItemInfo.IsCommodity(itemString) local bid = currentRow:GetField(isCommodity and "itemBuyout" or "bid") local buyout = currentRow:GetField(isCommodity and "itemBuyout" or "buyout") private.perItem = true context.scanFrame:GetBaseElement():ShowDialogFrame(UIElements.New("Frame", "frame") :SetLayout("VERTICAL") :SetSize(328, 328) :SetPadding(12) :AddAnchor("CENTER") :SetBackgroundColor("FRAME_BG", true) :SetMouseEnabled(true) :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["Edit Post"]) ) :AddChild(UIElements.New("Button", "closeBtn") :SetMargin(0, -4, 0, 0) :SetBackgroundAndSize("iconPack.24x24/Close/Default") :SetScript("OnClick", private.EditDialogCloseBtnOnClick) ) ) :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) :SetBackground(ItemInfo.GetTexture(itemString)) :SetTooltip(itemString) ) :AddChild(UIElements.New("Text", "name") :SetHeight(36) :SetFont("ITEM_BODY1") :SetText(TSM.UI.GetColoredItemName(itemString)) ) ) -- TODO: implement editing stack sizes :AddChild(UIElements.New("Frame", "stacksFrame") :SetLayout("HORIZONTAL") :SetHeight(20) :SetMargin(0, 0, 0, 16) :AddChild(UIElements.New("Text", "stacksText") :SetWidth("AUTO") :SetMargin(0, 8, 0, 0) :SetFont("BODY_BODY2") :SetText(L["Stack(s)"]..":") ) :AddChild(UIElements.New("Input", "stacksInput") :SetSize(62, 24) :SetMargin(0, 16, 0, 0) :SetBackgroundColor("PRIMARY_BG_ALT") :SetValidateFunc("NUMBER", "1:5000") :SetDisabled(true) :SetValue(currentRow:GetField("numStacks")) ) :AddChild(UIElements.New("Text", "quantityText") :SetWidth("AUTO") :SetMargin(0, 8, 0, 0) :SetFont("BODY_BODY2") :SetText(L["Quantity"]..":") ) :AddChild(UIElements.New("Input", "quantityInput") :SetSize(62, 24) :SetBackgroundColor("PRIMARY_BG_ALT") :SetValidateFunc("NUMBER", "1:5000") :SetDisabled(true) :SetValue(currentRow:GetField("stackSize")) ) ) :AddChild(UIElements.New("Frame", "duration") :SetLayout("HORIZONTAL") :SetHeight(24) :SetMargin(0, 0, 0, 24) :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(TSM.CONST.AUCTION_DURATIONS[currentRow:GetField("postTime")]) ) ) :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") :SetTextColor("TEXT") :SetFont("BODY_BODY2_MEDIUM") :SetJustifyH("RIGHT") :SetTextColor("INDICATOR") :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") :SetDisabled(isCommodity) :SetTabPaths("__parent.__parent.buyout.input", "__parent.__parent.buyout.input") :SetContext("bid") :SetValue(Money.ToString(bid, nil, "OPT_83_NO_COPPER")) :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, 16) :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") :SetContext(isCommodity and "itemBuyout" or "buyout") :SetTabPaths("__parent.__parent.bid.input", "__parent.__parent.bid.input") :SetValue(Money.ToString(buyout, nil, "OPT_83_NO_COPPER")) :SetScript("OnValidationChanged", private.BidBuyoutOnValidationChanged) :SetScript("OnValueChanged", private.BidBuyoutInputOnValueChanged) :SetScript("OnEnterPressed", private.BidBuyoutInputOnEnterPressed) ) ) :AddChild(UIElements.New("ActionButton", "saveBtn") :SetHeight(24) :SetText(SAVE) :SetContext(context) :SetScript("OnClick", fsmPrivate.EditPostingDetailsSaveOnClick) ) ) end function fsmPrivate.EditPostingDetailsSaveOnClick(button) local context = button:GetContext() assert(context.scanType == "POST") local currentRow = TSM.Auctioning.PostScan.GetCurrentRow() local buyout = private.ParseBidBuyout(button:GetElement("__parent.buyout.input"):GetValue()) local bid = min(private.ParseBidBuyout(button:GetElement("__parent.bid.input"):GetValue()), buyout) if not TSM.IsWowClassic() and ItemInfo.IsCommodity(context.itemString) and bid ~= buyout then Log.PrintUser(L["Did not change prices due to an invalid bid or buyout value."]) else local duration = Table.KeyByValue(TSM.CONST.AUCTION_DURATIONS, button:GetElement("__parent.duration.toggle"):GetValue()) if duration ~= currentRow:GetField("postTime") then TSM.Auctioning.PostScan.ChangePostDetail("postTime", duration) end -- update buyout first since doing so may change the bid if buyout ~= currentRow:GetField("buyout") then TSM.Auctioning.PostScan.ChangePostDetail("buyout", private.perItem and buyout or buyout / currentRow:GetField("stackSize")) end if not ItemInfo.IsCommodity(context.itemString) and bid ~= currentRow:GetField("bid") then TSM.Auctioning.PostScan.ChangePostDetail("bid", private.perItem and bid or bid / currentRow:GetField("stackSize")) end end fsmPrivate.UpdateDepositCost(context) fsmPrivate.UpdateScanFrame(context) button:GetBaseElement():HideDialog() end private.fsm = FSM.New("AUCTIONING") :AddState(FSM.NewState("ST_INIT") :SetOnEnter(function(context, scanType, scanContext) private.hasLastScan = false TSM.Auctioning.Log.Truncate() TSM.Auctioning.PostScan.Reset() TSM.Auctioning.CancelScan.Reset() if context.scanThreadId then Threading.Kill(context.scanThreadId) context.scanThreadId = nil end context.pausePending = nil context.itemString = nil context.isScanning = false if context.auctionScan then context.auctionScan:Release() context.auctionScan = nil private.auctionScan = context.auctionScan end if context.pendingFuture then context.pendingFuture:Cancel() context.pendingFuture = nil end if scanType then return "ST_STARTING_SCAN", scanType, scanContext elseif context.scanFrame then context.scanFrame:GetParentElement():SetPath("selection", true) context.scanFrame = nil end TSM.UI.AuctionUI.EndedScan(L["Auctioning"]) end) :AddTransition("ST_INIT") :AddTransition("ST_STARTING_SCAN") ) :AddState(FSM.NewState("ST_STARTING_SCAN") :SetOnEnter(function(context, scanType, scanContext) private.hasLastScan = true context.scanType = scanType if context.scanType == "POST" then context.scanThreadId = TSM.Auctioning.PostScan.Prepare() private.logQuery:ResetOrderBy() private.logQuery:OrderBy("index", true) elseif context.scanType == "CANCEL" then context.scanThreadId = TSM.Auctioning.CancelScan.Prepare() private.logQuery:ResetOrderBy() private.logQuery:OrderBy("index", false) else error("Invalid scan type: "..tostring(context.scanType)) end context.auctionScan = AuctionScan.GetManager() :SetResolveSellers(true) :SetScript("OnProgressUpdate", private.FSMAuctionScanOnProgressUpdate) private.auctionScan = context.auctionScan fsmPrivate.UpdateScanFrame(context) Threading.SetCallback(context.scanThreadId, private.FSMScanCallback) Threading.Start(context.scanThreadId, context.auctionScan, scanContext) return "ST_SCANNING" end) :AddTransition("ST_SCANNING") ) :AddState(FSM.NewState("ST_SCANNING") :SetOnEnter(function(context) context.isScanning = true fsmPrivate.UpdateScanFrame(context) end) :SetOnExit(function(context) context.isScanning = false end) :AddTransition("ST_RESULTS") :AddTransition("ST_INIT") :AddEvent("EV_SCAN_PROGRESS_UPDATE", function(context) local _, isPaused = context.auctionScan:GetProgress() if context.pausePending == isPaused then context.pausePending = nil end if isPaused and context.pausePending == nil then return "ST_RESULTS" else fsmPrivate.UpdateScanFrame(context) end end) :AddEvent("EV_SCAN_COMPLETE", function(context) Sound.PlaySound(private.settings.scanCompleteSound) return "ST_RESULTS" end) :AddEvent("EV_PAUSE_RESUME_CLICKED", function(context) assert(context.pausePending == nil) context.pausePending = true context.auctionScan:SetPaused(true) fsmPrivate.UpdateScanFrame(context) end) :AddEvent("EV_EDIT_BUTTON_CLICKED", function(context) fsmPrivate.ShowEditDialog(context) end) ) :AddState(FSM.NewState("ST_HANDLING_CONFIRM") :SetOnEnter(function(context, success, canRetry) local isDone = false if context.scanType == "POST" then TSM.Auctioning.PostScan.HandleConfirm(success, canRetry) local _, numConfirmed, numFailed, totalNum = TSM.Auctioning.PostScan.GetStatus() if numConfirmed == totalNum then if numFailed > 0 then -- TODO: need to wait for the player's bags to settle Log.PrintfUser(L["Retrying %d auction(s) which failed."], numFailed) TSM.Auctioning.PostScan.PrepareFailedPosts() else isDone = true end end elseif context.scanType == "CANCEL" then TSM.Auctioning.CancelScan.HandleConfirm(success, canRetry) local _, numConfirmed, numFailed, totalNum = TSM.Auctioning.CancelScan.GetStatus() if numConfirmed == totalNum then if numFailed > 0 then -- TODO: need to wait for the player's auctions to settle Log.PrintfUser(L["Retrying %d auction(s) which failed."], numFailed) TSM.Auctioning.CancelScan.PrepareFailedCancels() else isDone = true end end else error("Invalid scan type: "..tostring(context.scanType)) end local _, isPaused = context.auctionScan:GetProgress() if not isDone then return "ST_RESULTS" elseif isPaused then -- unpause the scan now that we're done assert(context.pausePending == nil) context.pausePending = false context.auctionScan:SetPaused(false) return "ST_SCANNING" else return "ST_DONE" end end) :AddTransition("ST_SCANNING") :AddTransition("ST_RESULTS") :AddTransition("ST_DONE") ) :AddState(FSM.NewState("ST_RESULTS") :SetOnEnter(function(context) local _, isPaused = context.auctionScan:GetProgress() if not isPaused then TSM.UI.AuctionUI.EndedScan(L["Auctioning"]) Threading.Kill(context.scanThreadId) end fsmPrivate.UpdateScanFrame(context) end) :AddTransition("ST_INIT") :AddTransition("ST_HANDLING_CONFIRM") :AddTransition("ST_SCANNING") :AddTransition("ST_DONE") :AddEvent("EV_PROCESS_CLICKED", function(context) context.scanFrame:GetBaseElement():HideDialog() local result, noRetry = nil, nil if context.scanType == "POST" then result, noRetry = TSM.Auctioning.PostScan.DoProcess() elseif context.scanType == "CANCEL" then result, noRetry = TSM.Auctioning.CancelScan.DoProcess() else error("Invalid scan type: "..tostring(context.scanType)) end if not result then -- we failed to post / cancel return "ST_HANDLING_CONFIRM", false, not noRetry elseif not TSM.IsWowClassic() then context.pendingFuture = result context.pendingFuture:SetScript("OnDone", private.FSMPendingFutureOneDone) end fsmPrivate.UpdateScanFrame(context) end) :AddEvent("EV_SKIP_CLICKED", function(context) local isDone = nil if context.scanType == "POST" then TSM.Auctioning.PostScan.DoSkip() local _, numConfirmed, numFailed, totalNum = TSM.Auctioning.PostScan.GetStatus() isDone = numConfirmed == totalNum and numFailed == 0 elseif context.scanType == "CANCEL" then TSM.Auctioning.CancelScan.DoSkip() local _, numConfirmed, numFailed, totalNum = TSM.Auctioning.CancelScan.GetStatus() isDone = numConfirmed == totalNum and numFailed == 0 else error("Invalid scan type: "..tostring(context.scanType)) end fsmPrivate.UpdateScanFrame(context) local _, isPaused = context.auctionScan:GetProgress() if isDone and isPaused then -- unpause the scan now that we're done assert(context.pausePending == nil) context.pausePending = false context.auctionScan:SetPaused(false) return "ST_SCANNING" elseif isDone then return "ST_DONE" end end) :AddEvent("EV_PENDING_FUTURE_DONE", function(context) assert(context.pendingFuture:IsDone()) local value = context.pendingFuture:GetValue() context.pendingFuture = nil if value == true then return "ST_HANDLING_CONFIRM", true, false elseif value == false then return "ST_HANDLING_CONFIRM", false, true elseif value == nil then return "ST_HANDLING_CONFIRM", false, false else error("Invalid value: "..tostring(value)) end end) :AddEvent("EV_AUCTION_POST_CONFIRM", function(context, success, canRetry) if context.scanType == "POST" then return "ST_HANDLING_CONFIRM", success, canRetry end end) :AddEvent("EV_AUCTION_CANCEL_CONFIRM", function(context, success, canRetry) if context.scanType == "CANCEL" then return "ST_HANDLING_CONFIRM", success, canRetry end end) :AddEvent("EV_PAUSE_RESUME_CLICKED", function(context) assert(context.pausePending == nil) context.pausePending = false context.auctionScan:SetPaused(false) return "ST_SCANNING" end) :AddEvent("EV_EDIT_BUTTON_CLICKED", function(context) fsmPrivate.ShowEditDialog(context) end) ) :AddState(FSM.NewState("ST_DONE") :SetOnEnter(function(context) private.canStartNewScan = true AuctionTracking.QueryOwnedAuctions() Sound.PlaySound(private.settings.confirmCompleteSound) fsmPrivate.UpdateScanFrame(context) end) :SetOnExit(function(context) private.canStartNewScan = false end) :AddTransition("ST_INIT") ) :AddDefaultEvent("EV_START_SCAN", function(context, scanType, scanContext) return "ST_INIT", scanType, scanContext end) :AddDefaultEvent("EV_SCAN_FRAME_SHOWN", function(context, scanFrame) context.scanFrame = scanFrame fsmPrivate.UpdateScanFrame(context) end) :AddDefaultEvent("EV_SCAN_FRAME_HIDDEN", function(context) context.scanFrame = nil context.itemString = nil end) :AddDefaultEventTransition("EV_BACK_BUTTON_CLICKED", "ST_INIT") :AddDefaultEventTransition("EV_AUCTION_HOUSE_CLOSED", "ST_INIT") :AddDefaultEvent("EV_PENDING_FUTURE_DONE", function(context) error("Unexpected pending future done event") end) :Init("ST_INIT", fsmContext) end function private.FSMAuctionScanOnProgressUpdate(auctionScan) -- this even is very spammy while we scan, so silence the FSM logging private.fsm:SetLoggingEnabled(false) private.fsm:ProcessEvent("EV_SCAN_PROGRESS_UPDATE") private.fsm:SetLoggingEnabled(true) end function private.FSMScanCallback() private.fsm:ProcessEvent("EV_SCAN_COMPLETE") end function private.FSMPendingFutureOneDone(value) private.fsm:ProcessEvent("EV_PENDING_FUTURE_DONE") end -- ============================================================================ -- Private Helper Functions -- ============================================================================ function private.BagScrollingTableIsSelectionEnabled(_, record) return record:GetField("firstOperation") and true or false end function private.BagGetOperationText(firstOperation) return firstOperation or Theme.GetFeedbackColor("RED"):ColorText(L["Skipped: No assigned operation"]) end function private.LogGetBuyoutText(buyout) return buyout == 0 and "-" or Money.ToString(buyout, nil, "OPT_83_NO_COPPER") end function private.LogGetIndexIcon(row) if row:GetField("state") == "PENDING" then -- color the circle icon to match the color of the text return TSM.UI.TexturePacks.GetColoredKey("iconPack.12x12/Circle", TSM.Auctioning.Log.GetColorFromReasonKey(row:GetField("reasonKey"))) else return "iconPack.12x12/Checkmark/Default" end end function private.MarketValueFunction(row) return CustomPrice.GetValue("dbmarket", row:GetItemString() or row:GetBaseItemString()) end function private.EditDialogCloseBtnOnClick(button) button:GetBaseElement():HideDialog() 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 row = TSM.Auctioning.PostScan.GetCurrentRow() local bidInput = button:GetElement("__parent.__parent.bid.input") local buyoutInput = button:GetElement("__parent.__parent.buyout.input") buyoutInput:SetFocused(false) :SetValue(row:GetField("buyout")) :Draw() bidInput:SetFocused(false) :SetValue(row:GetField("bid")) :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 row = TSM.Auctioning.PostScan.GetCurrentRow() local stackSize = row:GetField("stackSize") local bidInput = button:GetElement("__parent.__parent.bid.input") local buyoutInput = button:GetElement("__parent.__parent.buyout.input") buyoutInput:SetFocused(false) :SetValue(row:GetField("buyout") * stackSize) :Draw() bidInput:SetFocused(false) :SetValue(row:GetField("bid") * stackSize) :Draw() end function private.UpdateSaveButtonState(frame) local bidInput = frame:GetElement("bid.input") local buyoutInput = frame:GetElement("buyout.input") local bid = private.ParseBidBuyout(bidInput:GetValue()) local buyout = private.ParseBidBuyout(buyoutInput:GetValue()) frame:GetElement("saveBtn") :SetDisabled(bid > buyout or not bidInput:IsValid() or not buyoutInput:IsValid()) :Draw() end function private.BidBuyoutOnValidationChanged(input) private.UpdateSaveButtonState(input:GetElement("__parent.__parent")) end function private.BidBuyoutInputOnValueChanged(input) local frame = input:GetElement("__parent.__parent") local context = frame:GetElement("saveBtn"):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(context.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.UpdateSaveButtonState(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.UpdateSaveButtonState(frame) 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