WoWInterface SVN AuctionStats

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 1 to Rev 2
    Reverse comparison

Rev 1 → Rev 2

AuctionStats.lua New file
0,0 → 1,732
AuctionStats = LibStub("AceAddon-3.0"):NewAddon("AuctionStats", "AceEvent-3.0")
 
local L = AuctionStatLocals
 
local MAX_DATE_ROWS = 23
local MAX_DATA_ROWS = 19
local MAX_DATA_COLUMNS = 4
 
local monthTable = {day = 1, hour = 0}
 
-- Total data per day/month including the item statistics related to that
local auctionData = {}
 
-- Quick sortable tables we can use
local auctionDisplay = {}
 
function AuctionStats:OnInitialize()
self.defaults = {
profile = {
},
}
self.db = LibStub:GetLibrary("AceDB-3.0"):New("AuctionStatsDB", self.defaults)
 
-- No data found, default to current char
if( not self.db.profile.gatherData ) then
self.db.profile.gatherData = {[string.format("%s:%s", GetRealmName(), UnitName("player"))] = true}
end
 
SLASH_AUCTIONSTATS1 = "/auctionstats"
SLASH_AUCTIONSTATS2 = "/as"
SlashCmdList["AUCTIONSTATS"] = function(msg)
AuctionStats:CreateGUI()
AuctionStats.frame:Show()
end
end
 
local function sortTime(a, b)
return a < b
end
 
local function getTime(currentTime, day, hour)
monthTable.year = date("%Y", currentTime)
monthTable.month = date("%m", currentTime)
monthTable.day = day or date("%d", currentTime)
monthTable.hour = hour or 0
 
return time(monthTable)
end
 
local function hideTooltip()
GameTooltip:Hide()
end
 
local function showTooltip(self)
if( self.tooltip ) then
if( not self.button or not self.button:IsVisible() ) then
GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
else
GameTooltip:SetOwner(self.button, "ANCHOR_TOPLEFT")
end
 
GameTooltip:SetText(self.tooltip, nil, nil, nil, nil, 1)
end
end
 
local function monthToggle(self)
AuctionStats.frame.dateKey = nil
 
 
if( AuctionStats.frame.monthKey == self.monthKey ) then
AuctionStats.frame.monthKey = nil
else
AuctionStats.frame.dateKey = self.monthKey
AuctionStats.frame.monthKey = self.monthKey
end
 
AuctionStats:UpdateBrowseGUI()
AuctionStats:ViewBreakdown()
end
 
local function showDisplayGUI(self)
local month = getTime(self.dateKey, 1, 0)
if( AuctionStats.frame.dateKey == self.dateKey ) then
if( self.dateKey ~= month ) then
AuctionStats.frame.dateKey = month
AuctionStats.frame.monthKey = month
else
AuctionStats.frame.dateKey = nil
AuctionStats.frame.monthKey = nil
end
 
else
AuctionStats.frame.dateKey = self.dateKey
AuctionStats.frame.monthKey = month
AuctionStats.frame.resortList = true
end
 
AuctionStats:UpdateBrowseGUI()
AuctionStats:ViewBreakdown()
end
 
-- Quick merge function to add the data from one table to the main one
local mergeKeys = {"totalSold", "totalMade", "totalDeposit", "totalFee", "totalSpent", "totalBought"}
local function mergeDataTables(to, from)
for _, key in pairs(mergeKeys) do
if( from[key] ) then
to[key] = (to[key] or 0) + from[key]
end
 
end
end
 
function AuctionStats:ParseData()
auctionData = {}
for k in pairs(auctionDisplay) do auctionDisplay[k] = nil end
 
for charID in pairs(self.db.profile.gatherData) do
local server, name = string.split(":", charID)
 
-- Make sure this server/character has data
if( BeanCounterDB[server] and BeanCounterDB[server][name] ) then
local auctionDB = BeanCounterDB[server][name]
 
-- Loop through items we've succesfully bought out, or bid and won
for itemid, rows in pairs(auctionDB["completedBids/Buyouts"]) do
itemid = tonumber(itemid)
for uniqueID, itemData in pairs(rows) do
-- Annd loop through each transaction for this item
for _, line in pairs(itemData) do
local quantity, _, _, buyout, bid, buyer, arrivedAt = string.split(";", line)
local time = getTime(arrivedAt, nil, 10)
 
if( not auctionData[time] ) then
auctionData[time] = {type = "day", time = time, temp = {}}
end
 
if( not auctionData[time].temp[itemid] ) then
auctionData[time].temp[itemid] = { totalSold = 0, totalProfit = 0, totalMade = 0, totalDeposit = 0, totalFee = 0, totalSpent = 0, totalBought = 0 }
end
 
auctionData[time].temp[itemid].time = time
auctionData[time].temp[itemid].itemid = itemid
auctionData[time].temp[itemid].totalBought = auctionData[time].temp[itemid].totalBought + 1
 
-- If the buyout is 0 then we won it off of bid
buyout = tonumber(buyout)
if( buyout > 0 ) then
auctionData[time].temp[itemid].totalSpent = auctionData[time].temp[itemid].totalSpent + buyout
else
auctionData[time].temp[itemid].totalSpent = auctionData[time].temp[itemid].totalSpent + bid
end
end
end
end
 
-- Loop through items we've succesfully sold
for itemid, rows in pairs(auctionDB["completedAuctions"]) do
itemid = tonumber(itemid)
for uniqueID, itemData in pairs(rows) do
-- Loop through each item transaction
for _, line in pairs(itemData) do
local quantity, money, deposit, fee, buyout, bid, buyer, arrivedAt = string.split(";", line)
local time = getTime(arrivedAt, nil, 10)
if( not auctionData[time] ) then
auctionData[time] = {type = "day", time = time, temp = {}}
end
 
if( not auctionData[time].temp[itemid] ) then
auctionData[time].temp[itemid] = { totalSold = 0, totalProfit = 0, totalMade = 0, totalDeposit = 0, totalFee = 0, totalSpent = 0, totalBought = 0 }
end
 
 
auctionData[time].temp[itemid].time = time
auctionData[time].temp[itemid].itemid = itemid
auctionData[time].temp[itemid].totalSold = auctionData[time].temp[itemid].totalSold + 1
auctionData[time].temp[itemid].totalMade = auctionData[time].temp[itemid].totalMade + money
auctionData[time].temp[itemid].totalDeposit = auctionData[time].temp[itemid].totalDeposit + deposit
auctionData[time].temp[itemid].totalFee = auctionData[time].temp[itemid].totalFee + fee
end
end
end
end
end
 
-- Now make a summary based on month
for time, row in pairs(auctionData) do
if( row.type ~= "month" ) then
local month = getTime(time, 1, 0)
if( not auctionData[month] ) then
auctionData[month] = {type = "month", time = month, temp = {}}
end
 
for itemid, data in pairs(row.temp) do
if( not auctionData[month].temp[itemid] ) then
auctionData[month].temp[itemid] = {}
end
 
-- Merge the items total stats, this months total stats, and this days total stats
mergeDataTables(auctionData[month].temp[itemid], data)
end
end
end
 
-- Now we have to take our item tables, and turn them into indexed ones
for time, row in pairs(auctionData) do
row.totalProfit = 0
row.items = {}
 
-- Add some final info, and add it into an indexed table
for itemid, data in pairs(row.temp) do
data.time = time
data.itemid = itemid
data.itemLink = select(2, GetItemInfo(itemid)) or itemid
data.itemName = select(1, GetItemInfo(itemid)) or itemid
data.totalProfit = data.totalMade - data.totalSpent
 
row.totalProfit = row.totalProfit + data.totalProfit
 
table.insert(row.items, data)
mergeDataTables(auctionData[time], data)
end
 
-- Remove our temp table for gathering the data
row.temp = nil
end
 
-- Add in the time formats so we can actually do our display things
for time in pairs(auctionData) do
table.insert(auctionDisplay, time)
end
 
table.sort(auctionDisplay, sortTime)
end
 
-- This is quickly hacked together, I'll clean it up later
function AuctionStats:FormatNumber(number, decimal)
-- Quick "rounding"
if( decimal and math.floor(number) ~= number ) then
number = string.format("%.1f", number)
else
number = math.floor(number + 0.5)
end
 
while( true ) do
number, k = string.gsub(number, "^(-?%d+)(%d%d%d)", "%1,%2")
if( k == 0 ) then break end
 
end
 
return number
end
 
function AuctionStats:UpdateBrowseGUI()
local self = AuctionStats
local totalRows = 0
for id, key in pairs(auctionDisplay) do
local month = getTime(key, 1, 0)
if( self.frame.monthKey == month or month == key ) then
totalRows = totalRows + 1
end
end
 
FauxScrollFrame_Update(self.leftFrame.scroll, totalRows, MAX_DATE_ROWS - 1, 22)
 
-- Hide everything to reset it
for i=1, MAX_DATE_ROWS do
self.dateRows[i].button:Hide()
self.dateRows[i]:Hide()
end
 
