You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

859 lines
30 KiB

--@curseforge-project-slug: libsink-2-0@
-----------------------------------------------------------------------
-- Sink-2.0
local SINK20 = "LibSink-2.0"
local SINK20_MINOR = 110002
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,
}
-- Upgrade complete
local _G = _G
local format, gsub, wipe, next, select = string.format, string.gsub, table.wipe, next, select
local IsInRaid, IsInGroup, SendChatMessage = IsInRaid, IsInGroup, SendChatMessage
-- Make sure FCT is loaded
local EnableAddOn = C_AddOns.EnableAddOn or EnableAddOn
local IsAddOnLoaded = C_AddOns.IsAddOnLoaded or IsAddOnLoaded
local LoadAddOn = C_AddOns.LoadAddOn or LoadAddOn
EnableAddOn("Blizzard_CombatText")
local loadFCT = nil
if not IsAddOnLoaded("Blizzard_CombatText") then
loadFCT = function()
loadFCT = nil
LoadAddOn("Blizzard_CombatText")
end
end
local L = {}
L.DEFAULT = "Default"
L.CHAT = "Chat"
L.NONE = "None"
L.RW = "Raid Warning"
L.BLIZZARD = "Floating Combat Text"
L.CHANNEL = "Channel"
L.SAY = "Say"
L.PARTY = "Party"
L.INSTANCE_CHAT = "Instance"
L.GUILD_CHAT = "Guild Chat"
L.OFFICER_CHAT = "Officer Chat"
L.YELL = "Yell"
L.RAID = "Raid"
L.RAID_WARNING = "Raid Warning"
L.GROUP = "Group"
L.DEFAULT_DESC = "Route output from this addon through the first available handler, preferring scrolling combat text addons if available."
L.ROUTE = "Route output from this addon through %s."
L.UIERROR = "Blizzard Error Frame"
L.OUTPUT = "Output"
L.OUTPUT_DESC = "Where to route the output from this addon."
L.SCROLL = "Sub section"
L.SCROLL_DESC = "Set the sub section where messages should appear.\n\nOnly available for some outputs."
L.STICKY = "Sticky"
L.STICKY_DESC = "Set messages from this addon to appear as sticky.\n\nOnly available for some outputs."
L.NONE_DESC = "Hide all messages from this addon."
L.NOTINCHANNEL = "LibSink: %s (Sending to channel '%s' failed, you're not in it)"
do
local l = GetLocale()
if l == "koKR" then
L.DEFAULT = "기본"
L.CHAT = "대화"
L.NONE = "없음"
L.RW = "공격대 경보"
L.BLIZZARD = "전투 상황 알림"
L.CHANNEL = "채널"
L.SAY = "일반 대화"
L.PARTY = "파티"
L.INSTANCE_CHAT = "인스턴스"
L.GUILD_CHAT = "길드 대화"
L.OFFICER_CHAT = "길드관리자 대화"
L.YELL = "외침"
L.RAID = "공격대"
L.RAID_WARNING = "공격대 경보"
L.GROUP = "파티"
L["DEFAULT_DESC"] = "처음으로 사용 가능한 트레이너를 통해 이 애드온으로부터 출력을 보냅니다." -- Needs review
L["NONE_DESC"] = "이 애드온의 모든 메시지를 숨김니다." -- Needs review
L["NOTINCHANNEL"] = "LibSink: %s (%s 채널로 전송 실패)" -- Needs review
L["OUTPUT"] = "출력" -- Needs review
L["OUTPUT_DESC"] = "어디에 이 애드온의 메시지를 출력할지 선택합니다." -- Needs review
L["ROUTE"] = "%s|1을;를; 통해 이 애드온의 메시지를 출력합니다." -- Needs review
L["SCROLL"] = "스크롤 영역" -- Needs review
L["SCROLL_DESC"] = "메시지를 출력할 스크룰 영역을 설정합니다." -- Needs review
L["STICKY"] = "점착" -- Needs review
L["STICKY_DESC"] = "달라붙는 것처럼 보일 이 애드온의 메시지를 설정합니다." -- Needs review
L["UIERROR"] = "블리자드 오류 창" -- Needs review
elseif l == "frFR" then
L.DEFAULT = "Défaut"
L.CHAT = "Discussion"
L.NONE = "Aucun"
L.RW = "Avertissement Raid"
L.BLIZZARD = "Texte de combat flottant"
L.CHANNEL = "Canal"
L.SAY = "Dire"
L.PARTY = "Groupe"
L.INSTANCE_CHAT = "Instance"
L.GUILD_CHAT = "Guilde"
L.OFFICER_CHAT = "Officier"
L.YELL = "Crier"
L.RAID = "Raid"
L.RAID_WARNING = "Avertissement Raid"
L.GROUP = "Groupe"
L["DEFAULT_DESC"] = "Dirige la sortie de cet addon vers le premier gestionnaire disponible, de préférence les addons de texte de combat flottant si disponibles." -- Needs review
L["NONE_DESC"] = "Cache tous les messages de cet addon." -- Needs review
L["NOTINCHANNEL"] = "LibSink : %s (l'envoi vers le canal '%s' a échoué, car vous n'êtes pas dessus)" -- Needs review
L["OUTPUT"] = "Sortie" -- Needs review
L["OUTPUT_DESC"] = "Vers où diriger la sortie de cet addon." -- Needs review
L["ROUTE"] = "Dirige la sortie de cet addon vers %s" -- Needs review
L["SCROLL"] = "Sous-section" -- Needs review
L["SCROLL_DESC"] = [=[Définit la sous-section dans laquelle les messages doivent apparaître.
Disponible uniquement pour certaines sorties.]=] -- Needs review
L["STICKY"] = "Épinglé" -- Needs review
L["STICKY_DESC"] = [=[Fait apparaître les messages de cet addon comme épinglés.
Disponible uniquement pour certaines sorties.]=] -- Needs review
L["UIERROR"] = "Cadre des erreurs de Blizzard" -- Needs review
elseif l == "deDE" then
L.DEFAULT = "Standard"
L.CHAT = "Chat"
L.NONE = "Nichts"
L.RW = "Schlachtzugswarnung"
L.BLIZZARD = "Schwebender Kampftext"
L.CHANNEL = "Channel"
L.SAY = "Sagen"
L.PARTY = "Gruppe"
L.INSTANCE_CHAT = "Instanz"
L.GUILD_CHAT = "Gildenchat"
L.OFFICER_CHAT = "Offizierchat"
L.YELL = "Schreien"
L.RAID = "Schlachtzug"
L.RAID_WARNING = "Schlachtzugswarnung"
L.GROUP = "Gruppe"
L["DEFAULT_DESC"] = "Die Ausgaben dieses Addons werden durch den ersten verfügbaren Handler geleitet, es werden Schwebender-Kampftext-Addons bevorzugt, wenn diese vorhanden sind."
L["NONE_DESC"] = "Alle Meldungen dieses Addons verstecken."
L["NOTINCHANNEL"] = "LibSink : %s (Senden auf Channel \"%s\" gescheitert, da du nicht in ihm bist)"
L["OUTPUT"] = "Ausgabe"
L["OUTPUT_DESC"] = "Wohin die Ausgaben dieses Addons geleitet werden sollen."
L["ROUTE"] = "Die Ausgaben dieses Addons werden durch %s geleitet."
L["SCROLL"] = "Unterabschnitt"
L["SCROLL_DESC"] = [=[Stelle den Unterabschnitt ein, in dem die Nachrichten erscheinen sollen.
Dies ist nur für manche Ausgaben verfügbar.]=]
L["STICKY"] = "Fixiert"
L["STICKY_DESC"] = [=[Lässt Nachrichten dieses Addons als fixiert erscheinen, das heißt, dass die Ausgaben an einer festen Position auf dem Bildschirm erscheinen und dort wieder verschwinden.
Dies ist nur für manche Ausgaben verfügbar.]=]
L["UIERROR"] = "Blizzards Fehlerfenster"
elseif l == "zhCN" then
L.DEFAULT = "默认"
L.CHAT = "聊天"
L.NONE = ""
L.RW = "团队通知"
L.BLIZZARD = "浮动战斗信息"
L.CHANNEL = "频道"
L.SAY = ""
L.PARTY = "小队"
L.INSTANCE_CHAT = "副本"
L.GUILD_CHAT = "公会聊天"
L.OFFICER_CHAT = "官员聊天"
L.YELL = "大喊"
L.RAID = "团队"
L.RAID_WARNING = "团队通知"
L.GROUP = "小队"
L["DEFAULT_DESC"] = "从这个插件路由输出到第一个可用的处理程序,倾向于可用的滚动战斗文本插件。"
L["NONE_DESC"] = "隐藏此插件全部消息。"
L["NOTINCHANNEL"] = "LibSink:%s(发送到频道“%s”失败,不在此频道)"
L["OUTPUT"] = "输出"
L["OUTPUT_DESC"] = "从此插件路由输出。"
L["ROUTE"] = "从此插件通过%s路由输出。"
L["SCROLL"] = "子区段"
L["SCROLL_DESC"] = [=[设置子区段消息出现状态。
只在一些输出可用。]=]
L["STICKY"] = "固定"
L["STICKY_DESC"] = [=[设置信息从此插件出现状态为固定。
只在一些输出可用。]=]
L["UIERROR"] = "暴雪错误框体"
elseif l == "zhTW" then
L.DEFAULT = "預設值"
L.CHAT = "對話"
L.NONE = ""
L.RW = "團隊警告"
L.BLIZZARD = "浮動戰鬥文字"
L.CHANNEL = "頻道"
L.SAY = ""
L.PARTY = "隊伍"
L.INSTANCE_CHAT = "副本"
L.GUILD_CHAT = "公會對話"
L.OFFICER_CHAT = "幹部對話"
L.YELL = "大喊"
L.RAID = "團隊"
L.RAID_WARNING = "團隊警告"
L.GROUP = "小隊"
L["DEFAULT_DESC"] = "從這個插件路由輸出到第一個可用的處理程式,傾向於可用的滾動戰鬥文本插件。"
L["NONE_DESC"] = "隱藏此插件全部訊息。"
L["NOTINCHANNEL"] = "LibSink:%s(發送到頻道“%s”失敗,不在此頻道)"
L["OUTPUT"] = "輸出"
L["OUTPUT_DESC"] = "從此插件路由輸出。"
L["ROUTE"] = "從此插件通過%s路由輸出。"
L["SCROLL"] = "子區段"
L["SCROLL_DESC"] = [=[設置子區段訊息出現狀態。
只在一些輸出可用。 ]=]
L["STICKY"] = "固定"
L["STICKY_DESC"] = [=[設置訊息從此插件出現狀態為固定。
只在一些輸出可用。 ]=]
L["UIERROR"] = "暴雪錯誤框體"
elseif l == "ruRU" then
L.DEFAULT = "По умолчанию"
L.CHAT = "Каналы"
L.NONE = "Нет"
L.RW = "Объявление рейду"
L.BLIZZARD = "Текст боя"
L.CHANNEL = "Канал"
L.SAY = "Речь"
L.PARTY = "Группа"
L.INSTANCE_CHAT = "Подземелье"
L.GUILD_CHAT = "Канал гильдии"
L.OFFICER_CHAT = "Канал офицеров"
L.YELL = "Крик"
L.RAID = "Рейд"
L.RAID_WARNING = "Объявление рейду"
L.GROUP = "Группа"
L["DEFAULT_DESC"] = "Направлять вывод из этого аддона через первый доступный обработчик, предпочитая аддоны прокрутки журнала боя если они доступны."
L["NONE_DESC"] = "Скрыть все сообщения этого аддона"
L["NOTINCHANNEL"] = "LibSink: %s (Отправка в канал '%s' неудачна, вы не в нем)"
L["OUTPUT"] = "Вывод"
L["OUTPUT_DESC"] = "Куда направлять вывод из этого аддона."
L["ROUTE"] = "Направлять вывод из этого аддона через %s."
L["SCROLL"] = "Подразделы"
L["SCROLL_DESC"] = [=[Установить подраздел, где должны появляться сообщения.
Доступно только для некоторых выводов.]=]
L["STICKY"] = "Прикрепление"
L["STICKY_DESC"] = [=[Прикреплять сообщения из этого аддона
Доступно только для некоторых выводов.]=]
L["UIERROR"] = "Фрейм ошибок Blizzard."
elseif l == "esES" or l == "esMX" then
L.DEFAULT = "Predeterminado"
L.CHAT = "Chat"
L.NONE = "Ninguno"
L.RW = "Aviso de la banda"
L.BLIZZARD = "Texto flotante de combate"
L.CHANNEL = "Canal"
L.SAY = "Hablar"
L.PARTY = "Grupo"
L.INSTANCE_CHAT = "Estancia"
L.GUILD_CHAT = "Chat de hermandad"
L.OFFICER_CHAT = "Chat de oficiales"
L.YELL = "Gritar"
L.RAID = "Banda"
L.RAID_WARNING = "Aviso de la banda"
L.GROUP = "Grupo"
L["DEFAULT_DESC"] = "Ruta de salida de este addon mediante el primer controlador disponible, prefiriendo el desplazamiento de texto de combate si está disponible." -- Needs review
L["NONE_DESC"] = "Oculta todos los mensajes de este addon." -- Needs review
L["NOTINCHANNEL"] = "LibSink: %s (Falló al enviar al canal '%s', no estás en el)" -- Needs review
L["OUTPUT"] = "Salida" -- Needs review
L["OUTPUT_DESC"] = "Donde se ajustará la ruta de salida de este addon." -- Needs review
L["ROUTE"] = "Ruta de salida de este addon mediante %s." -- Needs review
L["SCROLL"] = "Sub sección." -- Needs review
L["SCROLL_DESC"] = [=[Ajusta la sub sección donde los mensajes deben aparecer.
Disponible sólo para algunas salidas.]=] -- Needs review
L["STICKY"] = "Chincheta" -- Needs review
L["STICKY_DESC"] = [=[Ajusta los mensajes de este addon para que aparezcan como chincheta.
Disponible sólo para algunas salidas.]=] -- Needs review
L["UIERROR"] = "Marco de Errores de Blizzard" -- Needs review
elseif l == "ptBR" then
L.DEFAULT = "Padrão"
L.CHAT = "Bate-papo"
L.NONE = "Nenhum"
L.RW = "Aviso do raide"
L.BLIZZARD = "Texto de combate"
L.CHANNEL = "Canal"
L.SAY = "Dizer"
L.PARTY = "Grupo"
L.INSTANCE_CHAT = "Instância"
L.GUILD_CHAT = "Bate-papo da guilda"
L.OFFICER_CHAT = "Bate-papo de oficiais"
L.YELL = "Gritar"
L.RAID = "Raide"
L.RAID_WARNING = "Aviso do raide"
L.GROUP = "Grupo"
--L.DEFAULT_DESC = "Route output from this addon through the first available handler, preferring scrolling combat text addons if available."
--L.ROUTE = "Route output from this addon through %s."
--L.UIERROR = "Blizzard Error Frame"
--L.OUTPUT = "Output"
--L.OUTPUT_DESC = "Where to route the output from this addon."
--L.SCROLL = "Sub section"
--L.SCROLL_DESC = "Set the sub section where messages should appear.\n\nOnly available for some outputs."
--L.STICKY = "Sticky"
--L.STICKY_DESC = "Set messages from this addon to appear as sticky.\n\nOnly available for some outputs."
--L.NONE_DESC = "Hide all messages from this addon."
--L.NOTINCHANNEL = "LibSink: %s (Sending to channel '%s' failed, you're not in it)"
elseif l == "itIT" then
L.DEFAULT = "Predefinito"
L.CHAT = "Chat"
L.NONE = "Nessuno"
L.RW = "Avviso incursione"
L.BLIZZARD = "Testo di combattimento"
L.CHANNEL = "Canale"
L.SAY = "Parla"
L.PARTY = "Gruppo"
L.INSTANCE_CHAT = "Istanza"
L.GUILD_CHAT = "Chat di gilda"
L.OFFICER_CHAT = "Chat degli ufficiali"
L.YELL = "Urla"
L.RAID = "Incursione"
L.RAID_WARNING = "Avviso incursione"
L.GROUP = "Gruppo"
L["DEFAULT_DESC"] = "Indirizza l'uscita da questo addon attraverso il primo metodo di uscita disponibile, preferibilmente un addon visivo a schermo se disponibile."
L["NONE_DESC"] = "Nasconti tutti i messaggi per questo addon."
L["NOTINCHANNEL"] = "LibSink: %s (Invio al canale '%s' non riuscito, non sei dentro)"
L["OUTPUT"] = "Uscita"
L["OUTPUT_DESC"] = "Dove indirizzare l'uscita da questo addon."
L["ROUTE"] = "Indirizza l'uscita da questo addon attraverso %s."
L["SCROLL"] = "Sotto sezione"
L["SCROLL_DESC"] = [=[Imposta la sotto sezione in cui i messaggi devono apparire.
Disponibile solo per alcune uscite.]=]
L["STICKY"] = "Importante"
L["STICKY_DESC"] = [=[Imposta i messaggi di questo addon di apparire come importanti.
Disponibile solo per alcune uscite.]=]
L["UIERROR"] = "Frame Errore Blizzard"
end
end
local SML = LibStub("LibSharedMedia-3.0", true)
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 = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20Sticky 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 = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20Sticky or sticky
MikSBT.DisplayMessage(text, location, s, r * 255, g * 255, b * 255, size, font, msbt_outlines[outline], icon)
end
local function blizzard(addon, text, r, g, b, font, size, outline, sticky, _, icon)
if icon then text = "\124T"..icon..":15:15:0:0:64:64:4:60:4:60\124t "..text end
local s = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20Sticky or sticky
if loadFCT then loadFCT() end
CombatText_AddMessage(text, CombatText_StandardScroll, r, g, b, s and "crit" or nil, false)
end
sink.channelMapping = sink.channelMapping or {
[L.SAY] = "SAY",
[L.PARTY] = "PARTY",
[L.INSTANCE_CHAT] = "INSTANCE_CHAT",
[L.GUILD_CHAT] = "GUILD",
[L.OFFICER_CHAT] = "OFFICER",
[L.YELL] = "YELL",
[L.RAID] = "RAID",
[L.RAID_WARNING] = "RAID_WARNING",
[L.GROUP] = "GROUP",
}
sink.channelMappingIds = sink.channelMappingIds or {}
sink.frame = sink.frame or CreateFrame("Frame")
sink.frame:UnregisterAllEvents()
sink.frame:RegisterEvent("CHANNEL_UI_UPDATE")
sink.frame:RegisterEvent("PLAYER_ENTERING_WORLD")
do
local function loop(...)
wipe(sink.channelMappingIds)
for i = 1, select("#", ...), 3 do
local id, name = select(i, ...)
sink.channelMappingIds[name] = id
end
for k, v in next, sink.channelMapping do
if v == "CHANNEL" and not sink.channelMappingIds[k] then
sink.channelMapping[k] = nil
end
end
for k in next, sink.channelMappingIds do sink.channelMapping[k] = "CHANNEL" end
end
local function rescanChannels() loop(GetChannelList()) end
sink.frame:SetScript("OnEvent", rescanChannels)
rescanChannels()
end
local function color_strip(a, b, c)
if b:sub(1,2) == "|H" then
return a..b..c
else
return b
end
end
local function channel(addon, text)
-- Sanitize the text, remove all color codes & icons
text = gsub(text, "(|c%x%x%x%x%x%x%x%x)(.-)(|r)", color_strip)
text = gsub(text, "|T.-|t", "")
local loc = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20ScrollArea
local chan = sink.channelMapping[loc]
if chan == "GROUP" then
chan = (IsInGroup(2) and "INSTANCE_CHAT") or (IsInRaid() and "RAID") or (IsInGroup() and "PARTY") or "SAY"
elseif chan == "CHANNEL" then
local id, name = GetChannelName(sink.channelMappingIds[loc])
if name then
SendChatMessage(text, "CHANNEL", nil, id)
else
print(format(L.NOTINCHANNEL, text, loc))
end
return
end
SendChatMessage(text, chan or "SAY")
end
-- |TTexturePath:size1:size2:xoffset:yoffset:dimx:dimy:coordx1:coordx2:coordy1:coordy2:red:green:blue|t
local function chat(_, text, r, g, b, _, _, _, _, _, icon)
if icon then text = "\124T"..icon..":15:15:0:0:64:64:4:60:4:60\124t"..text end
DEFAULT_CHAT_FRAME:AddMessage(text, r, g, b)
end
local function uierror(_, text, r, g, b, _, _, _, _, _, icon)
if icon then text = "\124T"..icon..":15:15:0:0:64:64:4:60:4:60\124t "..text end
UIErrorsFrame:AddMessage(text, r, g, b, 1.0)
end
local rw
do
local white = {r = 1, g = 1, b = 1}
function rw(_, text, r, g, b, _, _, _, _, _, icon)
if r or g or b then
text = format("\124cff%02x%02x%02x%s\124r", (r or 0) * 255, (g or 0) * 255, (b or 0) * 255, text)
end
if icon then text = "\124T"..icon..":15:15:0:0:64:64:4:60:4:60\124t "..text end
RaidNotice_AddMessage(RaidWarningFrame, text, white)
end
end
local function noop() --[[ noop! ]] end
local handlerPriority = { "SCT", "MikSBT" }
local customHandlersEnabled = {
SCT = function()
return _G.SCT and _G.SCT:IsEnabled()
end,
MikSBT = function()
return _G.MikSBT and not _G.MikSBT.IsModDisabled()
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 = 1, #handlerPriority do
local handler = handlerPriority[i]
local check = customHandlersEnabled[handler]
if check and check() then
currentHandler = handler
return sink.handlers[handler]
end
end
return blizzard
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 sctFrames = {"Incoming", "Outgoing", "Messages"}
local msbtFrames = nil
local function getScrollAreasForAddon(addon)
if type(addon) ~= "string" then return nil end
if addon == "MikSBT" then
if not msbtFrames then
msbtFrames = {}
for _, name in MikSBT.IterateScrollAreas() do
msbtFrames[#msbtFrames+1] = name
end
end
return msbtFrames
elseif addon == "SCT" then
return sctFrames
elseif addon == "Channel" then
local tmp = {}
for k in next, 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 = {"Scrolling Combat Text (SCT)", nil, shouldDisableSCT},
MikSBT = {"MikSBT", nil, shouldDisableMSBT},
Blizzard = {L.BLIZZARD},
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 format(L.ROUTE, 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 format(L.ROUTE, 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
local tbl = {}
for i = 1, #sa do
local n = sa[i]
tbl[n] = n
end
options["Ace3"][addon].args.ScrollArea.values = tbl
options["Ace3"][addon].args.ScrollArea.disabled = nil
else
options["Ace3"][addon].args.ScrollArea.disabled = true
options["Ace3"][addon].args.ScrollArea.values = emptyTable
end
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
local tbl = {}
for i = 1, #sa do
local n = sa[i]
tbl[n] = n
end
options["Ace3"][addon].args.ScrollArea.values = tbl
options["Ace3"][addon].args.ScrollArea.disabled = nil
else
options["Ace3"][addon].args.ScrollArea.disabled = true
options["Ace3"][addon].args.ScrollArea.values = emptyTable
end
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 next, sinkOptionGenerators do
options[generatorName] = options[generatorName] or {}
args[generatorName] = args[generatorName] or {}
for name, opts in next, 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(scrollAreaFunc) == "function" or type(scrollAreaFunc) == "string" or scrollAreaFunc == nil)
assert(type(hasSticky) == "boolean" or hasSticky == nil)
if sinks[shortName] or sink.handlers[shortName] then
error(format("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 _, v in next, 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 = {
SCT = sct,
MikSBT = msbt,
ChatFrame = chat,
Channel = channel,
UIErrorsFrame = uierror,
Blizzard = blizzard,
RaidWarning = rw,
None = noop,
}
-- Overwrite any handler functions from the old library
for k, v in next, 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 next, mixins do
target[v] = sink[v]
end
return target
end
for addon in next, sink.embeds do
sink:Embed(addon)
end