From 416fff7a44d9c8605f37c149a7e6185d92427596 Mon Sep 17 00:00:00 2001 From: ckaotik Date: Fri, 13 Nov 2009 19:26:31 +0100 Subject: [PATCH 01/90] first commit --- Broker_Garbage.toc | 21 + Libs/CallbackHandler-1.0.lua | 239 ++++++++ Libs/LibDataBroker-1.1.lua | 50 ++ Libs/LibQTip-1.0.lua | 1052 ++++++++++++++++++++++++++++++++ Libs/LibStub.lua | 30 + Libs/tekKonfig/tekKonfig.xml | 11 + Libs/tekKonfig/tekKonfigAboutPanel.lua | 115 ++++ Libs/tekKonfig/tekKonfigButton.lua | 55 ++ Libs/tekKonfig/tekKonfigCheckbox.lua | 46 ++ Libs/tekKonfig/tekKonfigDropdown.lua | 84 +++ Libs/tekKonfig/tekKonfigGroup.lua | 31 + Libs/tekKonfig/tekKonfigHeading.lua | 24 + Libs/tekKonfig/tekKonfigSlider.lua | 86 +++ Libs/tekKonfig/tekKonfigTopTab.lua | 67 ++ core.lua | 484 +++++++++++++++ options.lua | 137 +++++ 16 files changed, 2532 insertions(+) create mode 100644 Broker_Garbage.toc create mode 100644 Libs/CallbackHandler-1.0.lua create mode 100644 Libs/LibDataBroker-1.1.lua create mode 100644 Libs/LibQTip-1.0.lua create mode 100644 Libs/LibStub.lua create mode 100644 Libs/tekKonfig/tekKonfig.xml create mode 100644 Libs/tekKonfig/tekKonfigAboutPanel.lua create mode 100644 Libs/tekKonfig/tekKonfigButton.lua create mode 100644 Libs/tekKonfig/tekKonfigCheckbox.lua create mode 100644 Libs/tekKonfig/tekKonfigDropdown.lua create mode 100644 Libs/tekKonfig/tekKonfigGroup.lua create mode 100644 Libs/tekKonfig/tekKonfigHeading.lua create mode 100644 Libs/tekKonfig/tekKonfigSlider.lua create mode 100644 Libs/tekKonfig/tekKonfigTopTab.lua create mode 100644 core.lua create mode 100644 options.lua diff --git a/Broker_Garbage.toc b/Broker_Garbage.toc new file mode 100644 index 0000000..2cb96f0 --- /dev/null +++ b/Broker_Garbage.toc @@ -0,0 +1,21 @@ +## Interface: 30200 +## Dependencies: +## OptionalDeps: Auctionator, AuctionLite, Auctioneer +## SavedVariables: BG_GlobalDB +## SavedVariablesPerCharacter: + +## Title: Broker_Garbage +## Notes: Full bags no more! Shows your least valuable item .. and destroys it! +## Author: ckaotik (Raisa@EU-Die Todeskrallen) +## Version: 3.2.2v1 +## X-Category: Inventory +## X-Credits: GarbageFu + +Libs\LibStub.lua +Libs\LibQTip-1.0.lua +Libs\CallbackHandler-1.0.lua +Libs\LibDataBroker-1.1.lua +Libs\tekKonfig\tekKonfig.xml + +core.lua +options.lua \ No newline at end of file diff --git a/Libs/CallbackHandler-1.0.lua b/Libs/CallbackHandler-1.0.lua new file mode 100644 index 0000000..5ad658f --- /dev/null +++ b/Libs/CallbackHandler-1.0.lua @@ -0,0 +1,239 @@ +--[[ $Id: CallbackHandler-1.0.lua 60548 2008-02-07 11:04:06Z nevcairiel $ ]] +local MAJOR, MINOR = "CallbackHandler-1.0", 3 +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, ...) + if not rawget(events, eventname) or not next(events[eventname]) then return end + 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 + 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. + for self,func in pairs(callbacks) do + events[eventname][self] = func + -- fire OnUsed callback? + if first and registry.OnUsed then + registry.OnUsed(registry, target, eventname) + first = nil + 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. + diff --git a/Libs/LibDataBroker-1.1.lua b/Libs/LibDataBroker-1.1.lua new file mode 100644 index 0000000..6b10124 --- /dev/null +++ b/Libs/LibDataBroker-1.1.lua @@ -0,0 +1,50 @@ + +assert(LibStub, "LibDataBroker-1.1 requires LibStub") +assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0") + +local lib = LibStub:NewLibrary("LibDataBroker-1.1", 1) +if not lib then return end + + +lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib) +lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {} +local attributestorage, namestorage, proxystorage = lib.attributestorage, lib.namestorage, lib.proxystorage + +local domt = { + __metatable = "access denied", + __newindex = function(self, key, value) + if not attributestorage[self] then attributestorage[self] = {} end + if attributestorage[self][key] == value then return end + attributestorage[self][key] = value + local name = namestorage[self] + if not name then return end + lib.callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value) + lib.callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value) + lib.callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value) + lib.callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value) + end, + __index = function(self, key) + return attributestorage[self] and attributestorage[self][key] + end, +} + +function lib:NewDataObject(name) + if proxystorage[name] then return end + + local dataobj = setmetatable({}, domt) + proxystorage[name], namestorage[dataobj] = dataobj, name + lib.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj) + return dataobj +end + +function lib:DataObjectIterator() + return pairs(proxystorage) +end + +function lib:GetDataObjectByName(dataobjectname) + return proxystorage[dataobjectname] +end + +function lib:GetNameByDataObject(dataobject) + return namestorage[dataobject] +end diff --git a/Libs/LibQTip-1.0.lua b/Libs/LibQTip-1.0.lua new file mode 100644 index 0000000..ed1ad56 --- /dev/null +++ b/Libs/LibQTip-1.0.lua @@ -0,0 +1,1052 @@ +local MAJOR = "LibQTip-1.0" +local MINOR = 29 -- Should be manually increased +assert(LibStub, MAJOR.." requires LibStub") + +local lib, oldminor = LibStub:NewLibrary(MAJOR, MINOR) +if not lib then return end -- No upgrade needed + +------------------------------------------------------------------------------ +-- Upvalued globals +------------------------------------------------------------------------------ +local type = type +local select = select +local error = error +local pairs, ipairs = pairs, ipairs +local tonumber, tostring = tonumber, tostring +local strfind = string.find +local math = math +local min, max = math.min, math.max +local setmetatable = setmetatable +local tinsert, tremove = tinsert, tremove +local wipe = wipe + +local CreateFrame = CreateFrame +local UIParent = UIParent +local MouseIsOver = MouseIsOver + +------------------------------------------------------------------------------ +-- Tables and locals +------------------------------------------------------------------------------ +lib.frameMetatable = lib.frameMetatable or {__index = CreateFrame("Frame")} + +lib.tipPrototype = lib.tipPrototype or setmetatable({}, lib.frameMetatable) +lib.tipMetatable = lib.tipMetatable or {__index = lib.tipPrototype} + +lib.providerPrototype = lib.providerPrototype or {} +lib.providerMetatable = lib.providerMetatable or {__index = lib.providerPrototype} + +lib.cellPrototype = lib.cellPrototype or setmetatable({}, lib.frameMetatable) +lib.cellMetatable = lib.cellMetatable or { __index = lib.cellPrototype } + +lib.activeTooltips = lib.activeTooltips or {} + +lib.tooltipHeap = lib.tooltipHeap or {} +lib.frameHeap = lib.frameHeap or {} +lib.tableHeap = lib.tableHeap or {} + +local tipPrototype = lib.tipPrototype +local tipMetatable = lib.tipMetatable + +local providerPrototype = lib.providerPrototype +local providerMetatable = lib.providerMetatable + +local cellPrototype = lib.cellPrototype +local cellMetatable = lib.cellMetatable + +local activeTooltips = lib.activeTooltips + +------------------------------------------------------------------------------ +-- Private methods for Caches and Tooltip +------------------------------------------------------------------------------ +local AcquireTooltip, ReleaseTooltip +local AcquireCell, ReleaseCell +local AcquireTable, ReleaseTable + +local InitializeTooltip, SetTooltipSize, ResetTooltipSize, LayoutColspans +local SetFrameScript, ClearFrameScripts + +------------------------------------------------------------------------------ +-- Cache debugging. +------------------------------------------------------------------------------ +--[===[@debug@ +local usedTables, usedFrames, usedTooltips = 0, 0, 0 +--@end-debug@]===] + +------------------------------------------------------------------------------ +-- Internal constants to tweak the layout +------------------------------------------------------------------------------ +local TOOLTIP_PADDING = 10 +local CELL_MARGIN_H = 6 +local CELL_MARGIN_V = 3 + +------------------------------------------------------------------------------ +-- Public library API +------------------------------------------------------------------------------ +--- Create or retrieve the tooltip with the given key. +-- If additional arguments are passed, they are passed to :SetColumnLayout for the acquired tooltip. +-- @name LibQTip:Acquire(key[, numColumns, column1Justification, column2justification, ...]) +-- @param key string or table - the tooltip key. Any value that can be used as a table key is accepted though you should try to provide unique keys to avoid conflicts. +-- Numbers and booleans should be avoided and strings should be carefully chosen to avoid namespace clashes - no "MyTooltip" - you have been warned! +-- @return tooltip Frame object - the acquired tooltip. +-- @usage Acquire a tooltip with at least 5 columns, justification : left, center, left, left, left +--
local tip = LibStub('LibQTip-1.0'):Acquire('MyFooBarTooltip', 5, "LEFT", "CENTER")
+function lib:Acquire(key, ...) + if key == nil then error("attempt to use a nil key", 2) end + local tooltip = activeTooltips[key] + if not tooltip then + tooltip = AcquireTooltip() + InitializeTooltip(tooltip, key) + activeTooltips[key] = tooltip + end + if select('#', ...) > 0 then + -- Here we catch any error to properly report it for the calling code + local ok, msg = pcall(tooltip.SetColumnLayout, tooltip, ...) + if not ok then error(msg, 2) end + end + return tooltip +end + +function lib:Release(tooltip) + local key = tooltip and tooltip.key + if not key or activeTooltips[key] ~= tooltip then return end + ReleaseTooltip(tooltip) + activeTooltips[key] = nil +end + +function lib:IsAcquired(key) + if key == nil then error("attempt to use a nil key", 2) end + return not not activeTooltips[key] +end + +function lib:IterateTooltips() + return pairs(activeTooltips) +end + +------------------------------------------------------------------------------ +-- Frame cache +------------------------------------------------------------------------------ +local frameHeap = lib.frameHeap + +local function AcquireFrame(parent) + local frame = tremove(frameHeap) or CreateFrame("Frame") + frame:SetParent(parent) + --[===[@debug@ + usedFrames = usedFrames + 1 + --@end-debug@]===] + return frame +end + +local function ReleaseFrame(frame) + frame:Hide() + frame:SetParent(nil) + frame:ClearAllPoints() + frame:SetBackdrop(nil) + ClearFrameScripts(frame) + tinsert(frameHeap, frame) + --[===[@debug@ + usedFrames = usedFrames - 1 + --@end-debug@]===] +end + +------------------------------------------------------------------------------ +-- Dirty layout handler +------------------------------------------------------------------------------ +lib.layoutCleaner = lib.layoutCleaner or CreateFrame('Frame') + +local layoutCleaner = lib.layoutCleaner +layoutCleaner.registry = layoutCleaner.registry or {} + +function layoutCleaner:RegisterForCleanup(tooltip) + self.registry[tooltip] = true + self:Show() +end + +function layoutCleaner:CleanupLayouts() + self:Hide() + for tooltip in pairs(self.registry) do + LayoutColspans(tooltip) + end + wipe(self.registry) +end +layoutCleaner:SetScript('OnUpdate', layoutCleaner.CleanupLayouts) + +------------------------------------------------------------------------------ +-- CellProvider and Cell +------------------------------------------------------------------------------ +function providerPrototype:AcquireCell() + local cell = tremove(self.heap) + if not cell then + cell = setmetatable(CreateFrame("Frame", nil, UIParent), self.cellMetatable) + if type(cell.InitializeCell) == 'function' then + cell:InitializeCell() + end + end + self.cells[cell] = true + return cell +end + +function providerPrototype:ReleaseCell(cell) + if not self.cells[cell] then return end + if type(cell.ReleaseCell) == 'function' then + cell:ReleaseCell() + end + self.cells[cell] = nil + tinsert(self.heap, cell) +end + +function providerPrototype:GetCellPrototype() + return self.cellPrototype, self.cellMetatable +end + +function providerPrototype:IterateCells() + return pairs(self.cells) +end + +function lib:CreateCellProvider(baseProvider) + local cellBaseMetatable, cellBasePrototype + if baseProvider and baseProvider.GetCellPrototype then + cellBasePrototype, cellBaseMetatable = baseProvider:GetCellPrototype() + else + cellBaseMetatable = cellMetatable + end + local cellPrototype = setmetatable({}, cellBaseMetatable) + local cellProvider = setmetatable({}, providerMetatable) + cellProvider.heap = {} + cellProvider.cells = {} + cellProvider.cellPrototype = cellPrototype + cellProvider.cellMetatable = { __index = cellPrototype } + return cellProvider, cellPrototype, cellBasePrototype +end + +------------------------------------------------------------------------------ +-- Basic label provider +------------------------------------------------------------------------------ +if not lib.LabelProvider then + lib.LabelProvider, lib.LabelPrototype = lib:CreateCellProvider() +end + +local labelProvider = lib.LabelProvider +local labelPrototype = lib.LabelPrototype + +function labelPrototype:InitializeCell() + self.fontString = self:CreateFontString() + self.fontString:SetFontObject(GameTooltipText) +end + +function labelPrototype:SetupCell(tooltip, value, justification, font, l_pad, r_pad, max_width, min_width, ...) + local fs = self.fontString + fs:SetFontObject(font or tooltip:GetFont()) + fs:SetJustifyH(justification) + fs:SetText(tostring(value)) + + l_pad = l_pad or 0 + r_pad = r_pad or 0 + + local width = fs:GetStringWidth() + l_pad + r_pad + + fs:SetPoint("TOPLEFT", self, "TOPLEFT", l_pad, 0) + fs:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -r_pad, 0) + + if max_width and min_width and (max_width < min_width) then + error("maximum width cannot be lower than minimum width: "..tostring(max_width).." < "..tostring(min_width), 2) + end + if min_width and width < min_width then width = min_width end + if max_width and max_width < width then width = max_width end + + fs:SetWidth(width) + fs:Show() + + -- Use GetHeight() instead of GetStringHeight() so lines which are longer than width will wrap. + return width, fs:GetHeight() +end + +function labelPrototype:GetPosition() return self._line, self._column end + +------------------------------------------------------------------------------ +-- Tooltip cache +------------------------------------------------------------------------------ +local tooltipHeap = lib.tooltipHeap + +-- Returns a tooltip +function AcquireTooltip() + local tooltip = tremove(tooltipHeap) + if not tooltip then + tooltip = CreateFrame("Frame", nil, UIParent) + local scrollFrame = CreateFrame("ScrollFrame", nil, tooltip) + scrollFrame:SetPoint("TOP", tooltip, "TOP", 0, -TOOLTIP_PADDING) + scrollFrame:SetPoint("BOTTOM", tooltip, "BOTTOM", 0, TOOLTIP_PADDING) + scrollFrame:SetPoint("LEFT", tooltip, "LEFT", TOOLTIP_PADDING, 0) + scrollFrame:SetPoint("RIGHT", tooltip, "RIGHT", -TOOLTIP_PADDING, 0) + tooltip.scrollFrame = scrollFrame + local scrollChild = CreateFrame("Frame", nil, tooltip.scrollframe) + scrollFrame:SetScrollChild(scrollChild) + tooltip.scrollChild = scrollChild + setmetatable(tooltip, tipMetatable) + end + --[===[@debug@ + usedTooltips = usedTooltips + 1 + --@end-debug@]===] + return tooltip +end + +-- Cleans the tooltip and stores it in the cache +function ReleaseTooltip(tooltip) + tooltip:SetAutoHideDelay(nil) + tooltip:Hide() + tooltip:ClearAllPoints() + tooltip:Clear() + if tooltip.slider then + tooltip.slider:SetValue(0) + tooltip.slider:Hide() + tooltip.scrollFrame:SetPoint("RIGHT", tooltip, "RIGHT", -TOOLTIP_PADDING, 0) + tooltip:EnableMouseWheel(false) + tooltip:SetScript("OnMouseWheel", nil) + end + for i, column in ipairs(tooltip.columns) do + tooltip.columns[i] = ReleaseFrame(column) + end + tooltip.columns = ReleaseTable(tooltip.columns) + tooltip.lines = ReleaseTable(tooltip.lines) + tooltip.colspans = ReleaseTable(tooltip.colspans) + + layoutCleaner.registry[tooltip] = nil + tinsert(tooltipHeap, tooltip) + --[===[@debug@ + usedTooltips = usedTooltips - 1 + --@end-debug@]===] +end + +------------------------------------------------------------------------------ +-- Cell 'cache' (just a wrapper to the provider's cache) +------------------------------------------------------------------------------ +-- Returns a cell for the given tooltip from the given provider +function AcquireCell(tooltip, provider) + local cell = provider:AcquireCell(tooltip) + cell:SetParent(tooltip.scrollChild) + cell:SetFrameLevel(tooltip.scrollChild:GetFrameLevel() + 3) + cell._provider = provider + return cell +end + +-- Cleans the cell hands it to its provider for storing +function ReleaseCell(cell) + cell:Hide() + cell:ClearAllPoints() + cell:SetParent(nil) + cell:SetBackdrop(nil) + ClearFrameScripts(cell) + cell._font, cell._justification, cell._colSpan, cell._line, cell._column = nil + + cell._provider:ReleaseCell(cell) + cell._provider = nil +end + +------------------------------------------------------------------------------ +-- Table cache +------------------------------------------------------------------------------ +local tableHeap = lib.tableHeap + +-- Returns a table +function AcquireTable() + local tbl = tremove(tableHeap) or {} + --[===[@debug@ + usedTables = usedTables + 1 + --@end-debug@]===] + return tbl +end + +-- Cleans the table and stores it in the cache +function ReleaseTable(table) + wipe(table) + tinsert(tableHeap, table) + --[===[@debug@ + usedTables = usedTables - 1 + --@end-debug@]===] +end + +------------------------------------------------------------------------------ +-- Tooltip prototype +------------------------------------------------------------------------------ +function InitializeTooltip(tooltip, key) + ---------------------------------------------------------------------- + -- (Re)set frame settings + ---------------------------------------------------------------------- + tooltip:SetBackdrop(GameTooltip:GetBackdrop()) + tooltip:SetBackdropColor(GameTooltip:GetBackdropColor()) + tooltip:SetBackdropBorderColor(GameTooltip:GetBackdropBorderColor()) + tooltip:SetScale(GameTooltip:GetScale()) + tooltip:SetAlpha(1) + tooltip:SetFrameStrata("TOOLTIP") + tooltip:SetClampedToScreen(false) + + ---------------------------------------------------------------------- + -- Internal data. Since it's possible to Acquire twice without calling + -- release, check for pre-existence. + ---------------------------------------------------------------------- + tooltip.key = key + tooltip.columns = tooltip.columns or AcquireTable() + tooltip.lines = tooltip.lines or AcquireTable() + tooltip.colspans = tooltip.colspans or AcquireTable() + tooltip.regularFont = GameTooltipText + tooltip.headerFont = GameTooltipHeaderText + tooltip.labelProvider = labelProvider + + ---------------------------------------------------------------------- + -- Finishing procedures + ---------------------------------------------------------------------- + tooltip:SetAutoHideDelay(nil) + tooltip:Hide() + ResetTooltipSize(tooltip) +end + +function tipPrototype:SetDefaultProvider(myProvider) + if not myProvider then return end + self.labelProvider = myProvider +end + +function tipPrototype:GetDefaultProvider() return self.labelProvider end + +local function checkJustification(justification, level, silent) + if justification ~= "LEFT" and justification ~= "CENTER" and justification ~= "RIGHT" then + if silent then return false end + error("invalid justification, must one of LEFT, CENTER or RIGHT, not: "..tostring(justification), level+1) + end + return true +end + +function tipPrototype:SetColumnLayout(numColumns, ...) + if type(numColumns) ~= "number" or numColumns < 1 then + error("number of columns must be a positive number, not: "..tostring(numColumns), 2) + end + for i = 1, numColumns do + local justification = select(i, ...) or "LEFT" + checkJustification(justification, 2) + if self.columns[i] then + self.columns[i].justification = justification + else + self:AddColumn(justification) + end + end +end + +function tipPrototype:AddColumn(justification) + justification = justification or "LEFT" + checkJustification(justification, 2) + + local colNum = #self.columns + 1 + local column = self.columns[colNum] or AcquireFrame(self.scrollChild) + column:SetFrameLevel(self.scrollChild:GetFrameLevel() + 1) + column.justification = justification + column.width = 0 + column:SetWidth(1) + column:SetPoint("TOP", self.scrollChild) + column:SetPoint("BOTTOM", self.scrollChild) + + if colNum > 1 then + column:SetPoint("LEFT", self.columns[colNum - 1], "RIGHT", CELL_MARGIN_H, 0) + SetTooltipSize(self, self.width + CELL_MARGIN_H, self.height) + else + column:SetPoint("LEFT", self.scrollChild) + end + column:Show() + self.columns[colNum] = column + return colNum +end + +------------------------------------------------------------------------------ +-- Scrollbar data and functions +------------------------------------------------------------------------------ +local sliderBackdrop = { + ["bgFile"] = [[Interface\Buttons\UI-SliderBar-Background]], + ["edgeFile"] = [[Interface\Buttons\UI-SliderBar-Border]], + ["tile"] = true, + ["edgeSize"] = 8, + ["tileSize"] = 8, + ["insets"] = { + ["left"] = 3, + ["right"] = 3, + ["top"] = 3, + ["bottom"] = 3, + }, +} + +local function slider_OnValueChanged(self) + self.scrollFrame:SetVerticalScroll(self:GetValue()) +end + +local function tooltip_OnMouseWheel(self, delta) + local slider = self.slider + local currentValue = slider:GetValue() + local minValue,maxValue = slider:GetMinMaxValues() + if delta < 0 and currentValue < maxValue then + slider:SetValue(min(maxValue, currentValue + 10)) + elseif delta > 0 and currentValue > minValue then + slider:SetValue(max(minValue, currentValue - 10)) + end +end + +-- will resize the tooltip to fit the screen and show a scrollbar if needed +function tipPrototype:UpdateScrolling(maxheight) + self:SetClampedToScreen(false) + + -- all data is in the tooltip; fix colspan width and prevent the layout cleaner from messing up the tooltip later + LayoutColspans(self) + layoutCleaner.registry[self] = nil + + local topside = self:GetTop() + local bottomside = self:GetBottom() + local screensize = UIParent:GetHeight() + local tipsize = topside - bottomside + + -- if the tooltip would be too high, limit its height and show the slider + if bottomside < 0 or topside > screensize or (maxheight and tipsize > maxheight) then + local shrink = (bottomside < 0 and (5 - bottomside) or 0) + (topside > screensize and (topside - screensize + 5) or 0) + if maxheight and tipsize - shrink > maxheight then + shrink = tipsize - maxheight + end + self:SetHeight(2 * TOOLTIP_PADDING + self.height - shrink) + self:SetWidth(2 * TOOLTIP_PADDING + self.width + 20) + self.scrollFrame:SetPoint("RIGHT", self, "RIGHT", -(TOOLTIP_PADDING + 20), 0) + if not self.slider then + local slider = CreateFrame("Slider", nil, self) + self.slider = slider + slider:SetOrientation("VERTICAL") + slider:SetPoint("TOPRIGHT", self, "TOPRIGHT", -TOOLTIP_PADDING, -TOOLTIP_PADDING) + slider:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -TOOLTIP_PADDING, TOOLTIP_PADDING) + slider:SetBackdrop(sliderBackdrop) + slider:SetThumbTexture([[Interface\Buttons\UI-SliderBar-Button-Vertical]]) + slider:SetMinMaxValues(0, 1) + slider:SetValueStep(1) + slider:SetWidth(12) + slider.scrollFrame = self.scrollFrame + slider:SetScript("OnValueChanged", slider_OnValueChanged) + slider:SetValue(0) + end + self.slider:SetMinMaxValues(0, shrink) + self.slider:Show() + self:EnableMouseWheel(true) + self:SetScript("OnMouseWheel", tooltip_OnMouseWheel) + else + self:SetHeight(2 * TOOLTIP_PADDING + self.height) + self:SetWidth(2 * TOOLTIP_PADDING + self.width) + self.scrollFrame:SetPoint("RIGHT", self, "RIGHT", -TOOLTIP_PADDING, 0) + if self.slider then + self.slider:SetValue(0) + self.slider:Hide() + self:EnableMouseWheel(false) + self:SetScript("OnMouseWheel", nil) + end + end +end + +------------------------------------------------------------------------------ +-- Tooltip methods for changing its contents. +------------------------------------------------------------------------------ +function tipPrototype:Clear() + for i, line in ipairs(self.lines) do + for j, cell in pairs(line.cells) do + if cell then ReleaseCell(cell) end + end + ReleaseTable(line.cells) + line.cells = nil + ReleaseFrame(line) + self.lines[i] = nil + end + for i, column in ipairs(self.columns) do + column.width = 0 + column:SetWidth(1) + end + wipe(self.colspans) + ResetTooltipSize(self) +end + +local function checkFont(font, level, silent) + if not font or type(font) ~= 'table' or type(font.IsObjectType) ~= 'function' or not font:IsObjectType("Font") then + if silent then return false end + error("font must be Font instance, not: "..tostring(font), level+1) + end + return true +end + +function tipPrototype:SetFont(font) + checkFont(font, 2) + self.regularFont = font +end + +function tipPrototype:GetFont() return self.regularFont end + +function tipPrototype:SetHeaderFont(font) + checkFont(font, 2) + self.headerFont = font +end + +function tipPrototype:GetHeaderFont() return self.headerFont end + +function SetTooltipSize(tooltip, width, height) + tooltip:SetHeight(2 * TOOLTIP_PADDING + height) + tooltip.scrollChild:SetHeight(height) + tooltip.height = height + + tooltip:SetWidth(2 * TOOLTIP_PADDING + width) + tooltip.scrollChild:SetWidth(width) + tooltip.width = width +end + +-- Add 2 pixels to height so dangling letters (g, y, p, j, etc) are not clipped. +function ResetTooltipSize(tooltip) + SetTooltipSize(tooltip, max(0, (CELL_MARGIN_H * (#tooltip.columns - 1)) + (CELL_MARGIN_H / 2)), 2) +end + +local function EnlargeColumn(tooltip, column, width) + if width > column.width then + SetTooltipSize(tooltip, tooltip.width + width - column.width, tooltip.height) + + column.width = width + column:SetWidth(width) + end +end + +function LayoutColspans(tooltip) + local columns = tooltip.columns + for colRange, width in pairs(tooltip.colspans) do + local left, right = colRange:match("^(%d+)%-(%d+)$") + left, right = tonumber(left), tonumber(right) + for col = left, right-1 do + width = width - columns[col].width - CELL_MARGIN_H + end + EnlargeColumn(tooltip, columns[right], width) + end + wipe(tooltip.colspans) +end + +local function _SetCell(tooltip, lineNum, colNum, value, font, justification, colSpan, provider, ...) + local line = tooltip.lines[lineNum] + local cells = line.cells + + -- Unset: be quick + if value == nil then + local cell = cells[colNum] + if cell then + for i = colNum, colNum + cell._colSpan - 1 do + cells[i] = nil + end + ReleaseCell(cell) + end + return lineNum, colNum + end + + -- Check previous cell + local cell + local prevCell = cells[colNum] + if prevCell then + -- There is a cell here + font = font or prevCell._font + justification = justification or prevCell._justification + colSpan = colSpan or prevCell._colSpan + -- Clear the currently marked colspan + for i = colNum + 1, colNum + prevCell._colSpan - 1 do + cells[i] = nil + end + if provider == nil or prevCell._provider == provider then + -- Reuse existing cell + cell = prevCell + provider = cell._provider + else + -- A new cell is required + cells[colNum] = ReleaseCell(prevCell) + end + elseif prevCell == nil then + -- Creating a new cell, using meaningful defaults. + provider = provider or tooltip.labelProvider + font = font or tooltip.regularFont + justification = justification or tooltip.columns[colNum].justification or "LEFT" + colSpan = colSpan or 1 + else + error("overlapping cells at column "..colNum, 3) + end + + local tooltipWidth = #tooltip.columns + local rightColNum + if colSpan > 0 then + rightColNum = colNum + colSpan - 1 + if rightColNum > tooltipWidth then + error("ColSpan too big, cell extends beyond right-most column", 3) + end + else + -- Zero or negative: count back from right-most columns + rightColNum = max(colNum, tooltipWidth + colSpan) + -- Update colspan to its effective value + colSpan = 1 + rightColNum - colNum + end + + -- Cleanup colspans + for i = colNum + 1, rightColNum do + local cell = cells[i] + if cell then + ReleaseCell(cell) + elseif cell == false then + error("overlapping cells at column "..i, 3) + end + cells[i] = false + end + + -- Create the cell + if not cell then + cell = AcquireCell(tooltip, provider) + cells[colNum] = cell + end + + -- Anchor the cell + cell:SetPoint("LEFT", tooltip.columns[colNum]) + cell:SetPoint("RIGHT", tooltip.columns[rightColNum]) + cell:SetPoint("TOP", line) + cell:SetPoint("BOTTOM", line) + + -- Store the cell settings directly into the cell + -- That's a bit risky but is really cheap compared to other ways to do it + cell._font, cell._justification, cell._colSpan, cell._line, cell._column = font, justification, colSpan, lineNum, colNum + + -- Setup the cell content + local width, height = cell:SetupCell(tooltip, value, justification, font, ...) + cell:Show() + + if colSpan > 1 then + -- Postpone width changes until the tooltip is shown + local colRange = colNum.."-"..rightColNum + tooltip.colspans[colRange] = max(tooltip.colspans[colRange] or 0, width) + layoutCleaner:RegisterForCleanup(tooltip) + else + -- Enlarge the column and tooltip if need be + EnlargeColumn(tooltip, tooltip.columns[colNum], width) + end + + -- Enlarge the line and tooltip if need be + if height > line.height then + SetTooltipSize(tooltip, tooltip.width, tooltip.height + height - line.height) + + line.height = height + line:SetHeight(height) + end + + if rightColNum < tooltipWidth then + return lineNum, rightColNum + 1 + else + return lineNum, nil + end +end + +local function CreateLine(tooltip, font, ...) + if #tooltip.columns == 0 then + error("column layout should be defined before adding line", 3) + end + local lineNum = #tooltip.lines + 1 + local line = tooltip.lines[lineNum] or AcquireFrame(tooltip.scrollChild) + line:SetFrameLevel(tooltip.scrollChild:GetFrameLevel() + 2) + line:SetPoint('LEFT', tooltip.scrollChild) + line:SetPoint('RIGHT', tooltip.scrollChild) + if lineNum > 1 then + line:SetPoint('TOP', tooltip.lines[lineNum-1], 'BOTTOM', 0, -CELL_MARGIN_V) + SetTooltipSize(tooltip, tooltip.width, tooltip.height + CELL_MARGIN_V) + else + line:SetPoint('TOP', tooltip.scrollChild) + end + tooltip.lines[lineNum] = line + line.cells = line.cells or AcquireTable() + line.height = 0 + line:SetHeight(1) + line:Show() + + local colNum = 1 + for i = 1, #tooltip.columns do + local value = select(i, ...) + if value ~= nil then + lineNum, colNum = _SetCell(tooltip, lineNum, i, value, font, nil, 1, tooltip.labelProvider) + end + end + return lineNum, colNum +end + +function tipPrototype:AddLine(...) + return CreateLine(self, self.regularFont, ...) +end + +function tipPrototype:AddHeader(...) + return CreateLine(self, self.headerFont, ...) +end + +local GenericBackdrop = { + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", +} + +function tipPrototype:AddSeparator(height, r, g, b, a) + local lineNum, colNum = self:AddLine() + local line = self.lines[lineNum] + local color = NORMAL_FONT_COLOR + + height = height or 1 + SetTooltipSize(self, self.width, self.height + height) + line.height = height + line:SetHeight(height) + line:SetBackdrop(GenericBackdrop) + line:SetBackdropColor(r or color.r, g or color.g, b or color.b, a or 1) + return lineNum, colNum +end + +function tipPrototype:SetCellColor(lineNum, colNum, r, g, b, a) + local cell = self.lines[lineNum].cells[colNum] + + if cell then + local sr, sg, sb, sa = self:GetBackdropColor() + cell:SetBackdrop(GenericBackdrop) + cell:SetBackdropColor(r or sr, g or sg, b or sb, a or sa) + end +end + +function tipPrototype:SetColumnColor(colNum, r, g, b, a) + local column = self.columns[colNum] + + if column then + local sr, sg, sb, sa = self:GetBackdropColor() + column:SetBackdrop(GenericBackdrop) + column:SetBackdropColor(r or sr, g or sg, b or sb, a or sa) + end +end + +function tipPrototype:SetLineColor(lineNum, r, g, b, a) + local line = self.lines[lineNum] + + if line then + local sr, sg, sb, sa = self:GetBackdropColor() + line:SetBackdrop(GenericBackdrop) + line:SetBackdropColor(r or sr, g or sg, b or sb, a or sa) + end +end + +-- TODO: fixed argument positions / remove checks for performance? +function tipPrototype:SetCell(lineNum, colNum, value, ...) + -- Mandatory argument checking + if type(lineNum) ~= "number" then + error("line number must be a number, not: "..tostring(lineNum), 2) + elseif lineNum < 1 or lineNum > #self.lines then + error("line number out of range: "..tostring(lineNum), 2) + elseif type(colNum) ~= "number" then + error("column number must be a number, not: "..tostring(colNum), 2) + elseif colNum < 1 or colNum > #self.columns then + error("column number out of range: "..tostring(colNum), 2) + end + + -- Variable argument checking + local font, justification, colSpan, provider + local i, arg = 1, ... + if arg == nil or checkFont(arg, 2, true) then + i, font, arg = 2, ... + end + if arg == nil or checkJustification(arg, 2, true) then + i, justification, arg = i + 1, select(i, ...) + end + if arg == nil or type(arg) == 'number' then + i, colSpan, arg = i + 1, select(i, ...) + end + if arg == nil or type(arg) == 'table' and type(arg.AcquireCell) == 'function' then + i, provider = i + 1, arg + end + + return _SetCell(self, lineNum, colNum, value, font, justification, colSpan, provider, select(i, ...)) +end + +function tipPrototype:GetLineCount() return #self.lines end + +function tipPrototype:GetColumnCount() return #self.columns end + + +------------------------------------------------------------------------------ +-- Frame Scripts +------------------------------------------------------------------------------ +local highlight = CreateFrame("Frame", nil, UIParent) +highlight:SetFrameStrata("TOOLTIP") +highlight:Hide() + +highlight._texture = highlight:CreateTexture(nil, "OVERLAY") +highlight._texture:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") +highlight._texture:SetBlendMode("ADD") +highlight._texture:SetAllPoints(highlight) + +local scripts = { + OnEnter = function(frame, ...) + highlight:SetParent(frame) + highlight:SetAllPoints(frame) + highlight:Show() + if frame._OnEnter_func then + frame:_OnEnter_func(frame._OnEnter_arg, ...) + end + end, + OnLeave = function(frame, ...) + highlight:Hide() + highlight:ClearAllPoints() + highlight:SetParent(nil) + if frame._OnLeave_func then + frame:_OnLeave_func(frame._OnLeave_arg, ...) + end + end, + OnMouseDown = function(frame, ...) + frame:_OnMouseDown_func(frame._OnMouseDown_arg, ...) + end, + OnMouseUp = function(frame, ...) + frame:_OnMouseUp_func(frame._OnMouseUp_arg, ...) + end, +} + +function SetFrameScript(frame, script, func, arg) + if not scripts[script] then + return + end + frame["_"..script.."_func"] = func + frame["_"..script.."_arg"] = arg + if script == "OnMouseDown" or script == "OnMouseUp" then + if func then + frame:SetScript(script, scripts[script]) + else + frame:SetScript(script, nil) + end + end + -- if at least one script is set, set the OnEnter/OnLeave scripts for the highlight + if frame._OnEnter_func or frame._OnLeave_func or frame._OnMouseDown_func or frame._OnMouseUp_func then + frame:EnableMouse(true) + frame:SetScript("OnEnter", scripts.OnEnter) + frame:SetScript("OnLeave", scripts.OnLeave) + else + frame:EnableMouse(false) + frame:SetScript("OnEnter", nil) + frame:SetScript("OnLeave", nil) + end +end + +function ClearFrameScripts(frame) + if frame._OnEnter_func or frame._OnLeave_func or frame._OnMouseDown_func or frame._OnMouseUp_func then + frame:EnableMouse(false) + frame:SetScript("OnEnter", nil) + frame._OnEnter_func = nil + frame._OnEnter_arg = nil + frame:SetScript("OnLeave", nil) + frame._OnLeave_func = nil + frame._OnLeave_arg = nil + frame:SetScript("OnMouseDown", nil) + frame._OnMouseDown_func = nil + frame._OnMouseDown_arg = nil + frame:SetScript("OnMouseUp", nil) + frame._OnMouseUp_func = nil + frame._OnMouseUp_arg = nil + end +end + +function tipPrototype:SetLineScript(lineNum, script, func, arg) + SetFrameScript(self.lines[lineNum], script, func, arg) +end + +function tipPrototype:SetColumnScript(colNum, script, func, arg) + SetFrameScript(self.columns[colNum], script, func, arg) +end + +function tipPrototype:SetCellScript(lineNum, colNum, script, func, arg) + local cell = self.lines[lineNum].cells[colNum] + if cell then + SetFrameScript(cell, script, func, arg) + end +end + + +------------------------------------------------------------------------------ +-- Auto-hiding feature +------------------------------------------------------------------------------ + +-- Script of the auto-hiding child frame +local function AutoHideTimerFrame_OnUpdate(self, elapsed) + self.checkElapsed = self.checkElapsed + elapsed + if self.checkElapsed > 0.1 then + if MouseIsOver(self.parent) or (self.alternateFrame and MouseIsOver(self.alternateFrame)) then + self.elapsed = 0 + else + self.elapsed = self.elapsed + self.checkElapsed + if self.elapsed >= self.delay then + lib:Release(self.parent) + end + end + self.checkElapsed = 0 + end +end + +-- Usage: +-- :SetAutoHideDelay(0.25) => hides after 0.25sec outside of the tooltip +-- :SetAutoHideDelay(0.25, someFrame) => hides after 0.25sec outside of both the tooltip and someFrame +-- :SetAutoHideDelay() => disable auto-hiding (default) +function tipPrototype:SetAutoHideDelay(delay, alternateFrame) + local timerFrame = self.autoHideTimerFrame + delay = tonumber(delay) or 0 + + if delay > 0 then + if not timerFrame then + timerFrame = AcquireFrame(self) + timerFrame:SetScript("OnUpdate", AutoHideTimerFrame_OnUpdate) + self.autoHideTimerFrame = timerFrame + end + timerFrame.parent = self + timerFrame.checkElapsed = 0 + timerFrame.elapsed = 0 + timerFrame.delay = delay + timerFrame.alternateFrame = alternateFrame + timerFrame:Show() + elseif timerFrame then + self.autoHideTimerFrame = nil + timerFrame.alternateFrame = nil + timerFrame:SetScript("OnUpdate", nil) + ReleaseFrame(timerFrame) + end +end + +------------------------------------------------------------------------------ +-- "Smart" Anchoring +------------------------------------------------------------------------------ +local function GetTipAnchor(frame) + local x,y = frame:GetCenter() + if not x or not y then return "TOPLEFT", "BOTTOMLEFT" end + local hhalf = (x > UIParent:GetWidth() * 2/3) and "RIGHT" or (x < UIParent:GetWidth() / 3) and "LEFT" or "" + local vhalf = (y > UIParent:GetHeight() / 2) and "TOP" or "BOTTOM" + return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf +end + +function tipPrototype:SmartAnchorTo(frame) + if not frame then + error("Invalid frame provided.", 2) + end + self:ClearAllPoints() + self:SetClampedToScreen(true) + self:SetPoint(GetTipAnchor(frame)) +end + +------------------------------------------------------------------------------ +-- Debug slashcmds +------------------------------------------------------------------------------ +--[===[@debug@ +local print = print +local function PrintStats() + local tipCache = tostring(#tooltipHeap) + local frameCache = tostring(#frameHeap) + local tableCache = tostring(#tableHeap) + + print("Tooltips used: "..usedTooltips..", Cached: "..tipCache..", Total: "..tipCache + usedTooltips) + print("Frames used: "..usedFrames..", Cached: "..frameCache..", Total: "..frameCache + usedFrames) + print("Tables used: "..usedTables..", Cached: "..tableCache..", Total: "..tableCache + usedTables) + + local header = false + for k, v in pairs(activeTooltips) do + if not header then + print("Active tooltips:") + header = true + end + print("- "..k) + end +end + +SLASH_LibQTip1 = "/qtip" +SlashCmdList["LibQTip"] = PrintStats +--@end-debug@]===] diff --git a/Libs/LibStub.lua b/Libs/LibStub.lua new file mode 100644 index 0000000..0a41ac0 --- /dev/null +++ b/Libs/LibStub.lua @@ -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 diff --git a/Libs/tekKonfig/tekKonfig.xml b/Libs/tekKonfig/tekKonfig.xml new file mode 100644 index 0000000..688f431 --- /dev/null +++ b/Libs/tekKonfig/tekKonfig.xml @@ -0,0 +1,11 @@ + +