-- List!
local usedRows = 0
for id, key in pairs(auctionDisplay) do
local month = getTime(key, 1, 0)
if( ( self.frame.monthKey == month or month == key ) and id >= FauxScrollFrame_GetOffset(self.leftFrame.scroll) and usedRows < MAX_DATE_ROWS ) then
usedRows = usedRows + 1
 
local color
local data = auctionData[key]
if( data.totalProfit < 0 ) then
color = RED_FONT_COLOR_CODE
elseif( data.totalProfit > 0 ) then
color = GREEN_FONT_COLOR_CODE
else
color = "|cffffffff"
end
 
local row = self.dateRows[usedRows]
row.profit:SetFormattedText("[%s%s%sg]", color, self:FormatNumber(data.totalProfit / 10000), FONT_COLOR_CODE_CLOSE)
row.tooltip = string.format(L["Made: |cffffffff%s|rg\nSpent: |cffffffff%s|rg\nProfit: |cffffffff%s|rg"], self:FormatNumber(data.totalMade / 10000), self:FormatNumber(data.totalSpent / 10000), self:FormatNumber(data.totalProfit / 10000))
row.dateKey = data.time
row.type = data.type
row:Show()
 
-- If it's a day, show day, # if it isn't show month, year
if( data.type == "day" ) then
row:SetText(date("%A, %d", data.time))
else
row:SetText(date("%b %Y", data.time))
end
 
-- Highlight
if( self.frame.dateKey == data.time ) then
row:SetTextColor(1, 0.81, 0)
else
row:SetTextColor(1, 1, 1)
end
 
-- Pushy
if( data.type == "month" ) then
row.button.monthKey = getTime(row.dateKey, 1, 0)
 
if( self.frame.monthKey == data.time ) then
row.button:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP")
row.button:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN")
row.button:SetHighlightTexture("Interface\\Buttons\\UI-MinusButton-Hilight", "ADD")
else
row.button:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP")
row.button:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN")
row.button:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight", "ADD")
end
end
 
-- Reposition things based on the type, months have to be positioned over the button of course
if( data.type == "month" ) then
row:SetPoint("TOPLEFT", row.button, "TOPRIGHT", 0, 0)
row.profit:SetPoint("TOPRIGHT", row, "TOPRIGHT", -15, -1)
row.button:Show()
else
if( usedRows > 1 ) then
if( self.dateRows[usedRows - 1].type == "month" ) then
row:SetPoint("TOPLEFT", self.dateRows[usedRows - 1].button, "BOTTOMLEFT", 0, -1)
else
row:SetPoint("TOPLEFT", self.dateRows[usedRows - 1], "BOTTOMLEFT", 0, -1)
end
else
row:SetPoint("TOPLEFT", self.leftFrame.scroll, "TOPLEFT", 4, 0)
end
 
row.profit:SetPoint("TOPRIGHT", row, "TOPRIGHT", -1, -1)
row.button:Hide()
end
 
