-- ------------------------------------------------------------------------------ -- -- TradeSkillMaster_Destroying -- -- http://www.curse.com/addons/wow/tradeskillmaster_destroying -- -- -- -- A TradeSkillMaster Addon (http://tradeskillmaster.com) -- -- All Rights Reserved* - Detailed license information included with addon. -- -- ------------------------------------------------------------------------------ -- -- load the parent file (TSM) into a local variable and register this file as a module local TSM = select(2, ...) local GUI = TSM:NewModule("GUI", "AceEvent-3.0") -- loads the localization table -- local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Destroying") local private = {data={}, ignore={}} TSMAPI:RegisterForTracing(private, "TSM_Destroying.GUI_private") function GUI:OnEnable() private.frame = private:CreateDestroyingFrame() TSMAPI:CreateEventBucket("BAG_UPDATE", function() private:UpdateST() end, 0.2) GUI:RegisterEvent("LOOT_SLOT_CLEARED", private.LootChanged) GUI:RegisterEvent("LOOT_OPENED", private.LootOpened) GUI:RegisterEvent("LOOT_CLOSED", private.LootChanged) GUI:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED") GUI:RegisterEvent("UI_ERROR_MESSAGE", function(_, msg) if msg == ERR_INVALID_ITEM_TARGET or msg == SPELL_FAILED_ERROR then private.LootChanged() end end) TSMAPI:CreateTimeDelay("destroyingSTUpdate", 1, function() private:UpdateST() end) end function GUI:ShowFrame() private.hidden = nil private:UpdateST(true) end function private:CreateDestroyingFrame() local frameDefaults = { x = 850, y = 450, width = 200, height = 220, scale = 1, } local frame = TSMAPI:CreateMovableFrame("TSMDestroyingFrame", frameDefaults) frame:SetFrameStrata("HIGH") TSMAPI.Design:SetFrameBackdropColor(frame) local title = TSMAPI.GUI:CreateLabel(frame) title:SetText("TSM_Destroying") title:SetPoint("TOPLEFT") title:SetPoint("TOPRIGHT") title:SetHeight(20) local line = TSMAPI.GUI:CreateVerticalLine(frame, 0) line:ClearAllPoints() line:SetPoint("TOPRIGHT", -25, -1) line:SetWidth(2) line:SetHeight(22) local closeBtn = TSMAPI.GUI:CreateButton(frame, 18) closeBtn:SetPoint("TOPRIGHT", -3, -3) closeBtn:SetWidth(19) closeBtn:SetHeight(19) closeBtn:SetText("X") closeBtn:SetScript("OnClick", function() if InCombatLockdown() then return end TSM:Print(L["Hiding frame for the remainder of this session. Typing '/tsm destroy' will open the frame again."]) private.hidden = true frame:Hide() end) TSMAPI.GUI:CreateHorizontalLine(frame, -23) local stContainer = CreateFrame("Frame", nil, frame) stContainer:SetPoint("TOPLEFT", 0, -25) stContainer:SetPoint("BOTTOMRIGHT", 0, 30) TSMAPI.Design:SetFrameColor(stContainer) local stCols = { { name = L["Item"], width = 0.6, }, { name = L["Stack Size"], width = 0.4, align = "CENTER", }, } local handlers = { OnClick = function(_, data, self, button) if not data then return end if button == "RightButton" then if IsShiftKeyDown() then TSM.db.global.ignore[data.itemString] = true TSM:Printf(L["Ignoring all %s permanently. You can undo this in the Destroying options."], data.link) TSM.Options:UpdateIgnoreST() else private.ignore[data.itemString] = true TSM:Printf(L["Ignoring all %s this session (until your UI is reloaded)."], data.link) end private:UpdateST() end end, OnEnter = function(_, data, self) if not data then return end local color = TSMAPI.Design:GetInlineColor("link") GameTooltip:SetOwner(self, "ANCHOR_RIGHT") TSMAPI:SafeTooltipLink(data.itemString) -- GameTooltip:AddLine(data.link) GameTooltip:AddLine("- - -") GameTooltip:AddLine(format(L["%sRight-Click|r to ignore this item for this session. Hold %sshift|r to ignore permanently. You can remove items from permanent ignore in the Destroying options."], color, color), 1, 1, 1, 1, true) GameTooltip:Show() end, OnLeave = function() GameTooltip:ClearLines() GameTooltip:Hide() end } local st = TSMAPI:CreateScrollingTable(stContainer, stCols, handlers, 12) st:SetData({}) st:DisableSelection(true) frame.st = st local destroyBtn = TSMAPI.GUI:CreateButton(frame, 14, "TSMDestroyButton", true) destroyBtn:SetPoint("BOTTOMLEFT", 3, 3) destroyBtn:SetPoint("BOTTOMRIGHT", -3, 3) destroyBtn:SetHeight(20) destroyBtn:SetText(L["Destroy Next"]) destroyBtn:SetAttribute("type1", "macro") destroyBtn:SetAttribute("macrotext1", "") destroyBtn:SetScript("PreClick", function() if not destroyBtn:IsVisible() or #private.data == 0 then destroyBtn:SetAttribute("macrotext1", "") else local data = private.data[1] private.tempData = data destroyBtn:SetAttribute("macrotext1", format("/cast %s;\n/use %d %d", data.spell, data.bag, data.slot)) destroyBtn:Disable() TSMAPI:CancelFrame("destroyEnableDelay") TSMAPI:CreateTimeDelay("destroyEnableDelay", 3, function() if not UnitCastingInfo("player") and not LootFrame:IsVisible() then destroyBtn:Enable() end end) private.highStack = data.numDestroys > 1 private.currentSpell = data.spell end end) frame.destroyBtn = destroyBtn return frame end -- combine partial stacks function private:Stack() local partialStacks = {} for bag, slot, itemString, quantity in TSMAPI:GetBagIterator(nil, TSM.db.global.includeSoulbound) do local spell, perDestroy = TSM:IsDestroyable(bag, slot, itemString) if spell and quantity % perDestroy ~= 0 and not private.ignore[itemString] and not TSM.db.global.ignore[itemString] then partialStacks[itemString] = partialStacks[itemString] or {} tinsert(partialStacks[itemString], {bag, slot}) end end for itemString, locations in pairs(partialStacks) do for i=#locations, 2, -1 do local quantity = select(2, GetContainerItemInfo(unpack(locations[i]))) local maxStack = select(8, GetItemInfo(itemString)) if quantity == 0 or quantity == maxStack then break end for j=1, i-1 do local targetQuantity = select(2, GetContainerItemInfo(unpack(locations[j]))) if targetQuantity ~= maxStack then PickupContainerItem(unpack(locations[i])) PickupContainerItem(unpack(locations[j])) end end end end end local isDelayed function private:UpdateST(forceShow) if private.hidden then return end if (not private.frame or not private.frame:IsVisible()) and not forceShow and not isDelayed then TSMAPI:CreateTimeDelay("destroyBagUpdateDelay2", 1, function() isDelayed = true private:UpdateST() isDelayed = nil end) return end if InCombatLockdown() then return end if TSM.db.global.autoStack then private:Stack() end local stData = {} for bag, slot, itemString, quantity in TSMAPI:GetBagIterator(nil, TSM.db.global.includeSoulbound) do if not private.ignore[itemString] and not TSM.db.global.ignore[itemString] then local spell, perDestroy = TSM:IsDestroyable(bag, slot, itemString) local link = GetContainerItemLink(bag, slot) if spell and quantity >= perDestroy then local row = { cols = { { value = link, }, { value = quantity }, }, itemString = itemString, link = link, quantity = quantity, bag = bag, slot = slot, spell = spell, perDestroy = perDestroy, numDestroys = floor(quantity/perDestroy), } tinsert(stData, row) end end end if #stData == 0 then if forceShow then TSM:Print(L["Nothing to destroy in your bags."]) end private.frame.destroyBtn:Disable() private.frame:Hide() elseif (TSM.db.global.autoShow or forceShow) and not private.frame:IsVisible() then TSMAPI:CancelFrame("destroyEnableDelay") private.frame.destroyBtn:Enable() private.frame:Show() end private.data = CopyTable(stData) private.frame.st:SetData(stData) end function GUI:UNIT_SPELLCAST_INTERRUPTED(_, unit) if unit == "player" and private.frame and private.frame:IsVisible() then TSMAPI:CancelFrame("destroyEnableDelay") if InCombatLockdown() then TSMAPI:CreateTimeDelay("destroyEnableDelay", 1, function() if not UnitCastingInfo("player") and not LootFrame:IsVisible() and not InCombatLockdown() then private.frame.destroyBtn:Enable() end end) else private.frame.destroyBtn:Enable() end end end function private:LootOpened() if not private.currentSpell then return end local temp = {result={}, time=time()} for bag, slot, itemString, quantity, locked in TSMAPI:GetBagIterator(nil, TSM.db.global.includeSoulbound) do if locked and TSM:IsDestroyable(bag, slot, itemString) then temp.item = itemString break end end if temp.item and GetNumLootItems() > 0 then for i=1, GetNumLootItems() do local itemString = TSMAPI:GetItemString(GetLootSlotLink(i)) local quantity = select(3, GetLootSlotInfo(i)) or 0 if itemString and quantity > 0 then temp.result[itemString] = quantity end end TSM.db.global.history[private.currentSpell] = TSM.db.global.history[private.currentSpell] or {} tinsert(TSM.db.global.history[private.currentSpell], temp) TSM.Options:UpdateLogST() end private.currentSpell = nil end function private:LootChanged() if not private.tempData then return end if not LootFrame:IsVisible() then if private.frame and private.frame:IsVisible() then local quantity = select(2, GetContainerItemInfo(private.tempData.bag, private.tempData.slot)) if quantity == private.tempData.quantity or quantity == private.tempData.perDestroy then TSMAPI:CancelFrame("destroyEnableDelay") if InCombatLockdown() then TSMAPI:CreateTimeDelay("destroyEnableDelay", 1, function() if not UnitCastingInfo("player") and not LootFrame:IsVisible() and not InCombatLockdown() then private.frame.destroyBtn:Enable() end end) else private.frame.destroyBtn:Enable() end end end elseif private.highStack and GetNumLootItems() <= 1 then if private.frame and private.frame:IsVisible() then TSMAPI:CancelFrame("destroyEnableDelay") if InCombatLockdown() then TSMAPI:CreateTimeDelay("destroyEnableDelay", 1, function() if not UnitCastingInfo("player") and not LootFrame:IsVisible() and not InCombatLockdown() then private.frame.destroyBtn:Enable() end end) else private.frame.destroyBtn:Enable() end end end end