Initial commit: Chatter v1.2.11 (Curse package)

This commit is contained in:
2026-05-22 22:11:46 +02:00
commit 77ee87198c
49 changed files with 9519 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
--- AceConfigTab-3.0 provides support for tab-completion to AceConfig tables.
-- Note: This library is not yet finalized.
-- @class file
-- @name AceConfigTab-3.0
-- @release $Id: AceConfigTab-3.0.lua 769 2009-04-04 11:05:08Z nevcairiel $
local MAJOR, MINOR = "AceConfigTab-3.0", 1
local lib = LibStub:NewLibrary(MAJOR, MINOR)
if not lib then return end
local ac = LibStub("AceConsole-3.0")
local function printf(...)
DEFAULT_CHAT_FRAME:AddMessage(string.format(...))
end
-- getChildren(opt, ...)
--
-- Retrieve the next valid group args in an AceConfig table.
--
-- opt - AceConfig options table
-- ... - args following the slash command
--
-- opt will need to be determined by the slash-command
-- The args will be obtained using AceConsole:GetArgs() or something similar on the remainder of the line.
--
-- Returns arg1, arg2, ...
local function getLevel(opt, ...)
-- Walk down the options tree to the last arg in the commandline, or return if it does not follow the tree.
local path = ""
local lastChild
for i = 1, select('#', ...) do
local arg = select(i, ...)
if not arg or type(arg) == 'number' then break end
if opt.plugins then
for k in pairs(opt.plugins) do
if string.lower(k) == string.lower(arg) then
opt = opt.plugins[k]
path = path..arg.." "
lastChild = arg
break
end
end
elseif opt.args then
for k in pairs(opt.args) do
if string.lower(k) == string.lower(arg) then
opt = opt.args[k]
path = path..arg.." "
lastChild = arg
break
end
end
else
break
end
end
return opt, path
end
local function getChildren(opt, ...)
local lastChild, path
opt, path, lastChild = getLevel(opt, ...)
local args = {}
for _, field in ipairs({"args", "plugins"}) do
if type(opt[field]) == 'table' then
for k in pairs(opt[field]) do
if opt[field].type ~= 'header' then
table.insert(args, k)
end
end
end
end
return args, path
end
--LibStub("AceConfig-3.0"):RegisterOptionsTable("ag_UnitFrames", aUF.Options.table)
local function createWordlist(t, cmdline, pos)
local cmd = string.match(cmdline, "(/[^ \t\n]+)")
local argslist = string.sub(cmdline, pos, this:GetCursorPosition())
local opt -- TODO: figure out options table using cmd
opt = LibStub("AceConfigRegistry-3.0"):GetOptionsTable("ag_UnitFrames", "cmd", "AceTab-3.0") -- hardcoded temporarily for testing
if not opt then return end
local args, path = getChildren(opt, ac:GetArgs(argslist, #argslist/2)) -- largest # of args representable by a string of length #argslist, since they must be separated by spaces
for _, v in ipairs(args) do
table.insert(t, path..v)
end
end
local function usage(t, matches, _, cmdline)
local cmd = string.match(cmdline, "(/[^ \t\n]+)")
local argslist = string.sub(cmdline, #cmd, this:GetCursorPosition())
local opt -- TODO: figure out options table using cmd
opt = LibStub("AceConfigRegistry-3.0"):GetOptionsTable("ag_UnitFrames")("cmd", "AceTab-3.0") -- hardcoded temporarily for testing
if not opt then return end
local level = getLevel(opt, ac:GetArgs(argslist, #argslist/2)) -- largest # of args representable by a string of length #argslist, since they must be separated by spaces
local option
for _, m in pairs(matches) do
local tail = string.match(m, "([^ \t\n]+)$")
option = level.plugins and level.plugins[tail] or level.args and level.args[tail]
printf("%s - %s", tail, option.desc)
end
end
LibStub("AceTab-3.0"):RegisterTabCompletion("aguftest", "%/%w+ ", createWordlist, usage)
+443
View File
@@ -0,0 +1,443 @@
--- AceTab-3.0 provides support for tab-completion.
-- Note: This library is not yet finalized.
-- @class file
-- @name AceTab-3.0
-- @release $Id: AceTab-3.0.lua 947 2010-06-29 16:44:48Z nevcairiel $
local ACETAB_MAJOR, ACETAB_MINOR = 'AceTab-3.0', 8
local AceTab, oldminor = LibStub:NewLibrary(ACETAB_MAJOR, ACETAB_MINOR)
if not AceTab then return end -- No upgrade needed
local is335 = GetBuildInfo() >= "3.3.5"
AceTab.registry = AceTab.registry or {}
-- local upvalues
local _G = _G
local pairs = pairs
local ipairs = ipairs
local type = type
local registry = AceTab.registry
local strfind = string.find
local strsub = string.sub
local strlower = string.lower
local strformat = string.format
local strmatch = string.match
local function printf(...)
DEFAULT_CHAT_FRAME:AddMessage(strformat(...))
end
local function getTextBeforeCursor(this, start)
return strsub(this:GetText(), start or 1, this:GetCursorPosition())
end
-- Hook OnTabPressed and OnTextChanged for the frame, give it an empty matches table, and set its curMatch to 0, if we haven't done so already.
local function hookFrame(f)
if f.hookedByAceTab3 then return end
f.hookedByAceTab3 = true
if f == (is335 and ChatEdit_GetActiveWindow() or ChatFrameEditBox) then
local origCTP = ChatEdit_CustomTabPressed
function ChatEdit_CustomTabPressed(...)
if AceTab:OnTabPressed(f) then
return origCTP(...)
else
return true
end
end
else
local origOTP = f:GetScript('OnTabPressed')
if type(origOTP) ~= 'function' then
origOTP = function() end
end
f:SetScript('OnTabPressed', function(...)
if AceTab:OnTabPressed(f) then
return origOTP(...)
end
end)
end
f.at3curMatch = 0
f.at3matches = {}
end
local firstPMLength
local fallbacks, notfallbacks = {}, {} -- classifies completions into those which have preconditions and those which do not. Those without preconditions are only considered if no other completions have matches.
local pmolengths = {} -- holds the number of characters to overwrite according to pmoverwrite and the current prematch
-- ------------------------------------------------------------------------------
-- RegisterTabCompletion( descriptor, prematches, wordlist, usagefunc, listenframes, postfunc, pmoverwrite )
-- See http://www.wowace.com/wiki/AceTab-2.0 for detailed API documentation
--
-- descriptor string Unique identifier for this tab completion set
--
-- prematches string|table|nil String match(es) AFTER which this tab completion will apply.
-- AceTab will ignore tabs NOT preceded by the string(s).
-- If no value is passed, will check all tabs pressed in the specified editframe(s) UNLESS a more-specific tab complete applies.
--
-- wordlist function|table Function that will be passed a table into which it will insert strings corresponding to all possible completions, or an equivalent table.
-- The text in the editbox, the position of the start of the word to be completed, and the uncompleted partial word
-- are passed as second, third, and fourth arguments, to facilitate pre-filtering or conditional formatting, if desired.
--
-- usagefunc function|boolean|nil Usage statement function. Defaults to the wordlist, one per line. A boolean true squelches usage output.
--
-- listenframes string|table|nil EditFrames to monitor. Defaults to ChatFrameEditBox.
--
-- postfunc function|nil Post-processing function. If supplied, matches will be passed through this function after they've been identified as a match.
--
-- pmoverwrite boolean|number|nil Offset the beginning of the completion string in the editbox when making a completion. Passing a boolean true indicates that we want to overwrite
-- the entire prematch string, and passing a number will overwrite that many characters prior to the cursor.
-- This is useful when you want to use the prematch as an indicator character, but ultimately do not want it as part of the text, itself.
--
-- no return
-- ------------------------------------------------------------------------------
function AceTab:RegisterTabCompletion(descriptor, prematches, wordlist, usagefunc, listenframes, postfunc, pmoverwrite)
-- Arg checks
if type(descriptor) ~= 'string' then error("Usage: RegisterTabCompletion(descriptor, prematches, wordlist, usagefunc, listenframes, postfunc, pmoverwrite): 'descriptor' - string expected.", 3) end
if prematches and type(prematches) ~= 'string' and type(prematches) ~= 'table' then error("Usage: RegisterTabCompletion(descriptor, prematches, wordlist, usagefunc, listenframes, postfunc, pmoverwrite): 'prematches' - string, table, or nil expected.", 3) end
if type(wordlist) ~= 'function' and type(wordlist) ~= 'table' then error("Usage: RegisterTabCompletion(descriptor, prematches, wordlist, usagefunc, listenframes, postfunc, pmoverwrite): 'wordlist' - function or table expected.", 3) end
if usagefunc and type(usagefunc) ~= 'function' and type(usagefunc) ~= 'boolean' then error("Usage: RegisterTabCompletion(descriptor, prematches, wordlist, usagefunc, listenframes, postfunc, pmoverwrite): 'usagefunc' - function or boolean expected.", 3) end
if listenframes and type(listenframes) ~= 'string' and type(listenframes) ~= 'table' then error("Usage: RegisterTabCompletion(descriptor, prematches, wordlist, usagefunc, listenframes, postfunc, pmoverwrite): 'listenframes' - string or table expected.", 3) end
if postfunc and type(postfunc) ~= 'function' then error("Usage: RegisterTabCompletion(descriptor, prematches, wordlist, usagefunc, listenframes, postfunc, pmoverwrite): 'postfunc' - function expected.", 3) end
if pmoverwrite and type(pmoverwrite) ~= 'boolean' and type(pmoverwrite) ~= 'number' then error("Usage: RegisterTabCompletion(descriptor, prematches, wordlist, usagefunc, listenframes, postfunc, pmoverwrite): 'pmoverwrite' - boolean or number expected.", 3) end
local pmtable = type(prematches) == 'table' and prematches or {}
-- Mark this group as a fallback group if no value was passed.
if not prematches then
pmtable[1] = ""
fallbacks[descriptor] = true
-- Make prematches into a one-element table if it was passed as a string.
elseif type(prematches) == 'string' then
pmtable[1] = prematches
if prematches == "" then
fallbacks[descriptor] = true
else
notfallbacks[descriptor] = true
end
end
-- Make listenframes into a one-element table if it was not passed a table of frames.
if not listenframes then -- default
if is335 then
listenframes = {}
for i = 1, NUM_CHAT_WINDOWS do
listenframes[i] = _G["ChatFrame"..i.."EditBox"]
end
else
listenframes = { ChatFrameEditBox }
end
elseif type(listenframes) ~= 'table' or type(listenframes[0]) == 'userdata' and type(listenframes.IsObjectType) == 'function' then -- single frame or framename
listenframes = { listenframes }
end
-- Hook each registered listenframe and give it a matches table.
for _, f in pairs(listenframes) do
if type(f) == 'string' then
f = _G[f]
end
if type(f) ~= 'table' or type(f[0]) ~= 'userdata' or type(f.IsObjectType) ~= 'function' then
error(format(ACETAB_MAJOR..": Cannot register frame %q; it does not exist", f:GetName()))
end
if f then
if f:GetObjectType() ~= 'EditBox' then
error(format(ACETAB_MAJOR..": Cannot register frame %q; it is not an EditBox", f:GetName()))
else
hookFrame(f)
end
end
end
-- Everything checks out; register this completion.
if not registry[descriptor] then
registry[descriptor] = { prematches = pmtable, wordlist = wordlist, usagefunc = usagefunc, listenframes = listenframes, postfunc = postfunc, pmoverwrite = pmoverwrite }
end
end
function AceTab:IsTabCompletionRegistered(descriptor)
return registry and registry[descriptor]
end
function AceTab:UnregisterTabCompletion(descriptor)
registry[descriptor] = nil
pmolengths[descriptor] = nil
fallbacks[descriptor] = nil
notfallbacks[descriptor] = nil
end
-- ------------------------------------------------------------------------------
-- gcbs( s1, s2 )
--
-- s1 string First string to be compared
--
-- s2 string Second string to be compared
--
-- returns the greatest common substring beginning s1 and s2
-- ------------------------------------------------------------------------------
local function gcbs(s1, s2)
if not s1 and not s2 then return end
if not s1 then s1 = s2 end
if not s2 then s2 = s1 end
if #s2 < #s1 then
s1, s2 = s2, s1
end
if strfind(strlower(s2), "^"..strlower(s1)) then
return s1
else
return gcbs(strsub(s1, 1, -2), s2)
end
end
local cursor -- Holds cursor position. Set in :OnTabPressed().
-- ------------------------------------------------------------------------------
-- cycleTab()
-- For when a tab press has multiple possible completions, we need to allow the user to press tab repeatedly to cycle through them.
-- If we have multiple possible completions, all tab presses after the first will call this function to cycle through and insert the different possible matches.
-- This function will stop being called after OnTextChanged() is triggered by something other than AceTab (i.e. the user inputs a character).
-- ------------------------------------------------------------------------------
local previousLength, cMatch, matched, postmatch
local function cycleTab(this)
cMatch = 0 -- Counter across all sets. The pseudo-index relevant to this value and corresponding to the current match is held in this.at3curMatch
matched = false
-- Check each completion group registered to this frame.
for desc, compgrp in pairs(this.at3matches) do
-- Loop through the valid completions for this set.
for m, pm in pairs(compgrp) do
cMatch = cMatch + 1
if cMatch == this.at3curMatch then -- we're back to where we left off last time through the combined list
this.at3lastMatch = m
this.at3lastWord = pm
this.at3curMatch = cMatch + 1 -- save the new cMatch index
matched = true
break
end
end
if matched then break end
end
-- If our index is beyond the end of the list, reset the original uncompleted substring and let the cycle start over next time tab is pressed.
if not matched then
this.at3lastMatch = this.at3origMatch
this.at3lastWord = this.at3origWord
this.at3curMatch = 1
end
-- Insert the completion.
this:HighlightText(this.at3matchStart-1, cursor)
this:Insert(this.at3lastWord or '')
this.at3_last_precursor = getTextBeforeCursor(this) or ''
end
local IsSecureCmd = IsSecureCmd
local cands, candUsage = {}, {}
local numMatches = 0
local firstMatch, hasNonFallback, allGCBS, setGCBS, usage
local text_precursor, text_all, text_pmendToCursor
local matches, usagefunc -- convenience locals
-- Fill the this.at3matches[descriptor] tables with matching completion pairs for each entry, based on
-- the partial string preceding the cursor position and using the corresponding registered wordlist.
--
-- The entries of the matches tables are of the format raw_match = formatted_match, where raw_match is the plaintext completion and
-- formatted_match is the match after being formatted/altered/processed by the registered postfunc.
-- If no postfunc exists, then the formatted and raw matches are the same.
local pms, pme, pmt, prematchStart, prematchEnd, text_prematch, entry
local function fillMatches(this, desc, fallback)
entry = registry[desc]
-- See what frames are registered for this completion group. If the frame in which we pressed tab is one of them, then we start building matches.
for _, f in ipairs(entry.listenframes) do
if f == this then
-- Try each precondition string registered for this completion group.
for _, prematch in ipairs(entry.prematches) do
-- Test if our prematch string is satisfied.
-- If it is, then we find its last occurence prior to the cursor, calculate and store its pmoverwrite value (if applicable), and start considering completions.
if fallback then prematch = "%s" end
-- Find the last occurence of the prematch before the cursor.
pms, pme, pmt = nil, 1, ''
text_prematch, prematchEnd, prematchStart = nil, nil, nil
while true do
pms, pme, pmt = strfind(text_precursor, "("..prematch..")", pme)
if pms then
prematchStart, prematchEnd, text_prematch = pms, pme, pmt
pme = pme + 1
else
break
end
end
if not prematchStart and fallback then
prematchStart, prematchEnd, text_prematch = 0, 0, ''
end
if prematchStart then
-- text_pmendToCursor should be the sub-word/phrase to be completed.
text_pmendToCursor = strsub(text_precursor, prematchEnd + 1)
-- How many characters should we eliminate before the completion before writing it in.
pmolengths[desc] = entry.pmoverwrite == true and #text_prematch or entry.pmoverwrite or 0
-- This is where we will insert completions, taking the prematch overwrite into account.
this.at3matchStart = prematchEnd + 1 - (pmolengths[desc] or 0)
-- We're either a non-fallback set or all completions thus far have been fallback sets, and the precondition matches.
-- Create cands from the registered wordlist, filling it with all potential (unfiltered) completion strings.
local wordlist = entry.wordlist
local cands = type(wordlist) == 'table' and wordlist or {}
if type(wordlist) == 'function' then
wordlist(cands, text_all, prematchEnd + 1, text_pmendToCursor)
end
if cands ~= false then
matches = this.at3matches[desc] or {}
for i in pairs(matches) do matches[i] = nil end
-- Check each of the entries in cands to see if it completes the word before the cursor.
-- Finally, increment our match count and set firstMatch, if appropriate.
for _, m in ipairs(cands) do
if strfind(strlower(m), strlower(text_pmendToCursor), 1, 1) == 1 then -- we have a matching completion!
hasNonFallback = not fallback
matches[m] = entry.postfunc and entry.postfunc(m, prematchEnd + 1, text_all) or m
numMatches = numMatches + 1
if numMatches == 1 then
firstMatch = matches[m]
firstPMLength = pmolengths[desc] or 0
end
end
end
this.at3matches[desc] = numMatches > 0 and matches or nil
end
end
end
end
end
end
function AceTab:OnTabPressed(this)
if this:GetText() == '' then return true end
-- allow Blizzard to handle slash commands, themselves
if this == (is335 and ChatEdit_GetActiveWindow() or ChatFrameEditBox) then
local command = this:GetText()
if strfind(command, "^/[%a%d_]+$") then
return true
end
local cmd = strmatch(command, "^/[%a%d_]+")
if cmd and IsSecureCmd(cmd) then
return true
end
end
cursor = this:GetCursorPosition()
text_all = this:GetText()
text_precursor = getTextBeforeCursor(this) or ''
-- If we've already found some matches and haven't done anything since the last tab press, then (continue) cycling matches.
-- Otherwise, reset this frame's matches and proceed to creating our list of possible completions.
this.at3lastMatch = this.at3curMatch > 0 and (this.at3lastMatch or this.at3origWord)
-- Detects if we've made any edits since the last tab press. If not, continue cycling completions.
if text_precursor == this.at3_last_precursor then
return cycleTab(this)
else
for i in pairs(this.at3matches) do this.at3matches[i] = nil end
this.at3curMatch = 0
this.at3origWord = nil
this.at3origMatch = nil
this.at3lastWord = nil
this.at3lastMatch = nil
this.at3_last_precursor = text_precursor
end
numMatches = 0
firstMatch = nil
firstPMLength = 0
hasNonFallback = false
for i in pairs(pmolengths) do pmolengths[i] = nil end
for desc in pairs(notfallbacks) do
fillMatches(this, desc)
end
if not hasNonFallback then
for desc in pairs(fallbacks) do
fillMatches(this, desc, true)
end
end
if not firstMatch then
this.at3_last_precursor = "\0"
return true
end
-- We want to replace the entire word with our completion, so highlight it up to the cursor.
-- If only one match exists, then stick it in there and append a space.
if numMatches == 1 then
-- HighlightText takes the value AFTER which the highlighting starts, so we have to subtract 1 to have it start before the first character.
this:HighlightText(this.at3matchStart-1, cursor)
this:Insert(firstMatch)
this:Insert(" ")
else
-- Otherwise, we want to begin cycling through the valid completions.
-- Beginning a cycle also causes the usage statement to be printed, if one exists.
-- Print usage statements for each possible completion (and gather up the GCBS of all matches while we're walking the tables).
allGCBS = nil
for desc, matches in pairs(this.at3matches) do
-- Don't print usage statements for fallback completion groups if we have 'real' completion groups with matches.
if hasNonFallback and fallbacks[desc] then break end
-- Use the group's description as a heading for its usage statements.
DEFAULT_CHAT_FRAME:AddMessage(desc..":")
usagefunc = registry[desc].usagefunc
if not usagefunc then
-- No special usage processing; just print a list of the (formatted) matches.
for m, fm in pairs(matches) do
DEFAULT_CHAT_FRAME:AddMessage(fm)
allGCBS = gcbs(allGCBS, m)
end
else
-- Print a usage statement based on the corresponding registered usagefunc.
-- candUsage is the table passed to usagefunc to be filled with candidate = usage_statement pairs.
if type(usagefunc) == 'function' then
for i in pairs(candUsage) do candUsage[i] = nil end
-- usagefunc takes the greatest common substring of valid matches as one of its args, so let's find that now.
-- TODO: Make the GCBS function accept a vararg or table, after which we can just pass in the list of matches.
setGCBS = nil
for m in pairs(matches) do
setGCBS = gcbs(setGCBS, m)
end
allGCBS = gcbs(allGCBS, setGCBS)
usage = usagefunc(candUsage, matches, setGCBS, strsub(text_precursor, 1, prematchEnd))
-- If the usagefunc returns a string, then the entire usage statement has been taken care of by usagefunc, and we need only to print it...
if type(usage) == 'string' then
DEFAULT_CHAT_FRAME:AddMessage(usage)
-- ...otherwise, it should have filled candUsage with candidate-usage statement pairs, and we need to print the matching ones.
elseif next(candUsage) and numMatches > 0 then
for m, fm in pairs(matches) do
if candUsage[m] then DEFAULT_CHAT_FRAME:AddMessage(strformat("%s - %s", fm, candUsage[m])) end
end
end
end
end
-- Replace the original string with the greatest common substring of all valid completions.
this.at3curMatch = 1
this.at3origWord = strsub(text_precursor, this.at3matchStart, this.at3matchStart + pmolengths[desc] - 1) .. allGCBS or ""
this.at3origMatch = allGCBS or ""
this.at3lastWord = this.at3origWord
this.at3lastMatch = this.at3origMatch
this:HighlightText(this.at3matchStart-1, cursor)
this:Insert(this.at3origWord)
this.at3_last_precursor = getTextBeforeCursor(this) or ''
end
end
end
+5
View File
@@ -0,0 +1,5 @@
<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="AceTab-3.0.lua"/>
<Script file="AceConfigTab-3.0.lua"/>
</Ui>
+782
View File
@@ -0,0 +1,782 @@
--[[
Name: Sink-2.0
Revision: $Rev: 71 $
Author(s): Rabbit (rabbit.magtheridon@gmail.com), Antiarc (cheal@gmail.com)
Website: http://rabbit.nihilum.eu
Documentation: http://wiki.wowace.com/index.php/Sink-2.0
SVN: http://svn.wowace.com/wowace/trunk/SinkLib/Sink-2.0
Description: Library that handles chat output.
Dependencies: LibStub, SharedMedia-3.0 (optional)
License: GPL v2 or later.
]]
--[[
Copyright (C) 2008 Rabbit
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
]]
-----------------------------------------------------------------------
-- Sink-2.0
local SINK20 = "LibSink-2.0"
local SINK20_MINOR = 90000 + tonumber(("$Revision: 71 $"):match("(%d+)"))
local sink = LibStub:NewLibrary(SINK20, SINK20_MINOR)
if not sink then return end
-- Start upgrade
sink.storageForAddon = sink.storageForAddon or {}
sink.override = sink.override or {}
sink.msbt_registered_fonts = sink.msbt_registered_fonts or {}
sink.registeredScrollAreaFunctions = sink.registeredScrollAreaFunctions or {}
sink.handlers = sink.handlers or {}
sink.stickyAddons = sink.stickyAddons or {
Blizzard = true,
MikSBT = true,
SCT = true,
Parrot = true,
BCF = true,
}
-- Upgrade complete
local L_DEFAULT = "Default"
local L_DEFAULT_DESC = "Route output from this addon through the first available handler, preferring scrolling combat text addons if available."
local L_ROUTE = "Route output from this addon through %s."
local L_SCT = "Scrolling Combat Text"
local L_MSBT = "MikSBT"
local L_BIGWIGS = "BigWigs"
local L_BCF = "BlinkCombatFeedback"
local L_UIERROR = "Blizzard Error Frame"
local L_CHAT = "Chat"
local L_BLIZZARD = "Blizzard FCT"
local L_RW = "Raid Warning"
local L_PARROT = "Parrot"
local L_CHANNEL = "Channel"
local L_OUTPUT = "Output"
local L_OUTPUT_DESC = "Where to route the output from this addon."
local L_SCROLL = "Sub section"
local L_SCROLL_DESC = "Set the sub section where messages should appear.\n\nOnly available for some output sinks."
local L_STICKY = "Sticky"
local L_STICKY_DESC = "Set messages from this addon to appear as sticky.\n\nOnly available for some output sinks."
local L_NONE = "None"
local L_NONE_DESC = "Hide all messages from this addon."
local L_NOTINCHANNEL = " (You tried sending this to the channel %s, but it appears you are not there.)"
local l = GetLocale()
if l == "koKR" then
L_DEFAULT = "기본"
L_DEFAULT_DESC = "처음으로 사용 가능한 트레이너를 통해 이 애드온으로부터 출력을 보냅니다."
L_ROUTE = "%s|1을;를; 통해 이 애드온의 메시지를 출력합니다."
L_SCT = "Scrolling Combat Text"
L_MSBT = "MikSBT"
L_BIGWIGS = "BigWigs"
L_BCF = "블링크의 전투 메세지"
L_UIERROR = "블리자드 오류 창"
L_CHAT = "대화창"
L_BLIZZARD = "블리자드 FCT"
L_RW = "공격대 경보"
L_PARROT = "Parrot"
L_OUTPUT = "출력"
L_OUTPUT_DESC = "어디에 이 애드온의 메시지를 출력할지 선택합니다."
L_SCROLL = "스크롤 영역"
L_SCROLL_DESC = "메시지를 출력할 스크룰 영역을 설정합니다.\n\nParrot, SCT나 MikSBT만 사용 가능합니다."
L_STICKY = "점착"
L_STICKY_DESC = "달라붙는 것처럼 보일 이 애드온의 메시지를 설정합니다.\n\n블리자드 FCT, Parrot, SCT나 MikSBT만 사용 가능합니다."
L_NONE = "없음"
L_NONE_DESC = "이 애드온의 모든 메시지를 숨김니다."
elseif l == "frFR" then
L_DEFAULT = "Par défaut"
L_DEFAULT_DESC = "Transmet la sortie de cet addon via le premier handler disponible, de préférence les textes de combat défilants s'il y en a."
L_ROUTE = "Transmet la sortie de cet addon via %s."
L_SCT = "Scrolling Combat Text"
L_MSBT = "MikSBT"
L_BIGWIGS = "BigWigs"
L_BCF = "BlinkCombatFeedback"
L_UIERROR = "Cadre des erreurs"
L_CHAT = "Fenêtre de discussion"
L_BLIZZARD = "TCF de Blizzard"
L_RW = "Avertissement raid"
L_PARROT = "Parrot"
L_CHANNEL = "Canal"
L_OUTPUT = "Sortie"
L_OUTPUT_DESC = "Destination de la sortie de cet addon."
L_SCROLL = "Sous-section"
L_SCROLL_DESC = "Définit la sous-section où les messages doivent apparaitre.\n\nDisponible uniquement dans certains cas."
L_STICKY = "En évidence"
L_STICKY_DESC = "Fait en sortie que les messages de cet addon apparaissent en évidence.\n\nDisponible uniquement dans certains cas."
L_NONE = "Aucun"
L_NONE_DESC = "Masque tous les messages provenant de cet addon."
elseif l == "deDE" then
L_DEFAULT = "Voreinstellung"
L_DEFAULT_DESC = "Leitet die Ausgabe von diesem Addon zum ersten verfügbaren Ausgabeort, vorzugsweise Scrollende Kampf Text Addons wenn verfügbar."
L_ROUTE = "Schickt die Meldungen dieses Addons an %s."
L_SCT = "Scrolling Combat Text(SCT)"
L_MSBT = "MikSBT"
L_BIGWIGS = "BigWigs"
L_BCF = "BlinkCombatFeedback"
L_UIERROR = "Blizzard's Fehler Fenster"
L_CHAT = "Im Chat"
L_BLIZZARD = "Blizzard's schwebenden Kampftext"
L_RW = "Schlachtzug's Warnung"
L_PARROT = "Parrot"
L_OUTPUT = "Ausgabe"
L_OUTPUT_DESC = "Wohin die Meldungen des Addons gesendet werden soll."
L_SCROLL = "Scroll Bereich"
L_SCROLL_DESC = "Setzt die Scroll Bereich, wo die Meldungen erscheinen sollen.\n\nNur verfügbar für Parrot, SCT oder MikSBT."
L_STICKY = "Stehend"
L_STICKY_DESC = "Läßt Nachrichten von diesem Addon als stehende Nachrichten erscheinen.\n\nNur verfügbar für Blizzard FCT, Parrot, SCT oder MikSBT."
L_NONE = "Nirgends"
L_NONE_DESC = "Versteckt alle Meldungen von diesem Addon."
elseif l == "zhCN" then
L_DEFAULT = "默认"
L_DEFAULT_DESC = "插件的输出方式取决于第一个可用插件,例如有 SCT 插件,则优先使用。"
L_ROUTE = "经由%s显示信息。"
L_SCT = "SCT"
L_MSBT = "MikSBT"
L_BIGWIGS = "BigWigs"
L_BCF = "BlinkCombatFeedback"
L_UIERROR = "Blizzard 错误框体"
L_CHAT = "聊天框体"
L_BLIZZARD = "系统自带滚动战斗信息"
L_RW = "团队警告"
L_PARROT = "Parrot"
L_CHANNEL = "频道"
L_OUTPUT = "输出模式"
L_OUTPUT_DESC = "设置显示位置。"
L_SCROLL = "滚动区域"
L_SCROLL_DESC = "设置滚动信息显示位置。\n\n只有 Parrot、SCT 及 MikSBT 支持。"
L_STICKY = "固定"
L_STICKY_DESC = "设置信息固定显示位置。\n\n只有系统自带滚动战斗信息、Parrot、SCT 及 MikSBT 支持。"
L_NONE = "隐藏"
L_NONE_DESC = "隐藏所有来自插件的信息。"
elseif l == "zhTW" then
L_DEFAULT = "預設"
L_DEFAULT_DESC = "插件輸出經由第一個可使用的處理器顯示,如果有 SCT 的話,則優先使用。"
L_ROUTE = "插件輸出經由%s顯示。"
L_SCT = "SCT"
L_MSBT = "MikSBT"
L_BIGWIGS = "BigWigs"
L_BCF = "BlinkCombatFeedback"
L_UIERROR = "Blizzard 錯誤訊息框架"
L_CHAT = "聊天視窗"
L_BLIZZARD = "Blizzard 浮動戰鬥文字"
L_RW = "團隊警告"
L_PARROT = "Parrot"
L_OUTPUT = "顯示模式"
L_OUTPUT_DESC = "插件輸出經由哪裡顯示。"
L_SCROLL = "滾動區域"
L_SCROLL_DESC = "設定滾動訊息出現位置。\n\n只有 ParrotSCT 及 MikSBT 有支援。"
L_STICKY = "固定"
L_STICKY_DESC = "設定使用固定訊息。\n\n只有 Blizzard 浮動戰鬥文字,ParrotSCT 及 MikSBT 有支援。"
L_NONE = "隱藏"
L_NONE_DESC = "隱藏所有插件輸出。"
elseif l == "ruRU" then
L_DEFAULT = "По умолчанию"
L_DEFAULT_DESC = "Маршрут вывода сообщений данного аддона через первое доступное устройство, предпочитая доступные аддоны прокрутки текста боя."
L_ROUTE = "Маршрут вывода сообщений данного аддона через %s."
L_SCT = "SCT"
L_MSBT = "MikSBT"
L_BIGWIGS = "BigWigs"
L_BCF = "BlinkCombatFeedback"
L_UIERROR = "Фрейм ошибок Blizzard"
L_CHAT = "Чат"
L_BLIZZARD = "Blizzard FCT"
L_RW = "Объявление рейду"
L_PARROT = "Parrot"
L_CHANNEL = "Канал"
L_OUTPUT = "Вывод"
L_OUTPUT_DESC = "Куда выводить сообщения данного аддона."
L_SCROLL = "Область прокрутки"
L_SCROLL_DESC = "Назначить область прокрутки куда должны выводиться сообщения.\n\nДоступно только для Parrotа, SCT или MikSBT."
L_STICKY = "Клейкий"
L_STICKY_DESC = "Сделать сообщения данного аддона клейкими.\n\nДоступно только для Blizzard FCT, Parrot, SCT или MikSBT."
L_NONE = "Нету"
L_NONE_DESC = "Скрыть все сообщения данного аддона."
end
local SML = LibStub("LibSharedMedia-3.0", true)
local _G = getfenv(0)
local function getSticky(addon)
return sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20Sticky or nil
end
-- Thanks to Antiarc and his Soar-1.0 library for most of the 'meat' of the
-- sink-specific functions.
local function parrot(addon, text, r, g, b, font, size, outline, sticky, loc, icon)
local location = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20ScrollArea or "Notification"
local s = getSticky(addon) or sticky
Parrot:ShowMessage(text, location, s, r, g, b, font, size, outline, icon)
end
local sct_color = {}
local function sct(addon, text, r, g, b, font, size, outline, sticky, _, icon)
sct_color.r, sct_color.g, sct_color.b = r, g, b
local loc = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20ScrollArea or "Messages"
local location = (loc == "Outgoing" and SCT.FRAME1) or (loc == "Incoming" and SCT.FRAME2) or SCT.MSG
local s = getSticky(addon) or sticky
SCT:DisplayCustomEvent(text, sct_color, s, location, nil, icon)
end
local msbt_outlines = {["NORMAL"] = 1, ["OUTLINE"] = 2, ["THICKOUTLINE"] = 3}
local function msbt(addon, text, r, g, b, font, size, outline, sticky, _, icon)
if font and SML and not sink.msbt_registered_fonts[font] then
MikSBT.RegisterFont(font, SML:Fetch("font", font))
sink.msbt_registered_fonts[font] = true
end
local location = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20ScrollArea or MikSBT.DISPLAYTYPE_NOTIFICATION
local s = getSticky(addon) or sticky
MikSBT.DisplayMessage(text, location, s, r * 255, g * 255, b * 255, size, font, msbt_outlines[outline], icon)
end
local bcf_outlines = {NORMAL = "", OUTLINE = "OUTLINE", THICKOUTLINE = "THICKOUTLINE"}
local function bcf(addon, text, r, g, b, font, size, outline, sticky, _, icon)
if icon then text = "|T"..icon..":20:20:-5|t"..text end
local loc = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20ScrollArea or "Sticky"
local s = getSticky(addon) or sticky
BlinkCombatFeedback:DisplayCustomEvent({display = {msg = text, color = ("%02x%02x%02x"):format(r * 255, g * 255, b * 255), scrollArea = loc, scrollType = s and "Sticky" or "up", size = size, outling = bcf_outlines[outline], align = "center", font = font}})
end
local function blizzard(addon, text, r, g, b, font, size, outline, sticky, _, icon)
if icon then text = "|T"..icon..":20:20:-5|t"..text end
if tostring(SHOW_COMBAT_TEXT) ~= "0" then
local s = getSticky(addon) or sticky
CombatText_AddMessage(text, CombatText_StandardScroll, r, g, b, s and "crit" or nil, false)
else
UIErrorsFrame:AddMessage(text, r, g, b, 1.0)
end
end
sink.channelMapping = sink.channelMapping or {
[SAY] = "SAY",
[PARTY] = "PARTY",
[BATTLEGROUND] = "BATTLEGROUND",
[GUILD_CHAT] = "GUILD",
[OFFICER_CHAT] = "OFFICER",
[YELL] = "YELL",
[RAID] = "RAID",
[RAID_WARNING] = "RAID_WARNING",
[GROUP] = "GROUP",
}
sink.frame = sink.frame or CreateFrame("Frame")
sink.frame:RegisterEvent("CHANNEL_UI_UPDATE")
sink.frame:RegisterEvent("PLAYER_ENTERING_WORLD")
do
local newChannels = {}
local function loop(...)
wipe(newChannels)
for i = 1, select("#", ...), 2 do
local id, name = select(i, ...)
newChannels[name] = true
end
for k, v in pairs(sink.channelMapping) do
if v == "CHANNEL" and not newChannels[k] then
sink.channelMapping[k] = nil
end
end
for k in pairs(newChannels) do sink.channelMapping[k] = "CHANNEL" end
end
local function rescanChannels() loop(GetChannelList()) end
sink.frame:SetScript("OnEvent", rescanChannels)
rescanChannels()
end
local function channel(addon, text)
-- Sanitize the text, remove all color codes.
text = text:gsub("(|c%x%x%x%x%x%x%x%x)", ""):gsub("(|r)", "")
local loc = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20ScrollArea or "SAY"
local chan = sink.channelMapping[loc]
if chan == "GROUP" then
chan = select(2, IsInInstance()) == "pvp" and "BATTLEGROUND" or (UnitInRaid("player") and "RAID" or "PARTY")
if chan == "PARTY" and GetNumPartyMembers() == 0 then chan = "SAY" end
elseif chan == "CHANNEL" then
local id, name = GetChannelName(loc)
if name then
SendChatMessage(text, "CHANNEL", nil, id)
else
print(text .. L_NOTINCHANNEL)
end
return
end
SendChatMessage(text, chan or "SAY")
end
local function chat(addon, text, r, g, b, _, _, _, _, _, icon)
if icon then text = "|T"..icon..":15|t"..text end
DEFAULT_CHAT_FRAME:AddMessage(text, r, g, b)
end
local function uierror(addon, text, r, g, b, _, _, _, _, _, icon)
if icon then text = "|T"..icon..":20:20:-5|t"..text end
UIErrorsFrame:AddMessage(text, r, g, b, 1.0)
end
local rw
do
local white = {r = 1, g = 1, b = 1}
function rw(addon, text, r, g, b, _, _, _, _, _, icon)
if r or g or b then
local c = "|cff" .. string.format("%02x%02x%02x", (r or 0) * 255, (g or 0) * 255, (b or 0) * 255)
text = c .. text .. "|r"
end
if icon then text = "|T"..icon..":20:20:-5|t"..text end
RaidNotice_AddMessage(RaidWarningFrame, text, white)
end
end
local function noop() --[[ noop! ]] end
local handlerPriority = { "Parrot", "SCT", "MikSBT", "BCF" }
-- Thanks to ckk for these
local customHandlersEnabled = {
Parrot = function()
if not _G.Parrot then return end
return _G.Parrot.IsEnabled and _G.Parrot:IsEnabled() or _G.Parrot:IsActive()
end,
SCT = function()
return _G.SCT and _G.SCT:IsEnabled()
end,
BCF = function()
return bcfDB and bcfDB["enable"]
end,
}
-- Default to version 5 or higher now
local msbtVersion = tonumber(string.match(GetAddOnMetadata("MikScrollingBattleText", "Version") or "","^%d+\.%d+")) or 5
local isMSBTFive = math.floor(msbtVersion) > 4 and true or nil
if isMSBTFive then
customHandlersEnabled.MikSBT = function()
return _G.MikSBT and not _G.MikSBT.IsModDisabled()
end
else
customHandlersEnabled.MikSBT = function()
return _G.MikSBT and _G.MSBTProfiles and _G.MSBTProfiles.GetSavedVariables() and not MSBTProfiles.GetSavedVariables().UserDisabled
end
end
local currentHandler = nil
local function getPrioritizedSink()
if currentHandler then
local check = customHandlersEnabled[currentHandler]
if check and check() then
return sink.handlers[currentHandler]
end
end
for i, v in next, handlerPriority do
local check = customHandlersEnabled[v]
if check and check() then
currentHandler = v
return sink.handlers[v]
end
end
if SHOW_COMBAT_TEXT and tostring(SHOW_COMBAT_TEXT) ~= "0" then
return blizzard
end
return chat
end
local function pour(addon, text, r, g, b, ...)
local func = sink.override and sink.handlers[sink.override] or nil
if not func and sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20OutputSink then
local h = sink.storageForAddon[addon].sink20OutputSink
func = sink.handlers[h]
-- If this sink is not available now, find one manually.
if customHandlersEnabled[h] and not customHandlersEnabled[h]() then
func = nil
end
end
if not func then
func = getPrioritizedSink()
end
if not func then func = chat end
func(addon, text, r or 1, g or 1, b or 1, ...)
end
function sink:Pour(textOrAddon, ...)
local t = type(textOrAddon)
if t == "string" then
pour(self, textOrAddon, ...)
elseif t == "number" then
pour(self, tostring(textOrAddon), ...)
elseif t == "table" then
pour(textOrAddon, ...)
else
error("Invalid argument 2 to :Pour, must be either a string or a table.")
end
end
local sinks
do
-- Maybe we want to hide them instead of disable
local function shouldDisableSCT()
return not _G.SCT
end
local function shouldDisableMSBT()
return not _G.MikSBT
end
local function shouldDisableBCF()
return not ( bcfDB and bcfDB["enable"] )
end
local function shouldDisableParrot()
return not _G.Parrot
end
local function shouldDisableFCT()
return not SHOW_COMBAT_TEXT or tostring(SHOW_COMBAT_TEXT) == "0"
end
local sctFrames = {"Incoming", "Outgoing", "Messages"}
local msbtFrames = nil
local tmp = {}
local function getScrollAreasForAddon(addon)
if type(addon) ~= "string" then return nil end
if addon == "Parrot" then
if Parrot.GetScrollAreasChoices then
return Parrot:GetScrollAreasChoices()
else
return Parrot:GetScrollAreasValidate()
end
elseif addon == "MikSBT" then
if isMSBTFive then
if not msbtFrames then
msbtFrames = {}
for key, name in MikSBT.IterateScrollAreas() do
table.insert(msbtFrames, name)
end
end
return msbtFrames
else
return MikSBT.GetScrollAreaList()
end
elseif addon == "BCF" then
if bcfDB then
local bcfAreas = {}
for i = 1, #bcfDB["scrollAreas"] do
bcfAreas[#bcfAreas + 1] = bcfDB["scrollAreas"][i]["name"]
end
return bcfAreas
end
elseif addon == "SCT" then
return sctFrames
elseif addon == "Channel" then
wipe(tmp)
for k in pairs(sink.channelMapping) do
tmp[#tmp + 1] = k
end
return tmp
elseif sink.registeredScrollAreaFunctions[addon] then
return sink.registeredScrollAreaFunctions[addon]()
end
return nil
end
local emptyTable, args, options = {}, {}, {}
sinks = {
Default = {L_DEFAULT, L_DEFAULT_DESC},
SCT = {L_SCT, nil, shouldDisableSCT},
MikSBT = {L_MSBT, nil, shouldDisableMSBT},
BCF = {L_BCF, nil, shouldDisableBCF},
Parrot = {L_PARROT, nil, shouldDisableParrot},
Blizzard = {L_BLIZZARD, nil, shouldDisableFCT},
RaidWarning = {L_RW},
ChatFrame = {L_CHAT},
Channel = {L_CHANNEL},
UIErrorsFrame = {L_UIERROR},
None = {L_NONE, L_NONE_DESC}
}
local function getAce2SinkOptions(key, opts)
local name, desc, hidden = unpack(opts)
args["Ace2"][key] = {
type = "toggle",
name = name,
desc = desc or L_ROUTE:format(name),
isRadio = true,
hidden = hidden
}
end
function sink.GetSinkAce2OptionsDataTable(addon)
options["Ace2"][addon] = options["Ace2"][addon] or {
output = {
type = "group",
name = L_OUTPUT,
desc = L_OUTPUT_DESC,
pass = true,
get = function(key)
if not sink.storageForAddon[addon] then
return "Default"
end
if tostring(key) == "nil" then
-- Means AceConsole wants to list the output option,
-- so we should show which sink is currently used.
return sink.storageForAddon[addon].sink20OutputSink or L_DEFAULT
end
if key == "ScrollArea" then
return sink.storageForAddon[addon].sink20ScrollArea
elseif key == "Sticky" then
return sink.storageForAddon[addon].sink20Sticky
else
if sink.storageForAddon[addon].sink20OutputSink == key then
local sa = getScrollAreasForAddon(key)
options["Ace2"][addon].output.args.ScrollArea.validate = sa or emptyTable
options["Ace2"][addon].output.args.ScrollArea.disabled = not sa
options["Ace2"][addon].output.args.Sticky.disabled = not sink.stickyAddons[key]
end
return sink.storageForAddon[addon].sink20OutputSink and sink.storageForAddon[addon].sink20OutputSink == key or nil
end
end,
set = function(key, value)
if not sink.storageForAddon[addon] then return end
if key == "ScrollArea" then
sink.storageForAddon[addon].sink20ScrollArea = value
elseif key == "Sticky" then
sink.storageForAddon[addon].sink20Sticky = value
elseif value then
local sa = getScrollAreasForAddon(key)
options["Ace2"][addon].output.args.ScrollArea.validate = sa or emptyTable
options["Ace2"][addon].output.args.ScrollArea.disabled = not sa
options["Ace2"][addon].output.args.Sticky.disabled = not sink.stickyAddons[key]
sink.storageForAddon[addon].sink20OutputSink = key
end
end,
args = args["Ace2"],
disabled = function()
return (type(addon.IsActive) == "function" and not addon:IsActive()) or nil
end
}
}
return options["Ace2"][addon]
end
-- Ace3 options data table format
local function getAce3SinkOptions(key, opts)
local name, desc, hidden = unpack(opts)
args["Ace3"][key] = {
type = "toggle",
name = name,
desc = desc or L_ROUTE:format(name),
hidden = hidden
}
end
function sink.GetSinkAce3OptionsDataTable(addon)
if not options["Ace3"][addon] then
options["Ace3"][addon] = {
type = "group",
name = L_OUTPUT,
desc = L_OUTPUT_DESC,
args = args["Ace3"],
get = function(info)
local key = info[#info]
if not sink.storageForAddon[addon] then
return "Default"
end
if tostring(key) == "nil" then
-- Means AceConsole wants to list the output option,
-- so we should show which sink is currently used.
return sink.storageForAddon[addon].sink20OutputSink or L_DEFAULT
end
if key == "ScrollArea" then
return sink.storageForAddon[addon].sink20ScrollArea
elseif key == "Sticky" then
return sink.storageForAddon[addon].sink20Sticky
else
if sink.storageForAddon[addon].sink20OutputSink == key then
local sa = getScrollAreasForAddon(key)
if sa then
for k,v in ipairs(sa) do
sa[k] = nil
sa[v] = v
end
end
options["Ace3"][addon].args.ScrollArea.values = sa or emptyTable
options["Ace3"][addon].args.ScrollArea.disabled = not sa
options["Ace3"][addon].args.Sticky.disabled = not sink.stickyAddons[key]
end
return sink.storageForAddon[addon].sink20OutputSink and sink.storageForAddon[addon].sink20OutputSink == key or nil
end
end,
set = function(info, v)
local key = info[#info]
if not sink.storageForAddon[addon] then return end
if key == "ScrollArea" then
sink.storageForAddon[addon].sink20ScrollArea = v
elseif key == "Sticky" then
sink.storageForAddon[addon].sink20Sticky = v
elseif v then
local sa = getScrollAreasForAddon(key)
if sa then
for k,v in ipairs(sa) do
sa[k] = nil
sa[v] = v
end
end
options["Ace3"][addon].args.ScrollArea.values = sa or emptyTable
options["Ace3"][addon].args.ScrollArea.disabled = not sa
options["Ace3"][addon].args.Sticky.disabled = not sink.stickyAddons[key]
sink.storageForAddon[addon].sink20OutputSink = key
end
end,
disabled = function()
return (type(addon.IsEnabled) == "function" and not addon:IsEnabled()) or nil
end,
}
end
return options["Ace3"][addon]
end
local sinkOptionGenerators = {
["Ace2"] = getAce2SinkOptions,
["Ace3"] = getAce3SinkOptions
}
for generatorName, generator in pairs(sinkOptionGenerators) do
options[generatorName] = options[generatorName] or {}
args[generatorName] = args[generatorName] or {}
for name, opts in pairs(sinks) do
generator(name, opts)
end
end
args["Ace2"].ScrollArea = {
type = "text",
name = L_SCROLL,
desc = L_SCROLL_DESC,
validate = emptyTable,
order = -1,
disabled = true
}
args["Ace2"].Sticky = {
type = "toggle",
name = L_STICKY,
desc = L_STICKY_DESC,
validate = emptyTable,
order = -2,
disabled = true
}
args["Ace3"].ScrollArea = {
type = "select",
name = L_SCROLL,
desc = L_SCROLL_DESC,
values = emptyTable,
order = -1,
disabled = true
}
args["Ace3"].Sticky = {
type = "toggle",
name = L_STICKY,
desc = L_STICKY_DESC,
order = -2,
disabled = true
}
function sink:RegisterSink(shortName, name, desc, func, scrollAreaFunc, hasSticky)
assert(type(shortName) == "string")
assert(type(name) == "string")
assert(type(desc) == "string" or desc == nil)
assert(type(func) == "function" or type(func) == "string")
assert(type(scrollAreas) == "function" or scrollAreas == nil)
assert(type(hasSticky) == "boolean" or hasSticky == nil)
if sinks[shortName] or sink.handlers[shortName] then
error("There's already a sink by the short name %q.", shortName)
end
sinks[shortName] = {name, desc}
-- Save it for library upgrades.
if not sink.registeredSinks then sink.registeredSinks = {} end
sink.registeredSinks[shortName] = sinks[shortName]
if type(func) == "function" then
sink.handlers[shortName] = func
else
sink.handlers[shortName] = function(...)
self[func](self, ...)
end
end
if type(scrollAreaFunc) == "function" then
sink.registeredScrollAreaFunctions[shortName] = scrollAreaFunc
elseif type(scrollAreaFunc) == "string" then
sink.registeredScrollAreaFunctions[shortName] = function(...)
return self[scrollAreaFunc](self, ...)
end
end
sink.stickyAddons[shortName] = hasSticky and true or nil
for k, v in pairs(sinkOptionGenerators) do
v(shortName, sinks[shortName])
end
end
end
function sink.SetSinkStorage(addon, storage)
assert(type(addon) == "table")
assert(type(storage) == "table", "Storage must be a table")
sink.storageForAddon[addon] = storage
end
-- Sets a sink override for -all- addons, librarywide.
function sink:SetSinkOverride(override)
assert(type(override) == "string" or override == nil)
if override and not sink.handlers[override] then
error("There's no %q sink.", override)
end
sink.override = override
end
-- Put this at the bottom, because we need the local functions to exist first.
local handlers = {
Parrot = parrot,
SCT = sct,
MikSBT = msbt,
BCF = bcf,
ChatFrame = chat,
Channel = channel,
UIErrorsFrame = uierror,
Blizzard = blizzard,
RaidWarning = rw,
None = noop,
}
-- Overwrite any handler functions from the old library
for k, v in pairs(handlers) do
sink.handlers[k] = v
end
-----------------------------------------------------------------------
-- Embed handling
sink.embeds = sink.embeds or {}
local mixins = {
"Pour", "RegisterSink", "SetSinkStorage",
"GetSinkAce2OptionsDataTable", "GetSinkAce3OptionsDataTable"
}
function sink:Embed(target)
sink.embeds[target] = true
for _,v in pairs(mixins) do
target[v] = sink[v]
end
return target
end
for addon in pairs(sink.embeds) do
sink:Embed(addon)
end
+7
View File
@@ -0,0 +1,7 @@
<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/
C:\Projects\WoW\Bin\Interface\FrameXML\UI.xsd">
<script file="LibSink-2.0.lua"/>
</Ui>