-- Adjust width if no scroll
if( #(auctionDisplay) < MAX_DATE_ROWS ) then
self.dateRows[usedRows]:SetWidth(168)
else
self.dateRows[usedRows]:SetWidth(149)
end
end
end
end
 
local function sortBreakdownData(self)
if( self.sortType ~= AuctionStats.frame.sortType ) then
AuctionStats.frame.sortOrder = false
AuctionStats.frame.sortType = self.sortType
else
AuctionStats.frame.sortOrder = not AuctionStats.frame.sortOrder
end
 
AuctionStats.frame.resortList = true
AuctionStats:ViewBreakdown()
end
 
local function sortItemData(a, b)
local sortBy = AuctionStats.frame.sortType
if( AuctionStats.frame.sortOrder ) then
return a[sortBy] < b[sortBy]
else
return a[sortBy] > b[sortBy]
 
end
end
 
function AuctionStats:ViewBreakdown()
local self = AuctionStats
 
 
-- No data, or bad data
if( not self.frame.dateKey or not auctionData[self.frame.dateKey] ) then
for i=1, MAX_DATA_ROWS do
for j=1, MAX_DATA_COLUMNS do
self.rows[i][j]:Hide()
end
end
 
 
FauxScrollFrame_Update(self.middleFrame.scroll, 0, MAX_DATA_ROWS - 1, 22)
return
end
 
FauxScrollFrame_Update(self.middleFrame.scroll, #(auctionData[self.frame.dateKey].items), MAX_DATA_ROWS - 1, 22)
 
if( self.frame.resortList ) then
table.sort(auctionData[self.frame.dateKey].items, sortItemData)
self.frame.resortList = nil
end
 
-- Hide everything to reset it
for i=1, MAX_DATA_ROWS do
for j=1, MAX_DATA_COLUMNS do
self.rows[i][j]:Hide()
end
end
 
-- List!
local usedRows = 0
for id, data in pairs(auctionData[self.frame.dateKey].items) do
if( id >= FauxScrollFrame_GetOffset(self.middleFrame.scroll) and usedRows < MAX_DATA_ROWS ) then
usedRows = usedRows + 1
 
local row = self.rows[usedRows]
 
row[1]:SetText(data.itemLink)
row[1].tooltip = data.itemLink
 
row[2]:SetFormattedText("%s|cffffffffg|r", self:FormatNumber(data.totalMade / 10000))
row[2]:SetTextColor(0, 1, 0)
if( data.totalSold > 0 ) then
row[2].tooltip = string.format(L["Auctions Completed: %d\nAverage Price: |cffffffff%s|rg"], data.totalSold, self:FormatNumber((data.totalMade / data.totalSold) / 10000, true))
else
row[2].tooltip = string.format(L["Auctions Completed: %d\nAverage Price: |cffffffff%s|rg"], 0, 0)
end
 
row[3]:SetFormattedText("%s|cffffffffg|r", self:FormatNumber(data.totalSpent / 10000))
row[3]:SetTextColor(1, 0, 0)
if( data.totalBought > 0 ) then
row[3].tooltip = string.format(L["Auctions Won: %d\nAverage Price: |cffffffff%s|rg"], data.totalBought, self:FormatNumber((data.totalSpent / data.totalBought) / 10000, true))
else
row[3].tooltip = string.format(L["Auctions Won: %d\nAverage Price: |cffffffff%s|rg"], 0, 0)
end
 
row[4]:SetFormattedText("%s|cffffffffg|r", self:FormatNumber(data.totalProfit / 10000))
 
if( data.totalProfit < 0 ) then
row[4]:SetTextColor(1, 0, 0)
elseif( data.totalProfit > 0 ) then
row[4]:SetTextColor(0, 1, 0)
else
row[4]:SetTextColor(1, 1, 1)
end
 
for j=1, MAX_DATA_COLUMNS do
self.rows[usedRows][j]:Show()
end
end
end
end
 
-- GUI Creation
function AuctionStats:CreateGUI()
if( self.frame ) then
return
end
 
self.frame = CreateFrame("Frame", "AuctionStatsGUI", UIParent)
self.frame:SetWidth(550)
self.frame:SetHeight(400)
self.frame:SetMovable(true)
self.frame:EnableMouse(true)
self.frame:SetClampedToScreen(true)
self.frame:SetBackdrop({
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
edgeSize = 26,
insets = {left = 9, right = 9, top = 9, bottom = 9},
})
self.frame:SetScript("OnShow", function(self)
AuctionStats:ParseData()
 
-- Show the last days info if we have nothing selected
if( not self.dateKey ) then
self.dateKey = auctionDisplay[#(auctionDisplay)]
self.monthKey = getTime(self.dateKey, 1, 0)
AuctionStats:ViewBreakdown()
end
 
AuctionStats:UpdateBrowseGUI()
end)
 
self.frame.sortOrder = false
self.frame.sortType = "totalProfit"
self.frame.resortList = true
self.frame:Hide()
 
-- Make it act like a real frame
self.frame:SetAttribute("UIPanelLayout-defined", true)
self.frame:SetAttribute("UIPanelLayout-enabled", true)
self.frame:SetAttribute("UIPanelLayout-area", "doublewide")
self.frame:SetAttribute("UIPanelLayout-whileDead", true)
table.insert(UISpecialFrames, "AuctionStatsGUI")
 
-- Create the title/movy thing
local texture = self.frame:CreateTexture(nil, "ARTWORK")
texture:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
texture:SetPoint("TOP", 0, 12)
texture:SetWidth(250)
texture:SetHeight(60)
 
local title = CreateFrame("Button", nil, self.frame)
title:SetPoint("TOP", 0, 4)
title:SetText(L["Auction Stats"])
title:SetPushedTextOffset(0, 0)
 
title:SetTextFontObject(GameFontNormal)
title:SetHeight(20)
title:SetWidth(200)
title:RegisterForDrag("LeftButton")
title:SetScript("OnDragStart", function(self)
self.isMoving = true
AuctionStats.frame:StartMoving()
end)
 
title:SetScript("OnDragStop", function(self)
if( self.isMoving ) then
self.isMoving = nil
AuctionStats.frame:StopMovingOrSizing()
end
end)
 
-- Close button, this needs more work not too happy with how it looks
local button = CreateFrame("Button", nil, self.frame, "UIPanelCloseButton")
button:SetHeight(27)
button:SetWidth(27)
button:SetPoint("TOPRIGHT", -1, -1)
button:SetScript("OnClick", function()
HideUIPanel(AuctionStats.frame)
end)
 
-- Container frame backdrop
local backdrop = {
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true,
tileSize = 16,
edgeSize = 16,
insets = { left = 3, right = 3, top = 5, bottom = 3 }
}
 
-- Left 30%ish width panel
self.leftFrame = CreateFrame("Frame", nil, self.frame)
self.leftFrame:SetWidth(175)
self.leftFrame:SetHeight(368)
self.leftFrame:SetBackdrop(backdrop)
self.leftFrame:SetBackdropColor(0, 0, 0, 0.65)
self.leftFrame:SetBackdropBorderColor(0.75, 0.75, 0.75, 0.90)
self.leftFrame:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 12, -20)
 
-- Date scroll frame
self.leftFrame.scroll = CreateFrame("ScrollFrame", "AuctionStatsGUIScrollLeft", self.frame, "FauxScrollFrameTemplate")
self.leftFrame.scroll:SetPoint("TOPLEFT", self.leftFrame, "TOPLEFT", 0, -30)
self.leftFrame.scroll:SetPoint("BOTTOMRIGHT", self.leftFrame, "BOTTOMRIGHT", -26, 3)
self.leftFrame.scroll:SetScript("OnVerticalScroll", function() FauxScrollFrame_OnVerticalScroll(22, self.UpdateBrowseGUI) end)
 
-- Create the date listing for the scroll frame
self.dateRows = {}
 
for i=1, MAX_DATE_ROWS do
local row = CreateFrame("Button", nil, self.frame)
row:SetWidth(149)
row:SetHeight(14)
row:SetTextFontObject(GameFontHighlightSmall)
row:SetText("*")
row:GetFontString():SetPoint("LEFT", row, "LEFT", 0, 0)
row:SetScript("OnClick", showDisplayGUI)
row:SetScript("OnEnter", showTooltip)
row:SetScript("OnLeave", hideTooltip)
 
row.profit = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
row.profit:SetText("*")
row.profit:SetPoint("TOPRIGHT", row, "TOPRIGHT", -1, -1)
 
row.button = CreateFrame("Button", nil, self.frame)
row.button:SetScript("OnClick", monthToggle)
row.button:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP")
row.button:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN")
row.button:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight", "ADD")
row.button:SetHeight(14)
row.button:SetWidth(14)
 
if( i > 1 ) then
row.button:SetPoint("TOPLEFT", self.dateRows[i - 1], "BOTTOMLEFT", 0, -1)
else
row.button:SetPoint("TOPLEFT", self.leftFrame.scroll, "TOPLEFT", 4, 0)
end
 
self.dateRows[i] = row
end
 
-- Check data
self.gather = CreateFrame("Frame", "AuctionStatsGUIGatherDropdown", self.frame, "UIDropDownMenuTemplate")
self.gather:SetPoint("TOPLEFT", self.leftFrame, "TOPLEFT", -13, -2)
self.gather:SetScript("OnEnter", showTooltip)
self.gather:SetScript("OnLeave", hideTooltip)
self.gather.tooltip = L["Choose which characters data should be gathered from, from BeanCounter."],
self.gather:EnableMouse(true)
self.gather:SetScript("OnShow", function(self)
UIDropDownMenu_Initialize(self, AuctionStats.InitGatherDropdown)
UIDropDownMenu_SetWidth(150, self)
end)
 
-- Top frame, around 70% width panel
self.topFrame = CreateFrame("Frame", nil, self.frame)
self.topFrame:SetWidth(352)
self.topFrame:SetHeight(40)
self.topFrame:SetBackdrop(backdrop)
self.topFrame:SetBackdropColor(0, 0, 0, 0.65)
self.topFrame:SetBackdropBorderColor(0.75, 0.75, 0.75, 0.90)
self.topFrame:SetPoint("TOPLEFT", self.leftFrame, "TOPRIGHT", 0, 0)
 
-- Middle-ish frame, remaining space
self.middleFrame = CreateFrame("Frame", nil, self.frame)
self.middleFrame:SetWidth(352)
self.middleFrame:SetHeight(328)
self.middleFrame:SetBackdrop(backdrop)
self.middleFrame:SetBackdropColor(0, 0, 0, 0.65)
self.middleFrame:SetBackdropBorderColor(0.75, 0.75, 0.75, 0.90)
self.middleFrame:SetPoint("TOPLEFT", self.topFrame, "BOTTOMLEFT", 0, 0)
 
-- Date scroll frame
self.middleFrame.scroll = CreateFrame("ScrollFrame", "AuctionStatsGUIScrollMiddle", self.frame, "FauxScrollFrameTemplate")
self.middleFrame.scroll:SetPoint("TOPLEFT", self.middleFrame, "TOPLEFT", 0, -4)
self.middleFrame.scroll:SetPoint("BOTTOMRIGHT", self.middleFrame, "BOTTOMRIGHT", -26, 3)
self.middleFrame.scroll:SetScript("OnVerticalScroll", function() FauxScrollFrame_OnVerticalScroll(22, self.ViewBreakdown) end)
 
-- Sort tabs
self.sortButtons = {}
 
for i=1, MAX_DATA_COLUMNS do
local button = CreateFrame("Button", nil, self.frame)
button:SetScript("OnClick", sortBreakdownData)
button:SetHeight(20)
button:SetTextFontObject(GameFontNormalSmall)
button:Show()
 
self.sortButtons[i] = button
end
 
 
self.sortButtons[1].sortType = "itemName"
self.sortButtons[1]:SetText(L["Item"])
self.sortButtons[1]:SetWidth(self.sortButtons[1]:GetFontString():GetStringWidth() + 3)
self.sortButtons[1]:SetPoint("TOPLEFT", self.middleFrame, "TOPLEFT", 4, -2)
 
self.sortButtons[2].sortType = "totalMade"
self.sortButtons[2]:SetText(L["Made"])
self.sortButtons[2]:SetWidth(self.sortButtons[2]:GetFontString():GetStringWidth() + 3)
self.sortButtons[2]:SetPoint("TOPLEFT", self.sortButtons[1], "TOPRIGHT", 150, 0)
 
self.sortButtons[3].sortType = "totalSpent"
self.sortButtons[3]:SetText(L["Spent"])
self.sortButtons[3]:SetWidth(self.sortButtons[3]:GetFontString():GetStringWidth() + 3)
self.sortButtons[3]:SetPoint("TOPLEFT", self.sortButtons[2], "TOPRIGHT", 15, 0)
 
self.sortButtons[4].sortType = "totalProfit"
self.sortButtons[4]:SetText(L["Profit"])
self.sortButtons[4]:SetWidth(self.sortButtons[4]:GetFontString():GetStringWidth() + 3)
self.sortButtons[4]:SetPoint("TOPLEFT", self.sortButtons[3], "TOPRIGHT", 15, 0)
 
-- Rows
self.rows = {}
 
for i=1, MAX_DATA_ROWS do
self.rows[i] = {}
for j=1, MAX_DATA_COLUMNS do
local row = CreateFrame("Button", nil, self.frame)
row:SetHeight(15)
row:SetScript("OnEnter", showTooltip)
row:SetScript("OnLeave", hideTooltip)
row:SetTextFontObject(GameFontHighlightSmall)
row:SetPushedTextOffset(0, 0)
row:SetText("*")
row.fs = row:GetFontString()
row.fs:SetPoint("LEFT", row, "LEFT", 0, 0)
row.fs:SetHeight(15)
row.fs:SetJustifyH("LEFT")
row:Hide()
 
self.rows[i][j] = row
 
if( j > 1 ) then
row:SetWidth(50)
else
row:SetWidth(180)
end
 
 
if( i > 1 ) then
row:SetPoint("TOPLEFT", self.rows[i - 1][j], "BOTTOMLEFT", 0, -1)
else
row:SetPoint("TOPLEFT", self.sortButtons[j], "BOTTOMLEFT", 1, 1)
end
end
end
 
-- Positioning
self.frame:SetPoint("CENTER")
end
 
function AuctionStats:DropdownClicked()
local server, name = string.split(":", this.value)
 
UIDropDownMenu_SetText(string.format("%s - %s", name, server), AuctionStats.gather)
AuctionStats.db.profile.gatherData[string.format("%s:%s", server,name)] = this.checked and true or nil
end
 
function AuctionStats:InitGatherDropdown()
for server, charData in pairs(BeanCounterDB) do
if( server ~= "settings" ) then
for charName in pairs(charData) do
local charID = string.format("%s:%s", server, charName)
UIDropDownMenu_AddButton({ value = charID, text = string.format("%s - %s", charName, server), checked = AuctionStats.db.profile.gatherData[charID], keepShownOnClick = true, func = AuctionStats.DropdownClicked })
end
 
end
end
 
UIDropDownMenu_SetText(string.format("%s - %s", UnitName("player"), GetRealmName()), AuctionStats.gather)
end
 
-- DEBUG
--[[
local frame = CreateFrame("Frame")
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:SetScript("OnEvent", function()
AuctionStats:CreateGUI()
AuctionStats.frame:Show()
 
frame:UnregisterAllEvents()
end)
]]
\ No newline at end of file
localization.enUS.lua New file
0,0 → 1,16
AuctionStatLocals = {
["Auction Stats"] = "Auction Stats",
["Choose which characters data should be gathered from, from BeanCounter."] = "Choose which characters data should be gathered from, from BeanCounter.",
 
["Made: |cffffffff%s|rg\nSpent: |cffffffff%s|rg\nProfit: |cffffffff%s|rg"] = "Made: |cffffffff%s|rg\nSpent: |cffffffff%s|rg\nProfit: |cffffffff%s|rg",
 
["Auctions Completed: %d\nAverage Price: |cffffffff%s|rg"] = "Auctions Completed: %d\nAverage Price: |cffffffff%s|rg",
["Auctions Won: %d\nAverage Price: |cffffffff%s|rg"] = "Auctions Won: %d\nAverage Price: |cffffffff%s|rg",
 
["Item"] = "Item",
["Made"] = "Made",
["Spent"] = "Spent",
["Profit"] = "Profit",
["Avg Made"] = "Avg Made",
["Avg Spent"] = "Avg Spent",
}
\ No newline at end of file
AuctionStats.toc New file
0,0 → 1,18
## Interface: 23000
## Title: Auction Stats
## Notes: Plugin to BeanCounter that shows you more statistical information on your auctions
## SavedVariables: AuctionStatsDB
## Version: $Revision: 322 $
## Author: Mayen
## RequiredDeps: BeanCounter
 
libs\LibStub-1.0\LibStub-1.0.xml
libs\CallbackHandler-1.0\CallbackHandler-1.0.xml
libs\AceAddon-3.0\AceAddon-3.0.xml
libs\AceEvent-3.0\AceEvent-3.0.xml
 
localization.enUS.lua
localization.deDE.lua
localization.frFR.lua
 
AuctionStats.lua
localization.frFR.lua New file
0,0 → 1,7
if( GetLocale() ~= "frFR" ) then
return
end
 
AuctionStatLocalss = setmetatable({
}, {__index = AuctionStatLocalss})
 
localization.deDE.lua New file
0,0 → 1,6
if( GetLocale() ~= "deDE" ) then
return
end
 
AuctionStatLocalss = setmetatable({
}, {__index = AuctionStatLocalss})
\ No newline at end of file
libs/LibStub-1.0/LibStub-1.0.xml New file
0,0 → 1,4
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="LibStub-1.0.lua"/>
</Ui>
\ No newline at end of file
libs/LibStub-1.0/LibStub-1.0.lua New file
0,0 → 1,30
-- 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]
 
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
LibStub = LibStub or {libs = {}, minors = {} }
_G[LIBSTUB_MAJOR] = LibStub
LibStub.minor = LIBSTUB_MINOR
 
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
 
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
 
function LibStub:IterateLibraries() return pairs(self.libs) end
setmetatable(LibStub, { __call = LibStub.GetLibrary })
end
libs/CallbackHandler-1.0/CallbackHandler-1.0.xml New file
0,0 → 1,4
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="CallbackHandler-1.0.lua"/>
</Ui>
\ No newline at end of file
libs/CallbackHandler-1.0/CallbackHandler-1.0.lua New file
0,0 → 1,237
--[[ $Id: CallbackHandler-1.0.lua 60131 2008-02-03 13:03:56Z nevcairiel $ ]]
local MAJOR, MINOR = "CallbackHandler-1.0", 2
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
 
if not CallbackHandler then return end -- No upgrade needed
 
local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end}
 
