Zerotorescue@0: -- You can access this addon's object through: LibStub("AceAddon-3.0"):GetAddon("Inventory") Zerotorescue@0: local addon = LibStub("AceAddon-3.0"):NewAddon("Inventory", "AceEvent-3.0", "AceTimer-3.0"); Zerotorescue@0: Zerotorescue@0: local Media = LibStub("LibSharedMedia-3.0"); Zerotorescue@0: Media:Register("sound", "Cartoon FX", [[Sound\Doodad\Goblin_Lottery_Open03.wav]]); Zerotorescue@0: Media:Register("sound", "Cheer", [[Sound\Event Sounds\OgreEventCheerUnique.wav]]); Zerotorescue@0: Media:Register("sound", "Explosion", [[Sound\Doodad\Hellfire_Raid_FX_Explosion05.wav]]); Zerotorescue@0: Media:Register("sound", "Fel Nova", [[Sound\Spells\SeepingGaseous_Fel_Nova.wav]]); Zerotorescue@0: Media:Register("sound", "Fel Portal", [[Sound\Spells\Sunwell_Fel_PortalStand.wav]]); Zerotorescue@0: Media:Register("sound", "Magic Click", [[Sound\interface\MagicClick.wav]]); Zerotorescue@0: Media:Register("sound", "Rubber Ducky", [[Sound\Doodad\Goblin_Lottery_Open01.wav]]); Zerotorescue@0: Media:Register("sound", "Shing!", [[Sound\Doodad\PortcullisActive_Closed.wav]]); Zerotorescue@0: Media:Register("sound", "Simon Chime", [[Sound\Doodad\SimonGame_LargeBlueTree.wav]]); Zerotorescue@0: Media:Register("sound", "Simon Error", [[Sound\Spells\SimonGame_Visual_BadPress.wav]]); Zerotorescue@0: Media:Register("sound", "Simon Start", [[Sound\Spells\SimonGame_Visual_GameStart.wav]]); Zerotorescue@0: Media:Register("sound", "War Drums", [[Sound\Event Sounds\Event_wardrum_ogre.wav]]); Zerotorescue@0: Media:Register("sound", "Wham!", [[Sound\Doodad\PVP_Lordaeron_Door_Open.wav]]); Zerotorescue@0: Media:Register("sound", "Whisper Ping", [[Sound\interface\iTellMessage.wav]]); Zerotorescue@0: Media:Register("sound", "You Will Die!", [[Sound\Creature\CThun\CThunYouWillDIe.wav]]); Zerotorescue@0: Zerotorescue@0: local AceConfigDialog, AceConfigRegistry, AceSerializer; Zerotorescue@0: local groupIdToName = {}; Zerotorescue@0: local options = {}; Zerotorescue@0: Zerotorescue@0: function addon:OnInitialize() Zerotorescue@0: self:Debug("OnInitialize"); Zerotorescue@0: Zerotorescue@0: -- SAVED VARIABLES Zerotorescue@0: Zerotorescue@0: local defaults = { Zerotorescue@0: global = { Zerotorescue@0: groups = {}, Zerotorescue@0: defaults = { Zerotorescue@0: minimumStock = 60, Zerotorescue@0: alertBelowMinimum = true, Zerotorescue@0: summaryThresholdShow = 10, Zerotorescue@0: restockTarget = 60, Zerotorescue@0: minCraftingQueue = 0.05, Zerotorescue@0: bonusQueue = 0.1, Zerotorescue@0: priceThreshold = 0, Zerotorescue@0: trackAtCharacters = {}, Zerotorescue@0: colors = { Zerotorescue@0: red = 0; Zerotorescue@0: orange = 0.3; Zerotorescue@0: yellow = 0.6; Zerotorescue@0: green = 0.95; Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: factionrealm = { Zerotorescue@0: characters = {}, Zerotorescue@0: }, Zerotorescue@0: }; Zerotorescue@0: Zerotorescue@0: -- Register our saved variables database Zerotorescue@0: self.db = LibStub("AceDB-3.0"):New("InventoryDB", defaults, true); Zerotorescue@0: Zerotorescue@0: -- SLASH COMMANDS Zerotorescue@0: Zerotorescue@0: -- Disable the AddonLoader slash commands Zerotorescue@0: SLASH_INVENTORY1 = nil; Zerotorescue@0: SLASH_IY1 = nil; Zerotorescue@0: Zerotorescue@0: -- Register our own slash commands Zerotorescue@0: SLASH_INVENTORY1 = "/inventory"; Zerotorescue@0: SLASH_INVENTORY2 = "/iy"; Zerotorescue@0: SlashCmdList["INVENTORY"] = function(msg) Zerotorescue@0: self:CommandHandler(msg); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- INTERFACE OPTIONS Zerotorescue@0: Zerotorescue@0: -- Attempt to remove the interface options added by AddonLoader (if enabled) Zerotorescue@0: if AddonLoader and AddonLoader.RemoveInterfaceOptions then Zerotorescue@0: AddonLoader:RemoveInterfaceOptions("Inventory"); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- Now create our own options frame Zerotorescue@0: local frame = CreateFrame("Frame", nil, UIParent); Zerotorescue@0: frame:Hide(); Zerotorescue@0: frame.name = "Inventory"; Zerotorescue@0: frame:HookScript("OnShow", function(self) Zerotorescue@0: -- Refresh the frame to instantly show the right options Zerotorescue@0: InterfaceOptionsFrame_OpenToCategory(self.name) Zerotorescue@0: end); Zerotorescue@0: -- And add it to the interface options Zerotorescue@0: InterfaceOptions_AddCategory(frame); Zerotorescue@0: Zerotorescue@0: self:MakeWidgets(); Zerotorescue@0: Zerotorescue@0: -- Remember this character is mine Zerotorescue@0: local playerName = UnitName("player"); Zerotorescue@0: if not self.db.factionrealm.characters[playerName] then Zerotorescue@0: self.db.factionrealm.characters[playerName] = true; Zerotorescue@0: Zerotorescue@0: -- Default to tracking on all chars, untracking is a convenience, not tracking by default would probably get multiple issue reports. Zerotorescue@0: self.db.global.defaults.trackAtCharacters[playerName] = true; Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: function addon:MakeWidgets() Zerotorescue@0: local AceGUI = LibStub("AceGUI-3.0"); Zerotorescue@0: Zerotorescue@0: --[[ Zerotorescue@0: [ ItemLinkButton ] Zerotorescue@0: UserData: itemId, onClick event Zerotorescue@0: OnEnter: tooltip show Zerotorescue@0: OnLeave: tooltip hid Zerotorescue@0: Zerotorescue@0: ]] Zerotorescue@0: Zerotorescue@0: -- Define out custom item link button widget Zerotorescue@0: -- This will be called as if it's an input element, we overwrite some of the related functions which are called for default input fields Zerotorescue@0: Zerotorescue@0: local widgetType = "ItemLinkButton"; Zerotorescue@0: local widgetVersion = 1; Zerotorescue@0: Zerotorescue@0: -- Empty function for disabling functions Zerotorescue@0: local function Dummy() end Zerotorescue@0: Zerotorescue@0: -- Overwrite the SetText-function of our custom widgets Zerotorescue@0: local function CustomWidgetSetText(self, value, ...) Zerotorescue@0: if value then Zerotorescue@0: -- Remember the itemId in a local parameter (using :GetText, we'd have to run a pattern over it and it would all get silly) Zerotorescue@0: self.itemId = tonumber(value); Zerotorescue@0: Zerotorescue@0: if not self.itemId then error("itemId is not a number."); end Zerotorescue@0: Zerotorescue@0: -- Put the icon in front of it Zerotorescue@0: self:SetImage(GetItemIcon(self.itemId)); Zerotorescue@0: -- Standardize the size Zerotorescue@0: self:SetImageSize(16, 16); Zerotorescue@0: Zerotorescue@0: -- Make readable font Zerotorescue@0: self:SetFontObject(GameFontHighlight); Zerotorescue@0: Zerotorescue@0: -- We don't want to set the itemId as text, but rather the item link, so get that. Zerotorescue@0: value = select(2, GetItemInfo(value)) or ("Unknown (#%d)"):format(value); Zerotorescue@0: Zerotorescue@0: return self.originalSetText(self, value, ...); Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- Overwrite the OnEnter-event of our custom widgets to use our own function Zerotorescue@0: local function CustomWidgetOnEnter(self) Zerotorescue@0: if self.itemId then Zerotorescue@0: GameTooltip:SetOwner(self.frame, "ANCHOR_TOPRIGHT"); Zerotorescue@0: GameTooltip:SetHyperlink(("item:%d"):format(self.itemId)); Zerotorescue@0: GameTooltip:Show(); Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- Overwrite the OnClick-event of our custom widgets to use our own function Zerotorescue@0: local function CustomWidgetOnClick(self) Zerotorescue@0: local info = self:GetUserDataTable() Zerotorescue@0: Zerotorescue@0: info.option.set(self, info); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- Overwrite the SetCallback-function of our custom widgets Zerotorescue@0: local function CustomWidgetSetCallback(self, event, func, ...) Zerotorescue@0: if event == "OnEnter" then Zerotorescue@0: -- This event is called once when creating the widget Zerotorescue@0: -- When it becomes hidden, all events appear to be removed, but once it's shown again, OnEnter will be re-applied Zerotorescue@0: -- Hence any registered Callback must be done in here Zerotorescue@0: Zerotorescue@0: self.originalSetCallBack(self, "OnClick", CustomWidgetOnClick); Zerotorescue@0: Zerotorescue@0: return self.originalSetCallBack(self, event, CustomWidgetOnEnter, ...); Zerotorescue@0: else Zerotorescue@0: return self.originalSetCallBack(self, event, func, ...); Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function CustomWidgetHideTooltip() Zerotorescue@0: GameTooltip:Hide(); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- Makes an instance of our ItemLinkButton widget Zerotorescue@0: local function GetItemLinkButton() Zerotorescue@0: local widget = AceGUI:Create("InteractiveLabel"); Zerotorescue@0: widget.type = widgetType; Zerotorescue@0: Zerotorescue@0: -- We can only provide custom widgets for input, select and multiselect fields Zerotorescue@0: -- Input being the simplest, we use that - however, it provides two parameters: label and text. We only need one, disable the other. Zerotorescue@0: widget.SetLabel = Dummy; Zerotorescue@0: Zerotorescue@0: if not widget.originalSetText then Zerotorescue@0: -- When setting text we provide an itemId Zerotorescue@0: -- Use this itemId to set the icon and do all fancy stuff Zerotorescue@0: widget.originalSetText = widget.SetText; Zerotorescue@0: widget.SetText = CustomWidgetSetText; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: if not widget.originalSetCallBack then Zerotorescue@0: -- We don't want AceConfig to overwrite our OnEnter with the tooltip functions, so override that Zerotorescue@0: widget.originalSetCallBack = widget.SetCallback; Zerotorescue@0: widget.SetCallback = CustomWidgetSetCallback; Zerotorescue@0: Zerotorescue@0: -- Make sure it's called (AceConfig will probably repeat this, but we are prepared in case it's ever changed) Zerotorescue@0: widget:SetCallback("OnEnter", Dummy); Zerotorescue@0: widget:SetCallback("OnLeave", CustomWidgetHideTooltip); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: return widget; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: AceGUI:RegisterWidgetType(widgetType, GetItemLinkButton, widgetVersion); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: function addon:CommandHandler(message) Zerotorescue@0: local cmd, arg = string.split(" ", (message or ""), 2); Zerotorescue@0: cmd = string.lower(cmd); Zerotorescue@0: Zerotorescue@0: if cmd == "c" or cmd == "config" or cmd == "conf" or cmd == "option" or cmd == "options" or cmd == "opt" or cmd == "setting" or cmd == "settings" then Zerotorescue@0: self:Show(); Zerotorescue@0: elseif cmd == "d" or cmd == "debug" then Zerotorescue@0: self.debugChannel = false; Zerotorescue@0: for i = 1, NUM_CHAT_WINDOWS do Zerotorescue@0: local name = GetChatWindowInfo(i); Zerotorescue@0: Zerotorescue@0: if name:upper() == "DEBUG" then Zerotorescue@0: self.debugChannel = _G["ChatFrame" .. i]; Zerotorescue@0: Zerotorescue@0: print("A debug channel already exists, used the old one. (" .. i .. ")"); Zerotorescue@0: return; Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if not self.debugChannel then Zerotorescue@0: -- Create a new debug channel Zerotorescue@0: local chatFrame = FCF_OpenNewWindow('Debug'); Zerotorescue@0: ChatFrame_RemoveAllMessageGroups(chatFrame); Zerotorescue@0: self.debugChannel = chatFrame; Zerotorescue@0: Zerotorescue@0: print("New debug channel created."); Zerotorescue@0: end Zerotorescue@0: else Zerotorescue@0: print("Wrong command, available: /inventory config (or /inventory c)"); Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: function addon:Load() Zerotorescue@0: if not AceConfigDialog and not AceConfigRegistry then Zerotorescue@0: self:FillOptions(); Zerotorescue@0: Zerotorescue@0: -- Build options dialog Zerotorescue@0: AceConfigDialog = LibStub("AceConfigDialog-3.0"); Zerotorescue@0: AceConfigRegistry = LibStub("AceConfigRegistry-3.0"); Zerotorescue@0: -- Register options table Zerotorescue@0: LibStub("AceConfig-3.0"):RegisterOptionsTable("InventoryOptions", options); Zerotorescue@0: -- Set a nice default size (so that 4 normal sized elements fit next to eachother) Zerotorescue@0: AceConfigDialog:SetDefaultSize("InventoryOptions", 975, 600); Zerotorescue@0: Zerotorescue@0: -- In case the addon is loaded from another condition, always call the remove interface options Zerotorescue@0: if AddonLoader and AddonLoader.RemoveInterfaceOptions then Zerotorescue@0: AddonLoader:RemoveInterfaceOptions("Inventory"); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- Add to the blizzard addons options thing Zerotorescue@0: --AceConfigDialog:AddToBlizOptions("InventoryOptions"); Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: function addon:Show() Zerotorescue@0: self:Load(); Zerotorescue@0: Zerotorescue@0: AceConfigDialog:Open("InventoryOptions"); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: function addon:FillOptions() Zerotorescue@0: options = { Zerotorescue@0: type = "group", Zerotorescue@0: name = "Inventory", Zerotorescue@0: childGroups = "tree", Zerotorescue@0: args = { Zerotorescue@0: }, Zerotorescue@0: }; Zerotorescue@0: Zerotorescue@0: self:FillGeneralOptions(); Zerotorescue@0: Zerotorescue@0: options.args.profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db, true); Zerotorescue@0: options.args.profiles.order = 200; Zerotorescue@0: Zerotorescue@0: self:MakeGroupOptions(); Zerotorescue@0: Zerotorescue@0: self:FillGroupOptions(); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: function addon:FillGeneralOptions() Zerotorescue@0: options.args.general = { Zerotorescue@0: order = 100, Zerotorescue@0: type = "group", Zerotorescue@0: name = "General", Zerotorescue@0: desc = "Change general Inventory settings.", Zerotorescue@0: args = { Zerotorescue@0: general = { Zerotorescue@0: order = 0, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "General", Zerotorescue@0: args = { Zerotorescue@0: description = { Zerotorescue@0: order = 0, Zerotorescue@0: type = "description", Zerotorescue@0: name = "Change general settings unrelated to groups.", Zerotorescue@0: }, Zerotorescue@0: header = { Zerotorescue@0: order = 5, Zerotorescue@0: type = "header", Zerotorescue@0: name = "", Zerotorescue@0: }, Zerotorescue@0: auctionAddon = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "select", Zerotorescue@0: name = "Prefered pricing addon", Zerotorescue@0: values = { Zerotorescue@0: Auctioneer = "Auctioneer", Zerotorescue@0: Auctionator = "Auctionator", Zerotorescue@0: }, Zerotorescue@0: get = function() end, Zerotorescue@0: set = function(i, v) end, Zerotorescue@0: }, Zerotorescue@0: itemCountAddon = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "select", Zerotorescue@0: name = "Prefered item count addon", Zerotorescue@0: values = { Zerotorescue@0: Altoholic = "Altoholic", Zerotorescue@0: DataStore = "DataStore", Zerotorescue@0: ItemCount = "ItemCount", Zerotorescue@0: }, Zerotorescue@0: get = function() end, Zerotorescue@0: set = function(i, v) end, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: minimumStock = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "Minimum stock", Zerotorescue@0: args = { Zerotorescue@0: description = { Zerotorescue@0: order = 0, Zerotorescue@0: type = "description", Zerotorescue@0: name = "Here you can specify the default minimum amount of items you wish to keep in stock and related settings. The settings entered here will be used when you choose not to override the settings within an individual group.", Zerotorescue@0: }, Zerotorescue@0: header = { Zerotorescue@0: order = 5, Zerotorescue@0: type = "header", Zerotorescue@0: name = "", Zerotorescue@0: }, Zerotorescue@0: minimumStock = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 100000, Zerotorescue@0: softMax = 1000, Zerotorescue@0: step = 1, Zerotorescue@0: name = "Minimum stock", Zerotorescue@0: desc = "You can manually enter a value between 1.000 and 100.000 in the edit box if the provided range is insufficient.", Zerotorescue@0: get = function() return self.db.global.defaults.minimumStock; end, Zerotorescue@0: set = function(i, v) self.db.global.defaults.minimumStock = v; end, Zerotorescue@0: }, Zerotorescue@0: summaryThresholdShow = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 100, Zerotorescue@0: softMax = 10, Zerotorescue@0: step = 0.05, Zerotorescue@0: isPercent = true, Zerotorescue@0: name = "Show in summary when below", Zerotorescue@0: desc = "Show items in the summary when below this percentage of the minimum stock.\n\nYou can manually enter a value between 1.000% and 10.000% in the edit box if the provided range is insufficient.", Zerotorescue@0: get = function() return self.db.global.defaults.summaryThresholdShow; end, Zerotorescue@0: set = function(i, v) self.db.global.defaults.summaryThresholdShow = v; end, Zerotorescue@0: }, Zerotorescue@0: alertBelowMinimum = { Zerotorescue@0: order = 30, Zerotorescue@0: type = "toggle", Zerotorescue@0: name = "Alert when below minimum", Zerotorescue@0: desc = "Show an alert when this item gets below this threshold.", Zerotorescue@0: get = function() return self.db.global.defaults.alertBelowMinimum; end, Zerotorescue@0: set = function(i, v) self.db.global.defaults.alertBelowMinimum = v; end, Zerotorescue@0: }, Zerotorescue@0: trackAtCharacters = { Zerotorescue@0: order = 40, Zerotorescue@0: type = "multiselect", Zerotorescue@0: control = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead. Zerotorescue@0: name = "Track at", Zerotorescue@0: desc = "Select at which characters this should appear in the summary and generate alerts.", Zerotorescue@0: values = function() Zerotorescue@0: local temp = {}; Zerotorescue@0: for charName in pairs(self.db.factionrealm.characters) do Zerotorescue@0: temp[charName] = charName; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: return temp; Zerotorescue@0: end, Zerotorescue@0: get = function(i, v) Zerotorescue@0: return self.db.global.defaults.trackAtCharacters[v]; Zerotorescue@0: end, Zerotorescue@0: set = function(i, v, e) Zerotorescue@0: self.db.global.defaults.trackAtCharacters[v] = e or nil; Zerotorescue@0: end, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: refill = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "Replenishing stock", Zerotorescue@0: args = { Zerotorescue@0: description = { Zerotorescue@0: order = 0, Zerotorescue@0: type = "description", Zerotorescue@0: name = function() Zerotorescue@0: local r = "Here you can specify the default amount of items to which you wish to restock when you are collecting new items. This may be higher than the minimum stock. The settings entered here will be used when you choose not to override the settings within an individual group.\n\n"; Zerotorescue@0: Zerotorescue@0: r = r .. "When restocking the target amount is |cfffed000" .. self.db.global.defaults.restockTarget .. "|r of every item. Not queueing craftable items when only missing |cfffed000" .. floor( self.db.global.defaults.minCraftingQueue * self.db.global.defaults.restockTarget ) .. "|r (|cfffed000" .. ( self.db.global.defaults.minCraftingQueue * 100 ) .. "%|r) of the restock target."; Zerotorescue@0: Zerotorescue@0: return r; Zerotorescue@0: end, Zerotorescue@0: }, Zerotorescue@0: header = { Zerotorescue@0: order = 5, Zerotorescue@0: type = "header", Zerotorescue@0: name = "", Zerotorescue@0: }, Zerotorescue@0: restockTarget = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 100000, Zerotorescue@0: softMax = 1000, Zerotorescue@0: step = 1, Zerotorescue@0: name = "Restock target", Zerotorescue@0: desc = "You can manually enter a value between 1.000 and 100.000 in the edit box if the provided range is insufficient.", Zerotorescue@0: get = function() return self.db.global.defaults.restockTarget; end, Zerotorescue@0: set = function(i, v) self.db.global.defaults.restockTarget = v; end, Zerotorescue@0: }, Zerotorescue@0: minCraftingQueue = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 1, Zerotorescue@0: step = 0.01, -- 1% Zerotorescue@0: isPercent = true, Zerotorescue@0: name = "Don't queue if I only miss", Zerotorescue@0: desc = "Don't add a craftable item to the queue if I only miss this much or less of the restock target.\n\nExample: if your restock target is set to 60 and this is set to 5%, an item won't be queued unless you are missing more than 3 of it.", Zerotorescue@0: get = function() return self.db.global.defaults.minCraftingQueue; end, Zerotorescue@0: set = function(i, v) self.db.global.defaults.minCraftingQueue = v; end, Zerotorescue@0: }, Zerotorescue@0: bonusQueue = { Zerotorescue@0: order = 30, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 10, -- 1000% Zerotorescue@0: step = 0.01, -- 1% Zerotorescue@0: isPercent = true, Zerotorescue@0: name = "Bonus queue", Zerotorescue@0: desc = "Get additional items when I have none left.\n\nExample: if your restock target is set to 60 and this is set to 10%, you will get 66 items instead of just 60 if you end up with none left.", Zerotorescue@0: get = function() return self.db.global.defaults.bonusQueue; end, Zerotorescue@0: set = function(i, v) self.db.global.defaults.bonusQueue = v; end, Zerotorescue@0: }, Zerotorescue@0: priceThreshold = { Zerotorescue@0: order = 40, Zerotorescue@0: type = "input", Zerotorescue@0: name = "Price threshold", Zerotorescue@0: desc = "Only queue craftable items when they are worth at least this much according to your auction house addon.", Zerotorescue@0: validate = function() Zerotorescue@0: -- gold check Zerotorescue@0: end, Zerotorescue@0: get = function() return self.db.global.defaults.priceThreshold; end, Zerotorescue@0: set = function(i, v) self.db.global.defaults.priceThreshold = v; end, Zerotorescue@0: }, Zerotorescue@0: hideFromSummaryPriceThreshold = { Zerotorescue@0: order = 50, Zerotorescue@0: type = "toggle", Zerotorescue@0: name = "Don't show when below threshold", Zerotorescue@0: desc = "Hide items from the summary when their value is below the set price threshold.", Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: colorCodes = { Zerotorescue@0: order = 30, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "Color codes", Zerotorescue@0: args = { Zerotorescue@0: description = { Zerotorescue@0: order = 0, Zerotorescue@0: type = "description", Zerotorescue@0: name = "Change the color code thresholds based on the current stock remaining of the required minimum stock.", Zerotorescue@0: }, Zerotorescue@0: header = { Zerotorescue@0: order = 5, Zerotorescue@0: type = "header", Zerotorescue@0: name = "", Zerotorescue@0: }, Zerotorescue@0: green = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 1, Zerotorescue@0: step = 0.01, Zerotorescue@0: isPercent = true, Zerotorescue@0: name = "|cff00ff00Green|r", Zerotorescue@0: desc = "Show quantity in green when at least this much of the minimum stock is available.", Zerotorescue@0: get = function() return self.db.global.defaults.colors.green; end, Zerotorescue@0: set = function(i, v) self.db.global.defaults.colors.green = v; end, Zerotorescue@0: }, Zerotorescue@0: yellow = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 1, Zerotorescue@0: step = 0.01, Zerotorescue@0: isPercent = true, Zerotorescue@0: name = "|cffffff00Yellow|r", Zerotorescue@0: desc = "Show quantity in yellow when at least this much of the minimum stock is available.", Zerotorescue@0: get = function() return self.db.global.defaults.colors.yellow; end, Zerotorescue@0: set = function(i, v) self.db.global.defaults.colors.yellow = v; end, Zerotorescue@0: }, Zerotorescue@0: orange = { Zerotorescue@0: order = 30, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 1, Zerotorescue@0: step = 0.01, Zerotorescue@0: isPercent = true, Zerotorescue@0: name = "|cffff9933Orange|r", Zerotorescue@0: desc = "Show quantity in orange when at least this much of the minimum stock is available.", Zerotorescue@0: get = function() return self.db.global.defaults.colors.orange; end, Zerotorescue@0: set = function(i, v) self.db.global.defaults.colors.orange = v; end, Zerotorescue@0: }, Zerotorescue@0: red = { Zerotorescue@0: order = 40, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 1, Zerotorescue@0: step = 0.01, Zerotorescue@0: isPercent = true, Zerotorescue@0: name = "|cffff0000Red|r", Zerotorescue@0: desc = "Show quantity in red when at least this much of the minimum stock is available.", Zerotorescue@0: get = function() return self.db.global.defaults.colors.red; end, Zerotorescue@0: set = function(i, v) self.db.global.defaults.colors.red = v; end, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local count = 0; Zerotorescue@0: local temp = {}; Zerotorescue@0: Zerotorescue@0: local function SetOption(info, value) Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: local optionName = info[#info]; Zerotorescue@0: Zerotorescue@0: -- No need to store a setting if it's disabled (false) Zerotorescue@0: if not value and info.arg and not info.arg:find("override") then Zerotorescue@0: value = nil; Zerotorescue@0: Zerotorescue@0: -- If this is an override toggler then also set the related field to nil Zerotorescue@0: addon.db.global.groups[groupName][info.arg] = nil; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: addon.db.global.groups[groupName][optionName] = value; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function GetOptionByKey(groupName, optionName, noDefault) Zerotorescue@0: if addon.db.global.groups[groupName][optionName] ~= nil then Zerotorescue@0: return addon.db.global.groups[groupName][optionName]; Zerotorescue@0: elseif addon.db.global.defaults[optionName] and not noDefault then Zerotorescue@0: return addon.db.global.defaults[optionName]; Zerotorescue@0: else Zerotorescue@0: return nil; Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function GetOption(info) Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: local optionName = info[#info]; Zerotorescue@0: Zerotorescue@0: return GetOptionByKey(groupName, optionName); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function GetDisabled(info) Zerotorescue@0: if not info.arg or not info.arg:find("override") then Zerotorescue@0: return false; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: local optionName = info[#info]; Zerotorescue@0: Zerotorescue@0: return (GetOptionByKey(groupName, info.arg, true) == nil); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function ValidateGroupName(_, value) Zerotorescue@0: value = string.lower(string.trim(value or "")); Zerotorescue@0: Zerotorescue@0: for name, _ in pairs(addon.db.global.groups) do Zerotorescue@0: if string.lower(name) == value then Zerotorescue@0: return ("A group named \"%s\" already exists."):format(name); Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: return true; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function InGroup(itemId) Zerotorescue@0: -- Go through all groups to see if this item is already somewhere Zerotorescue@0: for groupName, values in pairs(addon.db.global.groups) do Zerotorescue@0: if values.items and values.items[itemId] then Zerotorescue@0: return groupName; Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: return; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function AddToGroup(groupName, itemId) Zerotorescue@0: if InGroup(itemId) then Zerotorescue@0: return false; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if not addon.db.global.groups[groupName].items then Zerotorescue@0: addon.db.global.groups[groupName].items = {}; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- Set this item Zerotorescue@0: addon.db.global.groups[groupName].items[itemId] = true; Zerotorescue@0: Zerotorescue@0: -- Now rebuild the list Zerotorescue@0: AceConfigRegistry:NotifyChange("InventoryOptions"); Zerotorescue@0: Zerotorescue@0: return true; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local tblAddItemTemplate = { Zerotorescue@0: order = 0, Zerotorescue@0: type = "input", Zerotorescue@0: name = function(info) Zerotorescue@0: local itemName, _, itemRarity = GetItemInfo(info[#info]); Zerotorescue@0: return tostring( 7 - (itemRarity or 0) ) .. (itemName or ""); Zerotorescue@0: end, Zerotorescue@0: get = function(info) Zerotorescue@0: return tostring(info[#info]); -- Ace is going to be anal about this if it's a numeric value, so we transmute it into a string here then back to a number on the other side Zerotorescue@0: end, Zerotorescue@0: set = function(widget, info) Zerotorescue@0: -- This is NOT a real "set", we pass the widget reference to this function which contains similar, but not the same, info. Zerotorescue@0: Zerotorescue@0: if widget.itemId then Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: Zerotorescue@0: if not AddToGroup(groupName, widget.itemId) then Zerotorescue@0: print("|cffff0000Couldn't add the item with itemId (" .. widget.itemId .. ") because it is already in a group.|r"); Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end, Zerotorescue@0: width = "double", Zerotorescue@0: dialogControl = "ItemLinkButton", Zerotorescue@0: }; Zerotorescue@0: Zerotorescue@0: local tblRemoveItemTemplate = { Zerotorescue@0: order = 0, Zerotorescue@0: type = "input", Zerotorescue@0: name = function(info) Zerotorescue@0: local itemName, _, itemRarity = GetItemInfo(info[#info]); Zerotorescue@0: return tostring( 7 - (itemRarity or 0) ) .. (itemName or ""); Zerotorescue@0: end, Zerotorescue@0: get = function(info) Zerotorescue@0: return tostring(info[#info]); -- Ace is going to be anal about this if it's a numeric value, so we transmute it into a string here then back to a number on the other side Zerotorescue@0: end, Zerotorescue@0: set = function(widget, info) Zerotorescue@0: -- This is NOT a real "set", we pass the widget reference to this function which contains similar, but not the same, info. Zerotorescue@0: Zerotorescue@0: if widget.itemId then Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: Zerotorescue@0: -- Unset this item Zerotorescue@0: addon.db.global.groups[groupName].items[widget.itemId] = nil; Zerotorescue@0: Zerotorescue@0: -- Now rebuild the list Zerotorescue@0: AceConfigRegistry:NotifyChange("InventoryOptions"); Zerotorescue@0: end Zerotorescue@0: end, Zerotorescue@0: width = "double", Zerotorescue@0: dialogControl = "ItemLinkButton", Zerotorescue@0: }; Zerotorescue@0: Zerotorescue@0: local function UpdateAddItemList(info) Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: Zerotorescue@0: if not addon.db.global.groups[groupName].items then Zerotorescue@0: addon.db.global.groups[groupName].items = {}; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- Merge all items from all groups together Zerotorescue@0: local items = {}; Zerotorescue@0: for groupName, values in pairs(addon.db.global.groups) do Zerotorescue@0: if values.items then Zerotorescue@0: for itemId, _ in pairs(values.items) do Zerotorescue@0: items[itemId] = true; Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local ref = options.args.groups.args[info[2]].args.add.args.list.args; Zerotorescue@0: Zerotorescue@0: -- Parse bags and show these Zerotorescue@0: for bagID = 4, 0, -1 do Zerotorescue@0: for slot = 1, GetContainerNumSlots(bagID) do Zerotorescue@0: local itemId = addon:GetItemId(GetContainerItemLink(bagID, slot)); Zerotorescue@0: Zerotorescue@0: if itemId then Zerotorescue@0: if not items[itemId] then Zerotorescue@0: -- If this item isn't used in any group yet Zerotorescue@0: ref[itemId] = tblAddItemTemplate; Zerotorescue@0: else Zerotorescue@0: -- It's already used in a group, don't show it Zerotorescue@0: ref[itemId] = nil; Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local function UpdateRemoveItemList(info) Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: Zerotorescue@0: if not addon.db.global.groups[groupName].items then Zerotorescue@0: addon.db.global.groups[groupName].items = {}; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local ref = options.args.groups.args[info[2]].args.remove.args.list.args; Zerotorescue@0: Zerotorescue@0: -- Unset all Zerotorescue@0: for itemId, _ in pairs(ref) do Zerotorescue@0: ref[itemId] = nil; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- Parse items in group and show these Zerotorescue@0: for itemId, _ in pairs(addon.db.global.groups[groupName].items) do Zerotorescue@0: ref[itemId] = tblRemoveItemTemplate; Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: function addon:GetItemId(itemLink) Zerotorescue@0: itemLink = itemLink and select(3, string.find(itemLink, "|Hitem:([-0-9]+):")); -- if itemLink is nil, it won't execute the second part Zerotorescue@0: itemLink = itemLink and tonumber(itemLink); Zerotorescue@0: Zerotorescue@0: return itemLink; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: -- Default group Zerotorescue@0: local defaultGroup = { Zerotorescue@0: order = 0, Zerotorescue@0: type = "group", Zerotorescue@0: childGroups = "tab", Zerotorescue@0: name = function(info) Zerotorescue@0: return groupIdToName[info[#info]]; Zerotorescue@0: end, Zerotorescue@0: args = { Zerotorescue@0: general = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "group", Zerotorescue@0: name = "Stock settings", Zerotorescue@0: desc = "Change the stock settings for just this group.", Zerotorescue@0: args = { Zerotorescue@0: minimumStock = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "Minimum stock", Zerotorescue@0: set = SetOption, Zerotorescue@0: get = GetOption, Zerotorescue@0: disabled = GetDisabled, Zerotorescue@0: args = { Zerotorescue@0: description = { Zerotorescue@0: order = 0, Zerotorescue@0: type = "description", Zerotorescue@0: name = "Here you can specify the minimum amount of items you wish to keep in stock and related settings for the currently selected group.", Zerotorescue@0: }, Zerotorescue@0: header = { Zerotorescue@0: order = 5, Zerotorescue@0: type = "header", Zerotorescue@0: name = "", Zerotorescue@0: }, Zerotorescue@0: overrideMinimumStock = { Zerotorescue@0: order = 9, Zerotorescue@0: type = "toggle", Zerotorescue@0: name = "Override min stock", Zerotorescue@0: desc = "Allows you to override the minimum stock setting for this group.", Zerotorescue@0: arg = "minimumStock", Zerotorescue@0: }, Zerotorescue@0: minimumStock = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 100000, Zerotorescue@0: softMax = 1000, Zerotorescue@0: step = 1, Zerotorescue@0: name = "Minimum stock", Zerotorescue@0: desc = "You can manually enter a value between 1.000 and 100.000 in the edit box if the provided range is insufficient.", Zerotorescue@0: arg = "overrideMinimumStock", Zerotorescue@0: }, Zerotorescue@0: overrideSummaryThresholdShow = { Zerotorescue@0: order = 19, Zerotorescue@0: type = "toggle", Zerotorescue@0: name = "Override summary showing", Zerotorescue@0: desc = "Allows you to override when this group should appear in the summary.", Zerotorescue@0: arg = "summaryThresholdShow", Zerotorescue@0: }, Zerotorescue@0: summaryThresholdShow = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 100, Zerotorescue@0: softMax = 10, Zerotorescue@0: step = 0.05, Zerotorescue@0: isPercent = true, Zerotorescue@0: name = "Show in summary when below", Zerotorescue@0: desc = "Show items in the summary when below the specified percentage of the minimum stock.\n\nYou can manually enter a value between 1.000% and 10.000% in the edit box if the provided range is insufficient.", Zerotorescue@0: arg = "overrideSummaryThresholdShow", Zerotorescue@0: }, Zerotorescue@0: overrideAlertBelowMinimum = { Zerotorescue@0: order = 29, Zerotorescue@0: type = "toggle", Zerotorescue@0: name = "Override minimum alert", Zerotorescue@0: desc = "Allows you to override wether an alert should be shown when an item in this group gets below the minimum stock threshold.", Zerotorescue@0: arg = "alertBelowMinimum", Zerotorescue@0: }, Zerotorescue@0: alertBelowMinimum = { Zerotorescue@0: order = 30, Zerotorescue@0: type = "toggle", Zerotorescue@0: name = "Alert when below minimum", Zerotorescue@0: desc = "Show an alert when an item in this group gets below the minimum stock threshold.", Zerotorescue@0: arg = "overrideAlertBelowMinimum", Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: refill = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "Replenishing stock", Zerotorescue@0: set = SetOption, Zerotorescue@0: get = GetOption, Zerotorescue@0: disabled = GetDisabled, Zerotorescue@0: args = { Zerotorescue@0: description = { Zerotorescue@0: order = 0, Zerotorescue@0: type = "description", Zerotorescue@0: name = function(info) Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: local r = "Here you can specify the amount of items to which you wish to restock when you are collecting new items for the currently selected group. This may be higher than the minimum stock.\n\n"; Zerotorescue@0: Zerotorescue@0: r = r .. "When restocking the target amount is |cfffed000" .. GetOptionByKey(groupName, "restockTarget") .. "|r of every item. Not queueing craftable items when only missing |cfffed000" .. floor( GetOptionByKey(groupName, "minCraftingQueue") * GetOptionByKey(groupName, "restockTarget") ) .. "|r (|cfffed000" .. ( GetOptionByKey(groupName, "minCraftingQueue") * 100 ) .. "%|r) of the restock target."; Zerotorescue@0: Zerotorescue@0: return r; Zerotorescue@0: end, Zerotorescue@0: }, Zerotorescue@0: header = { Zerotorescue@0: order = 5, Zerotorescue@0: type = "header", Zerotorescue@0: name = "", Zerotorescue@0: }, Zerotorescue@0: overrideRestockTarget = { Zerotorescue@0: order = 9, Zerotorescue@0: type = "toggle", Zerotorescue@0: name = "Override restock target", Zerotorescue@0: desc = "Allows you to override the restock target setting for this group.", Zerotorescue@0: arg = "restockTarget", Zerotorescue@0: }, Zerotorescue@0: restockTarget = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 100000, Zerotorescue@0: softMax = 1000, Zerotorescue@0: step = 1, Zerotorescue@0: name = "Restock target", Zerotorescue@0: desc = "You can manually enter a value between 1.000 and 100.000 in the edit box if the provided range is insufficient.", Zerotorescue@0: arg = "overrideRestockTarget", Zerotorescue@0: }, Zerotorescue@0: overrideMinCraftingQueue = { Zerotorescue@0: order = 19, Zerotorescue@0: type = "toggle", Zerotorescue@0: name = "Override min queue", Zerotorescue@0: desc = "Allows you to override the minimum craftable items queue setting for this group.", Zerotorescue@0: arg = "minCraftingQueue", Zerotorescue@0: }, Zerotorescue@0: minCraftingQueue = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "range", Zerotorescue@0: min = 0, Zerotorescue@0: max = 1, Zerotorescue@0: step = 0.01, Zerotorescue@0: isPercent = true, Zerotorescue@0: name = "Don't queue if I only miss", Zerotorescue@0: desc = "Don't add a craftable item to the queue if I only miss this much or less of the restock target.\n\nExample: if your restock target is set to 60 and this is set to 5%, an item won't be queued unless you are missing more than 3 of it.", Zerotorescue@0: arg = "overrideMinCraftingQueue", Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: group = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "group", Zerotorescue@0: name = "Group Management", Zerotorescue@0: desc = "Rename, delete or export this group.", Zerotorescue@0: args = { Zerotorescue@0: rename = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "group", Zerotorescue@0: name = "Rename", Zerotorescue@0: inline = true, Zerotorescue@0: args = { Zerotorescue@0: rename = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "input", Zerotorescue@0: name = "New group name", Zerotorescue@0: desc = "Change the name of this group to something else. You can also use item links here as you wish.", Zerotorescue@0: validate = ValidateGroupName, Zerotorescue@0: set = function(info, value) Zerotorescue@0: local oldGroupName = groupIdToName[info[2]]; Zerotorescue@0: Zerotorescue@0: addon.db.global.groups[value] = CopyTable(addon.db.global.groups[oldGroupName]); Zerotorescue@0: addon.db.global.groups[oldGroupName] = nil; Zerotorescue@0: Zerotorescue@0: groupIdToName[info[2]] = value; Zerotorescue@0: groupIdToName[value] = true; Zerotorescue@0: groupIdToName[oldGroupName] = nil; Zerotorescue@0: Zerotorescue@0: addon:FillGroupOptions(); Zerotorescue@0: end, Zerotorescue@0: get = function(info) Zerotorescue@0: return groupIdToName[info[2]]; Zerotorescue@0: end, Zerotorescue@0: width = "double", Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: delete = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "group", Zerotorescue@0: name = "Delete", Zerotorescue@0: inline = true, Zerotorescue@0: args = { Zerotorescue@0: delete = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "execute", Zerotorescue@0: name = "Delete group", Zerotorescue@0: desc = "Delete the currently selected group.", Zerotorescue@0: confirm = true, Zerotorescue@0: confirmText = "Are you sure you wish to |cffff0000DELETE|r this group? This action is not reversable!", Zerotorescue@0: func = function(info) Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: Zerotorescue@0: addon.db.global.groups[groupName] = nil; Zerotorescue@0: Zerotorescue@0: addon:FillGroupOptions(); Zerotorescue@0: end, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: export = { Zerotorescue@0: order = 30, Zerotorescue@0: type = "group", Zerotorescue@0: name = "Export", Zerotorescue@0: inline = true, Zerotorescue@0: args = { Zerotorescue@0: input = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "input", Zerotorescue@0: multiline = true, Zerotorescue@0: name = "Group data", Zerotorescue@0: width = "full", Zerotorescue@0: desc = "Export the group data for the currently selected group. Press CTRL-A to select all and CTRL-C to copy the text.", Zerotorescue@0: set = false, Zerotorescue@0: get = function(info) Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: Zerotorescue@0: -- We want to include the group name, so we copy the table then set another value Zerotorescue@0: local temp = CopyTable(addon.db.global.groups[groupName]); Zerotorescue@0: temp.name = groupName; Zerotorescue@0: Zerotorescue@0: if not AceSerializer then Zerotorescue@0: AceSerializer = LibStub("AceSerializer-3.0"); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: return AceSerializer:Serialize(temp); Zerotorescue@0: end, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: add = { Zerotorescue@0: order = 30, Zerotorescue@0: type = "group", Zerotorescue@0: name = "Add items", Zerotorescue@0: args = { Zerotorescue@0: singleAdd = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "Add items", Zerotorescue@0: args = { Zerotorescue@0: help = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "description", Zerotorescue@0: name = "You can add a single item to this group at a time by pasting the item-id or an item-link in the field to the left or you can also import multiple items at once by pasting exported item data in the field to the right. Scroll further down to add items based on your inventory contents.", Zerotorescue@0: }, Zerotorescue@0: itemLink = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "input", Zerotorescue@0: name = "Single item add (item-link or item-id)", Zerotorescue@0: desc = "Shift-click an item-link or enter an item-id to add the related item to this group. You can only add one item link or item id at a time.", Zerotorescue@0: validate = function(info, value) Zerotorescue@0: -- If the value is empty we'll allow passing to clear the carret Zerotorescue@0: if value == "" then return true; end Zerotorescue@0: Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: Zerotorescue@0: local itemId = addon:GetItemId(string.trim(value or "")) or tonumber(string.trim(value or "")); Zerotorescue@0: Zerotorescue@0: if not itemId then Zerotorescue@0: return "This is not a valid item link."; Zerotorescue@0: elseif InGroup(itemId) then Zerotorescue@0: return ("This item is already in the group \"%s\"."):format(InGroup(itemId)); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: return true; Zerotorescue@0: end, Zerotorescue@0: set = function(info, value) Zerotorescue@0: if value and value ~= "" then Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: Zerotorescue@0: local itemId = addon:GetItemId(string.trim(value or "")) or tonumber(string.trim(value or "")); Zerotorescue@0: Zerotorescue@0: AddToGroup(groupName, itemId); Zerotorescue@0: Zerotorescue@0: print(("Added %s"):format(select(2, GetItemInfo(itemId)) or ("Unknown (#%d)"):format(itemId))); Zerotorescue@0: end Zerotorescue@0: end, Zerotorescue@0: get = false, Zerotorescue@0: }, Zerotorescue@0: import = { Zerotorescue@0: order = 40, Zerotorescue@0: type = "input", Zerotorescue@0: name = "Import item data", Zerotorescue@0: desc = "Import item data from an exported item data-string. Any items already grouped will be skipped.", Zerotorescue@0: set = function(info, value) Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: Zerotorescue@0: local allItemIds = { string.split(";", value or "") }; Zerotorescue@0: Zerotorescue@0: for _, value in pairs(allItemIds) do Zerotorescue@0: local itemId = tonumber(value); Zerotorescue@0: Zerotorescue@0: if not itemId then Zerotorescue@0: print(("\"%s\" is not a number."):format(value)); Zerotorescue@0: elseif InGroup(itemId) then Zerotorescue@0: print(("Skipping %s (#%d) as it is already in the group |cfffed000%s|r."):format(select(2, GetItemInfo(itemId)) or "Unknown", itemId, InGroup(itemId))); Zerotorescue@0: else Zerotorescue@0: AddToGroup(groupName, itemId); Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end, Zerotorescue@0: get = false, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: massAdd = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "Mass add", Zerotorescue@0: args = { Zerotorescue@0: help = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "description", Zerotorescue@0: name = "Click the items you wish to add to this group or add multiple of these items at once by providing a name filter in the field below.", Zerotorescue@0: }, Zerotorescue@0: massAdd = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "input", Zerotorescue@0: name = "Add all items matching...", Zerotorescue@0: desc = "Add every item in your inventory matching the name entered in this field. If you enter \"Glyph\" as a filter, any items in your inventory containing this in their name will be added to this group.", Zerotorescue@0: --set = massAddItems, Zerotorescue@0: get = false, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: list = { Zerotorescue@0: order = 30, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "Item list", Zerotorescue@0: hidden = UpdateAddItemList, Zerotorescue@0: args = { Zerotorescue@0: Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: remove = { Zerotorescue@0: order = 40, Zerotorescue@0: type = "group", Zerotorescue@0: name = "Current items", Zerotorescue@0: args = { Zerotorescue@0: help = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "Help", Zerotorescue@0: hidden = false, Zerotorescue@0: args = { Zerotorescue@0: help = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "description", Zerotorescue@0: name = "Click the items you wish to remove from this group.", Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: list = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "Item list", Zerotorescue@0: hidden = UpdateRemoveItemList, Zerotorescue@0: args = { Zerotorescue@0: Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: export = { Zerotorescue@0: order = 30, Zerotorescue@0: type = "group", Zerotorescue@0: name = "Export", Zerotorescue@0: inline = true, Zerotorescue@0: args = { Zerotorescue@0: input = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "input", Zerotorescue@0: name = "Item data", Zerotorescue@0: width = "full", Zerotorescue@0: desc = "Export the item data for the currently selected group. Press CTRL-A to select all and CTRL-C to copy the text.", Zerotorescue@0: set = false, Zerotorescue@0: get = function(info) Zerotorescue@0: local groupName = groupIdToName[info[2]]; Zerotorescue@0: Zerotorescue@0: local combinedItemIds; Zerotorescue@0: -- Parse items in group and show these Zerotorescue@0: for itemId, _ in pairs(addon.db.global.groups[groupName].items) do Zerotorescue@0: if not combinedItemIds then Zerotorescue@0: combinedItemIds = tostring(itemId); Zerotorescue@0: else Zerotorescue@0: combinedItemIds = combinedItemIds .. (";%d"):format(itemId); Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: return combinedItemIds; -- We don't serialize this because we actually DO want people to be able to manually modify it - besides, parsing it isn't going to be hard Zerotorescue@0: end, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }; Zerotorescue@0: Zerotorescue@0: function addon:MakeGroupOptions() Zerotorescue@0: options.args.groups = { Zerotorescue@0: order = 1100, Zerotorescue@0: type = "group", Zerotorescue@0: name = "Groups", Zerotorescue@0: desc = "Change a group.", Zerotorescue@0: args = { Zerotorescue@0: create = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "Create a brand new group", Zerotorescue@0: args = { Zerotorescue@0: name = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "input", Zerotorescue@0: name = "Group name", Zerotorescue@0: desc = "The name of the group. You can also use item links as you wish.", Zerotorescue@0: validate = ValidateGroupName, Zerotorescue@0: set = function(_, value) Zerotorescue@0: self.db.global.groups[value] = {}; Zerotorescue@0: Zerotorescue@0: addon:FillGroupOptions(); Zerotorescue@0: end, Zerotorescue@0: get = false, Zerotorescue@0: width = "double", Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: import = { Zerotorescue@0: order = 20, Zerotorescue@0: type = "group", Zerotorescue@0: inline = true, Zerotorescue@0: name = "Import a group", Zerotorescue@0: args = { Zerotorescue@0: input = { Zerotorescue@0: order = 10, Zerotorescue@0: type = "input", Zerotorescue@0: multiline = true, Zerotorescue@0: name = "Group data", Zerotorescue@0: desc = "Paste the group data as provided by a group export. If you are trying to import multiple groups at the same time, make sure to use newlines to seperate them.", Zerotorescue@0: set = function(info, value) Zerotorescue@0: local temp = { string.split("\n", value or "") }; Zerotorescue@0: Zerotorescue@0: for no, current in pairs(temp) do Zerotorescue@0: if not AceSerializer then Zerotorescue@0: AceSerializer = LibStub("AceSerializer-3.0"); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local result, temp = AceSerializer:Deserialize(current); Zerotorescue@0: local name; Zerotorescue@0: Zerotorescue@0: if not temp.name then Zerotorescue@0: print("|cffff0000The provided data is not supported.|r"); Zerotorescue@0: return; Zerotorescue@0: else Zerotorescue@0: name = temp.name; Zerotorescue@0: temp.name = nil; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: local newGroupName = string.trim(string.lower(name or "")); Zerotorescue@0: Zerotorescue@0: for name in pairs(self.db.global.groups) do Zerotorescue@0: if string.lower(name) == newGroupName then Zerotorescue@0: print(("|cffff0000A group named \"%s\" already exists.|r"):format(name)); Zerotorescue@0: return; Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: self.db.global.groups[name] = temp; Zerotorescue@0: end Zerotorescue@0: end, Zerotorescue@0: get = false, Zerotorescue@0: width = "full", Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }, Zerotorescue@0: }; Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: function addon:FillGroupOptions() Zerotorescue@0: for id, name in pairs(groupIdToName) do Zerotorescue@0: if type(name) == "string" and not self.db.global.groups[name] then Zerotorescue@0: options.args.groups.args[id] = nil; Zerotorescue@0: groupIdToName[id] = nil; Zerotorescue@0: groupIdToName[name] = nil; Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: for name, values in pairs(self.db.global.groups) do Zerotorescue@0: if not groupIdToName[name] then Zerotorescue@0: options.args.groups.args[tostring(count)] = CopyTable(defaultGroup); Zerotorescue@0: Zerotorescue@0: groupIdToName[tostring(count)] = name; Zerotorescue@0: groupIdToName[name] = true; Zerotorescue@0: Zerotorescue@0: count = ( count + 1 ); Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: function addon:GetItemCount(itemId) Zerotorescue@0: return Altoholic:GetItemCount(itemId); Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: Zerotorescue@0: function addon:Debug(t) Zerotorescue@0: if not self.debugChannel and self.debugChannel ~= false then Zerotorescue@0: -- We want to check just once, so if you add a debug channel later just do a /reload (registering an event for this is wasted resources) Zerotorescue@0: self.debugChannel = false; Zerotorescue@0: Zerotorescue@0: for i = 1, NUM_CHAT_WINDOWS do Zerotorescue@0: local name = GetChatWindowInfo(i); Zerotorescue@0: Zerotorescue@0: if name:upper() == "DEBUG" then Zerotorescue@0: self.debugChannel = _G["ChatFrame" .. i]; Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: end Zerotorescue@0: Zerotorescue@0: if self.debugChannel then Zerotorescue@0: self.debugChannel:AddMessage(t); Zerotorescue@0: end Zerotorescue@0: end