comparison Core.lua @ 0:c6ff7ba0e8f6

Reasonably functional now. Cleaning up some stuff which might have to be reverted.
author Zerotorescue
date Thu, 07 Oct 2010 17:17:43 +0200
parents
children 9fff13c08f99
comparison
equal deleted inserted replaced
-1:000000000000 0:c6ff7ba0e8f6
1 -- You can access this addon's object through: LibStub("AceAddon-3.0"):GetAddon("Inventory")
2 local addon = LibStub("AceAddon-3.0"):NewAddon("Inventory", "AceEvent-3.0", "AceTimer-3.0");
3
4 local Media = LibStub("LibSharedMedia-3.0");
5 Media:Register("sound", "Cartoon FX", [[Sound\Doodad\Goblin_Lottery_Open03.wav]]);
6 Media:Register("sound", "Cheer", [[Sound\Event Sounds\OgreEventCheerUnique.wav]]);
7 Media:Register("sound", "Explosion", [[Sound\Doodad\Hellfire_Raid_FX_Explosion05.wav]]);
8 Media:Register("sound", "Fel Nova", [[Sound\Spells\SeepingGaseous_Fel_Nova.wav]]);
9 Media:Register("sound", "Fel Portal", [[Sound\Spells\Sunwell_Fel_PortalStand.wav]]);
10 Media:Register("sound", "Magic Click", [[Sound\interface\MagicClick.wav]]);
11 Media:Register("sound", "Rubber Ducky", [[Sound\Doodad\Goblin_Lottery_Open01.wav]]);
12 Media:Register("sound", "Shing!", [[Sound\Doodad\PortcullisActive_Closed.wav]]);
13 Media:Register("sound", "Simon Chime", [[Sound\Doodad\SimonGame_LargeBlueTree.wav]]);
14 Media:Register("sound", "Simon Error", [[Sound\Spells\SimonGame_Visual_BadPress.wav]]);
15 Media:Register("sound", "Simon Start", [[Sound\Spells\SimonGame_Visual_GameStart.wav]]);
16 Media:Register("sound", "War Drums", [[Sound\Event Sounds\Event_wardrum_ogre.wav]]);
17 Media:Register("sound", "Wham!", [[Sound\Doodad\PVP_Lordaeron_Door_Open.wav]]);
18 Media:Register("sound", "Whisper Ping", [[Sound\interface\iTellMessage.wav]]);
19 Media:Register("sound", "You Will Die!", [[Sound\Creature\CThun\CThunYouWillDIe.wav]]);
20
21 local AceConfigDialog, AceConfigRegistry, AceSerializer;
22 local groupIdToName = {};
23 local options = {};
24
25 function addon:OnInitialize()
26 self:Debug("OnInitialize");
27
28 -- SAVED VARIABLES
29
30 local defaults = {
31 global = {
32 groups = {},
33 defaults = {
34 minimumStock = 60,
35 alertBelowMinimum = true,
36 summaryThresholdShow = 10,
37 restockTarget = 60,
38 minCraftingQueue = 0.05,
39 bonusQueue = 0.1,
40 priceThreshold = 0,
41 trackAtCharacters = {},
42 colors = {
43 red = 0;
44 orange = 0.3;
45 yellow = 0.6;
46 green = 0.95;
47 },
48 },
49 },
50 factionrealm = {
51 characters = {},
52 },
53 };
54
55 -- Register our saved variables database
56 self.db = LibStub("AceDB-3.0"):New("InventoryDB", defaults, true);
57
58 -- SLASH COMMANDS
59
60 -- Disable the AddonLoader slash commands
61 SLASH_INVENTORY1 = nil;
62 SLASH_IY1 = nil;
63
64 -- Register our own slash commands
65 SLASH_INVENTORY1 = "/inventory";
66 SLASH_INVENTORY2 = "/iy";
67 SlashCmdList["INVENTORY"] = function(msg)
68 self:CommandHandler(msg);
69 end
70
71 -- INTERFACE OPTIONS
72
73 -- Attempt to remove the interface options added by AddonLoader (if enabled)
74 if AddonLoader and AddonLoader.RemoveInterfaceOptions then
75 AddonLoader:RemoveInterfaceOptions("Inventory");
76 end
77
78 -- Now create our own options frame
79 local frame = CreateFrame("Frame", nil, UIParent);
80 frame:Hide();
81 frame.name = "Inventory";
82 frame:HookScript("OnShow", function(self)
83 -- Refresh the frame to instantly show the right options
84 InterfaceOptionsFrame_OpenToCategory(self.name)
85 end);
86 -- And add it to the interface options
87 InterfaceOptions_AddCategory(frame);
88
89 self:MakeWidgets();
90
91 -- Remember this character is mine
92 local playerName = UnitName("player");
93 if not self.db.factionrealm.characters[playerName] then
94 self.db.factionrealm.characters[playerName] = true;
95
96 -- Default to tracking on all chars, untracking is a convenience, not tracking by default would probably get multiple issue reports.
97 self.db.global.defaults.trackAtCharacters[playerName] = true;
98 end
99 end
100
101 function addon:MakeWidgets()
102 local AceGUI = LibStub("AceGUI-3.0");
103
104 --[[
105 [ ItemLinkButton ]
106 UserData: itemId, onClick event
107 OnEnter: tooltip show
108 OnLeave: tooltip hid
109
110 ]]
111
112 -- Define out custom item link button widget
113 -- 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
114
115 local widgetType = "ItemLinkButton";
116 local widgetVersion = 1;
117
118 -- Empty function for disabling functions
119 local function Dummy() end
120
121 -- Overwrite the SetText-function of our custom widgets
122 local function CustomWidgetSetText(self, value, ...)
123 if value then
124 -- Remember the itemId in a local parameter (using :GetText, we'd have to run a pattern over it and it would all get silly)
125 self.itemId = tonumber(value);
126
127 if not self.itemId then error("itemId is not a number."); end
128
129 -- Put the icon in front of it
130 self:SetImage(GetItemIcon(self.itemId));
131 -- Standardize the size
132 self:SetImageSize(16, 16);
133
134 -- Make readable font
135 self:SetFontObject(GameFontHighlight);
136
137 -- We don't want to set the itemId as text, but rather the item link, so get that.
138 value = select(2, GetItemInfo(value)) or ("Unknown (#%d)"):format(value);
139
140 return self.originalSetText(self, value, ...);
141 end
142 end
143
144 -- Overwrite the OnEnter-event of our custom widgets to use our own function
145 local function CustomWidgetOnEnter(self)
146 if self.itemId then
147 GameTooltip:SetOwner(self.frame, "ANCHOR_TOPRIGHT");
148 GameTooltip:SetHyperlink(("item:%d"):format(self.itemId));
149 GameTooltip:Show();
150 end
151 end
152
153 -- Overwrite the OnClick-event of our custom widgets to use our own function
154 local function CustomWidgetOnClick(self)
155 local info = self:GetUserDataTable()
156
157 info.option.set(self, info);
158 end
159
160 -- Overwrite the SetCallback-function of our custom widgets
161 local function CustomWidgetSetCallback(self, event, func, ...)
162 if event == "OnEnter" then
163 -- This event is called once when creating the widget
164 -- When it becomes hidden, all events appear to be removed, but once it's shown again, OnEnter will be re-applied
165 -- Hence any registered Callback must be done in here
166
167 self.originalSetCallBack(self, "OnClick", CustomWidgetOnClick);
168
169 return self.originalSetCallBack(self, event, CustomWidgetOnEnter, ...);
170 else
171 return self.originalSetCallBack(self, event, func, ...);
172 end
173 end
174
175 local function CustomWidgetHideTooltip()
176 GameTooltip:Hide();
177 end
178
179 -- Makes an instance of our ItemLinkButton widget
180 local function GetItemLinkButton()
181 local widget = AceGUI:Create("InteractiveLabel");
182 widget.type = widgetType;
183
184 -- We can only provide custom widgets for input, select and multiselect fields
185 -- Input being the simplest, we use that - however, it provides two parameters: label and text. We only need one, disable the other.
186 widget.SetLabel = Dummy;
187
188 if not widget.originalSetText then
189 -- When setting text we provide an itemId
190 -- Use this itemId to set the icon and do all fancy stuff
191 widget.originalSetText = widget.SetText;
192 widget.SetText = CustomWidgetSetText;
193 end
194
195
196 if not widget.originalSetCallBack then
197 -- We don't want AceConfig to overwrite our OnEnter with the tooltip functions, so override that
198 widget.originalSetCallBack = widget.SetCallback;
199 widget.SetCallback = CustomWidgetSetCallback;
200
201 -- Make sure it's called (AceConfig will probably repeat this, but we are prepared in case it's ever changed)
202 widget:SetCallback("OnEnter", Dummy);
203 widget:SetCallback("OnLeave", CustomWidgetHideTooltip);
204 end
205
206 return widget;
207 end
208
209 AceGUI:RegisterWidgetType(widgetType, GetItemLinkButton, widgetVersion);
210 end
211
212 function addon:CommandHandler(message)
213 local cmd, arg = string.split(" ", (message or ""), 2);
214 cmd = string.lower(cmd);
215
216 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
217 self:Show();
218 elseif cmd == "d" or cmd == "debug" then
219 self.debugChannel = false;
220 for i = 1, NUM_CHAT_WINDOWS do
221 local name = GetChatWindowInfo(i);
222
223 if name:upper() == "DEBUG" then
224 self.debugChannel = _G["ChatFrame" .. i];
225
226 print("A debug channel already exists, used the old one. (" .. i .. ")");
227 return;
228 end
229 end
230
231 if not self.debugChannel then
232 -- Create a new debug channel
233 local chatFrame = FCF_OpenNewWindow('Debug');
234 ChatFrame_RemoveAllMessageGroups(chatFrame);
235 self.debugChannel = chatFrame;
236
237 print("New debug channel created.");
238 end
239 else
240 print("Wrong command, available: /inventory config (or /inventory c)");
241 end
242 end
243
244 function addon:Load()
245 if not AceConfigDialog and not AceConfigRegistry then
246 self:FillOptions();
247
248 -- Build options dialog
249 AceConfigDialog = LibStub("AceConfigDialog-3.0");
250 AceConfigRegistry = LibStub("AceConfigRegistry-3.0");
251 -- Register options table
252 LibStub("AceConfig-3.0"):RegisterOptionsTable("InventoryOptions", options);
253 -- Set a nice default size (so that 4 normal sized elements fit next to eachother)
254 AceConfigDialog:SetDefaultSize("InventoryOptions", 975, 600);
255
256 -- In case the addon is loaded from another condition, always call the remove interface options
257 if AddonLoader and AddonLoader.RemoveInterfaceOptions then
258 AddonLoader:RemoveInterfaceOptions("Inventory");
259 end
260
261 -- Add to the blizzard addons options thing
262 --AceConfigDialog:AddToBlizOptions("InventoryOptions");
263 end
264 end
265
266 function addon:Show()
267 self:Load();
268
269 AceConfigDialog:Open("InventoryOptions");
270 end
271
272 function addon:FillOptions()
273 options = {
274 type = "group",
275 name = "Inventory",
276 childGroups = "tree",
277 args = {
278 },
279 };
280
281 self:FillGeneralOptions();
282
283 options.args.profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db, true);
284 options.args.profiles.order = 200;
285
286 self:MakeGroupOptions();
287
288 self:FillGroupOptions();
289 end
290
291 function addon:FillGeneralOptions()
292 options.args.general = {
293 order = 100,
294 type = "group",
295 name = "General",
296 desc = "Change general Inventory settings.",
297 args = {
298 general = {
299 order = 0,
300 type = "group",
301 inline = true,
302 name = "General",
303 args = {
304 description = {
305 order = 0,
306 type = "description",
307 name = "Change general settings unrelated to groups.",
308 },
309 header = {
310 order = 5,
311 type = "header",
312 name = "",
313 },
314 auctionAddon = {
315 order = 10,
316 type = "select",
317 name = "Prefered pricing addon",
318 values = {
319 Auctioneer = "Auctioneer",
320 Auctionator = "Auctionator",
321 },
322 get = function() end,
323 set = function(i, v) end,
324 },
325 itemCountAddon = {
326 order = 20,
327 type = "select",
328 name = "Prefered item count addon",
329 values = {
330 Altoholic = "Altoholic",
331 DataStore = "DataStore",
332 ItemCount = "ItemCount",
333 },
334 get = function() end,
335 set = function(i, v) end,
336 },
337 },
338 },
339 minimumStock = {
340 order = 10,
341 type = "group",
342 inline = true,
343 name = "Minimum stock",
344 args = {
345 description = {
346 order = 0,
347 type = "description",
348 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.",
349 },
350 header = {
351 order = 5,
352 type = "header",
353 name = "",
354 },
355 minimumStock = {
356 order = 10,
357 type = "range",
358 min = 0,
359 max = 100000,
360 softMax = 1000,
361 step = 1,
362 name = "Minimum stock",
363 desc = "You can manually enter a value between 1.000 and 100.000 in the edit box if the provided range is insufficient.",
364 get = function() return self.db.global.defaults.minimumStock; end,
365 set = function(i, v) self.db.global.defaults.minimumStock = v; end,
366 },
367 summaryThresholdShow = {
368 order = 20,
369 type = "range",
370 min = 0,
371 max = 100,
372 softMax = 10,
373 step = 0.05,
374 isPercent = true,
375 name = "Show in summary when below",
376 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.",
377 get = function() return self.db.global.defaults.summaryThresholdShow; end,
378 set = function(i, v) self.db.global.defaults.summaryThresholdShow = v; end,
379 },
380 alertBelowMinimum = {
381 order = 30,
382 type = "toggle",
383 name = "Alert when below minimum",
384 desc = "Show an alert when this item gets below this threshold.",
385 get = function() return self.db.global.defaults.alertBelowMinimum; end,
386 set = function(i, v) self.db.global.defaults.alertBelowMinimum = v; end,
387 },
388 trackAtCharacters = {
389 order = 40,
390 type = "multiselect",
391 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.
392 name = "Track at",
393 desc = "Select at which characters this should appear in the summary and generate alerts.",
394 values = function()
395 local temp = {};
396 for charName in pairs(self.db.factionrealm.characters) do
397 temp[charName] = charName;
398 end
399
400 return temp;
401 end,
402 get = function(i, v)
403 return self.db.global.defaults.trackAtCharacters[v];
404 end,
405 set = function(i, v, e)
406 self.db.global.defaults.trackAtCharacters[v] = e or nil;
407 end,
408 },
409 },
410 },
411 refill = {
412 order = 20,
413 type = "group",
414 inline = true,
415 name = "Replenishing stock",
416 args = {
417 description = {
418 order = 0,
419 type = "description",
420 name = function()
421 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";
422
423 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.";
424
425 return r;
426 end,
427 },
428 header = {
429 order = 5,
430 type = "header",
431 name = "",
432 },
433 restockTarget = {
434 order = 10,
435 type = "range",
436 min = 0,
437 max = 100000,
438 softMax = 1000,
439 step = 1,
440 name = "Restock target",
441 desc = "You can manually enter a value between 1.000 and 100.000 in the edit box if the provided range is insufficient.",
442 get = function() return self.db.global.defaults.restockTarget; end,
443 set = function(i, v) self.db.global.defaults.restockTarget = v; end,
444 },
445 minCraftingQueue = {
446 order = 20,
447 type = "range",
448 min = 0,
449 max = 1,
450 step = 0.01, -- 1%
451 isPercent = true,
452 name = "Don't queue if I only miss",
453 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.",
454 get = function() return self.db.global.defaults.minCraftingQueue; end,
455 set = function(i, v) self.db.global.defaults.minCraftingQueue = v; end,
456 },
457 bonusQueue = {
458 order = 30,
459 type = "range",
460 min = 0,
461 max = 10, -- 1000%
462 step = 0.01, -- 1%
463 isPercent = true,
464 name = "Bonus queue",
465 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.",
466 get = function() return self.db.global.defaults.bonusQueue; end,
467 set = function(i, v) self.db.global.defaults.bonusQueue = v; end,
468 },
469 priceThreshold = {
470 order = 40,
471 type = "input",
472 name = "Price threshold",
473 desc = "Only queue craftable items when they are worth at least this much according to your auction house addon.",
474 validate = function()
475 -- gold check
476 end,
477 get = function() return self.db.global.defaults.priceThreshold; end,
478 set = function(i, v) self.db.global.defaults.priceThreshold = v; end,
479 },
480 hideFromSummaryPriceThreshold = {
481 order = 50,
482 type = "toggle",
483 name = "Don't show when below threshold",
484 desc = "Hide items from the summary when their value is below the set price threshold.",
485 },
486 },
487 },
488 colorCodes = {
489 order = 30,
490 type = "group",
491 inline = true,
492 name = "Color codes",
493 args = {
494 description = {
495 order = 0,
496 type = "description",
497 name = "Change the color code thresholds based on the current stock remaining of the required minimum stock.",
498 },
499 header = {
500 order = 5,
501 type = "header",
502 name = "",
503 },
504 green = {
505 order = 10,
506 type = "range",
507 min = 0,
508 max = 1,
509 step = 0.01,
510 isPercent = true,
511 name = "|cff00ff00Green|r",
512 desc = "Show quantity in green when at least this much of the minimum stock is available.",
513 get = function() return self.db.global.defaults.colors.green; end,
514 set = function(i, v) self.db.global.defaults.colors.green = v; end,
515 },
516 yellow = {
517 order = 20,
518 type = "range",
519 min = 0,
520 max = 1,
521 step = 0.01,
522 isPercent = true,
523 name = "|cffffff00Yellow|r",
524 desc = "Show quantity in yellow when at least this much of the minimum stock is available.",
525 get = function() return self.db.global.defaults.colors.yellow; end,
526 set = function(i, v) self.db.global.defaults.colors.yellow = v; end,
527 },
528 orange = {
529 order = 30,
530 type = "range",
531 min = 0,
532 max = 1,
533 step = 0.01,
534 isPercent = true,
535 name = "|cffff9933Orange|r",
536 desc = "Show quantity in orange when at least this much of the minimum stock is available.",
537 get = function() return self.db.global.defaults.colors.orange; end,
538 set = function(i, v) self.db.global.defaults.colors.orange = v; end,
539 },
540 red = {
541 order = 40,
542 type = "range",
543 min = 0,
544 max = 1,
545 step = 0.01,
546 isPercent = true,
547 name = "|cffff0000Red|r",
548 desc = "Show quantity in red when at least this much of the minimum stock is available.",
549 get = function() return self.db.global.defaults.colors.red; end,
550 set = function(i, v) self.db.global.defaults.colors.red = v; end,
551 },
552 },
553 },
554 },
555 };
556 end
557
558 local count = 0;
559 local temp = {};
560
561 local function SetOption(info, value)
562 local groupName = groupIdToName[info[2]];
563 local optionName = info[#info];
564
565 -- No need to store a setting if it's disabled (false)
566 if not value and info.arg and not info.arg:find("override") then
567 value = nil;
568
569 -- If this is an override toggler then also set the related field to nil
570 addon.db.global.groups[groupName][info.arg] = nil;
571 end
572
573 addon.db.global.groups[groupName][optionName] = value;
574 end
575
576 local function GetOptionByKey(groupName, optionName, noDefault)
577 if addon.db.global.groups[groupName][optionName] ~= nil then
578 return addon.db.global.groups[groupName][optionName];
579 elseif addon.db.global.defaults[optionName] and not noDefault then
580 return addon.db.global.defaults[optionName];
581 else
582 return nil;
583 end
584 end
585
586 local function GetOption(info)
587 local groupName = groupIdToName[info[2]];
588 local optionName = info[#info];
589
590 return GetOptionByKey(groupName, optionName);
591 end
592
593 local function GetDisabled(info)
594 if not info.arg or not info.arg:find("override") then
595 return false;
596 end
597
598 local groupName = groupIdToName[info[2]];
599 local optionName = info[#info];
600
601 return (GetOptionByKey(groupName, info.arg, true) == nil);
602 end
603
604 local function ValidateGroupName(_, value)
605 value = string.lower(string.trim(value or ""));
606
607 for name, _ in pairs(addon.db.global.groups) do
608 if string.lower(name) == value then
609 return ("A group named \"%s\" already exists."):format(name);
610 end
611 end
612
613 return true;
614 end
615
616 local function InGroup(itemId)
617 -- Go through all groups to see if this item is already somewhere
618 for groupName, values in pairs(addon.db.global.groups) do
619 if values.items and values.items[itemId] then
620 return groupName;
621 end
622 end
623
624 return;
625 end
626
627 local function AddToGroup(groupName, itemId)
628 if InGroup(itemId) then
629 return false;
630 end
631
632 if not addon.db.global.groups[groupName].items then
633 addon.db.global.groups[groupName].items = {};
634 end
635
636 -- Set this item
637 addon.db.global.groups[groupName].items[itemId] = true;
638
639 -- Now rebuild the list
640 AceConfigRegistry:NotifyChange("InventoryOptions");
641
642 return true;
643 end
644
645 local tblAddItemTemplate = {
646 order = 0,
647 type = "input",
648 name = function(info)
649 local itemName, _, itemRarity = GetItemInfo(info[#info]);
650 return tostring( 7 - (itemRarity or 0) ) .. (itemName or "");
651 end,
652 get = function(info)
653 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
654 end,
655 set = function(widget, info)
656 -- This is NOT a real "set", we pass the widget reference to this function which contains similar, but not the same, info.
657
658 if widget.itemId then
659 local groupName = groupIdToName[info[2]];
660
661 if not AddToGroup(groupName, widget.itemId) then
662 print("|cffff0000Couldn't add the item with itemId (" .. widget.itemId .. ") because it is already in a group.|r");
663 end
664 end
665 end,
666 width = "double",
667 dialogControl = "ItemLinkButton",
668 };
669
670 local tblRemoveItemTemplate = {
671 order = 0,
672 type = "input",
673 name = function(info)
674 local itemName, _, itemRarity = GetItemInfo(info[#info]);
675 return tostring( 7 - (itemRarity or 0) ) .. (itemName or "");
676 end,
677 get = function(info)
678 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
679 end,
680 set = function(widget, info)
681 -- This is NOT a real "set", we pass the widget reference to this function which contains similar, but not the same, info.
682
683 if widget.itemId then
684 local groupName = groupIdToName[info[2]];
685
686 -- Unset this item
687 addon.db.global.groups[groupName].items[widget.itemId] = nil;
688
689 -- Now rebuild the list
690 AceConfigRegistry:NotifyChange("InventoryOptions");
691 end
692 end,
693 width = "double",
694 dialogControl = "ItemLinkButton",
695 };
696
697 local function UpdateAddItemList(info)
698 local groupName = groupIdToName[info[2]];
699
700 if not addon.db.global.groups[groupName].items then
701 addon.db.global.groups[groupName].items = {};
702 end
703
704 -- Merge all items from all groups together
705 local items = {};
706 for groupName, values in pairs(addon.db.global.groups) do
707 if values.items then
708 for itemId, _ in pairs(values.items) do
709 items[itemId] = true;
710 end
711 end
712 end
713
714 local ref = options.args.groups.args[info[2]].args.add.args.list.args;
715
716 -- Parse bags and show these
717 for bagID = 4, 0, -1 do
718 for slot = 1, GetContainerNumSlots(bagID) do
719 local itemId = addon:GetItemId(GetContainerItemLink(bagID, slot));
720
721 if itemId then
722 if not items[itemId] then
723 -- If this item isn't used in any group yet
724 ref[itemId] = tblAddItemTemplate;
725 else
726 -- It's already used in a group, don't show it
727 ref[itemId] = nil;
728 end
729 end
730 end
731 end
732 end
733
734 local function UpdateRemoveItemList(info)
735 local groupName = groupIdToName[info[2]];
736
737 if not addon.db.global.groups[groupName].items then
738 addon.db.global.groups[groupName].items = {};
739 end
740
741 local ref = options.args.groups.args[info[2]].args.remove.args.list.args;
742
743 -- Unset all
744 for itemId, _ in pairs(ref) do
745 ref[itemId] = nil;
746 end
747
748 -- Parse items in group and show these
749 for itemId, _ in pairs(addon.db.global.groups[groupName].items) do
750 ref[itemId] = tblRemoveItemTemplate;
751 end
752 end
753
754 function addon:GetItemId(itemLink)
755 itemLink = itemLink and select(3, string.find(itemLink, "|Hitem:([-0-9]+):")); -- if itemLink is nil, it won't execute the second part
756 itemLink = itemLink and tonumber(itemLink);
757
758 return itemLink;
759 end
760
761 -- Default group
762 local defaultGroup = {
763 order = 0,
764 type = "group",
765 childGroups = "tab",
766 name = function(info)
767 return groupIdToName[info[#info]];
768 end,
769 args = {
770 general = {
771 order = 10,
772 type = "group",
773 name = "Stock settings",
774 desc = "Change the stock settings for just this group.",
775 args = {
776 minimumStock = {
777 order = 10,
778 type = "group",
779 inline = true,
780 name = "Minimum stock",
781 set = SetOption,
782 get = GetOption,
783 disabled = GetDisabled,
784 args = {
785 description = {
786 order = 0,
787 type = "description",
788 name = "Here you can specify the minimum amount of items you wish to keep in stock and related settings for the currently selected group.",
789 },
790 header = {
791 order = 5,
792 type = "header",
793 name = "",
794 },
795 overrideMinimumStock = {
796 order = 9,
797 type = "toggle",
798 name = "Override min stock",
799 desc = "Allows you to override the minimum stock setting for this group.",
800 arg = "minimumStock",
801 },
802 minimumStock = {
803 order = 10,
804 type = "range",
805 min = 0,
806 max = 100000,
807 softMax = 1000,
808 step = 1,
809 name = "Minimum stock",
810 desc = "You can manually enter a value between 1.000 and 100.000 in the edit box if the provided range is insufficient.",
811 arg = "overrideMinimumStock",
812 },
813 overrideSummaryThresholdShow = {
814 order = 19,
815 type = "toggle",
816 name = "Override summary showing",
817 desc = "Allows you to override when this group should appear in the summary.",
818 arg = "summaryThresholdShow",
819 },
820 summaryThresholdShow = {
821 order = 20,
822 type = "range",
823 min = 0,
824 max = 100,
825 softMax = 10,
826 step = 0.05,
827 isPercent = true,
828 name = "Show in summary when below",
829 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.",
830 arg = "overrideSummaryThresholdShow",
831 },
832 overrideAlertBelowMinimum = {
833 order = 29,
834 type = "toggle",
835 name = "Override minimum alert",
836 desc = "Allows you to override wether an alert should be shown when an item in this group gets below the minimum stock threshold.",
837 arg = "alertBelowMinimum",
838 },
839 alertBelowMinimum = {
840 order = 30,
841 type = "toggle",
842 name = "Alert when below minimum",
843 desc = "Show an alert when an item in this group gets below the minimum stock threshold.",
844 arg = "overrideAlertBelowMinimum",
845 },
846 },
847 },
848 refill = {
849 order = 20,
850 type = "group",
851 inline = true,
852 name = "Replenishing stock",
853 set = SetOption,
854 get = GetOption,
855 disabled = GetDisabled,
856 args = {
857 description = {
858 order = 0,
859 type = "description",
860 name = function(info)
861 local groupName = groupIdToName[info[2]];
862 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";
863
864 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.";
865
866 return r;
867 end,
868 },
869 header = {
870 order = 5,
871 type = "header",
872 name = "",
873 },
874 overrideRestockTarget = {
875 order = 9,
876 type = "toggle",
877 name = "Override restock target",
878 desc = "Allows you to override the restock target setting for this group.",
879 arg = "restockTarget",
880 },
881 restockTarget = {
882 order = 10,
883 type = "range",
884 min = 0,
885 max = 100000,
886 softMax = 1000,
887 step = 1,
888 name = "Restock target",
889 desc = "You can manually enter a value between 1.000 and 100.000 in the edit box if the provided range is insufficient.",
890 arg = "overrideRestockTarget",
891 },
892 overrideMinCraftingQueue = {
893 order = 19,
894 type = "toggle",
895 name = "Override min queue",
896 desc = "Allows you to override the minimum craftable items queue setting for this group.",
897 arg = "minCraftingQueue",
898 },
899 minCraftingQueue = {
900 order = 20,
901 type = "range",
902 min = 0,
903 max = 1,
904 step = 0.01,
905 isPercent = true,
906 name = "Don't queue if I only miss",
907 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.",
908 arg = "overrideMinCraftingQueue",
909 },
910 },
911 },
912 },
913 },
914 group = {
915 order = 20,
916 type = "group",
917 name = "Group Management",
918 desc = "Rename, delete or export this group.",
919 args = {
920 rename = {
921 order = 10,
922 type = "group",
923 name = "Rename",
924 inline = true,
925 args = {
926 rename = {
927 order = 10,
928 type = "input",
929 name = "New group name",
930 desc = "Change the name of this group to something else. You can also use item links here as you wish.",
931 validate = ValidateGroupName,
932 set = function(info, value)
933 local oldGroupName = groupIdToName[info[2]];
934
935 addon.db.global.groups[value] = CopyTable(addon.db.global.groups[oldGroupName]);
936 addon.db.global.groups[oldGroupName] = nil;
937
938 groupIdToName[info[2]] = value;
939 groupIdToName[value] = true;
940 groupIdToName[oldGroupName] = nil;
941
942 addon:FillGroupOptions();
943 end,
944 get = function(info)
945 return groupIdToName[info[2]];
946 end,
947 width = "double",
948 },
949 },
950 },
951 delete = {
952 order = 20,
953 type = "group",
954 name = "Delete",
955 inline = true,
956 args = {
957 delete = {
958 order = 10,
959 type = "execute",
960 name = "Delete group",
961 desc = "Delete the currently selected group.",
962 confirm = true,
963 confirmText = "Are you sure you wish to |cffff0000DELETE|r this group? This action is not reversable!",
964 func = function(info)
965 local groupName = groupIdToName[info[2]];
966
967 addon.db.global.groups[groupName] = nil;
968
969 addon:FillGroupOptions();
970 end,
971 },
972 },
973 },
974 export = {
975 order = 30,
976 type = "group",
977 name = "Export",
978 inline = true,
979 args = {
980 input = {
981 order = 10,
982 type = "input",
983 multiline = true,
984 name = "Group data",
985 width = "full",
986 desc = "Export the group data for the currently selected group. Press CTRL-A to select all and CTRL-C to copy the text.",
987 set = false,
988 get = function(info)
989 local groupName = groupIdToName[info[2]];
990
991 -- We want to include the group name, so we copy the table then set another value
992 local temp = CopyTable(addon.db.global.groups[groupName]);
993 temp.name = groupName;
994
995 if not AceSerializer then
996 AceSerializer = LibStub("AceSerializer-3.0");
997 end
998
999 return AceSerializer:Serialize(temp);
1000 end,
1001 },
1002 },
1003 },
1004 },
1005 },
1006 add = {
1007 order = 30,
1008 type = "group",
1009 name = "Add items",
1010 args = {
1011 singleAdd = {
1012 order = 10,
1013 type = "group",
1014 inline = true,
1015 name = "Add items",
1016 args = {
1017 help = {
1018 order = 10,
1019 type = "description",
1020 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.",
1021 },
1022 itemLink = {
1023 order = 20,
1024 type = "input",
1025 name = "Single item add (item-link or item-id)",
1026 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.",
1027 validate = function(info, value)
1028 -- If the value is empty we'll allow passing to clear the carret
1029 if value == "" then return true; end
1030
1031 local groupName = groupIdToName[info[2]];
1032
1033 local itemId = addon:GetItemId(string.trim(value or "")) or tonumber(string.trim(value or ""));
1034
1035 if not itemId then
1036 return "This is not a valid item link.";
1037 elseif InGroup(itemId) then
1038 return ("This item is already in the group \"%s\"."):format(InGroup(itemId));
1039 end
1040
1041 return true;
1042 end,
1043 set = function(info, value)
1044 if value and value ~= "" then
1045 local groupName = groupIdToName[info[2]];
1046
1047 local itemId = addon:GetItemId(string.trim(value or "")) or tonumber(string.trim(value or ""));
1048
1049 AddToGroup(groupName, itemId);
1050
1051 print(("Added %s"):format(select(2, GetItemInfo(itemId)) or ("Unknown (#%d)"):format(itemId)));
1052 end
1053 end,
1054 get = false,
1055 },
1056 import = {
1057 order = 40,
1058 type = "input",
1059 name = "Import item data",
1060 desc = "Import item data from an exported item data-string. Any items already grouped will be skipped.",
1061 set = function(info, value)
1062 local groupName = groupIdToName[info[2]];
1063
1064 local allItemIds = { string.split(";", value or "") };
1065
1066 for _, value in pairs(allItemIds) do
1067 local itemId = tonumber(value);
1068
1069 if not itemId then
1070 print(("\"%s\" is not a number."):format(value));
1071 elseif InGroup(itemId) then
1072 print(("Skipping %s (#%d) as it is already in the group |cfffed000%s|r."):format(select(2, GetItemInfo(itemId)) or "Unknown", itemId, InGroup(itemId)));
1073 else
1074 AddToGroup(groupName, itemId);
1075 end
1076 end
1077 end,
1078 get = false,
1079 },
1080 },
1081 },
1082 massAdd = {
1083 order = 20,
1084 type = "group",
1085 inline = true,
1086 name = "Mass add",
1087 args = {
1088 help = {
1089 order = 10,
1090 type = "description",
1091 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.",
1092 },
1093 massAdd = {
1094 order = 20,
1095 type = "input",
1096 name = "Add all items matching...",
1097 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.",
1098 --set = massAddItems,
1099 get = false,
1100 },
1101 },
1102 },
1103 list = {
1104 order = 30,
1105 type = "group",
1106 inline = true,
1107 name = "Item list",
1108 hidden = UpdateAddItemList,
1109 args = {
1110
1111 },
1112 },
1113 },
1114 },
1115 remove = {
1116 order = 40,
1117 type = "group",
1118 name = "Current items",
1119 args = {
1120 help = {
1121 order = 10,
1122 type = "group",
1123 inline = true,
1124 name = "Help",
1125 hidden = false,
1126 args = {
1127 help = {
1128 order = 10,
1129 type = "description",
1130 name = "Click the items you wish to remove from this group.",
1131 },
1132 },
1133 },
1134 list = {
1135 order = 20,
1136 type = "group",
1137 inline = true,
1138 name = "Item list",
1139 hidden = UpdateRemoveItemList,
1140 args = {
1141
1142 },
1143 },
1144 export = {
1145 order = 30,
1146 type = "group",
1147 name = "Export",
1148 inline = true,
1149 args = {
1150 input = {
1151 order = 10,
1152 type = "input",
1153 name = "Item data",
1154 width = "full",
1155 desc = "Export the item data for the currently selected group. Press CTRL-A to select all and CTRL-C to copy the text.",
1156 set = false,
1157 get = function(info)
1158 local groupName = groupIdToName[info[2]];
1159
1160 local combinedItemIds;
1161 -- Parse items in group and show these
1162 for itemId, _ in pairs(addon.db.global.groups[groupName].items) do
1163 if not combinedItemIds then
1164 combinedItemIds = tostring(itemId);
1165 else
1166 combinedItemIds = combinedItemIds .. (";%d"):format(itemId);
1167 end
1168 end
1169
1170 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
1171 end,
1172 },
1173 },
1174 },
1175 },
1176 },
1177 },
1178 };
1179
1180 function addon:MakeGroupOptions()
1181 options.args.groups = {
1182 order = 1100,
1183 type = "group",
1184 name = "Groups",
1185 desc = "Change a group.",
1186 args = {
1187 create = {
1188 order = 10,
1189 type = "group",
1190 inline = true,
1191 name = "Create a brand new group",
1192 args = {
1193 name = {
1194 order = 10,
1195 type = "input",
1196 name = "Group name",
1197 desc = "The name of the group. You can also use item links as you wish.",
1198 validate = ValidateGroupName,
1199 set = function(_, value)
1200 self.db.global.groups[value] = {};
1201
1202 addon:FillGroupOptions();
1203 end,
1204 get = false,
1205 width = "double",
1206 },
1207 },
1208 },
1209 import = {
1210 order = 20,
1211 type = "group",
1212 inline = true,
1213 name = "Import a group",
1214 args = {
1215 input = {
1216 order = 10,
1217 type = "input",
1218 multiline = true,
1219 name = "Group data",
1220 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.",
1221 set = function(info, value)
1222 local temp = { string.split("\n", value or "") };
1223
1224 for no, current in pairs(temp) do
1225 if not AceSerializer then
1226 AceSerializer = LibStub("AceSerializer-3.0");
1227 end
1228
1229 local result, temp = AceSerializer:Deserialize(current);
1230 local name;
1231
1232 if not temp.name then
1233 print("|cffff0000The provided data is not supported.|r");
1234 return;
1235 else
1236 name = temp.name;
1237 temp.name = nil;
1238 end
1239
1240 local newGroupName = string.trim(string.lower(name or ""));
1241
1242 for name in pairs(self.db.global.groups) do
1243 if string.lower(name) == newGroupName then
1244 print(("|cffff0000A group named \"%s\" already exists.|r"):format(name));
1245 return;
1246 end
1247 end
1248
1249 self.db.global.groups[name] = temp;
1250 end
1251 end,
1252 get = false,
1253 width = "full",
1254 },
1255 },
1256 },
1257 },
1258 };
1259 end
1260
1261 function addon:FillGroupOptions()
1262 for id, name in pairs(groupIdToName) do
1263 if type(name) == "string" and not self.db.global.groups[name] then
1264 options.args.groups.args[id] = nil;
1265 groupIdToName[id] = nil;
1266 groupIdToName[name] = nil;
1267 end
1268 end
1269
1270 for name, values in pairs(self.db.global.groups) do
1271 if not groupIdToName[name] then
1272 options.args.groups.args[tostring(count)] = CopyTable(defaultGroup);
1273
1274 groupIdToName[tostring(count)] = name;
1275 groupIdToName[name] = true;
1276
1277 count = ( count + 1 );
1278 end
1279 end
1280 end
1281
1282 function addon:GetItemCount(itemId)
1283 return Altoholic:GetItemCount(itemId);
1284 end
1285
1286
1287
1288
1289 function addon:Debug(t)
1290 if not self.debugChannel and self.debugChannel ~= false then
1291 -- 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)
1292 self.debugChannel = false;
1293
1294 for i = 1, NUM_CHAT_WINDOWS do
1295 local name = GetChatWindowInfo(i);
1296
1297 if name:upper() == "DEBUG" then
1298 self.debugChannel = _G["ChatFrame" .. i];
1299 end
1300 end
1301 end
1302
1303 if self.debugChannel then
1304 self.debugChannel:AddMessage(t);
1305 end
1306 end