local type = type
local pcall = pcall
local pairs = pairs
local assert = assert
local concat = table.concat
local loadstring = loadstring
local next = next
local select = select
local type = type
local xpcall = xpcall
 
local function errorhandler(err)
return geterrorhandler()(err)
end
 
local function CreateDispatcher(argCount)
local code = [[
local next, xpcall, eh = ...
 
local method, ARGS
local function call() method(ARGS) end
 
local function dispatch(handlers, ...)
local index
index, method = next(handlers)
if not method then return end
local OLD_ARGS = ARGS
ARGS = ...
repeat
xpcall(call, eh)
index, method = next(handlers, index)
until not method
ARGS = OLD_ARGS
end
 
return dispatch
]]
 
local ARGS, OLD_ARGS = {}, {}
for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end
code = code:gsub("OLD_ARGS", concat(OLD_ARGS, ", ")):gsub("ARGS", concat(ARGS, ", "))
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler)
end
 
local Dispatchers = setmetatable({}, {__index=function(self, argCount)
local dispatcher = CreateDispatcher(argCount)
rawset(self, argCount, dispatcher)
return dispatcher
end})
 
--------------------------------------------------------------------------
-- CallbackHandler:New
--
-- target - target object to embed public APIs in
-- RegisterName - name of the callback registration API, default "RegisterCallback"
-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback"
-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API.
 
function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName, OnUsed, OnUnused)
-- TODO: Remove this after beta has gone out
assert(not OnUsed and not OnUnused, "ACE-80: OnUsed/OnUnused are deprecated. Callbacks are now done to registry.OnUsed and registry.OnUnused")
 
RegisterName = RegisterName or "RegisterCallback"
UnregisterName = UnregisterName or "UnregisterCallback"
if UnregisterAllName==nil then -- false is used to indicate "don't want this method"
UnregisterAllName = "UnregisterAllCallbacks"
end
 
-- we declare all objects and exported APIs inside this closure to quickly gain access
-- to e.g. function names, the "target" parameter, etc
 
 
-- Create the registry object
local events = setmetatable({}, meta)
local registry = { recurse=0, events=events }
 
-- registry:Fire() - fires the given event/message into the registry
function registry:Fire(eventname, ...)
local oldrecurse = registry.recurse
registry.recurse = oldrecurse + 1
 
Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...)
 
registry.recurse = oldrecurse
 
if registry.insertQueue and oldrecurse==0 then
-- Something in one of our callbacks wanted to register more callbacks; they got queued
for eventname,callbacks in pairs(registry.insertQueue) do
for self,func in pairs(callbacks) do
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
events[eventname][self] = func
-- fire OnUsed callback?
if first and registry.OnUsed then
registry.OnUsed(registry, target, eventname)
end
end
end
registry.insertQueue = nil
end
end
 
-- Registration of a callback, handles:
-- self["method"], leads to self["method"](self, ...)
-- self with function ref, leads to functionref(...)
-- "addonId" (instead of self) with function ref, leads to functionref(...)
-- all with an optional arg, which, if present, gets passed as first argument (after self if present)
target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]])
if type(eventname) ~= "string" then
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2)
end
 
method = method or eventname
 
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
 
if type(method) ~= "string" and type(method) ~= "function" then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2)
end
 
local regfunc
 
if type(method) == "string" then
-- self["method"] calling style
if type(self) ~= "table" then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2)
elseif self==target then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2)
elseif type(self[method]) ~= "function" then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2)
end
 
if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
local arg=select(1,...)
regfunc = function(...) self[method](self,arg,...) end
else
regfunc = function(...) self[method](self,...) end
end
else
-- function ref with self=object or self="addonId"
if type(self)~="table" and type(self)~="string" then
error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string expected.", 2)
end
 
if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
local arg=select(1,...)
regfunc = function(...) method(arg,...) end
else
regfunc = method
end
end
 
 
if events[eventname][self] or registry.recurse<1 then
-- if registry.recurse<1 then
-- we're overwriting an existing entry, or not currently recursing. just set it.
events[eventname][self] = regfunc
-- fire OnUsed callback?
if registry.OnUsed and first then
registry.OnUsed(registry, target, eventname)
end
else
-- we're currently processing a callback in this registry, so delay the registration of this new entry!
-- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency
registry.insertQueue = registry.insertQueue or setmetatable({},meta)
registry.insertQueue[eventname][self] = regfunc
end
end
 
-- Unregister a callback
target[UnregisterName] = function(self, eventname)
if not self or self==target then
error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2)
end
if type(eventname) ~= "string" then
error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2)
end
if rawget(events, eventname) and events[eventname][self] then
events[eventname][self] = nil
-- Fire OnUnused callback?
if registry.OnUnused and not next(events[eventname]) then
registry.OnUnused(registry, target, eventname)
end
end
if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then
registry.insertQueue[eventname][self] = nil
end
end
 
