/
self:RestorePosition(tip:GetName()) |
end |
--[[ OH Frame stuff]]-- |
--[[ |
--OH Frame stuff |
--]]-- |
--[[ frame generalization stuff ]]-- |
-- for the tooltip stuff |
local RegisterCheckOption, RegisterSliderOption |
--Localization.deDE.lua |
if GetLocale() ~= "deDE" then return end |
LootTrackerLocals = setmetatable({ |
["Sets"] = "Sätze", |
}, {__index = LootTrackerLocals}) |
-- [[ LootTrack Debug Levels ]]-- |
local _version = "1.08-20200" |
--[[ Localisations ]]-- |
local L = LootTrackerLocals |
--[[ Variables ]]-- |
local _ |
local _db -- to hold the list database |
local _itemCache |
local _playerName = UnitName('player') -- available at login |
local _server = GetRealmName() |
local _bagItems, _bankItems = {}, {} |
local _bankOpen |
local _hooked = false |
local _equippedSlots = { |
HeadSlot = false, |
NeckSlot = false, |
ShoulderSlot = false, |
BackSlot = false, |
ChestSlot = false, |
ShirtSlot = false, |
TabardSlot = false, |
WristSlot = false, |
HandsSlot = false, |
WaistSlot = false, |
LegsSlot = false, |
FeetSlot = false, |
Finger0Slot = false, |
Finger1Slot = false, |
Trinket0Slot = false, |
Trinket1Slot = false, |
MainHandSlot = false, |
SecondaryHandSlot = false, |
RangedSlot = false, |
} |
local MAXITEMS = 40000 |
local ClickTip = LibStub:GetLibrary("ClickTip-Beta0", true) |
-- Binding Variables |
BINDING_HEADER_LOOTTRACKER = "LootTracker (".._version..")" |
BINDING_NAME_LTTOGGLECONFIG = L["Toggle Config"] |
BINDING_NAME_LTTOGGLETRACK = L["Toggle Tracker"] |
--[[ For OH Stuff ]]-- |
local OH = LibStub:GetLibrary("OptionHouse-1.1") |
local ui = OH:RegisterAddOn("LootTracker", "LootTracker", "JoshBorke", _version) |
local ohpar = OH and OH:GetFrame("addon") |
local configFrame, visFrame |
local currentSet-- current set means we've selected a set as a sub cat |
local selectedSet -- selectSet is the ID of the line we've currently selected |
local selectedLine -- selectedLine is the index of the line we've currently selected |
LootTracker = DongleStub("Dongle-1.0"):New("LootTracker") |
local LootTracker = LootTracker |
--[[ Local functions ]]-- |
--[[ Function to handle initialization ]]-- |
function LootTracker:Initialize() |
self.defaults = { |
profile = { |
positions = {}, |
trackedLists = {}, |
tracker = { |
old = { |
alpha = 1, |
scale = 1, |
combatHidden = false, |
locked = false, |
squish = false, |
}, |
new = { |
alpha = 1, |
scale = 1, |
combatHidden = false, |
locked = false, |
squish = false, |
}, |
ctc = false, |
hideifnone = false, |
collapsed = {}, |
} |
}, |
global = { |
lists = {}, -- for storing lists |
cache = {}, -- for storing bag/bank items |
items ={}, -- for storing item->name conversions |
}, |
} |
self:CreateSlashCommands() |
if (not _hooked) then |
hooksecurefunc('SetItemRef', |
function(link, text, button) |
if (not link) then return end |
if (self.inputItemFrame and self.inputItemFrame:IsShown()) then |
if (IsShiftKeyDown()) then |
local name,link = GetItemInfo(link) |
self.inputItemFrame.item:SetText(link) |
end |
end |
end ) |
hooksecurefunc('ChatEdit_InsertLink', |
function(text) |
if (not text) then return end |
if (self.inputItemFrame and self.inputItemFrame:IsShown()) then |
if (IsShiftKeyDown()) then |
local name,link = GetItemInfo(text) |
self.inputItemFrame.item:SetText(link) |
end |
end |
end ) |
_hooked = true |
end |
for name in pairs(_equippedSlots) do |
_equippedSlots[name] = GetInventorySlotInfo(name) |
end |
end |
function LootTracker:Enable() |
self.db = self:InitializeDB("LootTrackerDB", self.defaults) |
_db = self.db |
_itemCache = self.db.global.cache |
if (not _itemCache[_server]) then |
_itemCache[_server] = { |
[_playerName] = {} |
} |
elseif (not _itemCache[_server][_playerName]) then |
_itemCache[_server][_playerName] = {} |
end |
self:UpdateItemIDs() |
self:RegisterEvent("BANKFRAME_OPENED", "UpdateBankItemCounts") |
self:RegisterEvent("BANKFRAME_CLOSED", function() _bankOpen = false end) |
self:RegisterEvent("PLAYERBANKSLOTS_CHANGED", "UpdateBankItemCounts") |
self:RegisterEvent("PLAYERBANKBAGSLOTS_CHANGED", "UpdateBankItemCounts") |
self:RegisterEvent("BAG_UPDATE", "UpdateBagItemCounts") |
self:RegisterEvent("PLAYER_REGEN_DISABLED", function() |
local options = _db.profile.tracker |
if options.old.combatHidden and self.oldTracker then self.oldTracker:Hide() end |
if options.new.combatHidden and self.newTracker then self.newTracker:Hide() end |
end ) |
self:RegisterEvent("PLAYER_REGEN_ENABLED", function() |
local options = _db.profile.tracker |
if options.old.shown and self.oldTracker then self:ShowTracker() end |
if options.new.shown and self.newTracker then self.newTracker:Show() end |
end) |
self:UpdateBagItemCounts() |
self:PruneSets() |
if not ClickTip then |
_db.profile.tracker.new.shown = false |
end |
if (_db.profile.tracker.old.shown) then |
self:ShowTracker() |
end |
if (_db.profile.tracker.new.shown) then |
self:ShowNewTracker() |
end |
ui:RegisterCategory(L["Visuals"], self, "CreateRandomConfigs") |
ui:RegisterCategory(L["Sets"], self, "PopulateOHFrame", true) -- don't cache the frame |
local sets = _db.global.lists |
for i = 1, #sets do |
ui:RegisterSubCategory(L["Sets"], sets[i], self, "PopulateOHFrame", true) -- don't cache the frame |
end |
end |
function LootTracker:Disable() |
--self:SavePosition("LootTrackerTrackingFrame") |
self:SavePosition("LootTrackerConfigFrame") |
end |
function LootTracker:CreateSlashCommands() |
local cmd = self:InitializeSlashCommand("LootTracker Slash Command", "LOOTTRACKER", "loottracker", "lt", "ltr") |
local ShowConfig = function(opt) |
local tolower = string.lower |
if tolower(opt) == 'vis' then |
OH:Open("LootTracker",L["Visuals"]) |
elseif tolower(opt) == ' sets' then |
OH:Open("LootTracker",L["Sets"]) |
else |
OH:Open("LootTracker") |
end |
end |
cmd:RegisterSlashHandler("config - Show configuration frame", "^config(.*)$", ShowConfig) |
cmd:RegisterSlashHandler("sets - Show the sets configuration", "^sets$", function() OH:Open("LootTracker",L["Sets"]) end) |
cmd:RegisterSlashHandler("visuals - Show the visual configurations", "^vis.*$", function() OH:Open("LootTracker",L["Visuals"]) end) |
local Reset = function() |
self:ResetOptions() |
_db:ResetDB() |
_itemCache = _db.global.cache |
self.currentSet = nil |
if (self.configFrame) then |
self:UpdateSetScroll() |
self:UpdateItemScroll() |
end |
end |
local ResetCache = function() |
_db.global.cache = {} |
_itemCache = _db.global.cache |
self:UpdateBagItemCounts() |
if (self.configFrame) then |
self:UpdateItemScroll() |
end |
self:PopulateTrackerFrame() |
self:PopulateNewTracker() |
end |
cmd:RegisterSlashHandler("reset - Reset the configuration", "^reset$", Reset) |
cmd:RegisterSlashHandler("resetCache - Reset the item count totals", "^resetCache$", ResetCache) |
end |
function LootTracker:UpdateBagItemCounts() |
local match = string.match |
local itemLink, itemID, count |
local cache = _itemCache[_server][_playerName] |
local name |
if (not cache) then |
cache = {} |
_itemCache[_server][_playerName] = cache |
end |
local bagItems = cache.bagItems |
if (not bagItems) then |
bagItems = {} |
cache.bagItems = bagItems |
end |
for item, count in pairs(bagItems) do |
bagItems[item] = 0 |
end |
for name, id in pairs(_equippedSlots) do |
itemLink = GetInventoryItemLink("player", id) |
if (itemLink) then |
name = GetItemInfo(itemLink) |
if (name) then |
count = GetInventoryItemCount("player", id) |
bagItems[name] = count + (bagItems[name] or 0) |
end |
end |
end |
for bag = 0, 4 do |
for slot = 1, GetContainerNumSlots(bag) do |
itemLink = GetContainerItemLink(bag, slot) |
if (itemLink) then |
--itemID = match(itemLink, "item:(%-?%d+)") |
name = GetItemInfo(itemLink) |
if (name) then |
_, count = GetContainerItemInfo(bag, slot) |
--bagItems[itemID] = count + (bagItems[itemID] or 0) |
bagItems[name] = count + (bagItems[name] or 0) |
end |
end |
end |
end |
if (_bankOpen) then |
self:UpdateBankItemCounts() -- no event fires for putting an item in the bank |
end |
if (self.oldTracker and self.oldTracker:IsShown()) then |
self:PopulateTrackerFrame() -- update the tracker frame |
self:PopulateNewTracker() -- update the tracker frame |
end |
end |
function LootTracker:UpdateBankItemCounts() |
local match = string.match |
local itemLink, itemID, count |
local cache = _itemCache[_server][_playerName] |
local name |
if (not cache) then |
cache = {} |
_itemCache[_server][_playerName] = cache |
end |
local bankItems = cache.bankItems |
if (not bankItems) then |
bankItems = {} |
cache.bankItems = bankItems |
end |
for item, count in pairs(bankItems) do |
bankItems[item] = 0 |
end |
local bag = -1 |
_bankOpen = true |
local bankSlots = GetNumBankSlots() |
for slot = 1, GetContainerNumSlots(bag) do |
itemLink = GetContainerItemLink(bag, slot) |
if (itemLink) then |
--itemID = match(itemLink, "item:(%-?%d+)") |
name = GetItemInfo(itemLink) |
_, count = GetContainerItemInfo(bag, slot) |
--bankItems[itemID] = count + (bankItems[itemID] or 0) |
bankItems[name] = count + (bankItems[name] or 0) |
end |
end |
if (bankSlots > 0) then |
for cbag = 1, bankSlots do |
bag = cbag + 4 |
for slot = 1, GetContainerNumSlots(bag) do |
itemLink = GetContainerItemLink(bag, slot) |
if (itemLink) then |
--itemID = match(itemLink, "item:(%-?%d+)") |
_, count = GetContainerItemInfo(bag, slot) |
name = GetItemInfo(itemLink) |
--bankItems[itemID] = count + (bankItems[itemID] or 0) |
bankItems[name] = count + (bankItems[name] or 0) |
--self:Print(string.format("%s: %s", name, count)) |
end |
end |
end |
end |
if (self.oldTracker and self.oldTracker:IsShown()) then |
self:PopulateTrackerFrame() -- update the tracker frame |
self:PopulateNewTracker() -- update the tracker frame |
end |
end |
function LootTracker:GetItemCount(name, player) |
local cache = _itemCache[_server] |
if (not cache) then return 0 end |
local total = 0 |
if (_db.profile.tracker.ctc and not player) then |
player = _playerName |
end |
if (player) then |
cache = cache[player] |
if (not cache) then return total end |
for bag, items in pairs(cache) do |
if (items[name]) then |
total = items[name] + total |
end |
end |
else |
for player, bags in pairs(cache) do |
for bag, items in pairs(bags) do |
if (items[name]) then |
total = items[name] + total |
end |
end |
end |
end |
return total |
end |
function LootTracker:UpdateSetName(setName) |
if (self.newSet) then |
self:CreateNewSet(setName) |
return |
else |
local lists = _db.global.lists |
if (not _db.global) then _db.global = {} end |
if (not _db.global.lists) then |
_db.global.lists = {} |
lists = _db.global.lists |
end |
ui:RemoveSubCategory(L["Sets"], self.currentSet) -- remove the current set name |
ui:RegisterSubCategory(L["Sets"], setName, self, "PopulateOHFrame", true) -- don't cache the frame |
if (lists[self.currentSet]) then |
lists[setName] = lists[self.currentSet] |
lists[self.currentSet] = nil |
table.insert(lists, setName) |
for index, set in pairs(lists) do |
if (set == self.currentSet) then |
table.remove(lists, index) |
end |
end |
table.sort(lists) |
end |
end |
end |
function LootTracker:CreateNewSet(setName) |
local _lists = _db.global.lists |
if (not _db.global) then _db.global = {} end |
if (not _db.global.lists) then |
_db.global.lists = {} |
_lists = _db.global.lists |
end |
if (not _lists[setName]) then |
_lists[setName] = {} |
table.insert(_lists, setName) |
table.sort(_lists) |
ui:RegisterSubCategory(L["Sets"], setName, self, "PopulateOHFrame", true) -- don't cache the frame |
self:updateScrollList() |
end |
end |
function LootTracker:RemoveSet(setName) |
local _lists = _db.global.lists |
if (not _db.global) then _db.global = {} end |
if (not _db.global.lists) then |
_db.global.lists = {} |
_lists = _db.global.lists |
end |
if (_lists[setName]) then |
_lists[setName] = nil |
for index, set in pairs(_lists) do |
if (set == setName) then |
table.remove(_lists, index) |
end |
end |
-- make sure to remove it from the tracked list |
local tracked = _db.profile.trackedLists |
if tracked then |
for i=1,#tracked do |
local list = tracked[i] |
if list == setName then |
table.remove(tracked, i) |
end |
end |
end |
end |
return true |
end |
-- called to prune out previously removed sets |
function LootTracker:PruneSets() |
local tracked = _db.profile.trackedLists |
local lists = _db.global.lists |
local list |
local i, max = 1, #tracked |
while (true) do |
list = tracked[i] |
if (list and not lists[list]) then |
table.remove(tracked,i) |
else |
i = i+1 |
end |
if (i > max) then break end |
end |
end |
local function myListSort(id1, id2) |
local name1, name2 |
if (id1) then |
name1 = GetItemInfo(id1) |
end |
if (id2) then |
name2 = GetItemInfo(id2) |
end |
if (not name1 or not name2) then return end |
return name1 < name2 |
end |
function LootTracker:UpdateItemIDs() |
-- here we update the items lookup table in case we don't have one generated already |
local cache = _itemCache[_server] |
local itemsDB = _db.global.items |
local name, link, itemID |
local name2, link2, found |
local MAXITEMS = 40000 |
for player, bags in pairs(cache) do |
for bag, items in pairs(bags) do |
for item, count in pairs(items) do |
name, link = GetItemInfo(item) |
if (not name) then |
found = false |
for i=1, MAXITEMS do |
if (not found) then |
name2, link2 = GetItemInfo(i) |
if (name == name2) then |
name = name2 |
link = link2 |
found = true |
end |
end |
end |
end |
if (name and link) then |
itemID = string.match(link, "item:(%-?%d+)") |
itemsDB[name] = itemID |
itemsDB[itemID] = name |
end |
end |
end |
end |
end |
function LootTracker:GetItemID(name,id) |
local db = _db.global.items |
local res |
local name2, link, itemID |
if (name) then |
res = db[name] |
elseif (id) then |
res = db[id] |
end |
if (not res) then |
for i=1, MAXITEMS do |
name2, link = GetItemInfo(i) |
if (name == name2) then |
break |
end |
end |
itemID = string.match(link, "item:(%-?%d+)") |
res = itemID |
end |
return res |
end |
function LootTracker:GetItemNameFromInput(input) |
local name |
local lower = string.lower |
local linput = lower(input) |
local itemID = string.match(input, "item:(%-?%d+)") |
if (not itemID) then |
local db = _db.global.items |
-- find the itemID... |
if db[input] then |
local name, itemLink = GetItemInfo(db[input]) |
if name then |
return name, string.match(itemLink, "item:(%-?%d+)") |
end |
end |
for i=1, MAXITEMS do |
local name, itemLink = GetItemInfo(i) |
if name then |
if lower(name or '') == lower(input) or string.match(itemLink, "item:(%-?%d+)") == input then |
return name, string.match(itemLink, "item:(%-?%d+)") |
end |
end |
end |
else |
name = GetItemInfo(itemID) |
end |
return name, itemID |
end |
function LootTracker:UpdateItem(itemLink, count) |
local list = _db.global.lists[currentSet] |
local name, itemID = self:GetItemNameFromInput(itemLink) |
local found |
if (not name) then return false end |
if (not list) then return false end |
if (not itemID) then return false end |
if (count == '') then count = nil end |
for index, ID in pairs(list) do |
if (ID == name) then |
found = true |
end |
end |
if (not found) then |
_db.global.items[itemID] = name |
_db.global.items[name] = itemID |
table.insert(list, name) |
table.sort(list, myListSort) |
end |
list[name] = count --or 0 |
return true |
end |
function LootTracker:RemoveItem(itemID, set) |
local list = _db.global.lists[set] |
if (not list or not itemID) then return false end |
list[itemID] = nil |
for index, ID in pairs(list) do |
if (ID == itemID) then |
table.remove(list, index) |
end |
end |
return true |
end |
function LootTracker:IsSetTracked(setName) |
local lists = _db.profile.trackedLists |
for index, list in pairs(lists) do |
if (list == setName) then |
return true |
end |
end |
end |
function LootTracker:ToggleTrackSet(setName) |
local lists = _db.profile.trackedLists |
local found = false |
for index, list in pairs(lists) do |
if (list == setName) then |
found = true |
table.remove(lists, index) -- removing from a sorted lists leaves it sorted |
end |
end |
if (found) then return false end -- if we removed, return false |
table.insert(lists, setName) |
table.sort(lists) |
return true -- adding = return true |
end |
--[[ Frame Functions ]]-- |
function LootTracker:AddToTip(itemName) |
local cache = _itemCache[_server] |
if (not cache) then return end |
local count |
local txt = '' |
for player in pairs(cache) do |
count = self:GetItemCount(itemName, player) |
if (count and count > 0) then |
GameTooltip:AddLine(string.format("%s: %d", player, self:GetItemCount(itemName, player))) |
end |
end |
end |
local TL, TR, BL, BR = "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT" |
-- copied from PerfectRaid, credit goes to cladhaire |
function LootTracker:SavePosition(name) |
local f = getglobal(name) |
if (not f) then return end |
local x,y = f:GetCenter() |
local anchor = 'CENTER' |
local s = f:GetEffectiveScale() |
--x,y = x*s,y*s |
local opt = _db.profile.positions[name] |
if not opt then |
_db.profile.positions[name] = {} |
opt = _db.profile.positions[name] |
end |
local h, w = UIParent:GetHeight(), UIParent:GetWidth() |
local xOff, yOff, anchor = 0, 0, 'CENTER' |
local fW, fH = f:GetWidth() / 2, f:GetHeight() / 2 |
local left, top, right, bottom = x - fW, y + fH, x + fW, y - fH |
if (x > w/2) then -- on the right half of the screen |
if (y > h/2) then -- top half |
xOff = -(w - right) |
yOff = -(h - top) |
anchor = TR |
else -- bottom half |
xOff = -(w - right) |
yOff = bottom |
anchor = BR |
end |
else -- on the left half of the screen |
if (y > h/2) then -- top half |
xOff = left |
yOff = -(h - top) |
anchor = TL |
else -- bottom half |
xOff = left |
yOff = bottom |
anchor = BL |
end |
end |
opt.PosX = xOff*s |
opt.PosY = yOff*s |
opt.anchor = anchor |
end |
-- copied from PerfectRaid, credit goes to cladhaire |
function LootTracker:RestorePosition(name) |
local f = getglobal(name) |
local opt = _db.profile.positions[name] |
if not opt then |
_db.profile.positions[name] = {} |
opt = _db.profile.positions[name] |
end |
local h, w = UIParent:GetHeight(), UIParent:GetWidth() |
local x = opt.PosX |
local y = opt.PosY |
local anchor = opt.anchor |
local s = f:GetEffectiveScale() |
if not x or not y or not anchor then |
f:ClearAllPoints() |
f:SetPoint("CENTER", UIParent, "CENTER", 0, 0) |
return |
end |
x,y = x/s,y/s |
f:ClearAllPoints() |
f:SetPoint(anchor, UIParent, anchor, x, y) |
end |
function LootTracker:CreateTrackerFrame() |
if (self.oldTracker) then return end |
local tooltip = CreateFrame('GameTooltip', 'LootTrackerTrackingFrame', UIParent, 'GameTooltipTemplate') |
tooltip:SetBackdrop({ |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, |
tileSize = 16, |
edgeSize = 16, |
insets = { left = 5, right = 5, top = 5, bottom = 5 }, |
}) |
tooltip:SetBackdropColor(24/255, 24/255, 24/255, 1) |
tooltip:EnableMouse(true) |
tooltip:SetMovable(true) |
--tooltip:RegisterForDrag("LeftButton") |
tooltip:SetScript("OnMouseDown",function(this, button) tooltip:StartMoving() end) |
tooltip:SetScript("OnMouseUp",function() self:SavePosition(tooltip:GetName()); tooltip:StopMovingOrSizing() end) |
tooltip:SetOwner(UIParent, "ANCHOR_NONE") |
tooltip:SetClampedToScreen(true) |
tooltip:SetFrameStrata("BACKGROUND") |
tooltip:Show() |
tooltip:SetScript("OnShow", function() self:PopulateTrackerFrame() end) |
self.oldTracker = tooltip |
return tooltip |
end |
function LootTracker:CreateSetInputFrame() |
if (self.inputSetFrame) then return end |
local frame = CreateFrame('Frame', nil, OH:GetFrame("addon")) |
frame:SetFrameStrata("DIALOG") |
frame:SetFrameLevel(frame:GetFrameLevel()+6) |
frame:SetWidth(250) |
frame:SetHeight(75) |
frame:SetBackdrop({ |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, |
tileSize = 16, |
edgeSize = 16, |
insets = { left = 1, right = 1, top = 1, bottom = 1 }, |
}) |
frame:ClearAllPoints() |
frame:SetBackdropColor(24/255, 24/255, 24/255) |
frame:EnableMouse(true) |
frame:SetMovable(true) |
frame:RegisterForDrag("LeftButton") |
frame:Show() |
frame:SetScript("OnDragStart", function() this:StartMoving() end) |
frame:SetScript("OnDragStop", function() this:StopMovingOrSizing() end) |
frame:SetClampedToScreen(true) |
local e = CreateFrame('EditBox','LootTrackerSetEditBox',frame,'InputBoxTemplate') |
local f = frame:CreateFontString(nil,'OVERLAY','GameFontNormalSmall') |
e.label = f |
e:SetHeight(26) |
e:SetWidth(225) |
f:SetPoint('BOTTOMLEFT',e,'TOPLEFT',-2,0) |
e:SetAutoFocus(true) |
e:SetFont("Fonts\\FRIZQT__.TTF", 16) |
e:SetTextColor(1,1,1) |
f:SetText('Set Name') |
e:SetPoint('TOPLEFT', frame, 'TOPLEFT', 15, -23) |
frame.ok = self:CreateButton(frame, 75, 20, 'Ok', 'BOTTOMLEFT', frame, 'BOTTOMLEFT', 10, 5) |
frame.cancel = self:CreateButton(frame, 75, 20, 'Cancel', 'BOTTOMRIGHT', frame, 'BOTTOMRIGHT', -10, 5) |
frame:SetScript("OnHide", function(this) e:SetText('') end) |
frame.editBox = e |
frame.editBox:SetScript("OnEnterPressed", |
function(this) |
self:UpdateSetName(e:GetText()) |
selectedLine = nil |
self:updateScrollList(true) |
frame:Hide() |
end ) |
frame.cancel:SetScript("OnClick", function(this) frame:Hide() end) |
frame.editBox:SetScript("OnEscapePressed", function(this) frame:Hide() end) |
frame.ok:SetScript("OnClick", |
function(this) |
self:UpdateSetName(e:GetText()) |
selectedLine = nil |
self:updateScrollList(true) |
frame:Hide() |
end ) |
frame:SetPoint('CENTER') |
self.inputSetFrame = frame |
end |
function LootTracker:CreateItemInputFrame() |
if (self.inputItemFrame) then return end |
local frame = CreateFrame('Frame', nil, OH:GetFrame("addon")) |
frame:SetFrameStrata("DIALOG") |
frame:SetFrameLevel(frame:GetFrameLevel()+7) |
frame:SetWidth(250) |
frame:SetHeight(75) |
frame:SetBackdrop({ |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, |
tileSize = 16, |
edgeSize = 16, |
insets = { left = 1, right = 1, top = 1, bottom = 1 }, |
}) |
frame:ClearAllPoints() |
frame:SetBackdropColor(24/255, 24/255, 24/255) |
frame:EnableMouse(true) |
frame:SetMovable(true) |
frame:RegisterForDrag("LeftButton") |
frame:Show() |
frame:SetScript("OnDragStart", function() this:StartMoving() end) |
frame:SetScript("OnDragStop", function() this:StopMovingOrSizing() end) |
frame:SetClampedToScreen(true) |
local e = CreateFrame('EditBox','LootTrackerItemCountEditBox',frame,'InputBoxTemplate') |
local f = frame:CreateFontString(nil,'OVERLAY','GameFontNormalSmall') |
e.label = f |
e:SetHeight(26) |
e:SetWidth(45) |
f:SetPoint('BOTTOMLEFT',e,'TOPLEFT',-2,0) |
e:SetAutoFocus(false) |
e:SetFont("Fonts\\FRIZQT__.TTF", 16) |
e:SetTextColor(1,1,1) |
f:SetText('Goal') |
e:SetPoint('TOPLEFT', frame, 'TOPLEFT', 15, -23) |
frame.num = e |
f = frame:CreateFontString(nil, 'OVERLAY', 'GameFontNormalSmall') |
f:SetText('x') |
f:SetPoint('LEFT', frame.num, 'RIGHT', 1, 0) |
e:SetNumeric(true) |
e:SetMaxLetters(4) |
e = CreateFrame('EditBox','LootTrackerItemNameEditBox',frame,'InputBoxTemplate') |
e:SetPoint('LEFT', frame.num, 'RIGHT', 12, 0) |
e:SetHeight(26) |
e:SetWidth(165) |
e:SetAutoFocus(false) |
e:SetFont("Fonts\\FRIZQT__.TTF", 16) |
e:SetTextColor(1,1,1) |
f = frame:CreateFontString(nil,'OVERLAY','GameFontNormalSmall') |
e.label = f |
f:SetPoint('BOTTOMLEFT',e,'TOPLEFT',-2,0) |
f:SetText('Item') |
frame.item = e |
frame.ok = self:CreateButton(frame, 75, 20, 'Ok', 'BOTTOMLEFT', frame, 'BOTTOMLEFT', 10, 5) |
frame.cancel = self:CreateButton(frame, 75, 20, 'Cancel', 'BOTTOMRIGHT', frame, 'BOTTOMRIGHT', -10, 5) |
frame.num:SetScript("OnTabPressed", function(this) frame.item:SetFocus() end) |
frame.item:SetScript("OnEnterPressed", |
function(this) |
local result = self:UpdateItem(frame.item:GetText(), frame.num:GetText()) |
if result then |
frame.item:SetText('') |
frame.num:SetText('') |
self:updateScrollList() |
self:PopulateTrackerFrame() |
self:PopulateNewTracker() |
if (not IsShiftKeyDown()) then |
frame:Hide() |
end |
else |
UIErrorsFrame:AddMessage(L['LootTracker: Invalid item entered.']) |
end |
end ) |
frame:SetScript("OnHide", function(this) |
frame.item:SetText('') |
frame.num:SetText('') |
end ) |
frame.cancel:SetScript("OnClick",function(this) frame:Hide() end) |
frame.item:SetScript("OnEscapePressed", function(this) frame:Hide() end) |
frame.ok:SetScript("OnClick", |
function(this) |
local result = self:UpdateItem(frame.item:GetText(), frame.num:GetText()) |
if result then |
frame.item:SetText('') |
frame.num:SetText('') |
self:updateScrollList() |
self:PopulateTrackerFrame() |
self:PopulateNewTracker() |
if (not IsShiftKeyDown()) then |
frame:Hide() |
end |
else |
UIErrorsFrame:AddMessage('LootTracker: Invalid item entered.') |
end |
end ) |
frame:SetPoint('CENTER') |
self.inputItemFrame = frame |
end |
-- idea (and most of the code) taken from KC_Items2 |
function LootTracker:CreateButton(parent, width, height, text, ...) |
local button = CreateFrame('Button', nil, parent, 'OptionsButtonTemplate') |
button:SetWidth(width) |
button:SetHeight(height) |
button:SetFont("Fonts\\FRIZQT__.TTF", 12) |
--button:SetTextColor(1,1,1) |
button:SetText(text) |
if select("#",...) > 0 then button:SetPoint(...) end |
return button |
end |
function LootTracker:ShowTracker() |
_db.profile.tracker.old.shown = true |
if (not self.oldTracker) then |
self:CreateTrackerFrame() |
if (_db.profile.tracker.old.lock) then |
self.oldTracker:SetBackdropBorderColor(24/255, 24/255, 24/255, 0) |
self.oldTracker:EnableMouse(false) |
end |
end |
self.oldTracker:Show() |
self.oldTracker:SetOwner(UIParent, "ANCHOR_NONE") |
self.oldTracker:SetScale(_db.profile.tracker.old.scale) |
self.oldTracker:SetAlpha(_db.profile.tracker.old.alpha) |
self:PopulateTrackerFrame() |
end |
function LootTracker:ToggleConfig() |
OH:Open("LootTracker") |
end |
function LootTracker:ToggleTracker() |
if (LootTrackerTrackingFrame) then |
if (LootTrackerTrackingFrame:IsVisible()) then |
LootTrackerTrackingFrame:Hide() |
return |
end |
end |
LootTracker:ShowTracker() |
end |
function LootTracker:ShowSetInputFrame(text) |
if (not self.inputSetFrame) then |
self:CreateSetInputFrame() |
end |
if (text) then |
self.inputSetFrame.editBox:SetText(text) |
end |
self.inputSetFrame:Show() |
end |
function LootTracker:ShowItemInputFrame(text, count) |
if (not self.inputItemFrame) then |
self:CreateItemInputFrame() |
end |
if (text) then |
self.inputItemFrame.item:SetText(text) |
end |
if (count) then |
self.inputItemFrame.num:SetText(count) |
end |
self.inputItemFrame:Show() |
end |
-- follwing bit of code taken from tablet-2.0. credit goes to them |
local getLine, freeLine |
do |
local tinsert, tremove = table.insert, table.remove |
local textures = {} |
getLine = function(parent) |
local t = tremove(textures) |
if (not t) then |
t = parent:CreateFontString(nil, 'ARTWORK') |
t:SetFontObject(GameTooltipText) |
end |
t:Show() |
t:SetParent(parent) |
return t |
end |
freeLine = function(t) |
t:SetText('') |
t:SetParent(UIParent) |
t:Hide() |
tinsert(textures, t) |
end |
end |
function LootTracker:AddLineToTracker(text, r, g, b) |
local frame = self.oldTracker |
local prev = frame.prev |
local offset = frame.offset or 1 |
local line = frame.lines[offset] or getLine(frame) |
frame.lines[offset] = line |
if (prev and prev ~= line) then |
line:SetPoint('TOPLEFT', prev, 'BOTTOMLEFT') |
else |
line:SetPoint('TOPLEFT', frame, 'TOPLEFT', 6, -5) |
end |
line:SetText(text) |
line:SetTextColor(r, g, b) |
line:Show() |
frame.offset = offset + 1 |
frame.prev = line |
end |
function LootTracker:PopulateTrackerFrame() |
local tracked = _db.profile.trackedLists |
local lists = _db.global.lists |
local options = _db.profile.tracker.old |
local list, items, name, item |
local tip = self.oldTracker |
local text |
local count |
if (not tip) then return end -- jump out if we aren't showing anything |
if (not options.shown) then tip:Hide() return end -- it's hidden, don't show it |
tip:ClearLines() |
local hideifnone = _db.profile.tracker.hideifnone |
local hideincomplete = _db.profile.tracker.hideincomplete |
local squish = options.squish |
local continue |
--tip:SetScale(options.scale) |
if (options.lock) then |
tip:SetBackdropBorderColor(24/255, 24/255, 24/255, 0) |
tip:EnableMouse(false) |
end |
tip:AddLine("LootTracker", 0.75, 0.61, 0) |
for i=1, #tracked do |
list = tracked[i] |
tip:AddLine(list, 0.75, 0.61, 0) |
items = lists[list] |
for j=1, #items do |
continue = false |
name = items[j] |
count = self:GetItemCount(name) |
if hideifnone and count == 0 then |
continue = true |
end |
if (items[name]) then |
if hideincomplete and count < tonumber(items[name]) then continue = true end |
text = string.format("%d/%d", count, items[name]) |
else |
text = count |
end |
if not continue then |
if (squish) then |
text = string.format(" - %s: %s", name or 'name', text or 'text') |
tip:AddLine(text, 0.8, 0.8, 0.8) |
else |
tip:AddDoubleLine(' - '..name..':', text, 0.8, 0.8, 0.8) |
end |
end |
end |
end |
tip:Show() |
local r, g, b = tip:GetBackdropColor() |
tip:SetBackdropColor(r, g, b, options.alpha) |
self:RestorePosition(tip:GetName()) |
end |
--[[ all new stuff ]]-- |
function LootTracker:IsSetCollapsed(set) |
return _db.profile.tracker.collapsed[set] |
end |
function LootTracker:CollapseSet(set, collapse) |
_db.profile.tracker.collapsed[set] = collapse |
end |
function LootTracker:ToggleCollapsedSet(set) |
_db.profile.tracker.collapsed[set] = not _db.profile.tracker.collapsed[set] |
end |
function LootTracker:CreateNewTracker() |
if (self.newTracker) then return end |
local tooltip = ClickTip:GetTip("LTAdvancedTracker", UIParent) |
tooltip:EnableMouse(true) |
tooltip:SetMovable(true) |
tooltip:SetScript("OnMouseDown",function(this, button) this:StartMoving() end) |
tooltip:SetScript("OnMouseUp",function() self:SavePosition(tooltip:GetName()); tooltip:StopMovingOrSizing() end) |
self.newTracker = tooltip |
tooltip:SetScript("OnShow", function() self:PopulateNewTracker() end) |
tooltip:SetClampedToScreen(true) |
return tooltip |
end |
local clickers = { |
SetClicker = function(set) |
if (set) then |
LootTracker:ToggleCollapsedSet(set) |
LootTracker:PopulateNewTracker() |
end |
end, |
--ItemClicker = function() return end, |
} |
local function ClickHandler(line, button) |
local f = line.ltType |
local func = clickers[f] |
if (func and type(func) == "function") then |
func(line.ltParam) |
else |
-- clicked on a item |
--ChatFrame1:AddMessage("Clicked!") |
end |
end |
function LootTracker:ShowNewTracker() |
_db.profile.tracker.new.shown = true |
if (not self.newTracker) then |
self:CreateNewTracker() |
if (_db.profile.tracker.new.lock) then |
self.newTracker:SetBackdropBorderColor(24/255, 24/255, 24/255, 0) |
self.newTracker:EnableMouse(false) |
end |
end |
self.newTracker:Show() |
self.newTracker:SetScale(_db.profile.tracker.new.scale) |
self.newTracker:SetAlpha(_db.profile.tracker.new.alpha) |
self:PopulateNewTracker() |
end |
local tipLines = {} |
function LootTracker:PopulateNewTracker() |
local tracked = _db.profile.trackedLists |
local lists = _db.global.lists |
local options = _db.profile.tracker |
local list, items, name, item |
local tip = self.newTracker |
local text |
local cLineN = 1 |
local collapsed |
if (not tip) then return end -- jump out if we aren't showing anything |
if (not options.new.shown) then tip:Hide() return end -- it's hidden, don't show it |
local squish = options.new.squish |
local hideifnone = options.hideifnone |
local hideincomplete = options.hideincomplete |
tip:SetScale(options.new.scale) |
if (options.new.lock) then |
tip:SetBackdropBorderColor(24/255, 24/255, 24/255, 0) |
tip:EnableMouse(false) |
end |
local line, entries = tip:GetLine(cLineN) |
if (entries and entries > 1) then |
tip:ChangeLine(line, 1) |
tip:ChangeEntry(line, 1, "LootTracker", 0.75, 0.61, 0, GameTooltipHeaderText) |
elseif (entries) then |
tip:ChangeEntry(line, 1, "LootTracker", 0.75, 0.61, 0, GameTooltipHeaderText) |
else |
line = tip:AddLine("LootTracker", 0.75, 0.61, 0, GameTooltipHeaderText) |
end |
line:EnableMouse(nil) |
cLineN = cLineN + 1 |
for i=1, #tracked do |
list = tracked[i] |
line, entries = tip:GetLine(cLineN) |
collapsed = self:IsSetCollapsed(list) |
text = (collapsed and "+ " or "- ") .. list |
if (entries) then |
if (entries > 1) then |
tip:ChangeLine(line, 1) |
tip:ChangeEntry(line, 1, text, 0.75, 0.61, 0, GameTooltipText) |
else |
tip:ChangeEntry(line, 1, text, 0.75, 0.61, 0, GameTooltipText) |
end |
else |
line = tip:AddLine(text, 0.75, 0.61, 0, GameTooltipText) |
line:SetScript("OnClick", ClickHandler) |
line:SetMovable(true) |
line:RegisterForDrag('LeftButton') |
line:SetScript('OnDragStart', function(this, button) tip:StartMoving() end) |
line:SetScript('OnDragStop', function(this) self:SavePosition(tip:GetName()); tip:StopMovingOrSizing() end) |
end |
line.ltType = "SetClicker" |
line.ltParam = list |
line:EnableMouse(true) |
cLineN = cLineN + 1 |
if (not collapsed) then |
items = lists[list] |
for j=1, #items do |
local continue = false |
line, entries = tip:GetLine(cLineN) |
name = items[j] |
local count = self:GetItemCount(name) |
if hideifnone and count == 0 then continue = true end |
if (items[name]) then |
if hideincomplete and count < tonumber(items[name]) then continue = true end |
text = string.format("%d/%d", count, items[name]) |
else |
text = count |
end |
if not continue then |
if (squish) then |
text = string.format(" - %s: %s", name or 'name', text or 'text') |
if (entries) then |
if (entries > 1) then |
tip:ChangeLine(line, 1) |
tip:ChangeEntry(line, 1, text, nil, nil, nil, GameTooltipText) |
else |
tip:ChangeEntry(line, 1, text, nil, nil, nil, GameTooltipText) |
end |
else |
line = tip:AddLine(text, nil, nil, nil, GameTooltipText) |
line:SetScript("OnClick", ClickHandler) |
line:EnableMouse(true) |
end |
else |
if (entries) then |
if (entries ~= 2) then |
tip:ChangeLine(line, 2) |
tip:ChangeEntry(line, 1, ' - '..name..':', 0.8, 0.8, 0.8, GameTooltipText) |
tip:ChangeEntry(line, 2, text, 0.75, 0.61, 0, GameTooltipText) |
else |
tip:ChangeEntry(line, 1, ' - '..name..':', 0.8, 0.8, 0.8, GameTooltipText) |
tip:ChangeEntry(line, 2, text, 0.75, 0.61, 0, GameTooltipText) |
end |
else |
line = tip:AddDoubleLine(' - '..name..':', text, 0.8, 0.8, 0.8, 0.75, 0.61, 0, GameTooltipText, GameTooltipText) |
line:SetScript("OnClick", ClickHandler) |
line:SetMovable(true) |
line:RegisterForDrag('LeftButton') |
line:SetScript('OnDragStart', function(this, button) tip:StartMoving() end) |
line:SetScript('OnDragStop', function(this) self:SavePosition(tip:GetName()); tip:StopMovingOrSizing() end) |
end |
end |
line.ltType = "ItemClicker" |
line.ltParam = name |
cLineN = cLineN + 1 |
end |
end |
end |
end |
tip:ClearLines(cLineN) |
tip:Show() |
local r, g, b = tip:GetBackdropColor() |
tip:SetBackdropColor(r, g, b, options.alpha) |
self:RestorePosition(tip:GetName()) |
end |
--[[ |
--OH Frame stuff |
--]]-- |
--[[ frame generalization stuff ]]-- |
-- for the tooltip stuff |
local RegisterCheckOption, RegisterSliderOption |
do |
local helps = {} -- for storing help stuff |
-- timeout is how long before we forget we were showing, timeToShow is how |
-- long before we actually show, these values seem fairly sane |
local timeOut, timeToShow = 2.0, 0.5 |
local totalElapsed, showTooltip, state = 0 |
local onUpdate = function(frame, elapsed) |
totalElapsed = totalElapsed + elapsed |
if showTooltip then |
if totalElapsed > timeToShow and state == 1 then -- actually show stuff |
helps[showTooltip](showTooltip) |
state = 2 |
totalElapsed = 0 |
end |
if totalElapsed < timeOut and state == 2 then -- we need to reshow the tooltip quickly |
helps[showTooltip](showTooltip) |
state = 2 |
totalElapsed = 0 |
end |
else |
if totalElapsed > timeOut then -- reset our timeout |
state = 1 |
visFrame:SetScript("OnUpdate", nil) |
end |
end |
end |
local registerHelp = function(frame, helpFunc) |
helps[frame] = helpFunc |
end |
local showHelp = function(frame) |
showTooltip = frame |
totalElapsed = 0 |
if state ~= 2 then state = 1 end |
if visFrame then |
visFrame:SetScript("OnUpdate", onUpdate) |
end |
end |
local hideHelp = function() |
totalElapsed = 0 |
showTooltip = nil |
state = 2 |
GameTooltip:Hide() |
end |
RegisterCheckOption = function(frame, get, set, help) |
frame:SetScript("OnClick", function(self) set(self:GetChecked() or false) end ) |
frame:SetScript("OnShow", function(self) self:SetChecked(get()) end ) |
if help then |
registerHelp(frame, help) |
frame:SetScript("OnEnter", showHelp) |
frame:SetScript("OnLeave", hideHelp) |
end |
end |
RegisterSliderOption = function(frame, get, set, help) |
frame:SetScript("OnValueChanged", function(self) set(self:GetValue()) end ) |
frame:SetScript("OnShow", function(self) self:SetValue(get()) end ) |
if help then |
registerHelp(frame, help) |
frame:SetScript("OnEnter", showHelp) |
frame:SetScript("OnLeave", hideHelp) |
end |
end |
end |
local function CreateCheckButton(par) |
local f = CreateFrame('CheckButton', nil, par) |
f:SetHeight(32) |
f:SetWidth(32) |
f.text = f:CreateFontString(nil, nil, "GameFontNormalSmall") |
f.text:SetPoint("LEFT", f, "RIGHT", -2, 0) |
local t = f:CreateTexture() |
f:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") |
f:SetPushedTexture("Interface\\Buttons\\UI-CheckBox-Down") |
f:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight") |
f:GetHighlightTexture():SetBlendMode("ADD") |
f:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") |
f:SetDisabledCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check-Disable") |
f:SetHitRectInsets(0, -100, 0, 0) |
f:SetScript("OnEnter", function(self) |
if self.tooltipText then |
GameTooltip:SetOwner(this, "ANCHOR_RIGHT") |
GameTooltip:SetText(this.tooltipText, nil, nil, nil, nil, 1) |
end |
if self.tooltipRequirement then |
GameTooltip:AddLine(this.tooltipRequirement, "", 1.0, 1.0, 1.0) |
GameTooltip:Show() |
end |
end ) |
f:SetScript("OnLeave", function(self) GameTooltip:Hide() end) |
return f |
end |
local bg = { |
bgFile = "Interface\\Buttons\\UI-SliderBar-Background", |
edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", |
tile = true, |
tileSize = 8, |
edgeSize = 8, |
insets = { left = 3, right = 3, top = 6, bottom = 6 } |
} |
local function CreateSlider(par) |
local f = CreateFrame('Slider', nil, par) |
f:SetBackdrop(bg) |
f:SetWidth(128) |
f:SetHeight(17) |
f:SetOrientation("HORIZONTAL") |
f:SetHitRectInsets(0, 0, -10, -10) |
local fs = f:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") |
fs:SetPoint('BOTTOM', f, "TOP", 0, 2) |
f.text = fs |
fs = f:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") |
fs:SetPoint("TOPLEFT", f, "BOTTOMLEFT", 2, 3) |
f.low = fs |
fs = f:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") |
fs:SetPoint("TOPRIGHT", f, "BOTTOMRIGHT", -2, 3) |
f.high = fs |
local text = f:CreateTexture() |
text:SetTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal") |
text:SetHeight(32) |
text:SetWidth(32) |
f:SetThumbTexture(text) |
f.thumb = text |
f:SetScript("OnEnter", function(self) |
if self.tooltipText then |
GameTooltip:SetOwner(this, "ANCHOR_RIGHT") |
GameTooltip:SetText(this.tooltipText, nil, nil, nil, nil, 1) |
end |
if self.tooltipRequirement then |
GameTooltip:AddLine(this.tooltipRequirement, "", 1.0, 1.0, 1.0) |
GameTooltip:Show() |
end |
end ) |
f:SetScript("OnLeave", function(self) GameTooltip:Hide() end) |
return f |
end |
local boxBG = { |
--bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", --options frame background |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", -- kc_linkview frame background |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, |
tileSize = 16, |
edgeSize = 16, |
insets = { left = 5, right = 5, top = 5, bottom = 5 } |
} |
function LootTracker:CreateRandomConfigs() |
local frame = CreateFrame('Frame', nil, OH:GetFrame("addon")) |
frame:Show() |
visFrame = frame |
local options = _db.profile |
local box, check, slider |
box = CreateFrame('Frame', nil, frame) |
box:SetBackdrop(boxBG) |
box:SetBackdropBorderColor(0.4, 0.4, 0.4) |
box:SetBackdropColor(24/255, 24/255, 24/255) |
box:SetHeight(80) |
box:SetWidth(10) |
box:SetPoint("TOPLEFT", 5, -15) |
box:SetPoint("TOPRIGHT", frame, -5, -15) |
box.title = box:CreateFontString(nil, "BACKGROUND", "GameFontHighlight") |
box.title:SetPoint("BOTTOMLEFT", box, "TOPLEFT", 9, 0) |
box.title:SetText(L["Standard Tracker Options"]) |
self.oldTrackerBox = box |
check = CreateCheckButton(box) |
check:SetPoint("TOPLEFT", 5, -5) |
check.text:SetText(L["Show normal tracker"]) |
RegisterCheckOption(check, function() return options.tracker.old.shown end, |
function(val) |
options.tracker.old.shown = val |
if val then self:ShowTracker() elseif self.oldTracker then self.oldTracker:Hide() end |
end, function(this) |
GameTooltip:SetOwner(this, "ANCHOR_LEFT") |
if this:GetChecked() then |
GameTooltip:SetText(L['Use this checkbox to hide the normal tracker']) |
else |
GameTooltip:SetText(L['Use this checkbox to show the normal tracker']) |
end |
GameTooltip:Show() |
end) |
self.showOldTracker = check |
check = CreateCheckButton(frame) |
check:SetPoint("TOPLEFT", self.showOldTracker, "TOPRIGHT", 200, 0) |
check.text:SetText(L["Hide in combat"]) |
RegisterCheckOption(check, function() return options.tracker.old.combatHidden end, |
function(val) |
options.tracker.old.combatHidden = val |
if val and InCombatLockdown() and self.oldTracker then |
self.oldTracker:Hide() |
end |
end, function(this) |
GameTooltip:SetOwner(this, "ANCHOR_LEFT") |
GameTooltip:SetText(L['Hide in combat']) |
if this:GetChecked() then |
GameTooltip:AddLine(L['Use this checkbox to show the tracker while in combat'], nil, nil, nil, 1) |
else |
GameTooltip:AddLine(L['Use this checkbox to hide the tracker while in combat'], nil, nil, nil, 1) |
end |
GameTooltip:Show() |
end ) |
self.hideInCombatOld = check |
check = CreateCheckButton(frame) |
check:SetPoint("TOPLEFT", self.hideInCombatOld, "TOPRIGHT", 200, 0) |
check.text:SetText(L["Lock Tracker"]) |
RegisterCheckOption(check, function() return options.tracker.old.lock end, |
function(val) |
options.tracker.old.lock = val |
if self.oldTracker then |
self.oldTracker:SetBackdropBorderColor(24/255, 24/255, 24/255, options.tracker.old.lock and 0 or 1) |
self.oldTracker:EnableMouse(not options.tracker.old.lock) |
end |
end, function(this) |
GameTooltip:SetOwner(this, "ANCHOR_LEFT") |
GameTooltip:SetText(L['Lock Tracker']) |
GameTooltip:AddLine() |
if this:GetChecked() then |
GameTooltip:AddLine(L['Use this checkbox to unlock the tracker'], nil, nil, nil, 1) |
else |
GameTooltip:AddLine(L['Use this checkbox to lock the tracker in its current position'], nil, nil, nil, 1) |
end |
GameTooltip:Show() |
end ) |
self.lockOldTracker = check |
check = CreateCheckButton(frame) |
check:SetPoint("TOPLEFT", self.showOldTracker, "BOTTOMLEFT", 0, 0) |
check.text:SetText(L["Squish numbers to name"]) |
RegisterCheckOption(check, function() return options.tracker.old.squish end, |
function(val) |
options.tracker.old.squish = val |
self:PopulateTrackerFrame() |
self:PopulateNewTracker() |
end, function(this) |
GameTooltip:SetOwner(this, "ANCHOR_LEFT") |
GameTooltip:SetText(L['Squish numbers to name']) |
GameTooltip:AddLine() |
if this:GetChecked() then |
GameTooltip:AddLine(L['Use this checkbox to separate item counts from the item name'], nil, nil, nil, 1) |
else |
GameTooltip:AddLine(L['Use this checkbox to force item counts next to the item name'], nil, nil, nil, 1) |
end |
GameTooltip:Show() |
end ) |
self.squishOld = check |
slider = CreateSlider(frame) |
slider:SetPoint("TOPLEFT", self.squishOld, "TOPRIGHT", 200, -10) |
slider.text:SetText(L["Background Alpha"]) |
slider.low:SetText(0) |
slider.high:SetText(100) |
slider:SetMinMaxValues(0, 100) |
slider:SetValueStep(5) |
RegisterSliderOption(slider, function() return options.tracker.old.alpha*100 end, function(val) |
local alpha = val/100 |
options.tracker.old.alpha = alpha |
if self.oldTracker then |
local r, g, b = self.oldTracker:GetBackdropColor() |
self.oldTracker:SetBackdropColor(r, g, b, alpha) |
r, g, b = self.oldTracker:GetBackdropBorderColor() |
self.oldTracker:SetBackdropBorderColor(r, g, b, alpha) |
end |
end, function() |
GameTooltip:SetOwner(slider, "ANCHOR_LEFT") |
GameTooltip:SetText(L['Background Alpha']) |
GameTooltip:AddLine() |
GameTooltip:AddLine(L['Use this slider to change the transparency of the background for the tracker'], nil, nil, nil, 1) |
GameTooltip:Show() |
end) |
self.alphaOld = slider |
--[[slider = CreateSlider(frame) |
slider:SetPoint("TOPLEFT", self.alphaOld, "TOPRIGHT", 108, 0) |
slider.text:SetText(L["Scale"]) |
slider.low:SetText(1) |
slider.high:SetText(200) |
slider:SetMinMaxValues(1, 200) |
slider:SetValueStep(20) |
RegisterSliderOption(slider, function() return options.tracker.old.scale*100 end, function(val) |
scale = val/100 |
options.tracker.old.scale = scale |
if self.oldTracker then |
self:SavePosition('LootTrackerTrackingFrame') |
self.oldTracker:SetScale(scale) |
self:RestorePosition('LootTrackerTrackingFrame') |
end |
end) |
self.scaleOld = slider]]-- |
if ClickTip then |
box = CreateFrame('Frame', nil, frame) |
box:SetBackdrop(boxBG) |
box:SetBackdropBorderColor(0.4, 0.4, 0.4) |
box:SetBackdropColor(24/255, 24/255, 24/255) |
box:SetHeight(80) |
box:SetWidth(10) |
box:SetPoint("TOPLEFT", self.oldTrackerBox, "BOTTOMLEFT", 0, -15) |
box:SetPoint("TOPRIGHT", self.oldTrackerBox, "BOTTOMRIGHT", 0, -15) |
box.title = box:CreateFontString(nil, "BACKGROUND", "GameFontHighlight") |
box.title:SetPoint("BOTTOMLEFT", box, "TOPLEFT", 9, 0) |
box.title:SetText(L["Advanced Tracker Options"]) |
self.newTrackerBox = box |
check = CreateCheckButton(box) |
check:SetPoint("TOPLEFT", 5, -5) |
check.text:SetText(L["Show advanced tracker"]) |
RegisterCheckOption(check, function() return options.tracker.new.shown end, |
function(val) |
options.tracker.new.shown = val |
if val then self:ShowNewTracker() elseif self.newTracker then self.newTracker:Hide() end |
end, function(this) |
GameTooltip:SetOwner(this, "ANCHOR_LEFT") |
if this:GetChecked() then |
GameTooltip:SetText(L['Use this checkbox to hide the advanced tracker']) |
else |
GameTooltip:SetText(L['Use this checkbox to show the advanced tracker']) |
end |
GameTooltip:Show() |
end) |
self.showNewTracker = check |
check = CreateCheckButton(frame) |
check:SetPoint("TOPLEFT", self.showNewTracker, "TOPRIGHT", 200, 0) |
check.text:SetText(L["Hide in combat"]) |
RegisterCheckOption(check, function() return options.tracker.new.combatHidden end, |
function(val) |
options.tracker.new.combatHidden = val |
if val and InCombatLockdown() and self.newTracker then |
self.newTracker:Hide() |
end |
end, function(this) |
GameTooltip:SetOwner(this, "ANCHOR_LEFT") |
GameTooltip:SetText(L['Hide in combat']) |
if this:GetChecked() then |
GameTooltip:AddLine(L['Use this checkbox to show the tracker while in combat'], nil, nil, nil, 1) |
else |
GameTooltip:AddLine(L['Use this checkbox to hide the tracker while in combat'], nil, nil, nil, 1) |
end |
GameTooltip:Show() |
end ) |
self.hideInCombatNew = check |
check = CreateCheckButton(frame) |
check:SetPoint("TOPLEFT", self.hideInCombatNew, "TOPRIGHT", 200, 0) |
check.text:SetText(L["Lock advanced tracker"]) |
RegisterCheckOption(check, function() return options.tracker.new.lock end, |
function(val) |
options.tracker.new.lock = val |
if self.newTracker then |
self.newTracker:SetBackdropBorderColor(24/255, 24/255, 24/255, options.tracker.new.lock and 0 or 1) |
self.newTracker:EnableMouse(not options.tracker.new.lock) |
end |
end, function(this) |
GameTooltip:SetOwner(this, "ANCHOR_LEFT") |
GameTooltip:SetText(L['Lock Tracker']) |
GameTooltip:AddLine() |
if this:GetChecked() then |
GameTooltip:AddLine(L['Use this checkbox to unlock the tracker'], nil, nil, nil, 1) |
else |
GameTooltip:AddLine(L['Use this checkbox to lock the tracker in its current position'], nil, nil, nil, 1) |
end |
GameTooltip:Show() |
end ) |
self.lockNewTracker = check |
check = CreateCheckButton(frame) |
check:SetPoint("TOPLEFT", self.showNewTracker, "BOTTOMLEFT", 0, 0) |
check.text:SetText(L["Squish numbers to name"]) |
RegisterCheckOption(check, function() return options.tracker.new.squish end, |
function(val) |
options.tracker.new.squish = val |
self:PopulateTrackerFrame() |
self:PopulateNewTracker() |
end, function(this) |
GameTooltip:SetOwner(this, "ANCHOR_LEFT") |
GameTooltip:SetText(L['Squish numbers to name']) |
GameTooltip:AddLine() |
if this:GetChecked() then |
GameTooltip:AddLine(L['Use this checkbox to separate item counts from the item name'], nil, nil, nil, 1) |
else |
GameTooltip:AddLine(L['Use this checkbox to force item counts next to the item name'], nil, nil, nil, 1) |
end |
GameTooltip:Show() |
end ) |
self.squishNew = check |
slider = CreateSlider(frame) |
slider:SetPoint("TOPLEFT", self.squishNew, "TOPRIGHT", 200, -10) |
slider.text:SetText(L["Background Alpha"]) |
slider.low:SetText(0) |
slider.high:SetText(100) |
slider:SetMinMaxValues(0, 100) |
slider:SetValueStep(5) |
RegisterSliderOption(slider, function() return options.tracker.new.alpha*100 end, |
function(val) |
local alpha = val/100 |
options.tracker.new.alpha = alpha |
if self.newTracker then |
local r, g, b = self.newTracker:GetBackdropColor() |
self.newTracker:SetBackdropColor(r, g, b, alpha) |
r, g, b = self.newTracker:GetBackdropBorderColor() |
self.newTracker:SetBackdropBorderColor(r, g, b, alpha) |
end |
end, function() |
GameTooltip:SetOwner(slider, "ANCHOR_LEFT") |
GameTooltip:SetText(L['Background Alpha']) |
GameTooltip:AddLine() |
GameTooltip:AddLine(L['Use this slider to change the transparency of the background for the tracker'], nil, nil, nil, 1) |
GameTooltip:Show() |
end) |
self.alphaNew = slider |
--[[slider = CreateSlider(frame) |
slider:SetPoint("TOPLEFT", self.alphaOld, "TOPRIGHT", 108, 0) |
slider.text:SetText(L["Scale"]) |
slider.low:SetText(1) |
slider.high:SetText(200) |
slider:SetMinMaxValues(1, 200) |
slider:SetValueStep(20) |
RegisterSliderOption(slider, function() return options.tracker.old.scale*100 end, function(val) |
scale = val/100 |
options.tracker.old.scale = scale |
if self.oldTracker then |
self:SavePosition('LootTrackerTrackingFrame') |
self.oldTracker:SetScale(scale) |
self:RestorePosition('LootTrackerTrackingFrame') |
end |
end) |
self.scaleOld = slider]]-- |
end |
box = CreateFrame('Frame', nil, frame) |
box:SetBackdrop(boxBG) |
box:SetBackdropBorderColor(0.4, 0.4, 0.4) |
box:SetBackdropColor(24/255, 24/255, 24/255) |
box:SetHeight(80) |
box:SetWidth(10) |
box:SetPoint("TOPLEFT", self.newTrackerBox or self.oldTrackerBox, "BOTTOMLEFT", 0, -15) |
box:SetPoint("TOPRIGHT", self.newTrackerBox or self.oldTrackerBox, "BOTTOMRIGHT", 0, -15) |
box.title = box:CreateFontString(nil, "BACKGROUND", "GameFontHighlight") |
box.title:SetPoint("BOTTOMLEFT", box, "TOPLEFT", 9, 0) |
box.title:SetText(L["Global Tracker Options"]) |
self.newTrackerBox = box |
check = CreateCheckButton(box) |
check:SetPoint("TOPLEFT", 5, -5) |
check.text:SetText(L["Hide if no items available"]) |
RegisterCheckOption(check, function() return options.tracker.hideifnone end, |
function(val) |
options.tracker.hideifnone = val |
self:PopulateTrackerFrame() |
self:PopulateNewTracker() |
end, function(this) |
GameTooltip:SetOwner(this, "ANCHOR_LEFT") |
GameTooltip:SetText(L['Hide if no items available']) |
GameTooltip:AddLine() |
if this:GetChecked() then |
GameTooltip:AddLine(L['Use this checkbox to show the item in the tracker even if there are no items available'], nil, nil, nil, 1) |
else |
GameTooltip:AddLine(L['Use this checkbox to hide the item in the tracker if there are no items available'], nil, nil, nil, 1) |
end |
GameTooltip:Show() |
end) |
self.hideIfNone = check |
check = CreateCheckButton(box) |
check:SetPoint("TOPLEFT", self.hideIfNone, "BOTTOMLEFT", 0, 0) |
check.text:SetText(L["Hide if incomplete"]) |
RegisterCheckOption(check, function() return options.tracker.hideincomplete end, |
function(val) |
options.tracker.hideincomplete = val |
self:PopulateTrackerFrame() |
self:PopulateNewTracker() |
end, function(this) |
GameTooltip:SetOwner(this, "ANCHOR_LEFT") |
GameTooltip:SetText(L['Hide if incomplete']) |
GameTooltip:AddLine() |
if this:GetChecked() then |
GameTooltip:AddLine(L['Use this checkbox to show the item in the tracker even if the goal has not been met'], nil, nil, nil, 1) |
else |
GameTooltip:AddLine(L['Use this checkbox to hide the item in the tracker if the goal has not been met'], nil, nil, nil, 1) |
end |
GameTooltip:Show() |
end) |
self.hideIncomplete = check |
check = CreateCheckButton(box) |
check:SetPoint("TOPLEFT", 5, -5) |
check.text:SetText(L["Current toon count only"]) |
check:SetPoint("TOPLEFT", self.hideIfNone, "TOPRIGHT", 200, 0) |
RegisterCheckOption(check, function() return options.tracker.ctc end, |
function(val) |
options.tracker.ctc = val |
self:PopulateTrackerFrame() |
self:PopulateNewTracker() |
end, function(this) |
GameTooltip:SetOwner(this, "ANCHOR_LEFT") |
GameTooltip:SetText(L['Current toon count only']) |
GameTooltip:AddLine() |
if this:GetChecked() then |
GameTooltip:AddLine(L['Use this checkbox to show item totals from all toons'], nil, nil, nil, 1) |
else |
GameTooltip:AddLine(L['Use this checkbox to only show item totals from current toon'], nil, nil, nil, 1) |
end |
GameTooltip:Show() |
end) |
self.currentToonCount = check |
return frame |
end |
local function updateTextures(show) |
for i = 1, #configFrame.rows do |
local button = configFrame.rows[i] |
if show then |
button.item:Show() |
button.left:ClearAllPoints() |
button.left:SetPoint('LEFT', button, 'LEFT', 34, 2) |
--button.highlight:ClearAllPoints() |
button.highlight:SetPoint('TOPLEFT', button, 'TOPLEFT', 33, 0) |
button.name:ClearAllPoints() |
button.name:SetPoint("TOPLEFT", button, "TOPLEFT", 43, 0) |
button.count:SetText('Item') |
button.count:Show() |
else |
button.item:Hide() |
button.left:ClearAllPoints() |
button.left:SetPoint('LEFT', button, 'LEFT', 2, 2) |
--button.highlight:ClearAllPoints() |
button.highlight:SetPoint('TOPLEFT', button, 'TOPLEFT', 1, 0) |
button.name:ClearAllPoints() |
button.name:SetPoint("TOPLEFT", button, "TOPLEFT", 9, 0) |
button.count:SetText('') |
button.count:Hide() |
end |
end |
end |
local function updateButtons() |
if not configFrame.curBtn then |
configFrame.RemoveX:Disable() |
configFrame.EditX:Disable() |
else |
configFrame.RemoveX:Enable() |
configFrame.EditX:Enable() |
end |
end |
local function trackList(row) |
local button = configFrame.rows[row] |
local set = _db.global.lists[button.index] |
local added = LootTracker:ToggleTrackSet(set) |
LootTracker:PopulateTrackerFrame() |
LootTracker:PopulateNewTracker() |
if (added) then |
button.check:Show() |
button.check:ClearAllPoints() |
button.check:SetPoint("LEFT", button, 'LEFT', button.name:GetStringWidth()+24, 0) |
else |
button.check:Hide() |
end |
end |
local function onLeave() GameTooltip:Hide() end |
local function onEnter(this) |
if this.link then |
if this.link ~= true then |
GameTooltip:SetOwner(this,"ANCHOR_LEFT") |
GameTooltip:SetHyperlink(this.link) |
LootTracker:AddToTip(this.itemName) |
GameTooltip:Show() |
else |
GameTooltip:SetOwner(this,"ANCHOR_LEFT") |
GameTooltip:SetText(this.name:GetText()) |
LootTracker:AddToTip(this.itemName) |
GameTooltip:Show() |
end |
elseif this.itemName then |
GameTooltip:SetOwner(this,"ANCHOR_LEFT") |
LootTracker:AddToTip(this.itemName) |
GameTooltip:Show() |
else -- it is a set, add the number of items |
local set = _db.global.lists[this.index] |
local items = _db.global.lists[set] |
local numItems = items and #items or 0 |
GameTooltip:SetOwner(this,"ANCHOR_LEFT") |
GameTooltip:SetText(set) |
local name, link, rarity, level, minlevel, itype, subtype, stackCount, equipLoc, text |
local item, itemID |
for i=1, numItems do |
item = items[i] |
itemID = LootTracker:GetItemID(item) |
name, link, rarity, level, minlevel, itype, subtype, stackCount, equipLoc, text = GetItemInfo(itemID) |
if (items[name]) then |
GameTooltip:AddDoubleLine(name or item, string.format("%d/%d", LootTracker:GetItemCount(name), items[name])) |
else |
GameTooltip:AddDoubleLine(name or item, LootTracker:GetItemCount(name)) |
end |
end |
GameTooltip:AddLine() |
if (LootTracker:IsSetTracked(set)) then |
GameTooltip:AddLine(L["Hint: Shift click to remove from tracker"], 0, 1, 0, 1) |
else |
GameTooltip:AddLine(L["Hint: Shift click to add to tracker"], 0, 1, 0, 1) |
end |
GameTooltip:Show() |
end |
end |
local function updateHighlights(clickedRow, reset) |
if reset and configFrame.curBtn then |
configFrame.curBtn:UnlockHighlight() |
configFrame.curBtn = nil |
end |
if clickedRow then |
local button = configFrame.rows[clickedRow] |
if button == configFrame.curBtn then |
button:UnlockHighlight() |
configFrame.curBtn = nil |
else |
if configFrame.curBtn then configFrame.curBtn:UnlockHighlight() end |
configFrame.curBtn = button |
button:LockHighlight() |
end |
selectedLine = button.index |
end |
updateButtons() |
end |
function LootTracker:updateScrollList(resetHighlights) |
local scrollFrame = configFrame.scroll |
local itemID |
if resetHighlights then updateHighlights(nil, true) end |
if not currentSet then |
local sets, numSets = _db.global.lists |
local offset = FauxScrollFrame_GetOffset(scrollFrame) |
numSets = #sets |
local resize = numSets <= 8 |
local buttons, maxButtons = configFrame.rows, #configFrame.rows |
FauxScrollFrame_Update(scrollFrame, numSets, maxButtons, scrollFrame.buttonHeight) |
local i, j, button |
for i=1,maxButtons do |
j=i + offset |
button = buttons[i] |
button.link = nil |
button.itemName = nil |
if j <= numSets then |
button.name:SetText(sets[j]) |
if (self:IsSetTracked(sets[j])) then |
button.check:SetPoint('LEFT', button, 'LEFT', button.name:GetStringWidth()+24,0) |
button.check:Show() |
else |
button.check:Hide() |
end |
if resize then |
button:SetWidth(625) |
else |
button:SetWidth(600) |
end |
button:Show() |
button.index = j |
if j == selectedLine then |
button:LockHighlight() |
configFrame.curBtn = button |
end |
else |
button.index = nil |
button:SetText('') |
button:Hide() |
end |
end |
else |
local buttons, maxButtons = configFrame.rows, #configFrame.rows |
local highlights = scrollFrame.highlights |
local numItems, items = 0 |
items = _db.global.lists[currentSet] |
if (items) then numItems = #items end |
local resize = numItems <= 8 |
FauxScrollFrame_Update(scrollFrame, numItems, maxButtons, scrollFrame.buttonHeight) |
local i, j, item, link, name, itemID |
local name, link, rarity, level, minlevel, itype, subtype, stackCount, equipLoc, text |
local offset = FauxScrollFrame_GetOffset(scrollFrame) |
local button |
for i=1,maxButtons do |
j=i + offset |
button = buttons[i] |
if j <= numItems then |
item = items[j] |
itemID = self:GetItemID(item) |
name, link, rarity, level, minlevel, itype, subtype, stackCount, equipLoc, text = GetItemInfo(itemID) |
button.name:SetText(link or name or item or 'Error') |
if (items[name]) then |
button.count:SetText(string.format("%d/%d", self:GetItemCount(name), items[name])) |
else |
button.count:SetText(self:GetItemCount(name)) |
end |
if text then |
button.texture:SetTexture(text) |
else |
button.texture:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") |
end |
if resize then |
button:SetWidth(625) |
else |
button:SetWidth(600) |
end |
button.index = j |
button.link = link or true |
button.itemName = name |
button:Show() |
else |
button.index = nil |
button.link = nil |
button.itemName = nil |
button:SetText('') |
button:Hide() |
end |
button.check:Hide() |
end |
end |
end |
local function createConfigFrame(self) |
local name = "LTOHConfig" |
local frame = CreateFrame('Frame', name, OH:GetFrame("addon")) |
frame:SetToplevel(true) |
frame:SetAllPoints() |
local RemoveX = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate") |
RemoveX:SetWidth(80) |
RemoveX:SetHeight(22) |
RemoveX:SetPoint("BOTTOMRIGHT", OH:GetFrame("main"), "BOTTOMRIGHT", -8, 14) |
RemoveX:SetText(L["Remove Set"]) |
frame.RemoveX = RemoveX |
RemoveX:SetScript("OnClick", function(f) |
if not currentSet then -- current set means we've selected a set as a sub cat |
local setName = _db.global.lists[selectedLine] |
local removed = self:RemoveSet(setName) |
if removed then |
selectedLine = nil |
ui:RemoveSubCategory(L["Sets"], setName) -- remove the sub cat |
updateHighlights(nil, true) |
self:updateScrollList() |
end |
else |
local set = _db.global.lists[currentSet] |
local item = set[selectedLine] |
local removed = self:RemoveItem(item, currentSet) |
if removed then |
selectedLine = nil |
updateHighlights(nil, true) |
self:updateScrollList() |
end |
end |
end) |
local EditX = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate") |
EditX:SetWidth(80) |
EditX:SetHeight(22) |
EditX:SetPoint("RIGHT", RemoveX, "LEFT") |
EditX:SetText(L["Rename Set"]) |
EditX:SetScript("OnClick", function() end) |
frame.EditX = EditX |
EditX:SetScript('OnClick', |
function(f) |
if not currentSet then |
self.newSet = nil |
self.currentSet = _db.global.lists[selectedLine] |
self:ShowSetInputFrame(_db.global.lists[selectedLine]) |
else |
local set = _db.global.lists[currentSet] |
local name = set[selectedLine] |
local item = self:GetItemID(name) |
local count = set[name] |
local n2, link = GetItemInfo(item) |
if not n2 then |
self:ShowItemInputFrame(item, count) |
else |
self:ShowItemInputFrame(link, count) |
end |
end |
end) |
local AddX = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate") |
AddX:SetWidth(80) |
AddX:SetHeight(22) |
AddX:SetPoint("RIGHT", EditX, "LEFT") |
AddX:SetText(L["Add Set"]) |
frame.AddX = AddX |
AddX:SetScript("OnClick", |
function(f) |
if not currentSet then |
self.newSet = true |
self:ShowSetInputFrame() |
else |
self:ShowItemInputFrame() |
end |
end) |
-- stolen from optionhouse and modified to look like AH |
frame.rows = {} |
for i=1, 8 do |
local button = CreateFrame("Button", nil, frame) |
button:SetWidth(597) |
button:SetHeight(37) |
local texture = button:CreateTexture(nil, "BACKGROUND") |
texture:SetTexture("Interface\\AuctionFrame\\UI-AuctionItemNameFrame") |
button.right = texture |
texture:SetTexCoord(0.75, 0.828125, 0, 1.0) |
texture:SetPoint("RIGHT", button, "RIGHT", 0, 2) |
texture:SetWidth(10) |
texture:SetHeight(32) |
local texture = button:CreateTexture(nil, "BACKGROUND") |
texture:SetTexture("Interface\\AuctionFrame\\UI-AuctionItemNameFrame") |
button.left = texture |
texture:SetTexCoord(0, 0.078125, 0, 1.0) |
texture:SetPoint("Left", button, "LEFT", 34, 2) |
texture:SetWidth(10) |
texture:SetHeight(32) |
local texture = button:CreateTexture(nil, "BACKGROUND") |
texture:SetTexture("Interface\\AuctionFrame\\UI-AuctionItemNameFrame") |
button.center = texture |
texture:SetTexCoord(0.078125, 0.75, 0, 1.0) |
texture:SetPoint("LEFT", button.left, "RIGHT", 0, 0) |
texture:SetPoint("RIGHT", button.right, "LEFT", 0, 0) |
texture:SetWidth(10) |
texture:SetHeight(32) |
local name = button:CreateFontString(nil, "BACKGROUND", 'GameFontNormal') |
button.name = name |
name:SetPoint("TOPLEFT", button, "TOPLEFT", 43, 0) |
name:SetHeight(32) |
name:SetWidth(167) |
name:SetJustifyH("LEFT") |
name:SetText("Button: "..i) |
name:SetTextColor(1,1,1) |
button.check = button:CreateTexture('BACKGROUND') |
button.check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") |
button.check:SetHeight(16) |
button.check:SetWidth(16) |
button.check:Show() |
button.check:SetPoint('LEFT', name, 'RIGHT', 0, 0) |
local count = button:CreateFontString(nil, "BACKGROUND", 'GameFontNormal') |
button.count = count |
count:SetHeight(32) |
count:SetWidth(167) |
count:SetJustifyH("RIGHT") |
count:SetPoint("TOPRIGHT", button, "TOPRIGHT", -5, 0) |
count:SetText("Count: "..i) |
local item = CreateFrame('Button', nil, button) |
local texture = item:CreateTexture(nil, 'BORDER') |
texture:SetHeight(60) |
texture:SetWidth(60) |
texture:SetTexture("Interface\\Buttons\\UI-QuickSlot2") |
texture:SetPoint("CENTER", item, "CENTER", 0, 0) |
item:SetHeight(32) |
item:SetWidth(32) |
item:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0) |
item:SetNormalTexture(texture) |
item:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square") |
button.item = item |
button.texture = item:CreateTexture(nil, "ARTWORK") |
button.texture:SetAllPoints(item) |
local texture = button:CreateTexture(nil, 'HIGHLIGHT') |
texture:SetWidth(597) |
texture:SetHeight(32) |
texture:SetPoint('TOPLEFT', button, 'TOPLEFT', 33, 0) |
texture:SetPoint('TOPRIGHT', button, 'TOPRIGHT', 0, 0) |
texture:SetTexture("Interface\\HelpFrame\\HelpFrameButton-Highlight") |
texture:SetTexCoord(0, 1, 0, 0.578125) |
texture:SetBlendMode("add") |
button.highlight = texture |
button:SetHighlightTexture(texture) |
button:SetHighlightTexture("Interface\\HelpFrame\\HelpFrameButton-Highlight") |
button:SetScript("OnClick", function(self, button) |
if IsShiftKeyDown() and not currentSet then |
trackList(i) |
return |
end |
updateHighlights(i) |
end) |
button:SetScript("OnEnter", onEnter) |
button:SetScript("OnLeave", onLeave) |
if( i > 1 ) then |
button:SetPoint("TOPLEFT", frame.rows[i-1], "BOTTOMLEFT", 0, 0) |
else |
button:SetPoint("TOPLEFT", frame, "TOPLEFT", 195, -110) |
end |
frame.rows[i] = button |
end |
frame.scroll = CreateFrame("ScrollFrame", name.."Scroll", frame, "FauxScrollFrameTemplate") |
frame.scroll.buttonHeight = 37 |
frame.scroll:SetPoint("TOPLEFT", frame, "TOPLEFT", 25, -105) |
frame.scroll:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -35, 38) |
frame.scroll:SetScript("OnVerticalScroll", function() FauxScrollFrame_OnVerticalScroll(frame.scroll.buttonHeight, function() self:updateScrollList() end) end) |
local texture = frame.scroll:CreateTexture(nil, "BACKGROUND") |
texture:SetWidth(31) |
texture:SetHeight(256) |
texture:SetPoint("TOPLEFT", frame.scroll, "TOPRIGHT", -2, 5) |
texture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar") |
texture:SetTexCoord(0, 0.484375, 0, 1.0) |
local texture = frame.scroll:CreateTexture(nil, "BACKGROUND") |
texture:SetWidth(31) |
texture:SetHeight(106) |
texture:SetPoint("BOTTOMLEFT", frame.scroll, "BOTTOMRIGHT", -2, -2) |
texture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar") |
texture:SetTexCoord(0.515625, 1.0, 0, 0.4140625) |
return frame |
end |
function LootTracker:PopulateOHFrame(cat, subcat) |
if not configFrame then |
configFrame = createConfigFrame(self) |
end |
if cat and subcat == '' then |
currentSet = nil |
elseif cat and subcat ~= '' then |
if cat == L["Sets"] then |
currentSet = subcat |
end |
end |
if not currentSet then |
configFrame.AddX:SetText(L["Add Set"]) |
configFrame.EditX:SetText(L["Rename Set"]) |
configFrame.RemoveX:SetText(L["Remove Set"]) |
else |
configFrame.AddX:SetText(L["Add Item"]) |
configFrame.EditX:SetText(L["Edit Item"]) |
configFrame.RemoveX:SetText(L["Del Item"]) |
end |
updateTextures(currentSet) |
updateHighlights(nil, true) |
self:updateScrollList() |
configFrame:Show() |
return configFrame |
end |
function LootTracker:ResetOptions() |
local sets = _db.global.lists |
local ui = ui |
for i = 1, #sets do |
ui:RemoveSubCategory(L["Sets"], sets[i]) -- remove the sub cat |
end |
end |
------------------------------- |
-- Author Settings -- |
------------------------------- |
local addonname = "LootTracker" -- The name of your addon, duh! |
local contact = "joshborke@gmail.com" -- This is the email address you wish localizers to contact you at |
-- This table indicates what locales are supported by your addon |
-- true == fully translated |
-- false or nil == no translations |
-- any other value == partially translated |
local locales = { |
deDE = false, -- German |
enGB = false, -- British English |
enUS = true, -- American English |
esES = false, -- Spanish |
frFR = false, -- French |
koKR = false, -- Korean |
ruRU = false, -- Russian |
zhCN = false, -- Chinese (simplified) |
zhTW = false, -- Chinese (traditional) |
} |
------------------------------ |
-- Locale testing -- |
------------------------------ |
local loc = GetLocale() |
local status = locales[loc] |
if not status then -- Not translated |
local s = loc == "deDE" and "%s wurde nicht in deine Sprache uebersetzt. Wenn du helfen moechtest wende dich bitte an <%s> fuer Details, wie du Uebersetzungen einsenden kannst." |
or loc == "frFR" and "%s n'a pas \195\169t\195\169 traduit dans votre langue. Veuillez contacter <%s> pour les d\195\169tails concernant la submission de traductions." |
or loc == "esES" and "%s no ha sido traducido a tu lenguaje. Por favor contacta con <%s> para los detalles del envio de traducciones." |
or "%s has not been translated into your language. Please contact <%s> for details on submitting translations." |
DEFAULT_CHAT_FRAME:AddMessage(string.format(s, addonname, contact)) |
elseif status ~= true then -- Partially translated |
local s = loc == "deDE" and "%s wurde nur teilweise in deine Sprache uebersetzt. Wenn du helfen moechtest wende dich bitte an <%s> fuer Details, wie du Uebersetzungen einsenden kannst." |
or loc == "frFR" and "%s n'est que partiellement traduit dans votre langue. Veuillez contacter <%s> pour les d\195\169tails concernant la submission de traductions." |
or loc == "esES" and "%s sólo está parcialmente traducido a tu lenguaje. Por favor contacta con <%s> para los detalles del envio de traducciones." |
or "%s is only partially translated into your language. Please contact <%s> for details on submitting translations." |
DEFAULT_CHAT_FRAME:AddMessage(string.format(s, addonname, contact)) |
end |
--[[------------------------------------------------------------------------- |
Copyright (c) 2006-2007, Dongle Development Team |
All rights reserved. |
Redistribution and use in source and binary forms, with or without |
modification, are permitted provided that the following conditions are |
met: |
* Redistributions of source code must retain the above copyright |
notice, this list of conditions and the following disclaimer. |
* Redistributions in binary form must reproduce the above |
copyright notice, this list of conditions and the following |
disclaimer in the documentation and/or other materials provided |
with the distribution. |
* Neither the name of the Dongle Development Team nor the names of |
its contributors may be used to endorse or promote products derived |
from this software without specific prior written permission. |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---------------------------------------------------------------------------]] |
local major = "DongleStub" |
local minor = tonumber(string.match("$Revision: 313 $", "(%d+)") or 1) |
local g = getfenv(0) |
if not g.DongleStub or g.DongleStub:IsNewerVersion(major, minor) then |
local lib = setmetatable({}, { |
__call = function(t,k) |
if type(t.versions) == "table" and t.versions[k] then |
return t.versions[k].instance |
else |
error("Cannot find a library with name '"..tostring(k).."'", 2) |
end |
end |
}) |
function lib:IsNewerVersion(major, minor) |
local versionData = self.versions and self.versions[major] |
-- If DongleStub versions have differing major version names |
-- such as DongleStub-Beta0 and DongleStub-1.0-RC2 then a second |
-- instance will be loaded, with older logic. This code attempts |
-- to compensate for that by matching the major version against |
-- "^DongleStub", and handling the version check correctly. |
if major:match("^DongleStub") then |
local oldmajor,oldminor = self:GetVersion() |
if self.versions and self.versions[oldmajor] then |
return minor > oldminor |
else |
return true |
end |
end |
if not versionData then return true end |
local oldmajor,oldminor = versionData.instance:GetVersion() |
return minor > oldminor |
end |
local function NilCopyTable(src, dest) |
for k,v in pairs(dest) do dest[k] = nil end |
for k,v in pairs(src) do dest[k] = v end |
end |
function lib:Register(newInstance, activate, deactivate) |
assert(type(newInstance.GetVersion) == "function", |
"Attempt to register a library with DongleStub that does not have a 'GetVersion' method.") |
local major,minor = newInstance:GetVersion() |
assert(type(major) == "string", |
"Attempt to register a library with DongleStub that does not have a proper major version.") |
assert(type(minor) == "number", |
"Attempt to register a library with DongleStub that does not have a proper minor version.") |
-- Generate a log of all library registrations |
if not self.log then self.log = {} end |
table.insert(self.log, string.format("Register: %s, %s", major, minor)) |
if not self:IsNewerVersion(major, minor) then return false end |
if not self.versions then self.versions = {} end |
local versionData = self.versions[major] |
if not versionData then |
-- New major version |
versionData = { |
["instance"] = newInstance, |
["deactivate"] = deactivate, |
} |
self.versions[major] = versionData |
if type(activate) == "function" then |
table.insert(self.log, string.format("Activate: %s, %s", major, minor)) |
activate(newInstance) |
end |
return newInstance |
end |
local oldDeactivate = versionData.deactivate |
local oldInstance = versionData.instance |
versionData.deactivate = deactivate |
local skipCopy |
if type(activate) == "function" then |
table.insert(self.log, string.format("Activate: %s, %s", major, minor)) |
skipCopy = activate(newInstance, oldInstance) |
end |
-- Deactivate the old libary if necessary |
if type(oldDeactivate) == "function" then |
local major, minor = oldInstance:GetVersion() |
table.insert(self.log, string.format("Deactivate: %s, %s", major, minor)) |
oldDeactivate(oldInstance, newInstance) |
end |
-- Re-use the old table, and discard the new one |
if not skipCopy then |
NilCopyTable(newInstance, oldInstance) |
end |
return oldInstance |
end |
function lib:GetVersion() return major,minor end |
local function Activate(new, old) |
-- This code ensures that we'll move the versions table even |
-- if the major version names are different, in the case of |
-- DongleStub |
if not old then old = g.DongleStub end |
if old then |
new.versions = old.versions |
new.log = old.log |
end |
g.DongleStub = new |
end |
-- Actually trigger libary activation here |
local stub = g.DongleStub or lib |
lib = stub:Register(lib, Activate) |
end |
--[[------------------------------------------------------------------------- |
Begin Library Implementation |
---------------------------------------------------------------------------]] |
local major = "Dongle-1.0" |
local minor = tonumber(string.match("$Revision: 629 $", "(%d+)") or 1) + 500 |
-- ** IMPORTANT NOTE ** |
-- Due to some issues we had previously with Dongle revision numbers |
-- we need to artificially inflate the minor revision number, to ensure |
-- we load sequentially. |
assert(DongleStub, string.format("%s requires DongleStub.", major)) |
if not DongleStub:IsNewerVersion(major, minor) then return end |
local Dongle = {} |
local methods = { |
"RegisterEvent", "UnregisterEvent", "UnregisterAllEvents", "IsEventRegistered", |
"RegisterMessage", "UnregisterMessage", "UnregisterAllMessages", "TriggerMessage", "IsMessageRegistered", |
"EnableDebug", "IsDebugEnabled", "Print", "PrintF", "Debug", "DebugF", "Echo", "EchoF", |
"InitializeDB", |
"InitializeSlashCommand", |
"NewModule", "HasModule", "IterateModules", |
} |
local registry = {} |
local lookup = {} |
local loadqueue = {} |
local loadorder = {} |
local events = {} |
local databases = {} |
local commands = {} |
local messages = {} |
local frame |
--[[------------------------------------------------------------------------- |
Message Localization |
---------------------------------------------------------------------------]] |
local L = { |
["ADDMESSAGE_REQUIRED"] = "The frame you specify must have an 'AddMessage' method.", |
["ALREADY_REGISTERED"] = "A Dongle with the name '%s' is already registered.", |
["BAD_ARGUMENT"] = "bad argument #%d to '%s' (%s expected, got %s)", |
["BAD_ARGUMENT_DB"] = "bad argument #%d to '%s' (DongleDB expected)", |
["CANNOT_DELETE_ACTIVE_PROFILE"] = "You cannot delete your active profile. Change profiles, then attempt to delete.", |
["DELETE_NONEXISTANT_PROFILE"] = "You cannot delete a non-existant profile.", |
["MUST_CALLFROM_DBOBJECT"] = "You must call '%s' from a Dongle database object.", |
["MUST_CALLFROM_REGISTERED"] = "You must call '%s' from a registered Dongle.", |
["MUST_CALLFROM_SLASH"] = "You must call '%s' from a Dongle slash command object.", |
["PROFILE_DOES_NOT_EXIST"] = "Profile '%s' doesn't exist.", |
["REPLACE_DEFAULTS"] = "You are attempting to register defaults with a database that already contains defaults.", |
["SAME_SOURCE_DEST"] = "Source/Destination profile cannot be the same profile.", |
["EVENT_REGISTER_SPECIAL"] = "You cannot register for the '%s' event. Use the '%s' method instead.", |
["Unknown"] = "Unknown", |
["INJECTDB_USAGE"] = "Usage: DongleCmd:InjectDBCommands(db, ['copy', 'delete', 'list', 'reset', 'set'])", |
["DBSLASH_PROFILE_COPY_DESC"] = "profile copy <name> - Copies profile <name> into your current profile.", |
["DBSLASH_PROFILE_COPY_PATTERN"] = "^profile copy (.+)$", |
["DBSLASH_PROFILE_DELETE_DESC"] = "profile delete <name> - Deletes the profile <name>.", |
["DBSLASH_PROFILE_DELETE_PATTERN"] = "^profile delete (.+)$", |
["DBSLASH_PROFILE_LIST_DESC"] = "profile list - Lists all valid profiles.", |
["DBSLASH_PROFILE_LIST_PATTERN"] = "^profile list$", |
["DBSLASH_PROFILE_RESET_DESC"] = "profile reset - Resets the current profile.", |
["DBSLASH_PROFILE_RESET_PATTERN"] = "^profile reset$", |
["DBSLASH_PROFILE_SET_DESC"] = "profile set <name> - Sets the current profile to <name>.", |
["DBSLASH_PROFILE_SET_PATTERN"] = "^profile set (.+)$", |
["DBSLASH_PROFILE_LIST_OUT"] = "Profile List:", |
} |
--[[------------------------------------------------------------------------- |
Utility functions for Dongle use |
---------------------------------------------------------------------------]] |
local function assert(level,condition,message) |
if not condition then |
error(message,level) |
end |
end |
local function argcheck(value, num, ...) |
if type(num) ~= "number" then |
error(L["BAD_ARGUMENT"]:format(2, "argcheck", "number", type(num)), 1) |
end |
for i=1,select("#", ...) do |
if type(value) == select(i, ...) then return end |
end |
local types = strjoin(", ", ...) |
local name = string.match(debugstack(2,2,0), ": in function [`<](.-)['>]") |
error(L["BAD_ARGUMENT"]:format(num, name, types, type(value)), 3) |
end |
local function safecall(func,...) |
local success,err = pcall(func,...) |
if not success then |
geterrorhandler()(err) |
end |
end |
--[[------------------------------------------------------------------------- |
Dongle constructor, and DongleModule system |
---------------------------------------------------------------------------]] |
function Dongle:New(name, obj) |
argcheck(name, 2, "string") |
argcheck(obj, 3, "table", "nil") |
if not obj then |
obj = {} |
end |
if registry[name] then |
error(string.format(L["ALREADY_REGISTERED"], name)) |
end |
local reg = {["obj"] = obj, ["name"] = name} |
registry[name] = reg |
lookup[obj] = reg |
lookup[name] = reg |
for k,v in pairs(methods) do |
obj[v] = self[v] |
end |
-- Add this Dongle to the end of the queue |
table.insert(loadqueue, obj) |
return obj,name |
end |
function Dongle:NewModule(name, obj) |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "NewModule")) |
argcheck(name, 2, "string") |
argcheck(obj, 3, "table", "nil") |
obj,name = Dongle:New(name, obj) |
if not reg.modules then reg.modules = {} end |
reg.modules[obj] = obj |
reg.modules[name] = obj |
return obj,name |
end |
function Dongle:HasModule(module) |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "HasModule")) |
argcheck(module, 2, "string", "table") |
return reg.modules and reg.modules[module] |
end |
local function ModuleIterator(t, name) |
if not t then return end |
local obj |
repeat |
name,obj = next(t, name) |
until type(name) == "string" or not name |
return name,obj |
end |
function Dongle:IterateModules() |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "IterateModules")) |
return ModuleIterator, reg.modules |
end |
--[[------------------------------------------------------------------------- |
Event registration system |
---------------------------------------------------------------------------]] |
local function OnEvent(frame, event, ...) |
local eventTbl = events[event] |
if eventTbl then |
for obj,func in pairs(eventTbl) do |
if type(func) == "string" then |
if type(obj[func]) == "function" then |
safecall(obj[func], obj, event, ...) |
end |
else |
safecall(func, event, ...) |
end |
end |
end |
end |
local specialEvents = { |
["PLAYER_LOGIN"] = "Enable", |
["PLAYER_LOGOUT"] = "Disable", |
} |
function Dongle:RegisterEvent(event, func) |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "RegisterEvent")) |
argcheck(event, 2, "string") |
argcheck(func, 3, "string", "function", "nil") |
local special = (self ~= Dongle) and specialEvents[event] |
if special then |
error(string.format(L["EVENT_REGISTER_SPECIAL"], event, special), 3) |
end |
-- Name the method the same as the event if necessary |
if not func then func = event end |
if not events[event] then |
events[event] = {} |
frame:RegisterEvent(event) |
end |
events[event][self] = func |
end |
function Dongle:UnregisterEvent(event) |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "UnregisterEvent")) |
argcheck(event, 2, "string") |
local tbl = events[event] |
if tbl then |
tbl[self] = nil |
if not next(tbl) then |
events[event] = nil |
frame:UnregisterEvent(event) |
end |
end |
end |
function Dongle:UnregisterAllEvents() |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "UnregisterAllEvents")) |
for event,tbl in pairs(events) do |
tbl[self] = nil |
if not next(tbl) then |
events[event] = nil |
frame:UnregisterEvent(event) |
end |
end |
end |
function Dongle:IsEventRegistered(event) |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "IsEventRegistered")) |
argcheck(event, 2, "string") |
local tbl = events[event] |
return tbl |
end |
--[[------------------------------------------------------------------------- |
Inter-Addon Messaging System |
---------------------------------------------------------------------------]] |
function Dongle:RegisterMessage(msg, func) |
argcheck(self, 1, "table") |
argcheck(msg, 2, "string") |
argcheck(func, 3, "string", "function", "nil") |
-- Name the method the same as the message if necessary |
if not func then func = msg end |
if not messages[msg] then |
messages[msg] = {} |
end |
messages[msg][self] = func |
end |
function Dongle:UnregisterMessage(msg) |
argcheck(self, 1, "table") |
argcheck(msg, 2, "string") |
local tbl = messages[msg] |
if tbl then |
tbl[self] = nil |
if not next(tbl) then |
messages[msg] = nil |
end |
end |
end |
function Dongle:UnregisterAllMessages() |
argcheck(self, 1, "table") |
for msg,tbl in pairs(messages) do |
tbl[self] = nil |
if not next(tbl) then |
messages[msg] = nil |
end |
end |
end |
function Dongle:TriggerMessage(msg, ...) |
argcheck(self, 1, "table") |
argcheck(msg, 2, "string") |
local msgTbl = messages[msg] |
if not msgTbl then return end |
for obj,func in pairs(msgTbl) do |
if type(func) == "string" then |
if type(obj[func]) == "function" then |
safecall(obj[func], obj, msg, ...) |
end |
else |
safecall(func, msg, ...) |
end |
end |
end |
function Dongle:IsMessageRegistered(msg) |
argcheck(self, 1, "table") |
argcheck(msg, 2, "string") |
local tbl = messages[msg] |
return tbl[self] |
end |
--[[------------------------------------------------------------------------- |
Debug and Print utility functions |
---------------------------------------------------------------------------]] |
function Dongle:EnableDebug(level, frame) |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "EnableDebug")) |
argcheck(level, 2, "number", "nil") |
argcheck(frame, 3, "table", "nil") |
assert(3, type(frame) == "nil" or type(frame.AddMessage) == "function", L["ADDMESSAGE_REQUIRED"]) |
reg.debugFrame = frame or ChatFrame1 |
reg.debugLevel = level |
end |
function Dongle:IsDebugEnabled() |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "EnableDebug")) |
return reg.debugLevel, reg.debugFrame |
end |
local function argsToStrings(a1, ...) |
if select("#", ...) > 0 then |
return tostring(a1), argsToStrings(...) |
else |
return tostring(a1) |
end |
end |
local function printHelp(obj, method, header, frame, msg, ...) |
local reg = lookup[obj] |
assert(4, reg, string.format(L["MUST_CALLFROM_REGISTERED"], method)) |
local name = reg.name |
if header then |
msg = "|cFF33FF99"..name.."|r: "..tostring(msg) |
end |
if select("#", ...) > 0 then |
msg = string.join(", ", msg, argsToStrings(...)) |
end |
frame:AddMessage(msg) |
end |
local function printFHelp(obj, method, header, frame, msg, ...) |
local reg = lookup[obj] |
assert(4, reg, string.format(L["MUST_CALLFROM_REGISTERED"], method)) |
local name = reg.name |
local success,txt |
if header then |
msg = "|cFF33FF99%s|r: " .. msg |
success,txt = pcall(string.format, msg, name, ...) |
else |
success,txt = pcall(string.format, msg, ...) |
end |
if success then |
frame:AddMessage(txt) |
else |
error(string.gsub(txt, "'%?'", string.format("'%s'", method)), 3) |
end |
end |
function Dongle:Print(msg, ...) |
local reg = lookup[self] |
assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "Print")) |
argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata") |
return printHelp(self, "Print", true, DEFAULT_CHAT_FRAME, msg, ...) |
end |
function Dongle:PrintF(msg, ...) |
local reg = lookup[self] |
assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "PrintF")) |
argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata") |
return printFHelp(self, "PrintF", true, DEFAULT_CHAT_FRAME, msg, ...) |
end |
function Dongle:Echo(msg, ...) |
local reg = lookup[self] |
assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "Echo")) |
argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata") |
return printHelp(self, "Echo", false, DEFAULT_CHAT_FRAME, msg, ...) |
end |
function Dongle:EchoF(msg, ...) |
local reg = lookup[self] |
assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "EchoF")) |
argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata") |
return printFHelp(self, "EchoF", false, DEFAULT_CHAT_FRAME, msg, ...) |
end |
function Dongle:Debug(level, ...) |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "Debug")) |
argcheck(level, 2, "number") |
if reg.debugLevel and level <= reg.debugLevel then |
printHelp(self, "Debug", true, reg.debugFrame, ...) |
end |
end |
function Dongle:DebugF(level, ...) |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "DebugF")) |
argcheck(level, 2, "number") |
if reg.debugLevel and level <= reg.debugLevel then |
printFHelp(self, "DebugF", true, reg.debugFrame, ...) |
end |
end |
--[[------------------------------------------------------------------------- |
Database System |
---------------------------------------------------------------------------]] |
local dbMethods = { |
"RegisterDefaults", "SetProfile", "GetProfiles", "DeleteProfile", "CopyProfile", |
"GetCurrentProfile", "ResetProfile", "ResetDB", |
"RegisterNamespace", |
} |
local function copyTable(src) |
local dest = {} |
for k,v in pairs(src) do |
if type(k) == "table" then |
k = copyTable(k) |
end |
if type(v) == "table" then |
v = copyTable(v) |
end |
dest[k] = v |
end |
return dest |
end |
local function copyDefaults(dest, src, force) |
for k,v in pairs(src) do |
if k == "*" then |
if type(v) == "table" then |
-- Values are tables, need some magic here |
local mt = { |
__cache = {}, |
__index = function(t,k) |
local mt = getmetatable(dest) |
local cache = rawget(mt, "__cache") |
local tbl = rawget(cache, k) |
if not tbl then |
local parent = t |
local parentkey = k |
tbl = copyTable(v) |
rawset(cache, k, tbl) |
local mt = getmetatable(tbl) |
if not mt then |
mt = {} |
setmetatable(tbl, mt) |
end |
local newindex = function(t,k,v) |
rawset(parent, parentkey, t) |
rawset(t, k, v) |
end |
rawset(mt, "__newindex", newindex) |
end |
return tbl |
end, |
} |
setmetatable(dest, mt) |
-- Now need to set the metatable on any child tables |
for dkey,dval in pairs(dest) do |
copyDefaults(dval, v) |
end |
else |
-- Values are not tables, so this is just a simple return |
local mt = {__index = function() return v end} |
setmetatable(dest, mt) |
end |
elseif type(v) == "table" then |
if not dest[k] then dest[k] = {} end |
copyDefaults(dest[k], v, force) |
else |
if (dest[k] == nil) or force then |
dest[k] = v |
end |
end |
end |
end |
local function removeDefaults(db, defaults) |
if not db then return end |
for k,v in pairs(defaults) do |
if k == "*" and type(v) == "table" then |
-- check for any defaults that have been changed |
local mt = getmetatable(db) |
local cache = rawget(mt, "__cache") |
for cacheKey,cacheValue in pairs(cache) do |
removeDefaults(cacheValue, v) |
if next(cacheValue) ~= nil then |
-- Something's changed |
rawset(db, cacheKey, cacheValue) |
end |
end |
-- Now loop through all the actual k,v pairs and remove |
for key,value in pairs(db) do |
removeDefaults(value, v) |
end |
elseif type(v) == "table" and db[k] then |
removeDefaults(db[k], v) |
if not next(db[k]) then |
db[k] = nil |
end |
else |
if db[k] == defaults[k] then |
db[k] = nil |
end |
end |
end |
end |
local function initSection(db, section, svstore, key, defaults) |
local sv = rawget(db, "sv") |
local tableCreated |
if not sv[svstore] then sv[svstore] = {} end |
if not sv[svstore][key] then |
sv[svstore][key] = {} |
tableCreated = true |
end |
local tbl = sv[svstore][key] |
if defaults then |
copyDefaults(tbl, defaults) |
end |
rawset(db, section, tbl) |
return tableCreated, tbl |
end |
local dbmt = { |
__index = function(t, section) |
local keys = rawget(t, "keys") |
local key = keys[section] |
if key then |
local defaultTbl = rawget(t, "defaults") |
local defaults = defaultTbl and defaultTbl[section] |
if section == "profile" then |
local new = initSection(t, section, "profiles", key, defaults) |
if new then |
Dongle:TriggerMessage("DONGLE_PROFILE_CREATED", t, rawget(t, "parent"), rawget(t, "sv_name"), key) |
end |
elseif section == "profiles" then |
local sv = rawget(t, "sv") |
if not sv.profiles then sv.profiles = {} end |
rawset(t, "profiles", sv.profiles) |
elseif section == "global" then |
local sv = rawget(t, "sv") |
if not sv.global then sv.global = {} end |
if defaults then |
copyDefaults(sv.global, defaults) |
end |
rawset(t, section, sv.global) |
else |
initSection(t, section, section, key, defaults) |
end |
end |
return rawget(t, section) |
end |
} |
local function initdb(parent, name, defaults, defaultProfile, olddb) |
-- This allows us to use an arbitrary table as base instead of saved variable name |
local sv |
if type(name) == "string" then |
sv = getglobal(name) |
if not sv then |
sv = {} |
setglobal(name, sv) |
end |
elseif type(name) == "table" then |
sv = name |
end |
-- Generate the database keys for each section |
local char = string.format("%s - %s", UnitName("player"), GetRealmName()) |
local realm = GetRealmName() |
local class = select(2, UnitClass("player")) |
local race = select(2, UnitRace("player")) |
local faction = UnitFactionGroup("player") |
local factionrealm = string.format("%s - %s", faction, realm) |
-- Make a container for profile keys |
if not sv.profileKeys then sv.profileKeys = {} end |
-- Try to get the profile selected from the char db |
local profileKey = sv.profileKeys[char] or defaultProfile or char |
sv.profileKeys[char] = profileKey |
local keyTbl= { |
["char"] = char, |
["realm"] = realm, |
["class"] = class, |
["race"] = race, |
["faction"] = faction, |
["factionrealm"] = factionrealm, |
["global"] = true, |
["profile"] = profileKey, |
["profiles"] = true, -- Don't create until we need |
} |
-- If we've been passed an old database, clear it out |
if olddb then |
for k,v in pairs(olddb) do olddb[k] = nil end |
end |
-- Give this database the metatable so it initializes dynamically |
local db = setmetatable(olddb or {}, dbmt) |
-- Copy methods locally |
for idx,method in pairs(dbMethods) do |
db[method] = Dongle[method] |
end |
-- Set some properties in the object we're returning |
db.profiles = sv.profiles |
db.keys = keyTbl |
db.sv = sv |
db.sv_name = name |
db.defaults = defaults |
db.parent = parent |
databases[db] = true |
return db |
end |
function Dongle:InitializeDB(name, defaults, defaultProfile) |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "InitializeDB")) |
argcheck(name, 2, "string", "table") |
argcheck(defaults, 3, "table", "nil") |
argcheck(defaultProfile, 4, "string", "nil") |
return initdb(self, name, defaults, defaultProfile) |
end |
-- This function operates on a Dongle DB object |
function Dongle.RegisterDefaults(db, defaults) |
assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "RegisterDefaults")) |
assert(3, db.defaults == nil, L["REPLACE_DEFAUTS"]) |
argcheck(defaults, 2, "table") |
for section,key in pairs(db.keys) do |
if defaults[section] and rawget(db, section) then |
copyDefaults(db[section], defaults[section]) |
end |
end |
db.defaults = defaults |
end |
function Dongle:ClearDBDefaults() |
for db in pairs(databases) do |
local defaults = db.defaults |
local sv = db.sv |
if db and defaults then |
for section,key in pairs(db.keys) do |
if defaults[section] and rawget(db, section) then |
removeDefaults(db[section], defaults[section]) |
end |
end |
for section,key in pairs(db.keys) do |
local tbl = rawget(db, section) |
if tbl and not next(tbl) then |
if sv[section] then |
if type(key) == "string" then |
sv[section][key] = nil |
else |
sv[section] = nil |
end |
end |
end |
end |
end |
end |
end |
function Dongle.SetProfile(db, name) |
assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "SetProfile")) |
argcheck(name, 2, "string") |
local old = db.profile |
local defaults = db.defaults and db.defaults.profile |
if defaults then |
-- Remove the defaults from the old profile |
removeDefaults(old, defaults) |
end |
db.profile = nil |
db.keys["profile"] = name |
db.sv.profileKeys[db.keys.char] = name |
Dongle:TriggerMessage("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.keys.profile) |
end |
function Dongle.GetProfiles(db, t) |
assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "GetProfiles")) |
argcheck(t, 2, "table", "nil") |
t = t or {} |
local i = 1 |
for profileKey in pairs(db.sv.profiles) do |
t[i] = profileKey |
i = i + 1 |
end |
return t, i - 1 |
end |
function Dongle.GetCurrentProfile(db) |
assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "GetCurrentProfile")) |
return db.keys.profile |
end |
function Dongle.DeleteProfile(db, name) |
assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "DeleteProfile")) |
argcheck(name, 2, "string") |
if db.keys.profile == name then |
error(L["CANNOT_DELETE_ACTIVE_PROFILE"], 2) |
end |
assert(type(db.sv.profiles[name]) == "table", L["DELETE_NONEXISTANT_PROFILE"]) |
db.sv.profiles[name] = nil |
Dongle:TriggerMessage("DONGLE_PROFILE_DELETED", db, db.parent, db.sv_name, name) |
end |
function Dongle.CopyProfile(db, name) |
assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "CopyProfile")) |
argcheck(name, 2, "string") |
assert(3, db.keys.profile ~= name, L["SAME_SOURCE_DEST"]) |
assert(3, type(db.sv.profiles[name]) == "table", string.format(L["PROFILE_DOES_NOT_EXIST"], name)) |
local profile = db.profile |
local source = db.sv.profiles[name] |
copyDefaults(profile, source, true) |
Dongle:TriggerMessage("DONGLE_PROFILE_COPIED", db, db.parent, db.sv_name, name, db.keys.profile) |
end |
function Dongle.ResetProfile(db) |
assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "ResetProfile")) |
local profile = db.profile |
for k,v in pairs(profile) do |
profile[k] = nil |
end |
local defaults = db.defaults and db.defaults.profile |
if defaults then |
copyDefaults(profile, defaults) |
end |
Dongle:TriggerMessage("DONGLE_PROFILE_RESET", db, db.parent, db.sv_name, db.keys.profile) |
end |
function Dongle.ResetDB(db, defaultProfile) |
assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "ResetDB")) |
argcheck(defaultProfile, 2, "nil", "string") |
local sv = db.sv |
for k,v in pairs(sv) do |
sv[k] = nil |
end |
local parent = db.parent |
initdb(parent, db.sv_name, db.defaults, defaultProfile, db) |
Dongle:TriggerMessage("DONGLE_DATABASE_RESET", db, parent, db.sv_name, db.keys.profile) |
Dongle:TriggerMessage("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.keys.profile) |
return db |
end |
function Dongle.RegisterNamespace(db, name, defaults) |
assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "RegisterNamespace")) |
argcheck(name, 2, "string") |
argcheck(defaults, 3, "nil", "string") |
local sv = db.sv |
if not sv.namespaces then sv.namespaces = {} end |
if not sv.namespaces[name] then |
sv.namespaces[name] = {} |
end |
local newDB = initdb(db, sv.namespaces[name], defaults, db.keys.profile) |
-- Remove the :SetProfile method from newDB |
newDB.SetProfile = nil |
if not db.children then db.children = {} end |
table.insert(db.children, newDB) |
return newDB |
end |
--[[------------------------------------------------------------------------- |
Slash Command System |
---------------------------------------------------------------------------]] |
local slashCmdMethods = { |
"InjectDBCommands", |
"RegisterSlashHandler", |
"PrintUsage", |
} |
local function OnSlashCommand(cmd, cmd_line) |
if cmd.patterns then |
for idx,tbl in pairs(cmd.patterns) do |
local pattern = tbl.pattern |
if string.match(cmd_line, pattern) then |
local handler = tbl.handler |
if type(tbl.handler) == "string" then |
local obj |
-- Look in the command object before we look at the parent object |
if cmd[handler] then obj = cmd end |
if cmd.parent[handler] then obj = cmd.parent end |
if obj then |
obj[handler](obj, string.match(cmd_line, pattern)) |
end |
else |
handler(string.match(cmd_line, pattern)) |
end |
return |
end |
end |
end |
cmd:PrintUsage() |
end |
function Dongle:InitializeSlashCommand(desc, name, ...) |
local reg = lookup[self] |
assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "InitializeSlashCommand")) |
argcheck(desc, 2, "string") |
argcheck(name, 3, "string") |
argcheck(select(1, ...), 4, "string") |
for i = 2,select("#", ...) do |
argcheck(select(i, ...), i+2, "string") |
end |
local cmd = {} |
cmd.desc = desc |
cmd.name = name |
cmd.parent = self |
cmd.slashes = { ... } |
for idx,method in pairs(slashCmdMethods) do |
cmd[method] = Dongle[method] |
end |
local genv = getfenv(0) |
for i = 1,select("#", ...) do |
genv["SLASH_"..name..tostring(i)] = "/"..select(i, ...) |
end |
genv.SlashCmdList[name] = function(...) OnSlashCommand(cmd, ...) end |
commands[cmd] = true |
return cmd |
end |
function Dongle.RegisterSlashHandler(cmd, desc, pattern, handler) |
assert(3, commands[cmd], string.format(L["MUST_CALLFROM_SLASH"], "RegisterSlashHandler")) |
argcheck(desc, 2, "string") |
argcheck(pattern, 3, "string") |
argcheck(handler, 4, "function", "string") |
if not cmd.patterns then |
cmd.patterns = {} |
end |
table.insert(cmd.patterns, { |
["desc"] = desc, |
["handler"] = handler, |
["pattern"] = pattern, |
}) |
end |
function Dongle.PrintUsage(cmd) |
assert(3, commands[cmd], string.format(L["MUST_CALLFROM_SLASH"], "PrintUsage")) |
local parent = cmd.parent |
parent:Echo(cmd.desc.."\n".."/"..table.concat(cmd.slashes, ", /")..":\n") |
if cmd.patterns then |
for idx,tbl in ipairs(cmd.patterns) do |
parent:Echo(" - " .. tbl.desc) |
end |
end |
end |
local dbcommands = { |
["copy"] = { |
L["DBSLASH_PROFILE_COPY_DESC"], |
L["DBSLASH_PROFILE_COPY_PATTERN"], |
"CopyProfile", |
}, |
["delete"] = { |
L["DBSLASH_PROFILE_DELETE_DESC"], |
L["DBSLASH_PROFILE_DELETE_PATTERN"], |
"DeleteProfile", |
}, |
["list"] = { |
L["DBSLASH_PROFILE_LIST_DESC"], |
L["DBSLASH_PROFILE_LIST_PATTERN"], |
}, |
["reset"] = { |
L["DBSLASH_PROFILE_RESET_DESC"], |
L["DBSLASH_PROFILE_RESET_PATTERN"], |
"ResetProfile", |
}, |
["set"] = { |
L["DBSLASH_PROFILE_SET_DESC"], |
L["DBSLASH_PROFILE_SET_PATTERN"], |
"SetProfile", |
}, |
} |
function Dongle.InjectDBCommands(cmd, db, ...) |
assert(3, commands[cmd], string.format(L["MUST_CALLFROM_SLASH"], "InjectDBCommands")) |
assert(3, databases[db], string.format(L["BAD_ARGUMENT_DB"], 2, "InjectDBCommands")) |
local argc = select("#", ...) |
assert(3, argc > 0, L["INJECTDB_USAGE"]) |
for i=1,argc do |
local cmdname = string.lower(select(i, ...)) |
local entry = dbcommands[cmdname] |
assert(entry, L["INJECTDB_USAGE"]) |
local func = entry[3] |
local handler |
if cmdname == "list" then |
handler = function(...) |
local profiles = db:GetProfiles() |
db.parent:Print(L["DBSLASH_PROFILE_LIST_OUT"] .. "\n" .. strjoin("\n", unpack(profiles))) |
end |
else |
handler = function(...) db[entry[3]](db, ...) end |
end |
cmd:RegisterSlashHandler(entry[1], entry[2], handler) |
end |
end |
--[[------------------------------------------------------------------------- |
Internal Message/Event Handlers |
---------------------------------------------------------------------------]] |
local function PLAYER_LOGOUT(event) |
Dongle:ClearDBDefaults() |
for k,v in pairs(registry) do |
local obj = v.obj |
if type(obj["Disable"]) == "function" then |
safecall(obj["Disable"], obj) |
end |
end |
end |
local PLAYER_LOGIN |
do |
local lockPlayerLogin = false |
function PLAYER_LOGIN() |
if lockPlayerLogin then return end |
lockPlayerLogin = true |
local obj = table.remove(loadorder, 1) |
while obj do |
if type(obj.Enable) == "function" then |
safecall(obj.Enable, obj) |
end |
obj = table.remove(loadorder, 1) |
end |
lockPlayerLogin = false |
end |
end |
local function ADDON_LOADED(event, ...) |
local obj = table.remove(loadqueue, 1) |
while obj do |
table.insert(loadorder, obj) |
if type(obj.Initialize) == "function" then |
safecall(obj.Initialize, obj) |
end |
obj = table.remove(loadqueue, 1) |
end |
if IsLoggedIn() then |
PLAYER_LOGIN() |
end |
end |
local function DONGLE_PROFILE_CHANGED(msg, db, parent, sv_name, profileKey) |
local children = db.children |
if children then |
for i,namespace in ipairs(children) do |
local old = namespace.profile |
local defaults = namespace.defaults and namespace.defaults.profile |
if defaults then |
-- Remove the defaults from the old profile |
removeDefaults(old, defaults) |
end |
namespace.profile = nil |
namespace.keys["profile"] = profileKey |
end |
end |
end |
--[[------------------------------------------------------------------------- |
DongleStub required functions and registration |
---------------------------------------------------------------------------]] |
function Dongle:GetVersion() return major,minor end |
local function Activate(self, old) |
if old then |
registry = old.registry or registry |
lookup = old.lookup or lookup |
loadqueue = old.loadqueue or loadqueue |
loadorder = old.loadorder or loadorder |
events = old.events or events |
databases = old.databases or databases |
commands = old.commands or commands |
messages = old.messages or messages |
frame = old.frame or CreateFrame("Frame") |
else |
frame = CreateFrame("Frame") |
local reg = {obj = self, name = "Dongle"} |
registry[major] = reg |
lookup[self] = reg |
lookup[major] = reg |
end |
self.registry = registry |
self.lookup = lookup |
self.loadqueue = loadqueue |
self.loadorder = loadorder |
self.events = events |
self.databases = databases |
self.commands = commands |
self.messages = messages |
self.frame = frame |
frame:SetScript("OnEvent", OnEvent) |
local lib = old or self |
-- Lets make sure the lookup table has us. |
lookup[lib] = lookup[major] |
-- Register for events using Dongle itself |
lib:RegisterEvent("ADDON_LOADED", ADDON_LOADED) |
lib:RegisterEvent("PLAYER_LOGIN", PLAYER_LOGIN) |
lib:RegisterEvent("PLAYER_LOGOUT", PLAYER_LOGOUT) |
lib:RegisterMessage("DONGLE_PROFILE_CHANGED", DONGLE_PROFILE_CHANGED) |
-- Convert all the modules handles |
for name,obj in pairs(registry) do |
for k,v in ipairs(methods) do |
obj[k] = self[v] |
end |
end |
-- Convert all database methods |
for db in pairs(databases) do |
for idx,method in ipairs(dbMethods) do |
db[method] = self[method] |
end |
end |
-- Convert all slash command methods |
for cmd in pairs(commands) do |
for idx,method in ipairs(slashCmdMethods) do |
cmd[method] = self[method] |
end |
end |
end |
-- Lets nuke any Dongle deactivate functions, please |
-- I hate nasty hacks. |
if DongleStub.versions and DongleStub.versions[major] then |
local reg = DongleStub.versions[major] |
reg.deactivate = nil |
end |
Dongle = DongleStub:Register(Dongle, Activate) |
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info |
-- LibStub is hereby placed in the Public Domain |
-- Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke |
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! |
local LibStub = _G[LIBSTUB_MAJOR] |
-- Check to see is this version of the stub is obsolete |
if not LibStub or LibStub.minor < LIBSTUB_MINOR then |
LibStub = LibStub or {libs = {}, minors = {} } |
_G[LIBSTUB_MAJOR] = LibStub |
LibStub.minor = LIBSTUB_MINOR |
-- LibStub:NewLibrary(major, minor) |
-- major (string) - the major version of the library |
-- minor (string or number ) - the minor version of the library |
-- |
-- returns nil if a newer or same version of the lib is already present |
-- returns empty library object or old library object if upgrade is needed |
function LibStub:NewLibrary(major, minor) |
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") |
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") |
local oldminor = self.minors[major] |
if oldminor and oldminor >= minor then return nil end |
self.minors[major], self.libs[major] = minor, self.libs[major] or {} |
return self.libs[major], oldminor |
end |
-- LibStub:GetLibrary(major, [silent]) |
-- major (string) - the major version of the library |
-- silent (boolean) - if true, library is optional, silently return nil if its not found |
-- |
-- throws an error if the library can not be found (except silent is set) |
-- returns the library object if found |
function LibStub:GetLibrary(major, silent) |
if not self.libs[major] and not silent then |
error(("Cannot find a library instance of %q."):format(tostring(major)), 2) |
end |
return self.libs[major], self.minors[major] |
end |
-- LibStub:IterateLibraries() |
-- |
-- Returns an iterator for the currently registered libraries |
function LibStub:IterateLibraries() |
return pairs(self.libs) |
end |
setmetatable(LibStub, { __call = LibStub.GetLibrary }) |
end |
--[[------------------------------------------------------------------------- |
Copyright (c) 2006-2007, Dongle Development Team |
All rights reserved. |
Redistribution and use in source and binary forms, with or without |
modification, are permitted provided that the following conditions are |
met: |
* Redistributions of source code must retain the above copyright |
notice, this list of conditions and the following disclaimer. |
* Redistributions in binary form must reproduce the above |
copyright notice, this list of conditions and the following |
disclaimer in the documentation and/or other materials provided |
with the distribution. |
* Neither the name of the Dongle Development Team nor the names of |
its contributors may be used to endorse or promote products derived |
from this software without specific prior written permission. |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---------------------------------------------------------------------------]] |
--[[------------------------------------------------------------------------- |
Begin Library Implementation |
---------------------------------------------------------------------------]] |
local major = "OptionHouse-1.1" |
local minor = tonumber(string.match("$Revision: 638 $", "(%d+)") or 1) |
assert(LibStub, string.format("%s requires LibStub.", major)) |
local OHInstance, oldRevision = LibStub:NewLibrary(major, minor) |
if( not OHInstance ) then return end |
local L = { |
["ERROR_NO_FRAME"] = "No frame returned for the addon \"%s\", category \"%s\", sub category \"%s\".", |
["NO_FUNC_PASSED"] = "You must associate a function with a category.", |
["BAD_ARGUMENT"] = "bad argument #%d to '%s' (%s expected, got %s)", |
["MUST_CALL"] = "You must call '%s' from an OptionHouse addon object.", |
["ADDON_ALREADYREG"] = "The addon '%s' is already registered with OptionHouse.", |
["UNKNOWN_TAB"] = "Cannot open tab #%d, only %d tabs are registered.", |
["CATEGORY_ALREADYREG"] = "The category '%s' already exists in '%s'", |
["NO_CATEGORYEXISTS"] = "No category named '%s' in '%s' exists.", |
["NO_SUBCATEXISTS"] = "No sub-category '%s' exists in '%s' for the addon '%s'.", |
["NO_PARENTCAT"] = "No parent category named '%s' exists in %s'", |
["SUBCATEGORY_ALREADYREG"] = "The sub-category named '%s' already exists in the category '%s' for '%s'", |
["UNKNOWN_FRAMETYPE"] = "Unknown frame type given '%s', only 'main', 'perf', 'addon', 'config' are supported.", |
["OPTION_HOUSE"] = "OptionHouse", |
["ENTERED_COMBAT"] = "|cFF33FF99OptionHouse|r: Configuration window closed due to entering combat.", |
["IN_COMBAT"] = "|cFF33FF99OptionHouse|r: Configuration window cannot be opened while in combat.", |
["SEARCH"] = "Search...", |
["ADDON_OPTIONS"] = "Addons", |
["VERSION"] = "Version: %s", |
["AUTHOR"] = "Author: %s", |
["TOTAL_SUBCATEGORIES"] = "Sub Categories: %d", |
["TAB_MANAGEMENT"] = "Management", |
["TAB_PERFORMANCE"] = "Performance", |
} |
local function assert(level,condition,message) |
if( not condition ) then |
error(message,level) |
end |
end |
local function argcheck(value, num, ...) |
if( type(num) ~= "number" ) then |
error(L["BAD_ARGUMENT"]:format(2, "argcheck", "number", type(num)), 1) |
end |
for i=1,select("#", ...) do |
if( type(value) == select(i, ...) ) then return end |
end |
local types = string.join(", ", ...) |
local name = string.match(debugstack(2,2,0), ": in function [`<](.-)['>]") |
error(L["BAD_ARGUMENT"]:format(num, name, types, type(value)), 3) |
end |
-- OptionHouse |
local OptionHouse = {} |
local tabfunctions = {} |
local methods = {"RegisterCategory", "RegisterSubCategory", "RemoveCategory", "RemoveSubCategory"} |
local addons = {} |
local regFrames = {} |
local openedByMenu |
local evtFrame |
local frame |
-- TABS |
local function resizeTab(tab) |
local textWidth = tab:GetFontString():GetWidth() |
tab.middleActive:SetWidth(textWidth) |
tab.middleInactive:SetWidth(textWidth) |
tab:SetWidth((2 * tab.leftActive:GetWidth()) + textWidth) |
tab.highlightTexture:SetWidth(textWidth + 20) |
end |
local function tabSelected(tab) |
tab:SetTextColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b) |
tab.highlightTexture:Hide() |
tab.leftActive:Show() |
tab.middleActive:Show() |
tab.rightActive:Show() |
tab.leftInactive:Hide() |
tab.middleInactive:Hide() |
tab.rightInactive:Hide() |
end |
local function tabDeselected(tab) |
tab:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b) |
tab.highlightTexture:Show() |
tab.leftInactive:Show() |
tab.middleInactive:Show() |
tab.rightInactive:Show() |
tab.leftActive:Hide() |
tab.middleActive:Hide() |
tab.rightActive:Hide() |
end |
local function setTab(id) |
if( frame.selectedTab ) then |
tabDeselected(frame.tabs[frame.selectedTab]) |
end |
frame.selectedTab = id |
tabSelected(frame.tabs[id]) |
end |
local function tabOnClick(self) |
local id |
if( type(self) ~= "number" ) then |
id = self:GetID() |
else |
id = self |
end |
setTab(id) |
for tabID, tab in pairs(tabfunctions) do |
if( tabID == id ) then |
if( type(tab.func) == "function" ) then |
tab.func() |
else |
tab.handler[tab.func](tab.handler) |
end |
if( tab.type == "browse" ) then |
frame.topLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-TopLeft") |
frame.top:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-Top") |
frame.topRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-TopRight") |
frame.bottomLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-BotLeft") |
frame.bottom:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-Bot") |
frame.bottomRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-BotRight") |
elseif( tab.type == "bid" ) then |
frame.topLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-TopLeft") |
frame.top:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-Top") |
frame.topRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-TopRight") |
frame.bottomLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-BotLeft") |
frame.bottom:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-Bot") |
frame.bottomRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-BotRight") |
end |
elseif( type(tab.func) == "function" ) then |
tab.func(true) |
else |
tab.handler[tab.func](tab.handler, true) |
end |
end |
end |
local function createTab(text, id) |
local tab = frame.tabs[id] |
if( not tab ) then |
tab = CreateFrame("Button", nil, frame) |
tab:SetHighlightFontObject(GameFontHighlightSmall) |
tab:SetTextFontObject(GameFontNormalSmall) |
tab:SetHighlightTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight") |
tab:SetText(text) |
tab:SetWidth(115) |
tab:SetHeight(32) |
tab:SetID(id) |
tab:SetScript("OnClick", tabOnClick) |
tab:GetFontString():SetPoint("CENTER", 0, 2) |
tab.highlightTexture = tab:GetHighlightTexture() |
tab.highlightTexture:ClearAllPoints() |
tab.highlightTexture:SetPoint("CENTER", tab:GetFontString(), 0, 0) |
tab.highlightTexture:SetBlendMode("ADD") |
-- TAB SELECTED TEXTURES |
tab.leftActive = tab:CreateTexture(nil, "ARTWORK") |
tab.leftActive:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ActiveTab") |
tab.leftActive:SetHeight(32) |
tab.leftActive:SetWidth(20) |
tab.leftActive:SetPoint("TOPLEFT", tab, "TOPLEFT") |
tab.leftActive:SetTexCoord(0, 0.15625, 0, 1.0) |
tab.middleActive = tab:CreateTexture(nil, "ARTWORK") |
tab.middleActive:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ActiveTab") |
tab.middleActive:SetHeight(32) |
tab.middleActive:SetWidth(20) |
tab.middleActive:SetPoint("LEFT", tab.leftActive, "RIGHT") |
tab.middleActive:SetTexCoord(0.15625, 0.84375, 0, 1.0) |
tab.rightActive = tab:CreateTexture(nil, "ARTWORK") |
tab.rightActive:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ActiveTab") |
tab.rightActive:SetHeight(32) |
tab.rightActive:SetWidth(20) |
tab.rightActive:SetPoint("LEFT", tab.middleActive, "RIGHT") |
tab.rightActive:SetTexCoord(0.84375, 1.0, 0, 1.0) |
-- TAB DESELECTED TEXTURES |
tab.leftInactive = tab:CreateTexture(nil, "ARTWORK") |
tab.leftInactive:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-InActiveTab") |
tab.leftInactive:SetHeight(32) |
tab.leftInactive:SetWidth(20) |
tab.leftInactive:SetPoint("TOPLEFT", tab, "TOPLEFT") |
tab.leftInactive:SetTexCoord(0, 0.15625, 0, 1.0) |
tab.middleInactive = tab:CreateTexture(nil, "ARTWORK") |
tab.middleInactive:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-InActiveTab") |
tab.middleInactive:SetHeight(32) |
tab.middleInactive:SetWidth(20) |
tab.middleInactive:SetPoint("LEFT", tab.leftInactive, "RIGHT") |
tab.middleInactive:SetTexCoord(0.15625, 0.84375, 0, 1.0) |
tab.rightInactive = tab:CreateTexture(nil, "ARTWORK") |
tab.rightInactive:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-InActiveTab") |
tab.rightInactive:SetHeight(32) |
tab.rightInactive:SetWidth(20) |
tab.rightInactive:SetPoint("LEFT", tab.middleInactive, "RIGHT") |
tab.rightInactive:SetTexCoord(0.84375, 1.0, 0, 1.0) |
frame.totalTabs = frame.totalTabs + 1 |
frame.tabs[id] = tab |
end |
tab:SetText(text) |
tab:Show() |
tabDeselected(tab) |
resizeTab(tab) |
if( id == 1 ) then |
tab:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 15, 11) |
else |
tab:SetPoint("TOPLEFT", frame.tabs[id - 1], "TOPRIGHT", -8, 0) |
end |
end |
-- SCROLL FRAME |
local function onVerticalScroll(self, offset) |
offset = ceil(offset) |
self.bar:SetValue(offset) |
self.offset = ceil(offset / self.displayNum) |
if( self.offset < 0 ) then |
self.offset = 0 |
end |
local min, max = self.bar:GetMinMaxValues() |
if( min == offset ) then |
self.up:Disable() |
else |
self.up:Enable() |
end |
if( max == offset ) then |
self.down:Disable() |
else |
self.down:Enable() |
end |
self.updateFunc(self.updateHandler) |
end |
local function onMouseWheel(self, offset) |
if( self.scroll ) then self = self.scroll end |
if( offset > 0 ) then |
self.bar:SetValue(self.bar:GetValue() - (self.bar:GetHeight() / 2)) |
else |
self.bar:SetValue(self.bar:GetValue() + (self.bar:GetHeight() / 2)) |
end |
end |
local function onParentMouseWheel(self, offset) |
onMouseWheel(self.scroll, offset) |
end |
local function updateScroll(scroll, totalRows) |
local max = (totalRows - scroll.displayNum) * scroll.displayNum |
-- Macs are unhappy if max is less then the min |
if( max < 0 ) then |
max = 0 |
end |
scroll.bar:SetMinMaxValues(0, max) |
if( totalRows > scroll.displayNum ) then |
scroll:Show() |
scroll.bar:Show() |
scroll.up:Show() |
scroll.down:Show() |
scroll.bar:GetThumbTexture():Show() |
else |
scroll:Hide() |
scroll.bar:Hide() |
scroll.up:Hide() |
scroll.down:Hide() |
scroll.bar:GetThumbTexture():Hide() |
end |
end |
local function onValueChanged(self, offset) |
self:GetParent():SetVerticalScroll(offset) |
end |
local function scrollButtonUp(self) |
local parent = self:GetParent() |
parent:SetValue(parent:GetValue() - (parent:GetHeight() / 2)) |
PlaySound("UChatScrollButton") |
end |
local function scrollButtonDown(self) |
local parent = self:GetParent() |
parent:SetValue(parent:GetValue() + (parent:GetHeight() / 2)) |
PlaySound("UChatScrollButton") |
end |
local function createScrollFrame(frame, displayNum, onScroll) |
frame:EnableMouseWheel(true) |
frame:SetScript("OnMouseWheel", onParentMouseWheel) |
frame.scroll = CreateFrame("ScrollFrame", nil, frame) |
frame.scroll:EnableMouseWheel(true) |
frame.scroll:SetWidth(16) |
frame.scroll:SetHeight(270) |
frame.scroll:SetScript("OnVerticalScroll", onVerticalScroll) |
frame.scroll:SetScript("OnMouseWheel", onMouseWheel) |
frame.scroll.offset = 0 |
frame.scroll.displayNum = displayNum |
frame.scroll.updateHandler = frame |
frame.scroll.updateFunc = onScroll |
-- Actual bar for scrolling |
frame.scroll.bar = CreateFrame("Slider", nil, frame.scroll) |
frame.scroll.bar:SetValueStep(frame.scroll.displayNum) |
frame.scroll.bar:SetMinMaxValues(0, 0) |
frame.scroll.bar:SetValue(0) |
frame.scroll.bar:SetWidth(16) |
frame.scroll.bar:SetScript("OnValueChanged", onValueChanged) |
frame.scroll.bar:SetPoint("TOPLEFT", frame.scroll, "TOPRIGHT", 6, -16) |
frame.scroll.bar:SetPoint("BOTTOMLEFT", frame.scroll, "BOTTOMRIGHT", 6, -16) |
-- Up/Down buttons |
frame.scroll.up = CreateFrame("Button", nil, frame.scroll.bar, "UIPanelScrollUpButtonTemplate") |
frame.scroll.up:ClearAllPoints() |
frame.scroll.up:SetPoint( "BOTTOM", frame.scroll.bar, "TOP" ) |
frame.scroll.up:SetScript("OnClick", scrollButtonUp) |
frame.scroll.down = CreateFrame("Button", nil, frame.scroll.bar, "UIPanelScrollDownButtonTemplate") |
frame.scroll.down:ClearAllPoints() |
frame.scroll.down:SetPoint( "TOP", frame.scroll.bar, "BOTTOM" ) |
frame.scroll.down:SetScript("OnClick", scrollButtonDown) |
-- That square thingy that shows where the bar is |
frame.scroll.bar:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob") |
local thumb = frame.scroll.bar:GetThumbTexture() |
thumb:SetHeight(16) |
thumb:SetWidth(16) |
thumb:SetTexCoord(0.25, 0.75, 0.25, 0.75) |
-- Border graphic |
frame.scroll.barUpTexture = frame.scroll:CreateTexture(nil, "BACKGROUND") |
frame.scroll.barUpTexture:SetWidth(31) |
frame.scroll.barUpTexture:SetHeight(256) |
frame.scroll.barUpTexture:SetPoint("TOPLEFT", frame.scroll.up, "TOPLEFT", -7, 5) |
frame.scroll.barUpTexture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar") |
frame.scroll.barUpTexture:SetTexCoord(0, 0.484375, 0, 1.0) |
frame.scroll.barDownTexture = frame.scroll:CreateTexture(nil, "BACKGROUND") |
frame.scroll.barDownTexture:SetWidth(31) |
frame.scroll.barDownTexture:SetHeight(106) |
frame.scroll.barDownTexture:SetPoint("BOTTOMLEFT", frame.scroll.down, "BOTTOMLEFT", -7, -3) |
frame.scroll.barDownTexture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar") |
frame.scroll.barDownTexture:SetTexCoord(0.515625, 1.0, 0, 0.4140625) |
end |
-- SEARCH INPUT |
local function focusGained(self) |
if( self.searchText ) then |
self.searchText = nil |
self:SetText("") |
self:SetTextColor(1, 1, 1, 1) |
end |
end |
local function focusLost(self) |
if( not self.searchText and string.trim(self:GetText()) == "" ) then |
self.searchText = true |
self:SetText(L["SEARCH"]) |
self:SetTextColor(0.90, 0.90, 0.90, 0.80) |
end |
end |
local function createSearchInput(frame, onChange) |
frame.search = CreateFrame("EditBox", nil, frame, "InputBoxTemplate") |
frame.search:SetHeight(19) |
frame.search:SetWidth(150) |
frame.search:SetAutoFocus(false) |
frame.search:ClearAllPoints() |
frame.search:SetPoint("CENTER", frame, "BOTTOMLEFT", 100, 25) |
frame.search.searchText = true |
frame.search:SetText(L["SEARCH"]) |
frame.search:SetTextColor(0.90, 0.90, 0.90, 0.80) |
frame.search:SetScript("OnTextChanged", onChange) |
frame.search:SetScript("OnEditFocusGained", focusGained) |
frame.search:SetScript("OnEditFocusLost", focusLost) |
end |
-- ADDON CONFIGURATION |
local function showTooltip(self) |
if( self.tooltip ) then |
GameTooltip:SetOwner(self, "ANCHOR_RIGHT") |
GameTooltip:SetText(self.tooltip, nil, nil, nil, nil, 1) |
end |
end |
local function hideTooltip() |
GameTooltip:Hide() |
end |
local function sortCategories(a, b) |
if( not b ) then |
return false |
end |
local aType = type(a.data.sortID) |
local bType = type(b.data.sortID) |
-- Sort categories/sub categories |
if( aType == "number" and bType == "number" ) then |
if( a.data.sortID == b.data.sortID ) then |
return ( a.name < b.name ) |
end |
return ( a.data.sortID < b.data.sortID ) |
elseif( aType == "number" and bType ~= "number" ) then |
return true |
elseif( bType == "number" and aType ~= "number" ) then |
return false |
end |
return ( a.name < b.name ) |
end |
-- Adds the actual row, will attempt to reuse the current row if able to |
local function addCategoryRow(type, name, tooltip, data, parent, addon) |
local frame = regFrames.addon |
for i=1, #(frame.categories) do |
-- Match type/name first |
if( frame.categories[i].type == type and frame.categories[i].name == name ) then |
-- Then make sure it's correct addons parent, if it's a category |
if( (parent and frame.categories[i].parent and frame.categories[i].parent == parent) or (not parent and not frame.categories[i].parent) ) then |
-- Now make sure it's the correct addon if it's a sub category |
if( (addon and frame.categories[i].addon and frame.categories[i].addon == addon) or (not addon and not frame.categories[i].addon) ) then |
frame.categories[i].tooltip = tooltip |
frame.categories[i].data = data |
return |
end |
end |
end |
end |
if( not data ) then |
data = {} |
end |
if( type == "addon" ) then |
data.sortID = name |
elseif( not data.sortID ) then |
data.sortID = 9999999 |
end |
table.insert(frame.categories, {name = name, type = type, tooltip = tooltip, data = data, parent = parent, addon = addon} ) |
frame.resortList = true |
end |
-- This removes the entire addon, we don't use this unless |
-- we're removing the last category |
local function removeAddonListing(addon) |
local frame = regFrames.addon |
for i=#(frame.categories), 1, -1 do |
if( frame.categories[i].addon == addon ) then |
table.remove(frame.categories, i) |
end |
end |
end |
-- Remove a specific category and/or sub category listing |
-- without needing to recreate the entire list |
local function removeCategoryListing(addon, name) |
local frame = regFrames.addon |
for i=#(frame.categories), 1, -1 do |
-- Remove the category requested |
if( frame.categories[i].type == "category" and frame.categories[i].name == name and frame.categories[i].addon == addon ) then |
table.remove(frame.categories, i) |
-- Remove all of it's sub categories |
elseif( frame.categories[i].type == "subcat" and frame.categories[i].parent == name and frame.categories[i].addon == addon ) then |
table.remove(frame.categories, i) |
end |
end |
end |
local function removeSubCategoryListing(addon, parentCat, name) |
local frame = regFrames.addon |
for i=#(frame.categories), 1, -1 do |
-- Remove the specific sub category |
if( frame.categories[i].type == "subcat" and frame.categories[i].name == name and frame.categories[i].parent == parentCat and frame.categories[i].addon == addon ) then |
table.remove(frame.categories, i) |
end |
end |
end |
-- We have a seperate function for adding addons |
-- so we can update a single addon out of the entire list |
-- if it's categories/sub categories get changed, or a new ones added |
local function addCategoryListing(name, addon) |
local tooltip = "|cffffffff" .. (addon.title or name) .. "|r" |
local data |
if( addon.version ) then |
tooltip = tooltip .. "\n" .. string.format(L["VERSION"], addon.version) |
end |
if( addon.author ) then |
tooltip = tooltip .. "\n" .. string.format(L["AUTHOR"], addon.author) |
end |
-- One category, make clicking the addon open that category |
if( addon.totalCats == 1 and addon.totalSubs == 0 ) then |
for catName, cat in pairs(addon.categories) do |
data = cat |
data.parentCat = catName |
break |
end |
-- Multiple categories, or sub categories |
else |
for catName, cat in pairs(addon.categories) do |
cat.parentCat = catName |
addCategoryRow("category", catName, cat.totalSubs > 0 and string.format(L["TOTAL_SUBCATEGORIES"], cat.totalSubs), cat, name, name) |
for subCatName, subCat in pairs(cat.sub) do |
subCat.parentCat = catName |
addCategoryRow("subcat", subCatName, nil, subCat, catName, name) |
end |
end |
end |
addCategoryRow("addon", name, (addon.version or addon.author) and tooltip, data, nil, name) |
end |
-- Recreates the entire listing |
local function createCategoryListing() |
regFrames.addon.categories = {} |
for name, addon in pairs(addons) do |
addCategoryListing(name, addon) |
end |
end |
local function openConfigFrame(data) |
local frame = regFrames.addon |
-- Clicking on an addon with multiple categories or sub categories will cause no data |
if( not data ) then |
-- Make sure the frames hidden when only the addon button is selected |
if( frame.shownFrame ) then |
frame.shownFrame:Hide() |
end |
return |
end |
if( data.handler or data.func ) then |
data.frame = nil |
if( type(data.func) == "string" ) then |
data.frame = data.handler[data.func](data.handler, data.parentCat or frame.selectedCategory, frame.selectedSubCat) |
elseif( type(data.handler) == "function" ) then |
data.frame = data.handler(data.parentCat or frame.selectedCategory, frame.selectedSubCat) |
end |
-- Mostly this is for authors, but it lets us clean up the logic a bit |
if( not data.frame ) then |
error(string.format(L["ERROR_NO_FRAME"], frame.selectedAddon, data.parentCat or frame.selectedCategory, frame.selectedSubCat), 3) |
end |
-- Validate location/width/height and force parent |
if( not data.frame:GetPoint() ) then |
data.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 190, -103) |
end |
if( data.frame:GetWidth() > 630 or data.frame:GetWidth() == 0 ) then |
data.frame:SetWidth(630) |
end |
if( data.frame:GetHeight() > 305 or data.frame:GetHeight() == 0 ) then |
data.frame:SetHeight(305) |
end |
data.frame:SetParent(frame) |
data.frame:SetFrameStrata("DIALOG") |
if( not data.noCache ) then |
local category |
-- Figure out which category we're modifying |
if( frame.selectedSubCat ~= "" ) then |
category = addons[frame.selectedAddon].categories[frame.selectedCategory].sub[frame.selectedSubCat] |
elseif( frame.selectedCategory ~= "" ) then |
category = addons[frame.selectedAddon].categories[frame.selectedCategory] |
elseif( frame.selectedAddon ~= "" ) then |
for catName, _ in pairs(addons[frame.selectedAddon].categories) do |
category = addons[frame.selectedAddon].categories[catName] |
end |
end |
-- Remove the handler/func and save the frame for next time |
if( category ) then |
category.handler = nil |
category.func = nil |
category.frame = data.frame |
end |
end |
end |
if( frame.shownFrame ) then |
frame.shownFrame:Hide() |
end |
-- Now show the current one |
if( data.frame and frame.selectedAddon ~= "" ) then |
data.frame:Show() |
frame.shownFrame = data.frame |
end |
end |
-- Displays the actual button |
local function displayCategoryRow(type, text, data, tooltip, highlighted) |
local frame = regFrames.addon |
-- We have to let this run completely |
-- so we know how many rows we have total |
frame.totalRows = frame.totalRows + 1 |
if( frame.totalRows <= frame.scroll.offset or frame.rowID >= 15 ) then |
return |
end |
frame.rowID = frame.rowID + 1 |
local button = frame.buttons[frame.rowID] |
local line = frame.lines[frame.rowID] |
if( highlighted ) then |
button:LockHighlight() |
else |
button:UnlockHighlight() |
end |
if( type == "addon" ) then |
button:SetText(text) |
button:GetFontString():SetPoint("LEFT", button, "LEFT", 4, 0) |
button:GetNormalTexture():SetAlpha(1.0) |
line:Hide() |
elseif( type == "category" ) then |
button:SetText(HIGHLIGHT_FONT_COLOR_CODE..text..FONT_COLOR_CODE_CLOSE) |
button:GetFontString():SetPoint("LEFT", button, "LEFT", 12, 0) |
button:GetNormalTexture():SetAlpha(0.4) |
line:Hide() |
elseif( type == "subcat" ) then |
button:SetText(HIGHLIGHT_FONT_COLOR_CODE..text..FONT_COLOR_CODE_CLOSE) |
button:GetFontString():SetPoint("LEFT", button, "LEFT", 20, 0) |
button:GetNormalTexture():SetAlpha(0.0) |
line:SetTexCoord(0, 0.4375, 0, 0.625) |
line:Show() |
end |
button.fs = button:GetFontString() |
button.tooltip = tooltip |
button.data = data |
button.type = type |
button.catText = text |
button:Show() |
end |
local function updateConfigList(openAlso) |
local frame = regFrames.addon |
frame.rowID = 0 |
frame.totalRows = 0 |
local lastID |
local searchBy = string.trim(string.lower(frame.search:GetText())) |
if( searchBy == "" or frame.search.searchText ) then |
searchBy = nil |
end |
-- Make sure stuff matches our search results |
for id, row in pairs(frame.categories) do |
if( searchBy and not string.match(string.lower(row.name), searchBy) ) then |
frame.categories[id].hide = true |
else |
frame.categories[id].hide = nil |
end |
end |
-- Resort list if needed |
if( frame.resortList ) then |
table.sort(frame.categories, sortCategories) |
frame.resortList = nil |
end |
-- Now display |
local opened |
for _, addon in pairs(frame.categories) do |
if( not addon.hide and addon.type == "addon" ) then |
-- Total addons |
if( addon.name == frame.selectedAddon ) then |
displayCategoryRow(addon.type, addon.name, addon.data, addon.tooltip, true) |
for _, cat in pairs(frame.categories) do |
-- Show all the categories with the addon as the parent |
if( not cat.hide and cat.parent == addon.name and cat.type == "category" ) then |
-- Total categories of the selected addon |
if( cat.name == frame.selectedCategory ) then |
displayCategoryRow(cat.type, cat.name, cat.data, cat.tooltip, true) |
for _, subCat in pairs(frame.categories) do |
-- We don't have to check type, because it's the only one that has .addon set |
if( not subCat.hide and subCat.parent == cat.name and subCat.addon == addon.name ) then |
-- Total sub categories of the selected addons selected category |
displayCategoryRow(subCat.type, subCat.name, subCat.data, subCat.tooltip, subCat.name == frame.selectedSubCat) |
lastID = frame.rowID |
if( openAlso ) then |
opened = subCat.data |
end |
end |
end |
if( lastID ) then |
frame.lines[lastID]:SetTexCoord(0.4375, 0.875, 0, 0.625) |
end |
-- Okay open the category then |
if( not opened and openAlso ) then |
opened = cat.data |
end |
else |
displayCategoryRow(cat.type, cat.name, cat.data, cat.tooltip) |
end |
end |
end |
if( not opened and openAlso ) then |
opened = addon.data |
end |
else |
displayCategoryRow(addon.type, addon.name, addon.data, addon.tooltip) |
end |
end |
end |
if( opened ) then |
openConfigFrame(opened) |
end |
updateScroll(frame.scroll, frame.totalRows) |
local wrapSize = 145 |
if( frame.totalRows > 15 ) then |
wrapSize = 135 |
end |
for i=1, 15 do |
local button = frame.buttons[i] |
if( frame.totalRows > 15 ) then |
button:SetWidth(140) |
else |
button:SetWidth(156) |
end |
if( button.fs ) then |
local wrapAt = wrapSize |
if( button.type == "category" ) then |
wrapAt = wrapAt - 5 |
elseif( frame.buttons[i].type == "subcat" ) then |
wrapAt = wrapAt - 10 |
end |
if( button.fs:GetStringWidth() > wrapAt ) then |
button.fs:SetWidth(wrapAt) |
else |
button.fs:SetWidth(button.fs:GetStringWidth()) |
end |
end |
-- We have less then 15 rows used |
-- and our index is equal or past our current |
if( frame.rowID < 15 and i > frame.rowID ) then |
button:Hide() |
end |
end |
end |
local function expandConfigList(self) |
local frame = regFrames.addon |
if( self.type == "addon" ) then |
if( frame.selectedAddon == self.catText ) then |
frame.selectedAddon = "" |
else |
frame.selectedAddon = self.catText |
end |
frame.selectedCategory = "" |
frame.selectedSubCat = "" |
elseif( self.type == "category" ) then |
if( frame.selectedCategory == self.catText ) then |
frame.selectedCategory = "" |
self.data = nil |
else |
frame.selectedCategory = self.catText |
end |
frame.selectedSubCat = "" |
elseif( self.type == "subcat" ) then |
if( frame.selectedSubCat == self.catText ) then |
frame.selectedSubCat = "" |
-- Make sure the frame gets hidden when deselecting |
self.data = addons[frame.selectedAddon].categories[frame.selectedCategory] |
else |
frame.selectedSubCat = self.catText |
end |
end |
openConfigFrame(self.data) |
updateConfigList() |
end |
local function createAddonFrame(hide) |
local frame = regFrames.addon |
if( frame and hide ) then |
frame:Hide() |
return |
elseif( hide ) then |
return |
elseif( not frame ) then |
frame = CreateFrame("Frame", nil, regFrames.main) |
frame:SetFrameStrata("DIALOG") |
frame:SetAllPoints(regFrames.main) |
regFrames.addon = frame |
frame.buttons = {} |
frame.lines = {} |
for i=1, 15 do |
local button = CreateFrame("Button", nil, frame) |
frame.buttons[i] = button |
button:SetHighlightFontObject(GameFontHighlightSmall) |
button:SetTextFontObject(GameFontNormalSmall) |
button:SetScript("OnClick", expandConfigList) |
button:SetScript("OnEnter", showTooltip) |
button:SetScript("OnLeave", hideTooltip) |
button:SetWidth(140) |
button:SetHeight(20) |
button:SetNormalTexture("Interface\\AuctionFrame\\UI-AuctionFrame-FilterBG") |
button:GetNormalTexture():SetTexCoord(0, 0.53125, 0, 0.625) |
button:SetHighlightTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight") |
button:GetHighlightTexture():SetBlendMode("ADD") |
-- For sub categories only |
local line = button:CreateTexture(nil, "BACKGROUND") |
frame.lines[i] = line |
line:SetWidth(7) |
line:SetHeight(20) |
line:SetPoint("LEFT", 13, 0) |
line:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-FilterLines") |
line:SetTexCoord(0, 0.4375, 0, 0.625) |
if( i > 1 ) then |
button:SetPoint("TOPLEFT", frame.buttons[i - 1], "BOTTOMLEFT", 0, 0) |
else |
button:SetPoint("TOPLEFT", 23, -105) |
end |
end |
createScrollFrame(frame, 15, updateConfigList) |
frame.scroll:SetPoint("TOPRIGHT", frame, "TOPLEFT", 158, -105) |
createSearchInput(frame, updateConfigList) |
createCategoryListing() |
end |
-- Reset selection |
frame.selectedAddon = "" |
frame.selectedCategory = "" |
frame.selectedSubCat = "" |
-- Hide the open config frame |
if( frame.shownFrame ) then |
frame.shownFrame:Hide() |
end |
updateConfigList() |
ShowUIPanel(frame) |
end |
local function createOHFrame() |
if( regFrames.main ) then |
return |
end |
frame = CreateFrame("Frame", nil, UIParent) |
frame:CreateTitleRegion() |
frame:SetClampedToScreen(true) |
frame:SetMovable(false) |
frame:SetFrameStrata("DIALOG") |
frame:SetWidth(832) |
frame:SetHeight(447) |
frame:SetPoint("TOPLEFT", 0, -104) |
frame.totalTabs = 0 |
frame.tabs = {} |
regFrames.main = frame |
-- If we don't hide it ourself, the panel layout becomes messed up |
-- because dynamically created frames are created shown |
frame:Hide() |
frame:SetScript("OnHide", function() |
if( openedByMenu ) then |
openedByMenu = nil |
PlaySound("gsTitleOptionExit"); |
ShowUIPanel(GameMenuFrame) |
end |
end) |
frame:SetScript("OnShow", function() |
if( OptionHouseDB and OptionHouseDB.position ) then |
frame:ClearAllPoints() |
frame:SetPoint("TOPLEFT", nil, "BOTTOMLEFT", OptionHouseDB.position.x, OptionHouseDB.position.y) |
end |
end) |
frame:SetAttribute("UIPanelLayout-defined", true) |
frame:SetAttribute("UIPanelLayout-enabled", true) |
frame:SetAttribute("UIPanelLayout-area", "doublewide") |
frame:SetAttribute("UIPanelLayout-whileDead", true) |
table.insert(UISpecialFrames, name) |
local title = frame:GetTitleRegion() |
title:SetWidth(757) |
title:SetHeight(20) |
title:SetPoint("TOPLEFT", 75, -15) |
-- Embedded version wont include the icon cause authors are more whiny then users |
-- Also, we want to use different methods of frame dragging |
if( not IsAddOnLoaded("OptionHouse") ) then |
local texture = frame:CreateTexture(nil, "OVERLAY") |
texture:SetWidth(57) |
texture:SetHeight(57) |
texture:SetPoint("TOPLEFT", 9, -7) |
SetPortraitTexture(texture, "player") |
frame:EnableMouse(true) |
else |
local texture = frame:CreateTexture(nil, "OVERLAY") |
texture:SetWidth(128) |
texture:SetHeight(128) |
texture:SetPoint("TOPLEFT", 9, -2) |
texture:SetTexture("Interface\\AddOns\\OptionHouse\\GnomePortrait") |
frame:EnableMouse(false) |
frame:SetMovable(not OptionHouseDB.locked) |
-- This goes in the entire bar where "OptionHouse" title text is |
local mover = CreateFrame("Button", nil, frame) |
mover:SetPoint("TOP", 25, -15) |
mover:SetHeight(19) |
mover:SetWidth(730) |
mover:SetScript("OnLeave", hideTooltip) |
mover:SetScript("OnEnter", showTooltip) |
mover:SetScript("OnMouseUp", function(self) |
if( self.isMoving ) then |
local parent = self:GetParent() |
parent:StopMovingOrSizing() |
OptionHouseDB.position = {x = parent:GetLeft(), y = parent:GetTop()} |
self.isMoving = nil |
end |
end) |
mover:SetScript("OnMouseDown", function(self, mouse) |
local parent = self:GetParent() |
-- Start moving! |
if( parent:IsMovable() and mouse == "LeftButton" ) then |
self.isMoving = true |
parent:StartMoving() |
-- Reset position |
elseif( mouse == "RightButton" ) then |
parent:ClearAllPoints() |
parent:SetPoint("TOPLEFT", 0, -104) |
OptionHouseDB.position = nil |
end |
end) |
end |
local title = frame:CreateFontString(nil, "OVERLAY") |
title:SetFontObject(GameFontNormal) |
title:SetPoint("TOP", 0, -18) |
title:SetText(L["OPTION_HOUSE"]) |
frame.topLeft = frame:CreateTexture(nil, "ARTWORK") |
frame.topLeft:SetWidth(256) |
frame.topLeft:SetHeight(256) |
frame.topLeft:SetPoint("TOPLEFT", 0, 0) |
frame.top = frame:CreateTexture(nil, "ARTWORK") |
frame.top:SetWidth(320) |
frame.top:SetHeight(256) |
frame.top:SetPoint("TOPLEFT", 256, 0) |
frame.topRight = frame:CreateTexture(nil, "ARTWORK") |
frame.topRight:SetWidth(256) |
frame.topRight:SetHeight(256) |
frame.topRight:SetPoint("TOPLEFT", frame.top, "TOPRIGHT", 0, 0) |
frame.bottomLeft = frame:CreateTexture(nil, "ARTWORK") |
frame.bottomLeft:SetWidth(256) |
frame.bottomLeft:SetHeight(256) |
frame.bottomLeft:SetPoint("TOPLEFT", 0, -256) |
frame.bottom = frame:CreateTexture(nil, "ARTWORK") |
frame.bottom:SetWidth(320) |
frame.bottom:SetHeight(256) |
frame.bottom:SetPoint("TOPLEFT", 256, -256) |
frame.bottomRight = frame:CreateTexture(nil, "ARTWORK") |
frame.bottomRight:SetWidth(256) |
frame.bottomRight:SetHeight(256) |
frame.bottomRight:SetPoint("TOPLEFT", frame.bottom, "TOPRIGHT", 0, 0) |
-- Make sure the configuration tab is first |
local tabs = {{func = createAddonFrame, text = L["ADDON_OPTIONS"], type = "browse"}} |
createTab(L["ADDON_OPTIONS"], 1) |
for id, tab in pairs(tabfunctions) do |
table.insert(tabs, tab) |
createTab(tab.text, id + 1) |
end |
tabfunctions = tabs |
local button = CreateFrame("Button", nil, frame, "UIPanelCloseButton") |
button:SetPoint("TOPRIGHT", 3, -8) |
button:SetScript("OnClick", function() |
HideUIPanel(frame) |
end) |
end |
-- PRIVATE API's |
-- While these aren't locked down to prevent being used |
-- You ARE using them are your own risk for future compatability |
function OptionHouse:CreateSearchInput(frame, onChange) |
createSearchInput(frame, onChange) |
end |
function OptionHouse:UpdateScroll(scroll, totalRows) |
updateScroll(scroll, totalRows) |
end |
function OptionHouse:CreateScrollFrame(frame, displayNum, onScroll) |
createScrollFrame(frame, displayNum, onScroll) |
end |
function OptionHouse.RegisterTab(self, text, func, type) |
-- Simple, effective you can't register a tab unless we list it here |
-- I highly doubt will ever need to add another one |
if( text ~= L["TAB_MANAGEMENT"] and text ~= L["TAB_PERFORMANCE"] ) then return end |
table.insert(tabfunctions, {func = func, handler = self, text = text, type = type}) |
-- Will create all of the tabs when the frame is created if needed |
if( not frame ) then |
return |
end |
createTab(text, #(tabfunctions)) |
end |
function OptionHouse.UnregisterTab(self, text) |
for i=#(tabfunctions), 1, -1 do |
if( tabfunctions[i].text == text ) then |
table.remove(tabfunctions, i) |
end |
end |
for i=1, frame.totalTabs do |
if( tabfunctions[i] ) then |
createTab(tabfunctions[i].text, i) |
else |
frame.tabs[i]:Hide() |
end |
end |
end |
function OptionHouse.GetAddOnData(self, name) |
if( not addons[name] ) then |
return nil, nil, nil |
end |
return addons[name].title, addons[name].author, addons[name].version |
end |
function OptionHouse.RegisterFrame(self, type, frame) |
if( type ~= "addon" and type ~= "manage" and type ~= "perf" and type ~= "main" ) then |
error(string.format(L["UNKNOWN_FRAMETYPE"], type), 3) |
end |
regFrames[type] = frame |
end |
-- PUBLIC API's |
function OptionHouse:GetFrame(type) |
if( type ~= "addon" and type ~= "manage" and type ~= "perf" and type ~= "main" ) then |
error(string.format(L["UNKNOWN_FRAMETYPE"], type), 3) |
end |
return regFrames[type] |
end |
function OptionHouse:Open(addonName, parentCat, childCat) |
if( InCombatLockdown() ) then |
DEFAULT_CHAT_FRAME:AddMessage(L["IN_COMBAT"]) |
return |
end |
argcheck(addonName, 1, "string", "nil") |
argcheck(parentCat, 2, "string", "nil") |
argcheck(childCat, 3, "string", "nil") |
createOHFrame() |
tabOnClick(1) |
if( not addonName ) then |
ShowUIPanel(frame) |
return |
end |
regFrames.addon.selectedAddon = addonName or "" |
regFrames.addon.selectedCategory = parentCat or "" |
regFrames.addon.selectedSubCat = childCat or "" |
updateConfigList(true) |
ShowUIPanel(frame) |
end |
function OptionHouse:OpenTab(id) |
if( InCombatLockdown() ) then |
DEFAULT_CHAT_FRAME:AddMessage(L["IN_COMBAT"]) |
return |
end |
argcheck(id, 1, "number") |
createOHFrame() |
assert(3, #(tabfunctions) > id, string.format(L["UNKNOWN_TAB"], id, #(tabfunctions))) |
tabOnClick(id) |
ShowUIPanel(frame) |
end |
function OptionHouse:RegisterAddOn(name, title, author, version) |
argcheck(name, 1, "string") |
argcheck(title, 2, "string", "nil") |
argcheck(author, 3, "string", "nil") |
argcheck(version, 4, "string", "number", "nil") |
assert(3, not addons[name], string.format(L["ADDON_ALREADYREG"], name)) |
addons[name] = {title = title, author = author, version = version, totalCats = 0, totalSubs = 0, categories = {}} |
addons[name].obj = {name = name} |
-- So we can upgrade the function pointer if a newer rev is found |
for id, method in pairs(methods) do |
addons[name].obj[method] = OptionHouse[method] |
end |
if( regFrames.addon ) then |
addCategoryListing(name, addons[name]) |
updateConfigList() |
end |
return addons[name].obj |
end |
function OptionHouse.RegisterCategory(addon, name, handler, func, noCache, sortID) |
argcheck(name, 2, "string") |
argcheck(handler, 3, "string", "function", "table") |
argcheck(func, 4, "string", "function", "nil") |
argcheck(noCache, 5, "boolean", "number", "nil") |
argcheck(sortID, 6, "number", "nil") |
assert(3, handler or func, L["NO_FUNC_PASSED"]) |
assert(3, addons[addon.name], string.format(L["MUST_CALL"], "RegisterCategory")) |
assert(3, addons[addon.name].categories, string.format(L["CATEGORY_ALREADYREG"], name, addon.name)) |
-- Category numbers are required so we know when to skip it because only one category/sub cat exists |
addons[addon.name].totalCats = addons[addon.name].totalCats + 1 |
addons[addon.name].categories[name] = {func = func, handler = handler, noCache = noCache, sub = {}, totalSubs = 0, sortID = sortID or 9999999} |
if( regFrames.addon ) then |
addCategoryListing(addon.name, addons[addon.name]) |
updateConfigList() |
end |
end |
function OptionHouse.RegisterSubCategory(addon, parentCat, name, handler, func, noCache, sortID) |
argcheck(parentCat, 2, "string") |
argcheck(name, 3, "string") |
argcheck(handler, 4, "string", "function", "table") |
argcheck(func, 5, "string", "function", "nil") |
argcheck(noCache, 6, "boolean", "number", "nil") |
argcheck(sortID, 7, "number", "nil") |
assert(3, handler or func, L["NO_FUNC_PASSED"]) |
assert(3, addons[addon.name], string.format(L["MUST_CALL"], "RegisterSubCategory")) |
assert(3, addons[addon.name].categories[parentCat], string.format(L["NO_PARENTCAT"], parentCat, addon.name)) |
assert(3, not addons[addon.name].categories[parentCat].sub[name], string.format(L["SUBCATEGORY_ALREADYREG"], name, parentCat, addon.name)) |
addons[addon.name].totalSubs = addons[addon.name].totalSubs + 1 |
addons[addon.name].categories[parentCat].totalSubs = addons[addon.name].categories[parentCat].totalSubs + 1 |
addons[addon.name].categories[parentCat].sub[name] = {handler = handler, func = func, noCache = noCache, sortID = sortID or 9999999} |
if( regFrames.addon ) then |
addCategoryListing(addon.name, addons[addon.name]) |
updateConfigList() |
end |
end |
function OptionHouse.RemoveCategory(addon, name) |
argcheck(name, 2, "string") |
assert(3, addons[addon.name], string.format(L["MUST_CALL"], "RemoveCategory")) |
assert(3, addons[addon.name].categories[name], string.format(L["NO_CATEGORYEXISTS"], name, addon.name)) |
addons[addon.name].totalCats = addons[addon.name].totalCats - 1 |
addons[addon.name].totalSubs = addons[addon.name].totalSubs - addons[addon.name].categories[name].totalSubs |
addons[addon.name].categories[name] = nil |
if( regFrames.addon ) then |
if( addons[addon.name].totalCats == 0 ) then |
removeAddonListing(addon.name) |
else |
removeCategoryListing(addon.name, name) |
end |
updateConfigList() |
end |
end |
function OptionHouse.RemoveSubCategory(addon, parentCat, name) |
argcheck(parentCat, 2, "string") |
argcheck(name, 2, "string") |
assert(3, addons[addon.name], string.format(L["MUST_CALL"], "RemoveSubCategory")) |
assert(3, addons[addon.name].categories[parentCat], string.format(L["NO_PARENTCAT"], name, addon.name)) |
assert(3, addons[addon.name].categories[parentCat].sub[name], string.format(L["NO_SUBCATEXISTS"], name, parentCat, addon.name)) |
addons[addon.name].totalSubs = addons[addon.name].totalSubs - 1 |
addons[addon.name].categories[parentCat].totalSubs = addons[addon.name].categories[parentCat].totalSubs - 1 |
addons[addon.name].categories[parentCat].sub[name] = nil |
if( regFrames.addon ) then |
-- If this means we only have no more sub categories |
-- and only one category we need to change how it works |
if( addons[addon.name].totalSubs == 0 and addons[addon.name].totalCats == 1 ) then |
removeAddonListing(addon.name) |
addCategoryListing(addon.name, addons[addon.name]) |
else |
removeSubCategoryListing(addon.name, parentCat, name) |
end |
updateConfigList() |
end |
end |
function OptionHouse:GetVersion() return major, minor end |
local function instanceLoaded() |
if( oldRevision ) then |
addons = OHInstance.addons or addons |
evtFrame = OHInstance.evtFrame or evtFrame |
tabfunctions = OHInstance.tabfunctions or tabfunctions |
else |
-- Secure headers are supported so don't want the window stuck open in combat |
evtFrame = CreateFrame("Frame") |
evtFrame:RegisterEvent("PLAYER_REGEN_DISABLED") |
evtFrame:RegisterEvent("ADDON_LOADED") |
evtFrame:SetScript("OnEvent",function(self, event) |
if( event == "PLAYER_REGEN_DISABLED" and frame and frame:IsShown() ) then |
HideUIPanel(frame) |
DEFAULT_CHAT_FRAME:AddMessage(L["ENTERED_COMBAT"]) |
end |
end) |
-- Make sure it hasn't been created already. |
-- don't have to upgrade the referance because it just uses the slash command |
-- which will upgrade below to use the current version anyway |
if( not GameMenuButtonOptionHouse ) then |
local menubutton = CreateFrame("Button", "GameMenuButtonOptionHouse", GameMenuFrame, "GameMenuButtonTemplate") |
menubutton:SetText(L["OPTION_HOUSE"]) |
menubutton:SetScript("OnClick", function() |
openedByMenu = true |
PlaySound("igMainMenuOption") |
HideUIPanel(GameMenuFrame) |
SlashCmdList["OPTHOUSE"]() |
end) |
-- Position below "Interface Options" |
local a1, fr, a2, x, y = GameMenuButtonKeybindings:GetPoint() |
menubutton:SetPoint(a1, fr, a2, x, y) |
GameMenuButtonKeybindings:SetPoint(a1, menubutton, a2, x, y) |
GameMenuFrame:SetHeight(GameMenuFrame:GetHeight() + 25) |
end |
end |
OptionHouse.addons = addons |
OptionHouse.evtFrame = evtFrame |
OptionHouse.tabfunctions = tabfunctions |
-- Upgrade functions to point towards the latest revision |
for name, addon in pairs(addons) do |
for _, method in pairs(methods) do |
addon.obj[method] = OptionHouse[method] |
end |
end |
SLASH_OPTHOUSE1 = "/opthouse" |
SLASH_OPTHOUSE2 = "/oh" |
SlashCmdList["OPTHOUSE"] = function(...) |
if( select(1, ...) == "" ) then |
OptionHouse:Open() |
else |
OptionHouse:Open(...) |
end |
end |
-- Now make it active |
for k, v in pairs(OptionHouse) do |
OHInstance[k] = v |
end |
end |
instanceLoaded() |
<Bindings> |
<Binding name="LTTOGGLECONFIG" header="LOOTTRACKER"> |
LootTracker:ToggleConfig() |
</Binding> |
<Binding name="LTTOGGLETRACK"> |
LootTracker:ToggleTracker() |
</Binding> |
</Bindings> |
## Interface: 20400 |
## Title: LootTracker |
## Version: 2.00-20400 |
## Notes: This provides a frame that will show you what loot you are tracking and how much you have of it. |
## DefaultState: Enabled |
## LoadOnDemand: 0 |
## SavedVariables: LootTrackerDB |
## OptionalDeps: ClickTip, Ace3 |
libs.xml |
localizations/LootTracker.enUS.lua |
localizations/LootTracker.deDE.lua |
LootTracker.lua |
local debug = false |
LootTrackerLocals = setmetatable({ |
['Add Item'] = 'Add Item', |
['Add Set'] = 'Add Set', |
['Advanced Tracker Options'] = 'Advance Tracker Options', |
['Background Alpha'] = 'Background Alpha', |
['Current toon count only'] = 'Current toon count only', |
['Del Item'] = 'Del Item', |
['Edit Item' ] = 'Edit Item', |
['Global Tracker Options'] = 'Global Tracker Options', |
['Hide if incomplete'] = 'Hide if incomplete', |
['Hide if no items available'] = 'Hide if no items available', |
['Hide in combat'] = 'Hide in combat', |
['Hint: Shift click to add to tracker'] = 'Hint: Shift click to add to tracker', |
['Hint: Shift click to remove from tracker'] = 'Hint: Shift click to remove from tracker', |
['Lock advanced tracker'] = 'Lock advanced tracker', |
['Lock Tracker'] = 'Lock Tracker', |
['LootTracker: Invalid item entered.'] = 'LootTracker: Invalid item entered.', |
['Remove Set'] = 'Remove Set', |
['Rename Set' ] = 'Rename Set', |
['Sets'] = 'Sets', |
['Show advanced tracker'] = 'Show advanced tracker', |
['Show normal tracker'] = 'Show normal tracker', |
['Squish numbers to name'] = 'Squish numbers to name', |
['Standard Tracker Options'] = 'Standard Tracker Options', |
['Toggle Config'] = 'Toggle Config', |
['Toggle Tracker'] = 'Toggle Tracker', |
['Use this checkbox to force item counts next to the item name'] = 'Use this checkbox to force item counts next to the item name', |
['Use this checkbox to hide the advanced tracker'] = 'Use this checkbox to hide the advanced tracker', |
['Use this checkbox to hide the item in the tracker if the goal has not been met'] = 'Use this checkbox to hide the item in the tracker if the goal has not been met', |
['Use this checkbox to hide the item in the tracker if there are no items available'] = 'Use this checkbox to hide the item in the tracker if there are no items available', |
['Use this checkbox to hide the normal tracker'] = 'Use this checkbox to hide the normal tracker', |
['Use this checkbox to hide the tracker while in combat'] = 'Use this checkbox to hide the tracker while in combat', |
['Use this checkbox to lock the tracker in its current position'] = 'Use this checkbox to lock the tracker in its current position', |
['Use this checkbox to only show item totals from current toon'] = 'Use this checkbox to only show item totals from current toon', |
['Use this checkbox to separate item counts from the item name'] = 'Use this checkbox to separate item counts from the item name', |
['Use this checkbox to show item totals from all toons'] = 'Use this checkbox to show item totals from all toons', |
['Use this checkbox to show the advanced tracker'] = 'Use this checkbox to show the advanced tracker', |
['Use this checkbox to show the item in the tracker even if the goal has not been met'] = 'Use this checkbox to show the item in the tracker even if the goal has not been met', |
['Use this checkbox to show the item in the tracker even if there are no items available'] = 'Use this checkbox to show the item in the tracker even if there are no items available', |
['Use this checkbox to show the normal tracker'] = 'Use this checkbox to show the normal tracker', |
['Use this checkbox to show the tracker while in combat'] = 'Use this checkbox to show the tracker while in combat', |
['Use this checkbox to unlock the tracker'] = 'Use this checkbox to unlock the tracker', |
['Use this slider to change the transparency of the background for the tracker'] = 'Use this slider to change the transparency of the background for the tracker', |
['Visuals'] = 'Visuals', |
}, {__index = function(self, key) |
rawset(self, key, key) |
if debug then |
ChatFrame1:AddMessage("Missing "..tostring(key)) |
end |
return key |
end}) |