-- OPTIONAL: Unregister all callbacks for given selfs/addonIds
if UnregisterAllName then
target[UnregisterAllName] = function(...)
if select("#",...)<1 then
error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2)
end
if select("#",...)==1 and ...==target then
error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2)
end
 
 
for i=1,select("#",...) do
local self = select(i,...)
if registry.insertQueue then
for eventname, callbacks in pairs(registry.insertQueue) do
if callbacks[self] then
callbacks[self] = nil
end
end
end
for eventname, callbacks in pairs(events) do
if callbacks[self] then
callbacks[self] = nil
-- Fire OnUnused callback?
if registry.OnUnused and not next(callbacks) then
registry.OnUnused(registry, target, eventname)
end
end
end
end
end
end
 
return registry
end
 
 
-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it
-- try to upgrade old implicit embeds since the system is selfcontained and
-- relies on closures to work.
 
libs/update.bat New file
0,0 → 1,5
svn export http://dev.wowace.com/wowace/trunk/Ace3/AceDB-3.0 --force
svn export http://dev.wowace.com/wowace/trunk/Ace3/AceAddon-3.0 --force
svn export http://dev.wowace.com/wowace/trunk/Ace3/AceEvent-3.0 --force
svn export http://dev.wowace.com/wowace/trunk/Ace3/CallbackHandler-1.0 --force
@PAUSE
\ No newline at end of file
libs/AceEvent-3.0/AceEvent-3.0.lua New file
0,0 → 1,76
--[[ $Id: AceEvent-3.0.lua 60131 2008-02-03 13:03:56Z nevcairiel $ ]]
local MAJOR, MINOR = "AceEvent-3.0", 3
local AceEvent = LibStub:NewLibrary(MAJOR, MINOR)
 
if not AceEvent then return end
 
local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
 
 
AceEvent.frame = AceEvent.frame or CreateFrame("Frame", "AceEvent30Frame") -- our event frame
AceEvent.embeds = AceEvent.embeds or {} -- what objects embed this lib
 
 
-- APIs and registry for blizzard events, using CallbackHandler lib
if not AceEvent.events then
AceEvent.events = CallbackHandler:New(AceEvent,
"RegisterEvent", "UnregisterEvent", "UnregisterAllEvents")
end
 
function AceEvent.events:OnUsed(target, eventname)
AceEvent.frame:RegisterEvent(eventname)
end
 
function AceEvent.events:OnUnused(target, eventname)
AceEvent.frame:UnregisterEvent(eventname)
end
 
 
-- APIs and registry for IPC messages, using CallbackHandler lib
if not AceEvent.messages then
AceEvent.messages = CallbackHandler:New(AceEvent,
"RegisterMessage", "UnregisterMessage", "UnregisterAllMessages"
)
AceEvent.SendMessage = AceEvent.messages.Fire
end
 
--- embedding and embed handling
local mixins = {
"RegisterEvent", "UnregisterEvent",
"RegisterMessage", "UnregisterMessage",
"SendMessage",
"UnregisterAllEvents", "UnregisterAllMessages",
}
 
-- AceEvent:Embed( target )
-- target (object) - target object to embed AceEvent in
--
-- Embeds AceEvent into the target object making the functions from the mixins list available on target:..
function AceEvent:Embed(target)
for k, v in pairs(mixins) do
target[v] = self[v]
end
self.embeds[target] = true
return target
end
 
-- AceEvent:OnEmbedDisable( target )
-- target (object) - target object that is being disabled
--
-- Unregister all events messages etc when the target disables.
-- this method should be called by the target manually or by an addon framework
function AceEvent:OnEmbedDisable(target)
target:UnregisterAllEvents()
target:UnregisterAllMessages()
end
 
-- Script to fire blizzard events into the event listeners
local events = AceEvent.events
AceEvent.frame:SetScript("OnEvent", function(this, event, ...)
events:Fire(event, ...)
end)
 
--- Finally: upgrade our old embeds
for target, v in pairs(AceEvent.embeds) do
AceEvent:Embed(target)
end
libs/AceEvent-3.0/AceEvent-3.0.xml New file
0,0 → 1,4
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="AceEvent-3.0.lua"/>
</Ui>
\ No newline at end of file
libs/AceAddon-3.0/AceAddon-3.0.xml New file
0,0 → 1,4
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="AceAddon-3.0.lua"/>
</Ui>
\ No newline at end of file
libs/AceAddon-3.0/AceAddon-3.0.lua New file
0,0 → 1,404
--[[ $Id: AceAddon-3.0.lua 60131 2008-02-03 13:03:56Z nevcairiel $ ]]
local MAJOR, MINOR = "AceAddon-3.0", 1
local AceAddon, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
 
if not AceAddon then return end -- No Upgrade needed.
 
AceAddon.frame = AceAddon.frame or CreateFrame("Frame", "AceAddon30Frame") -- Our very own frame
AceAddon.addons = AceAddon.addons or {} -- addons in general
AceAddon.statuses = AceAddon.statuses or {} -- statuses of addon.
AceAddon.initializequeue = AceAddon.initializequeue or {} -- addons that are new and not initialized
AceAddon.enablequeue = AceAddon.enablequeue or {} -- addons that are initialized and waiting to be enabled
AceAddon.embeds = AceAddon.embeds or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end }) -- contains a list of libraries embedded in an addon
 
local tinsert, tconcat = table.insert, table.concat
local fmt = string.format
local pairs, next, type = pairs, next, type
 
--[[
xpcall safecall implementation
]]
local xpcall = xpcall
 
local function errorhandler(err)
return geterrorhandler()(err)
end
 
local function CreateDispatcher(argCount)
local code = [[
local xpcall, eh = ...
local method, ARGS
local function call() return method(ARGS) end
 
local function dispatch(func, ...)
method = func
if not method then return end
ARGS = ...
return xpcall(call, eh)
end
 
return dispatch
]]
 
local ARGS = {}
for i = 1, argCount do ARGS[i] = "arg"..i end
code = code:gsub("ARGS", tconcat(ARGS, ", "))
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
end
 
local Dispatchers = setmetatable({}, {__index=function(self, argCount)
local dispatcher = CreateDispatcher(argCount)
rawset(self, argCount, dispatcher)
return dispatcher
end})
Dispatchers[0] = function(func)
return xpcall(func, errorhandler)
end
 
local function safecall(func, ...)
-- we check to see if the func is passed is actually a function here and don't error when it isn't
-- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
-- present execution should continue without hinderance
if type(func) == "function" then
return Dispatchers[select('#', ...)](func, ...)
end
end
 
-- local functions that will be implemented further down
local Enable, Disable, EnableModule, DisableModule, Embed, NewModule, GetModule, SetDefaultModuleState, SetDefaultModuleLibraries, SetEnabledState, SetDefaultModulePrototype
 
-- used in the addon metatable
local function addontostring( self ) return self.name end
 
-- AceAddon:NewAddon( name, [lib, lib, lib, ...] )
-- name (string) - unique addon object name
-- [lib] (string) - optional libs to embed in the addon object
--
-- returns the addon object when succesful
function AceAddon:NewAddon(name, ...)
if type(name) ~= "string" then error(("Usage: NewAddon(name, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) end
 
if self.addons[name] then error(("Usage: NewAddon(name, [lib, lib, lib, ...]): 'name' - Addon '%s' already exists."):format(name), 2) end
 
local addon = setmetatable( {name = name}, { __tostring = addontostring } )
self.addons[name] = addon
addon.modules = {}
addon.defaultModuleLibraries = {}
Embed( addon ) -- embed NewModule, GetModule methods
self:EmbedLibraries(addon, ...)
 
-- add to queue of addons to be initialized upon ADDON_LOADED
tinsert(self.initializequeue, addon)
return addon
end
 
-- AceAddon:GetAddon( name, [silent])
-- name (string) - unique addon object name
-- silent (boolean) - if true, addon is optional, silently return nil if its not found
--
-- throws an error if the addon object can not be found (except silent is set)
-- returns the addon object if found
function AceAddon:GetAddon(name, silent)
if not silent and not self.addons[name] then
error(("Usage: GetAddon(name): 'name' - Cannot find an AceAddon '%s'."):format(tostring(name)), 2)
end
return self.addons[name]
end
 
-- AceAddon:EmbedLibraries( addon, [lib, lib, lib, ...] )
-- addon (object) - addon to embed the libs in
-- [lib] (string) - optional libs to embed
function AceAddon:EmbedLibraries(addon, ...)
for i=1,select("#", ... ) do
local libname = select(i, ...)
self:EmbedLibrary(addon, libname, false, 3)
end
end
 
-- AceAddon:EmbedLibrary( addon, libname, silent, offset )
-- addon (object) - addon to embed the libs in
-- libname (string) - lib to embed
-- [silent] (boolean) - optional, marks an embed to fail silently if the library doesn't exist.
-- [offset] (number) - will push the error messages back to said offset defaults to 2
function AceAddon:EmbedLibrary(addon, libname, silent, offset)
local lib = LibStub:GetLibrary(libname, true)
if not lib and not silent then
error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Cannot find a library instance of %q."):format(tostring(libname)), offset or 2)
elseif lib and type(lib.Embed) == "function" then
lib:Embed(addon)
tinsert(self.embeds[addon], libname)
return true
elseif lib then
error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Library '%s' is not Embed capable"):format(libname), offset or 2)
end
end
 
-- addon:GetModule( name, [silent])
-- name (string) - unique module object name
-- silent (boolean) - if true, module is optional, silently return nil if its not found
--
-- throws an error if the addon object can not be found (except silent is set)
-- returns the module object if found
function GetModule(self, name, silent)
if not self.modules[name] and not silent then
error(("Usage: GetModule(name, silent): 'name' - Cannot find module '%s'."):format(tostring(name)), 2)
end
return self.modules[name]
end
 
local function IsModuleTrue(self) return true end
 
-- addon:NewModule( name, [prototype, [lib, lib, lib, ...] )
-- name (string) - unique module object name for this addon
-- prototype (object) - object to derive this module from, methods and values from this table will be mixed into the module, if a string is passed a lib is assumed
-- [lib] (string) - optional libs to embed in the addon object
--
-- returns the addon object when succesful
function NewModule(self, name, prototype, ...)
if type(name) ~= "string" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) end
if type(prototype) ~= "string" and type(prototype) ~= "table" and type(prototype) ~= "nil" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'prototype' - table (prototype), string (lib) or nil expected got '%s'."):format(type(prototype)), 2) end
 
if self.modules[name] then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - Module '%s' already exists."):format(name), 2) end
 
-- modules are basically addons. We treat them as such. They will be added to the initializequeue properly as well.
-- NewModule can only be called after the parent addon is present thus the modules will be initialized after their parent is.
local module = AceAddon:NewAddon(fmt("%s_%s", self.name or tostring(self), name))
 
module.IsModule = IsModuleTrue
module:SetEnabledState(self.defaultModuleState)
 
if type(prototype) == "string" then
AceAddon:EmbedLibraries(module, prototype, ...)
else
AceAddon:EmbedLibraries(module, ...)
end
AceAddon:EmbedLibraries(module, unpack(self.defaultModuleLibraries))
 
if not prototype or type(prototype) == "string" then
prototype = self.defaultModulePrototype or nil
end
 
if type(prototype) == "table" then
local mt = getmetatable(module)
mt.__index = prototype
setmetatable(module, mt) -- More of a Base class type feel.
end
 
safecall(self.OnModuleCreated, self, module) -- Was in Ace2 and I think it could be a cool thing to have handy.
self.modules[name] = module
 
return module
end
 
--addon:Enable()
-- Enables the Addon if possible, return true or false depending on success
function Enable(self)
self:SetEnabledState(true)
return AceAddon:EnableAddon(self)
end
 
--addon:Disable()
-- Disables the Addon if possible, return true or false depending on success
function Disable(self)
self:SetEnabledState(false)
return AceAddon:DisableAddon(self)
end
 
-- addon:EnableModule( name )
-- name (string) - unique module object name
--
-- Enables the Module if possible, return true or false depending on success
function EnableModule(self, name)
local module = self:GetModule( name )
return module:Enable()
end
 
-- addon:DisableModule( name )
-- name (string) - unique module object name
--
-- Disables the Module if possible, return true or false depending on success
function DisableModule(self, name)
local module = self:GetModule( name )
return module:Disable()
end
 
-- addon:SetDefaultModuleLibraries( [lib, lib, lib, ...] )
-- [lib] (string) - libs to embed in every module
function SetDefaultModuleLibraries(self, ...)
if next(self.modules) then
error("Usage: SetDefaultModuleLibraries(...): cannot change the module defaults after a module has been registered.", 2)
end
self.defaultModuleLibraries = {...}
end
 
-- addon:SetDefaultModuleState( state )
-- state (boolean) - default state for new modules (enabled=true, disabled=false)
function SetDefaultModuleState(self, state)
if next(self.modules) then
error("Usage: SetDefaultModuleState(state): cannot change the module defaults after a module has been registered.", 2)
end
self.defaultModuleState = state
end
 
-- addon:SetDefaultModulePrototype( prototype )
-- prototype (string or table) - the default prototype to use if none is specified on module creation
function SetDefaultModulePrototype(self, prototype)
if next(self.modules) then
error("Usage: SetDefaultModulePrototype(prototype): cannot change the module defaults after a module has been registered.", 2)
end
if type(prototype) ~= "table" then
error(("Usage: SetDefaultModulePrototype(prototype): 'prototype' - table expected got '%s'."):format(type(prototype)), 2)
end
self.defaultModulePrototype = prototype
end
 
-- addon:SetEnabledState ( state )
-- state ( boolean ) - set the state of an addon or module (enabled=true, disabled=false)
--
-- should only be called before any Enabling actually happend, aka in OnInitialize
function SetEnabledState(self, state)
self.enabledState = state
end
 
 
local function IterateModules(self) return pairs(self.modules) end
local function IterateEmbeds(self) return pairs(AceAddon.embeds[self]) end
local function IsEnabled(self) return self.enabledState end
local mixins = {
NewModule = NewModule,
GetModule = GetModule,
Enable = Enable,
Disable = Disable,
EnableModule = EnableModule,
DisableModule = DisableModule,
IsEnabled = IsEnabled,
SetDefaultModuleLibraries = SetDefaultModuleLibraries,
SetDefaultModuleState = SetDefaultModuleState,
SetDefaultModulePrototype = SetDefaultModulePrototype,
SetEnabledState = SetEnabledState,
IterateModules = IterateModules,
IterateEmbeds = IterateEmbeds,
}
local function IsModule(self) return false end
local pmixins = {
defaultModuleState = true,
enabledState = true,
IsModule = IsModule,
}
-- Embed( target )
-- target (object) - target object to embed aceaddon in
--
-- this is a local function specifically since it's meant to be only called internally
function Embed(target)
for k, v in pairs(mixins) do
target[k] = v
end
for k, v in pairs(pmixins) do
target[k] = target[k] or v
end
end
 
 
-- AceAddon:IntializeAddon( addon )
-- addon (object) - addon to intialize
--
-- calls OnInitialize on the addon object if available
-- calls OnEmbedInitialize on embedded libs in the addon object if available
function AceAddon:InitializeAddon(addon)
safecall(addon.OnInitialize, addon)
 
local embeds = self.embeds[addon]
for i = 1, #embeds do
local lib = LibStub:GetLibrary(embeds[i], true)
if lib then safecall(lib.OnEmbedInitialize, lib, addon) end
end
 
-- we don't call InitializeAddon on modules specifically, this is handled
-- from the event handler and only done _once_
end
 
-- AceAddon:EnableAddon( addon )
-- addon (object) - addon to enable
--
-- calls OnEnable on the addon object if available
-- calls OnEmbedEnable on embedded libs in the addon object if available
function AceAddon:EnableAddon(addon)
if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end
if self.statuses[addon.name] or not addon.enabledState then return false end
-- TODO: handle 'first'? Or let addons do it on their own?
safecall(addon.OnEnable, addon)
local embeds = self.embeds[addon]
for i = 1, #embeds do
local lib = LibStub:GetLibrary(embeds[i], true)
if lib then safecall(lib.OnEmbedEnable, lib, addon) end
end
self.statuses[addon.name] = addon.enabledState
 
-- enable possible modules.
for name, module in pairs(addon.modules) do
self:EnableAddon(module)
end
 
return true
end
 
-- AceAddon:DisableAddon( addon )
-- addon (object|string) - addon to disable
--
-- calls OnDisable on the addon object if available
-- calls OnEmbedDisable on embedded libs in the addon object if available
function AceAddon:DisableAddon(addon)
if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end
if not self.statuses[addon.name] then return false end
safecall( addon.OnDisable, addon )
local embeds = self.embeds[addon]
for i = 1, #embeds do
local lib = LibStub:GetLibrary(embeds[i], true)
if lib then safecall(lib.OnEmbedDisable, lib, addon) end
end
self.statuses[addon.name] = addon.enabledState
 
-- disable possible modules.
for name, module in pairs(addon.modules) do
self:DisableAddon(module)
end
 
return true
end
 
--The next few funcs are just because no one should be reaching into the internal registries
--Thoughts?
function AceAddon:IterateAddons() return pairs(self.addons) end
function AceAddon:IterateEmbedsOnAddon(addon) return pairs(self.embeds[addon]) end
function AceAddon:IterateAddonStatus() return pairs(self.statuses) end
function AceAddon:IterateModulesOfAddon(addon) return pairs(addon.modules) end
 
-- Event Handling
local function onEvent(this, event, arg1)
if event == "ADDON_LOADED" or event == "PLAYER_LOGIN" then
for i = 1, #AceAddon.initializequeue do
local addon = AceAddon.initializequeue[i]
if event == "ADDON_LOADED" then addon.baseName = arg1 end
AceAddon.initializequeue[i] = nil
AceAddon:InitializeAddon(addon)
tinsert(AceAddon.enablequeue, addon)
end
 
if IsLoggedIn() then
for i = 1, #AceAddon.enablequeue do
local addon = AceAddon.enablequeue[i]
AceAddon.enablequeue[i] = nil
AceAddon:EnableAddon(addon)
end
end
end
end
 
AceAddon.frame:RegisterEvent("ADDON_LOADED")
AceAddon.frame:RegisterEvent("PLAYER_LOGIN")
AceAddon.frame:SetScript("OnEvent", onEvent)
 
-- upgrade embeded
for name, addon in pairs(AceAddon.addons) do
Embed(addon)
end
libs/AceDB-3.0/AceDB-3.0.xml New file
0,0 → 1,4
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="AceDB-3.0.lua"/>
</Ui>
\ No newline at end of file
libs/AceDB-3.0/AceDB-3.0.lua New file
0,0 → 1,626
--[[ $Id: AceDB-3.0.lua 69511 2008-04-13 10:10:53Z nevcairiel $ ]]
local ACEDB_MAJOR, ACEDB_MINOR = "AceDB-3.0", 7
local AceDB, oldminor = LibStub:NewLibrary(ACEDB_MAJOR, ACEDB_MINOR)
 
if not AceDB then return end -- No upgrade needed
 
local type = type
local pairs, next = pairs, next
local rawget, rawset = rawget, rawset
local setmetatable = setmetatable
 
AceDB.db_registry = AceDB.db_registry or {}
AceDB.frame = AceDB.frame or CreateFrame("Frame")
 
local CallbackHandler
local CallbackDummy = { Fire = function() end }
 
local DBObjectLib = {}
 
--[[-------------------------------------------------------------------------
AceDB Utility Functions
---------------------------------------------------------------------------]]
 
-- Simple shallow copy for copying defaults
local function copyTable(src, dest)
if type(dest) ~= "table" then dest = {} end
if type(src) == "table" then
for k,v in pairs(src) do
if type(v) == "table" then
-- try to index the key first so that the metatable creates the defaults, if set, and use that table
v = copyTable(v, dest[k])
end
dest[k] = v
end
end
return dest
end
 
-- Called to add defaults to a section of the database
--
-- When a ["*"] default section is indexed with a new key, a table is returned
-- and set in the host table. These tables must be cleaned up by removeDefaults
-- in order to ensure we don't write empty default tables.
local function copyDefaults(dest, src)
-- this happens if some value in the SV overwrites our default value with a non-table
--if type(dest) ~= "table" then return end
for k, v in pairs(src) do
if k == "*" or k == "**" then
if type(v) == "table" then
-- This is a metatable used for table defaults
local mt = {
-- This handles the lookup and creation of new subtables
__index = function(t,k)
if k == nil then return nil end
local tbl = {}
copyDefaults(tbl, v)
rawset(t, k, tbl)
return tbl
end,
}
setmetatable(dest, mt)
-- handle already existing tables in the SV
for dk, dv in pairs(dest) do
if not rawget(src, dk) and type(dv) == "table" then
copyDefaults(dv, v)
end
end
else
-- Values are not tables, so this is just a simple return
local mt = {__index = function(t,k) return k~=nil and v or nil end}
setmetatable(dest, mt)
end
elseif type(v) == "table" then
if not rawget(dest, k) then rawset(dest, k, {}) end
if type(dest[k]) == "table" then
copyDefaults(dest[k], v)
if src['**'] then
copyDefaults(dest[k], src['**'])
end
end
else
if rawget(dest, k) == nil then
rawset(dest, k, v)
end
end
end
end
 
-- Called to remove all defaults in the default table from the database
local function removeDefaults(db, defaults, blocker)
for k,v in pairs(defaults) do
if k == "*" or k == "**" then
if type(v) == "table" then
-- Loop through all the actual k,v pairs and remove
for key, value in pairs(db) do
if type(value) == "table" then
-- if the key was not explicitly specified in the defaults table, just strip everything from * and ** tables
if defaults[key] == nil then
removeDefaults(value, v)
-- if the table is empty afterwards, remove it
if not next(value) then
db[key] = nil
end
-- if it was specified, only strip ** content, but block values which were set in the key table
elseif k == "**" then
removeDefaults(value, v, defaults[key])
end
end
end
elseif k == "*" then
-- check for non-table default
for key, value in pairs(db) do
if defaults[key] == nil and v == value then
db[key] = nil
end
end
end
elseif type(v) == "table" and type(db[k]) == "table" then
-- if a blocker was set, dive into it, to allow multi-level defaults
removeDefaults(db[k], v, blocker and blocker[k])
if not next(db[k]) then
db[k] = nil
end
else
-- check if the current value matches the default, and that its not blocked by another defaults table
if db[k] == defaults[k] and (not blocker or blocker[k] == nil) then
db[k] = nil
end
end
end
-- remove all metatables from the db
setmetatable(db, nil)
end
 
-- This is called when a table section is first accessed, to set up the defaults
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
 
-- Metatable to handle the dynamic creation of sections and copying of sections.
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
-- Callback: OnNewProfile, database, newProfileKey
t.callbacks:Fire("OnNewProfile", t, 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 validateDefaults(defaults, keyTbl, offset)
if not defaults then return end
offset = offset or 0
for k in pairs(defaults) do
if not keyTbl[k] or k == "profiles" then
error(("Usage: AceDBObject:RegisterDefaults(defaults): '%s' is not a valid datatype."):format(k), 3 + offset)
end
end
end
 
local preserve_keys = {
["callbacks"] = true,
["RegisterCallback"] = true,
["UnregisterCallback"] = true,
["UnregisterAllCallbacks"] = true,
["children"] = true,
}
 
local realmKey = GetRealmName()
local charKey = UnitName("player") .. " - " .. realmKey
local _, classKey = UnitClass("player")
local _, raceKey = UnitRace("player")
local factionKey = UnitFactionGroup("player")
local factionrealmKey = factionKey .. " - " .. realmKey
-- Actual database initialization function
local function initdb(sv, defaults, defaultProfile, olddb, parent)
-- Generate the database keys for each section
 
-- 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[charKey] or defaultProfile or charKey
sv.profileKeys[charKey] = profileKey
 
-- This table contains keys that enable the dynamic creation
-- of each section of the table. The 'global' and 'profiles'
-- have a key of true, since they are handled in a special case
local keyTbl= {
["char"] = charKey,
["realm"] = realmKey,
["class"] = classKey,
["race"] = raceKey,
["faction"] = factionKey,
["factionrealm"] = factionrealmKey,
["profile"] = profileKey,
["global"] = true,
["profiles"] = true,
}
 
validateDefaults(defaults, keyTbl, 1)
 
-- This allows us to use this function to reset an entire database
-- Clear out the old database
if olddb then
for k,v in pairs(olddb) do if not preserve_keys[k] then olddb[k] = nil end end
end
 
-- Give this database the metatable so it initializes dynamically
local db = setmetatable(olddb or {}, dbmt)
 
if not rawget(db, "callbacks") then
-- try to load CallbackHandler-1.0 if it loaded after our library
if not CallbackHandler then CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0", true) end
db.callbacks = CallbackHandler and CallbackHandler:New(db) or CallbackDummy
end
 
-- Copy methods locally into the database object, to avoid hitting
-- the metatable when calling methods
 
if not parent then
for name, func in pairs(DBObjectLib) do
db[name] = func
end
else
-- hack this one in
db.RegisterDefaults = DBObjectLib.RegisterDefaults
db.ResetProfile = DBObjectLib.ResetProfile
end
 
-- Set some properties in the database object
db.profiles = sv.profiles
db.keys = keyTbl
db.sv = sv
--db.sv_name = name
db.defaults = defaults
db.parent = parent
 
-- store the DB in the registry
AceDB.db_registry[db] = true
 
return db
end
 
-- handle PLAYER_LOGOUT
-- strip all defaults from all databases
local function logoutHandler(frame, event)
if event == "PLAYER_LOGOUT" then
for db in pairs(AceDB.db_registry) do
db.callbacks:Fire("OnDatabaseShutdown", db)
for section, key in pairs(db.keys) do
if db.defaults and db.defaults[section] and rawget(db, section) then
removeDefaults(db[section], db.defaults[section])
end
end
end
end
end
 
AceDB.frame:RegisterEvent("PLAYER_LOGOUT")
AceDB.frame:SetScript("OnEvent", logoutHandler)
 
 
--[[-------------------------------------------------------------------------
AceDB Object Method Definitions
---------------------------------------------------------------------------]]
 
-- DBObject:RegisterDefaults(defaults)
-- defaults (table) - A table of defaults for this database
--
-- Sets the defaults table for the given database object by clearing any
-- that are currently set, and then setting the new defaults.
function DBObjectLib:RegisterDefaults(defaults)
if defaults and type(defaults) ~= "table" then
error("Usage: AceDBObject:RegisterDefaults(defaults): 'defaults' - table or nil expected.", 2)
end
 
validateDefaults(defaults, self.keys)
 
-- Remove any currently set defaults
if self.defaults then
for section,key in pairs(self.keys) do
if self.defaults[section] and rawget(self, section) then
removeDefaults(self[section], self.defaults[section])
end
end
end
 
-- Set the DBObject.defaults table
self.defaults = defaults
 
-- Copy in any defaults, only touching those sections already created
if defaults then
for section,key in pairs(self.keys) do
if defaults[section] and rawget(self, section) then
copyDefaults(self[section], defaults[section])
end
end
end
end
 
-- DBObject:SetProfile(name)
-- name (string) - The name of the profile to set as the current profile
--
-- Changes the profile of the database and all of it's namespaces to the
-- supplied named profile
function DBObjectLib:SetProfile(name)
if type(name) ~= "string" then
error("Usage: AceDBObject:SetProfile(name): 'name' - string expected.", 2)
end
 
-- changing to the same profile, dont do anything
if name == self.keys.profile then return end
 
local oldProfile = self.profile
local defaults = self.defaults and self.defaults.profile
 
if oldProfile and defaults then
-- Remove the defaults from the old profile
removeDefaults(oldProfile, defaults)
end
 
self.profile = nil
self.keys["profile"] = name
self.sv.profileKeys[charKey] = name
 
-- populate to child namespaces
if self.children then
for _, db in pairs(self.children) do
DBObjectLib.SetProfile(db, name)
end
end
 
-- Callback: OnProfileChanged, database, newProfileKey
self.callbacks:Fire("OnProfileChanged", self, name)
end
 
-- DBObject:GetProfiles(tbl)
-- tbl (table) - A table to store the profile names in (optional)
--
-- Returns a table with the names of the existing profiles in the database.
-- You can optionally supply a table to re-use for this purpose.
function DBObjectLib:GetProfiles(tbl)
if tbl and type(tbl) ~= "table" then
error("Usage: AceDBObject:GetProfiles(tbl): 'tbl' - table or nil expected.", 2)
end
 
-- Clear the container table
if tbl then
for k,v in pairs(tbl) do tbl[k] = nil end
else
tbl = {}
end
 
local curProfile = self.keys.profile
 
local i = 0
for profileKey in pairs(self.profiles) do
i = i + 1
tbl[i] = profileKey
if curProfile and profileKey == curProfile then curProfile = nil end
end
 
-- Add the current profile, if it hasn't been created yet
if curProfile then
i = i + 1
tbl[i] = curProfile
end
 
return tbl, i
end
 
-- DBObject:GetCurrentProfile()
--
-- Returns the current profile name used by the database
function DBObjectLib:GetCurrentProfile()
return self.keys.profile
end
 
-- DBObject:DeleteProfile(name)
-- name (string) - The name of the profile to be deleted
--
-- Deletes a named profile. This profile must not be the active profile.
function DBObjectLib:DeleteProfile(name, silent)
if type(name) ~= "string" then
error("Usage: AceDBObject:DeleteProfile(name): 'name' - string expected.", 2)
end
 
if self.keys.profile == name then
error("Cannot delete the active profile in an AceDBObject.", 2)
end
 
if not rawget(self.sv.profiles, name) and not silent then
error("Cannot delete profile '" .. name .. "'. It does not exist.", 2)
end
 
self.sv.profiles[name] = nil
 
-- populate to child namespaces
if self.children then
for _, db in pairs(self.children) do
DBObjectLib.DeleteProfile(db, name, true)
end
end
 
-- Callback: OnProfileDeleted, database, profileKey
self.callbacks:Fire("OnProfileDeleted", self, name)
end
 
-- DBObject:CopyProfile(name)
-- name (string) - The name of the profile to be copied into the current profile
--
-- Copies a named profile into the current profile, overwriting any conflicting
-- settings.
function DBObjectLib:CopyProfile(name, silent)
if type(name) ~= "string" then
error("Usage: AceDBObject:CopyProfile(name): 'name' - string expected.", 2)
end
 
if name == self.keys.profile then
error("Cannot have the same source and destination profiles.", 2)
end
 
if not rawget(self.sv.profiles, name) and not silent then
error("Cannot copy profile '" .. name .. "'. It does not exist.", 2)
end
 
-- Reset the profile before copying
DBObjectLib.ResetProfile(self)
 
local profile = self.profile
local source = self.sv.profiles[name]
 
copyTable(source, profile)
 
-- populate to child namespaces
if self.children then
for _, db in pairs(self.children) do
DBObjectLib.CopyProfile(db, name, true)
end
end
 
-- Callback: OnProfileCopied, database, sourceProfileKey
self.callbacks:Fire("OnProfileCopied", self, name)
end
 
-- DBObject:ResetProfile()
-- noChildren (boolean) - if set to true, the reset will not be populated to the child namespaces of this DB object
--
-- Resets the current profile
function DBObjectLib:ResetProfile(noChildren)
local profile = self.profile
 
for k,v in pairs(profile) do
profile[k] = nil
end
 
local defaults = self.defaults and self.defaults.profile
if defaults then
copyDefaults(profile, defaults)
end
 
-- populate to child namespaces
if self.children and not noChildren then
for _, db in pairs(self.children) do
DBObjectLib.ResetProfile(db)
end
end
 
-- Callback: OnProfileReset, database
self.callbacks:Fire("OnProfileReset", self)
end
 
-- DBObject:ResetDB(defaultProfile)
-- defaultProfile (string) - The profile name to use as the default
--
-- Resets the entire database, using the string defaultProfile as the default
-- profile.
function DBObjectLib:ResetDB(defaultProfile)
if defaultProfile and type(defaultProfile) ~= "string" then
error("Usage: AceDBObject:ResetDB(defaultProfile): 'defaultProfile' - string or nil expected.", 2)
end
 
local sv = self.sv
for k,v in pairs(sv) do
sv[k] = nil
end
 
local parent = self.parent
 
initdb(sv, self.defaults, defaultProfile, self)
 
-- fix the child namespaces
if self.children then
if not sv.namespaces then sv.namespaces = {} end
for name, db in pairs(self.children) do
if not sv.namespaces[name] then sv.namespaces[name] = {} end
initdb(sv.namespaces[name], db.defaults, self.keys.profile, db, self)
end
end
 
-- Callback: OnDatabaseReset, database
self.callbacks:Fire("OnDatabaseReset", self)
-- Callback: OnProfileChanged, database, profileKey
self.callbacks:Fire("OnProfileChanged", self, self.keys["profile"])
 
return self
end
 
-- DBObject:RegisterNamespace(name [, defaults])
-- name (string) - The name of the new namespace
-- defaults (table) - A table of values to use as defaults
--
-- Creates a new database namespace, directly tied to the database. This
-- is a full scale database in it's own rights other than the fact that
-- it cannot control its profile individually
function DBObjectLib:RegisterNamespace(name, defaults)
if type(name) ~= "string" then
error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - string expected.", 2)
end
if defaults and type(defaults) ~= "table" then
error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'defaults' - table or nil expected.", 2)
end
if self.children and self.children[name] then
error ("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - a namespace with that name already exists.", 2)
end
 
local sv = self.sv
if not sv.namespaces then sv.namespaces = {} end
if not sv.namespaces[name] then
sv.namespaces[name] = {}
end
 
local newDB = initdb(sv.namespaces[name], defaults, self.keys.profile, nil, self)
 
if not self.children then self.children = {} end
self.children[name] = newDB
return newDB
end
 
--[[-------------------------------------------------------------------------
AceDB Exposed Methods
---------------------------------------------------------------------------]]
 
-- AceDB:New(name, defaults, defaultProfile)
-- name (table or string) - The name of variable, or table to use for the database
-- defaults (table) - A table of database defaults
-- defaultProfile (string) - The name of the default profile
--
-- Creates a new database object that can be used to handle database settings
-- and profiles.
function AceDB:New(tbl, defaults, defaultProfile)
if type(tbl) == "string" then
local name = tbl
tbl = getglobal(name)
if not tbl then
tbl = {}
setglobal(name, tbl)
end
end
 
if type(tbl) ~= "table" then
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'tbl' - table expected.", 2)
end
 
if defaults and type(defaults) ~= "table" then
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaults' - table expected.", 2)
end
 
if defaultProfile and type(defaultProfile) ~= "string" then
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaultProfile' - string expected.", 2)
end
 
return initdb(tbl, defaults, defaultProfile)
end
 
-- upgrade existing databases
for db in pairs(AceDB.db_registry) do
if not db.parent then
for name,func in pairs(DBObjectLib) do
db[name] = func
end
else
db.RegisterDefaults = DBObjectLib.RegisterDefaults
end
end