Files
coa-elvui/ElvUI/Modules/Chat/Chat.lua
T
2024-09-11 09:10:13 -07:00

2075 lines
66 KiB
Lua

local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
local CH = E:GetModule("Chat")
local LO = E:GetModule("Layout")
local Skins = E:GetModule("Skins")
local LibBase64 = E.Libs.Base64
local LSM = E.Libs.LSM
--Lua functions
local _G = _G
local time = time
local pairs, ipairs, unpack, select, tostring, pcall, next, tonumber, type = pairs, ipairs, unpack, select, tostring, pcall, next, tonumber, type
local tinsert, tremove, tconcat, wipe = table.insert, table.remove, table.concat, table.wipe
local gsub, find, gmatch, format, strtrim = string.gsub, string.find, string.gmatch, string.format, string.trim
local strlower, strmatch, strsub, strlen, strupper = strlower, strmatch, strsub, strlen, strupper
--WoW API / Variables
local BetterDate = BetterDate
local ChatEdit_ActivateChat = ChatEdit_ActivateChat
local ChatEdit_ChooseBoxForSend = ChatEdit_ChooseBoxForSend
local ChatEdit_ParseText = ChatEdit_ParseText
local ChatEdit_SetLastTellTarget = ChatEdit_SetLastTellTarget
local ChatFrame_ConfigEventHandler = ChatFrame_ConfigEventHandler
local ChatFrame_GetMessageEventFilters = ChatFrame_GetMessageEventFilters
local ChatFrame_SendTell = ChatFrame_SendTell
local ChatFrame_SystemEventHandler = ChatFrame_SystemEventHandler
local ChatHistory_GetAccessID = ChatHistory_GetAccessID
local Chat_GetChatCategory = Chat_GetChatCategory
local CreateFrame = CreateFrame
local FCFManager_ShouldSuppressMessage = FCFManager_ShouldSuppressMessage
local FCFTab_UpdateAlpha = FCFTab_UpdateAlpha
local FCF_GetCurrentChatFrame = FCF_GetCurrentChatFrame
local FCF_SavePositionAndDimensions = FCF_SavePositionAndDimensions
local FCF_SetLocked = FCF_SetLocked
local FCF_StartAlertFlash = FCF_StartAlertFlash
local FloatingChatFrame_OnEvent = FloatingChatFrame_OnEvent
local GMChatFrame_IsGM = GMChatFrame_IsGM
local GetChannelName = GetChannelName
local GetGuildRosterMOTD = GetGuildRosterMOTD
local GetMouseFocus = GetMouseFocus
local GetNumPartyMembers = GetNumPartyMembers
local GetNumRaidMembers = GetNumRaidMembers
local GetPlayerInfoByGUID = GetPlayerInfoByGUID
local HasLFGRestrictions = HasLFGRestrictions
local InCombatLockdown = InCombatLockdown
local IsAltKeyDown = IsAltKeyDown
local IsInInstance = IsInInstance
local IsMouseButtonDown = IsMouseButtonDown
local IsShiftKeyDown = IsShiftKeyDown
local PlaySoundFile = PlaySoundFile
local ScrollFrameTemplate_OnMouseWheel = ScrollFrameTemplate_OnMouseWheel
local StaticPopup_Visible = StaticPopup_Visible
local ToggleFrame = ToggleFrame
local UnitIsSameServer = UnitIsSameServer
local UnitName = UnitName
local hooksecurefunc = hooksecurefunc
local AFK = AFK
local CHAT_BN_CONVERSATION_GET_LINK = CHAT_BN_CONVERSATION_GET_LINK
local CHAT_FILTERED = CHAT_FILTERED
local CHAT_FRAMES = CHAT_FRAMES
local CHAT_IGNORED = CHAT_IGNORED
local CHAT_OPTIONS = CHAT_OPTIONS
local CHAT_RESTRICTED = CHAT_RESTRICTED
local DEFAULT_CHAT_FRAME = DEFAULT_CHAT_FRAME
local DND = DND
local ICON_LIST = ICON_LIST
local ICON_TAG_LIST = ICON_TAG_LIST
local MAX_WOW_CHAT_CHANNELS = MAX_WOW_CHAT_CHANNELS
local NUM_CHAT_WINDOWS = NUM_CHAT_WINDOWS
local RAID_WARNING = RAID_WARNING
local throttle = {}
CH.GuidCache = {}
CH.ClassNames = {}
CH.Keywords = {}
CH.Smileys = {}
local DEFAULT_STRINGS = {
BATTLEGROUND = L["BG"],
GUILD = L["G"],
PARTY = L["P"],
RAID = L["R"],
OFFICER = L["O"],
BATTLEGROUND_LEADER = L["BGL"],
PARTY_LEADER = L["PL"],
RAID_LEADER = L["RL"],
}
local hyperlinkTypes = {
["item"] = true,
["spell"] = true,
["unit"] = true,
["quest"] = true,
["enchant"] = true,
["achievement"] = true,
["instancelock"] = true,
["talent"] = true,
["glyph"] = true,
}
local tabTexs = {
"",
"Selected",
"Highlight"
}
local historyTypes = { -- the events set on the chats are still in FindURL_Events, this is used to ignore some types only
CHAT_MSG_WHISPER = "WHISPER",
CHAT_MSG_WHISPER_INFORM = "WHISPER",
CHAT_MSG_BN_WHISPER = "WHISPER",
CHAT_MSG_BN_WHISPER_INFORM = "WHISPER",
CHAT_MSG_GUILD = "GUILD",
CHAT_MSG_GUILD_ACHIEVEMENT = "GUILD",
CHAT_MSG_OFFICER = "OFFICER",
CHAT_MSG_PARTY = "PARTY",
CHAT_MSG_PARTY_LEADER = "PARTY",
CHAT_MSG_RAID = "RAID",
CHAT_MSG_RAID_LEADER = "RAID",
CHAT_MSG_RAID_WARNING = "RAID",
CHAT_MSG_BATTLEGROUND = "BATTLEGROUND",
CHAT_MSG_BATTLEGROUND_LEADER = "BATTLEGROUND",
CHAT_MSG_CHANNEL = "CHANNEL",
CHAT_MSG_SAY = "SAY",
CHAT_MSG_YELL = "YELL",
CHAT_MSG_EMOTE = "EMOTE" -- this never worked, check it sometime.
}
function CH:RemoveSmiley(key)
if key and (type(key) == "string") then
CH.Smileys[key] = nil
end
end
function CH:AddSmiley(key, texture)
if key and (type(key) == "string" and not find(key, ":%%", 1, true)) and texture then
CH.Smileys[key] = texture
end
end
local specialChatIcons
do --this can save some main file locals
local y = ":13:25"
-- local ElvMelon = E:TextureString(E.Media.ChatLogos.ElvMelon,y)
-- local ElvRainbow = E:TextureString(E.Media.ChatLogos.ElvRainbow,y)
-- local ElvRed = E:TextureString(E.Media.ChatLogos.ElvRed,y)
-- local ElvOrange = E:TextureString(E.Media.ChatLogos.ElvOrange,y)
-- local ElvYellow = E:TextureString(E.Media.ChatLogos.ElvYellow,y)
-- local ElvGreen = E:TextureString(E.Media.ChatLogos.ElvGreen,y)
-- local ElvBlue = E:TextureString(E.Media.ChatLogos.ElvBlue,y)
-- local ElvPurple = E:TextureString(E.Media.ChatLogos.ElvPurple,y)
local ElvPink = E:TextureString(E.Media.ChatLogos.ElvPink,y)
specialChatIcons = {
["Крольчонак-x100"] = ElvPink,
}
end
local function ChatFrame_OnMouseScroll(frame, delta)
if delta < 0 then
if IsShiftKeyDown() then
frame:ScrollToBottom()
elseif IsAltKeyDown() then
frame:ScrollDown()
else
for _ = 1, (CH.db.numScrollMessages or 3) do
frame:ScrollDown()
end
end
elseif delta > 0 then
if IsShiftKeyDown() then
frame:ScrollToTop()
elseif IsAltKeyDown() then
frame:ScrollUp()
else
for _ = 1, (CH.db.numScrollMessages or 3) do
frame:ScrollUp()
end
end
if CH.db.scrollDownInterval ~= 0 then
if frame.ScrollTimer then
CH:CancelTimer(frame.ScrollTimer, true)
end
frame.ScrollTimer = CH:ScheduleTimer("ScrollToBottom", CH.db.scrollDownInterval, frame)
end
end
end
function CH:GetGroupDistribution()
local inInstance, kind = IsInInstance()
if inInstance and (kind == "pvp") then
return "/bg "
elseif GetNumRaidMembers() > 0 then
return "/ra "
elseif GetNumPartyMembers() > 0 then
return "/p "
else
return "/s "
end
end
function CH:InsertEmotions(msg)
for word in gmatch(msg, "%s-(%S+)%s*") do
local pattern = E:EscapeString(word)
local emoji = CH.Smileys[pattern]
if emoji then
pattern = format("%s%s%s", "([%s%p]-)", pattern, "([%s%p]*)")
if strmatch(msg, pattern) then
local base64 = LibBase64:Encode(word)
if base64 then
msg = gsub(msg, pattern, format("%s%s%s%s%s", "%1|Helvmoji:%%", base64, "|h|cFFffffff|r|h", emoji, "%2"))
else
msg = gsub(msg, pattern, format("%s%s%s", "%1", emoji, "%2"))
end
end
end
end
return msg
end
function CH:GetSmileyReplacementText(msg)
if not msg or not self.db.emotionIcons or find(msg, "/run") or find(msg, "/dump") or find(msg, "/script") then
return msg
end
local origlen = strlen(msg)
local startpos = 1
local outstr = ""
local _, pos, endpos
while startpos <= origlen do
pos = find(msg, "|H", startpos, true)
endpos = pos or origlen
outstr = outstr .. CH:InsertEmotions(strsub(msg, startpos, endpos)) --run replacement on this bit
startpos = endpos + 1
if pos then
_, endpos = find(msg, "|h.-|h", startpos)
endpos = endpos or origlen
if startpos < endpos then
outstr = outstr .. strsub(msg, startpos, endpos) --don't run replacement on this bit
startpos = endpos + 1
end
end
end
return outstr
end
function CH:StyleChat(frame)
local name = frame:GetName()
_G[name.."TabText"]:FontTemplate(LSM:Fetch("font", self.db.tabFont), self.db.tabFontSize, self.db.tabFontOutline)
if frame.styled then return end
frame:SetFrameLevel(4)
frame:SetClampRectInsets(0, 0, 0, 0)
frame:SetClampedToScreen(false)
frame:StripTextures(true)
_G[name.."ButtonFrame"]:Kill()
local id = frame:GetID()
local tab = _G[name.."Tab"]
local editbox = _G[name.."EditBox"]
local language = _G[name.."EditBoxLanguage"]
--Character count
local charCount = editbox:CreateFontString()
charCount:FontTemplate()
charCount:SetTextColor(190, 190, 190, 0.4)
charCount:Point("TOPRIGHT", editbox, "TOPRIGHT", -5, 0)
charCount:Point("BOTTOMRIGHT", editbox, "BOTTOMRIGHT", -5, 0)
charCount:SetJustifyH("CENTER")
charCount:Width(40)
editbox.characterCount = charCount
for _, texName in ipairs(tabTexs) do
_G[format("%sTab%sLeft", name, texName)]:SetTexture(nil)
_G[format("%sTab%sMiddle", name, texName)]:SetTexture(nil)
_G[format("%sTab%sRight", name, texName)]:SetTexture(nil)
end
tab:SetHitRectInsets(0, 0, 11, 1)
tab.glow:Point("BOTTOMLEFT", 8, 2)
tab.glow:Point("BOTTOMRIGHT", -8, 2)
hooksecurefunc(tab, "SetAlpha", function(t, alpha)
if alpha ~= 1 and (not t.isDocked or GeneralDockManager.selected:GetID() == t:GetID()) then
t:SetAlpha(1)
elseif alpha < 0.6 then
t:SetAlpha(0.6)
end
end)
tab.text = _G[name.."TabText"]
if tab.conversationIcon then
tab.text:Point("LEFT", tab.leftTexture, "RIGHT", 10, -5)
tab.conversationIcon:ClearAllPoints()
tab.conversationIcon:Point("RIGHT", tab.text, "LEFT", -1, 0)
end
local function OnTextChanged(editBox)
local text = editBox:GetText()
local len = strlen(text)
local MIN_REPEAT_CHARACTERS = CH.db.numAllowedCombatRepeat
if MIN_REPEAT_CHARACTERS ~= 0 and InCombatLockdown() then
if len > MIN_REPEAT_CHARACTERS then
local repeatChar = true
for i = 1, MIN_REPEAT_CHARACTERS do
if strsub(text, -i, -i) ~= strsub(text, (-1 - i), (-1 - i)) then
repeatChar = false
break
end
end
if repeatChar then
editBox:Hide()
return
end
end
end
if text == "/tt " then
local unitname, realm = UnitName("target")
if unitname and realm and not UnitIsSameServer("player", "target") then
unitname = format("%s-%s", unitname, gsub(realm, " ", ""))
end
if unitname then
ChatFrame_SendTell(unitname, editBox.chatFrame)
else
UIErrorsFrame:AddMessage(E.InfoColor..L["Invalid Target"])
end
elseif text == "/gr " then
editBox:SetText(CH:GetGroupDistribution()..strsub(text, 5))
ChatEdit_ParseText(editBox, 0)
end
editbox.characterCount:SetText(len > 0 and (255 - len) or "")
end
local a, b, c = select(6, editbox:GetRegions())
a:Kill()
b:Kill()
c:Kill()
_G[format("%sEditBoxFocusLeft", name)]:Kill()
_G[format("%sEditBoxFocusMid", name)]:Kill()
_G[format("%sEditBoxFocusRight", name)]:Kill()
editbox:SetTemplate(nil, true)
editbox:SetAltArrowKeyMode(CH.db.useAltKey)
editbox:SetAllPoints(LeftChatDataPanel)
editbox:Hide()
for _, text in ipairs(ElvCharacterDB.ChatEditHistory) do
editbox:AddHistoryLine(text)
end
editbox:HookScript("OnTextChanged", OnTextChanged)
self:SecureHook(editbox, "AddHistoryLine", "ChatEdit_AddHistory")
editbox:HookScript("OnEditFocusGained", function(editBox)
if not LeftChatPanel:IsShown() then
LeftChatPanel.editboxforced = true
LeftChatToggleButton:GetScript("OnEnter")(LeftChatToggleButton)
editBox:Show()
end
end)
editbox:HookScript("OnEditFocusLost", function(editBox)
if LeftChatPanel.editboxforced then
LeftChatPanel.editboxforced = nil
if LeftChatPanel:IsShown() then
LeftChatToggleButton:GetScript("OnLeave")(LeftChatToggleButton)
editBox:Hide()
end
end
end)
language:Height(22)
language:StripTextures()
language:SetTemplate("Transparent")
language:Point("LEFT", editbox, "RIGHT", -32, 0)
--copy chat button
local copyButton = CreateFrame("Frame", format("CopyChatButton%d", id), frame)
copyButton:EnableMouse(true)
copyButton:SetAlpha(0.35)
copyButton:Size(20, 22)
copyButton:Point("TOPRIGHT", 0, id == 2 and -7 or -2)
copyButton:SetFrameLevel(frame:GetFrameLevel() + 5)
frame.copyButton = copyButton
local copyTexture = frame.copyButton:CreateTexture(nil, "OVERLAY")
copyTexture:SetInside()
copyTexture:SetTexture(E.Media.Textures.Copy)
copyButton.texture = copyTexture
copyButton:SetScript("OnMouseUp", function(_, btn)
if btn == "RightButton" and id == 1 then
ToggleFrame(ChatMenu)
else
CH:CopyChat(frame)
end
end)
copyButton:SetScript("OnEnter", function(button) button:SetAlpha(1) end)
copyButton:SetScript("OnLeave", function(button)
if _G[button:GetParent():GetName().."TabText"]:IsShown() then
button:SetAlpha(0.35)
else
button:SetAlpha(0)
end
end)
frame.styled = true
end
function CH:AddMessage(msg, infoR, infoG, infoB, infoID, accessID, typeID, extraData, isHistory, historyTime)
if CH.db.timeStampFormat ~= "NONE" then
local timeStamp = BetterDate(CH.db.timeStampFormat, isHistory == "ElvUI_ChatHistory" and historyTime or time())
if CH.db.useCustomTimeColor then
local color = CH.db.customTimeColor
local hexColor = E:RGBToHex(color.r, color.g, color.b)
msg = format("%s[%s]|r %s", hexColor, timeStamp, msg)
else
msg = format("[%s] %s", timeStamp, msg)
end
end
self.OldAddMessage(self, msg, infoR, infoG, infoB, infoID, accessID, typeID, extraData)
end
function CH:UpdateSettings()
for _, frameName in ipairs(CHAT_FRAMES) do
_G[frameName.."EditBox"]:SetAltArrowKeyMode(CH.db.useAltKey)
end
end
local removeIconFromLine
do
local raidIconFunc = function(x)
x = x ~= "" and _G["RAID_TARGET_"..x]
return x and ("{"..strlower(x).."}") or ""
end
local stripTextureFunc = function(w, x, y)
if x == "" then
return (w ~= "" and w) or (y ~= "" and y) or ""
end
end
local hyperLinkFunc = function(w, x, y)
if w ~= "" then return end
local emoji = (x ~= "" and x) and strmatch(x, "elvmoji:%%(.+)")
return (emoji and LibBase64:Decode(emoji)) or y
end
removeIconFromLine = function(text)
text = gsub(text, "|TInterface\\TargetingFrame\\UI%-RaidTargetingIcon_(%d+):0|t", raidIconFunc) --converts raid icons into {star} etc, if possible.
text = gsub(text, "(%s?)(|?)|T.-|t(%s?)", stripTextureFunc) --strip any other texture out but keep a single space from the side(s).
text = gsub(text, "(|?)|H(.-)|h(.-)|h", hyperLinkFunc) --strip hyperlink data only keeping the actual text.
return text
end
end
local function colorizeLine(text, r, g, b)
local hexCode = E:RGBToHex(r, g, b)
local hexReplacement = format("|r%s", hexCode)
text = gsub(text, "|r", hexReplacement) -- If the message contains color strings then we need to add message color hex code after every "|r"
text = format("%s%s|r", hexCode, text) -- Add message color
return text
end
local chatTypeIndexToName = {}
local copyLines = {}
for chatType in pairs(ChatTypeInfo) do
chatTypeIndexToName[GetChatTypeIndex(chatType)] = chatType
end
function CH:GetLines(frame)
local lineCount = 0
local _, message, lineID, info, r, g, b
for i = 1, frame:GetNumMessages() do
message, _, lineID = frame:GetMessageInfo(i)
if message then
info = ChatTypeInfo[chatTypeIndexToName[lineID]]
if info then
r, g, b = info.r, info.g, info.b
else
r, g, b = 1, 1, 1
end
message = removeIconFromLine(message)
message = colorizeLine(message, r, g, b)
lineCount = lineCount + 1
copyLines[lineCount] = message
end
end
return lineCount
end
function CH:CopyChat(frame)
if not self.copyChatFrame:IsShown() then
local lineCount = self:GetLines(frame)
local text = tconcat(copyLines, "\n", 1, lineCount)
self.copyChatFrame.editBox:SetText(text)
self.copyChatFrame:Show()
else
self.copyChatFrame:Hide()
end
end
function CH:OnEnter(frame)
_G[frame:GetName().."Text"]:Show()
if frame.conversationIcon then
frame.conversationIcon:Show()
end
end
function CH:OnLeave(frame)
_G[frame:GetName().."Text"]:Hide()
if frame.conversationIcon then
frame.conversationIcon:Hide()
end
end
function CH:SetupChatTabs(frame, hook)
if hook and (not self.hooks or not self.hooks[frame] or not self.hooks[frame].OnEnter) then
self:HookScript(frame, "OnEnter")
self:HookScript(frame, "OnLeave")
elseif not hook and self.hooks and self.hooks[frame] and self.hooks[frame].OnEnter then
self:Unhook(frame, "OnEnter")
self:Unhook(frame, "OnLeave")
end
if not hook then
_G[frame:GetName().."Text"]:Show()
if frame.owner and frame.owner.button and GetMouseFocus() ~= frame.owner.button then
frame.owner.button:SetAlpha(0.35)
end
if frame.conversationIcon then
frame.conversationIcon:Show()
end
elseif GetMouseFocus() ~= frame then
_G[frame:GetName().."Text"]:Hide()
if frame.owner and frame.owner.button and GetMouseFocus() ~= frame.owner.button then
frame.owner.button:SetAlpha(0)
end
if frame.conversationIcon then
frame.conversationIcon:Hide()
end
end
end
function CH:UpdateAnchors()
for _, frameName in ipairs(CHAT_FRAMES) do
local frame = _G[frameName.."EditBox"]
frame:ClearAllPoints()
if not E.db.datatexts.leftChatPanel and self.db.editBoxPosition == "BELOW_CHAT" then
frame:Point("TOPLEFT", ChatFrame1, "BOTTOMLEFT", -4, -4)
frame:Point("BOTTOMRIGHT", ChatFrame1, "BOTTOMRIGHT", 7, -LeftChatTab:GetHeight() - 4)
elseif self.db.editBoxPosition == "BELOW_CHAT" then
frame:SetAllPoints(LeftChatDataPanel)
else
frame:Point("BOTTOMLEFT", ChatFrame1, "TOPLEFT", -1, 3)
frame:Point("TOPRIGHT", ChatFrame1, "TOPRIGHT", 4, LeftChatTab:GetHeight() + 3)
end
end
CH:PositionChat(true)
end
local function FindRightChatID()
local rightChatID
for id, frameName in ipairs(CHAT_FRAMES) do
local chat = _G[frameName]
if E:FramesOverlap(chat, RightChatPanel) and not E:FramesOverlap(chat, LeftChatPanel) then
rightChatID = id
break
end
end
return rightChatID
end
function CH:UpdateChatTabs()
local fadeUndockedTabs = self.db.fadeUndockedTabs
local fadeTabsNoBackdrop = self.db.fadeTabsNoBackdrop
for id, frameName in ipairs(CHAT_FRAMES) do
local chat = _G[frameName]
local tab = _G[format("%sTab", frameName)]
if chat:IsShown() and (id <= NUM_CHAT_WINDOWS) and (id == self.RightChatWindowID) then
if self.db.panelBackdrop == "HIDEBOTH" or self.db.panelBackdrop == "LEFT" then
CH:SetupChatTabs(tab, fadeTabsNoBackdrop and true or false)
else
CH:SetupChatTabs(tab, false)
end
elseif not chat.isDocked and chat:IsShown() then
tab:SetParent(RightChatPanel)
chat:SetParent(RightChatPanel)
CH:SetupChatTabs(tab, fadeUndockedTabs and true or false)
else
if self.db.panelBackdrop == "HIDEBOTH" or self.db.panelBackdrop == "RIGHT" then
CH:SetupChatTabs(tab, fadeTabsNoBackdrop and true or false)
else
CH:SetupChatTabs(tab, false)
end
end
end
end
function CH:RefreshToggleButtons()
LeftChatToggleButton:SetAlpha(E.db.LeftChatPanelFaded and E.db.chat.fadeChatToggles and 0 or 1)
RightChatToggleButton:SetAlpha(E.db.RightChatPanelFaded and E.db.chat.fadeChatToggles and 0 or 1)
end
function CH:PositionChat(override)
if (InCombatLockdown() and not override and self.initialMove) or (IsMouseButtonDown("LeftButton") and not override) then return end
if not RightChatPanel or not LeftChatPanel then return end
if not self.db.lockPositions or not E.private.chat.enable then return end
RightChatPanel:Size(self.db.separateSizes and self.db.panelWidthRight or self.db.panelWidth, self.db.separateSizes and self.db.panelHeightRight or self.db.panelHeight)
LeftChatPanel:Size(self.db.panelWidth, self.db.panelHeight)
CombatLogQuickButtonFrame_Custom:Size(LeftChatTab:GetWidth(), LeftChatTab:GetHeight())
self.RightChatWindowID = FindRightChatID()
local fadeUndockedTabs = self.db.fadeUndockedTabs
local fadeTabsNoBackdrop = self.db.fadeTabsNoBackdrop
for id, frameName in ipairs(CHAT_FRAMES) do
local BASE_OFFSET = 57 + E.Spacing*3
local chat = _G[frameName]
local tab = _G[format("%sTab", frameName)]
tab.isDocked = chat.isDocked
tab.owner = chat
if chat:IsShown() and (id <= NUM_CHAT_WINDOWS) and id == self.RightChatWindowID then
chat:ClearAllPoints()
if E.db.datatexts.rightChatPanel then
chat:Point("BOTTOMLEFT", RightChatDataPanel, "TOPLEFT", 1, 4)
else
BASE_OFFSET = BASE_OFFSET - 24
chat:Point("BOTTOMLEFT", RightChatDataPanel, "BOTTOMLEFT", 1, 2)
end
if id ~= 2 then
chat:Size((self.db.separateSizes and self.db.panelWidthRight or self.db.panelWidth) - 11, (self.db.separateSizes and self.db.panelHeightRight or self.db.panelHeight) - BASE_OFFSET)
else
chat:Size(self.db.panelWidth - 11, (self.db.panelHeight - BASE_OFFSET) - CombatLogQuickButtonFrame_Custom:GetHeight())
end
--Pass a 2nd argument which prevents an infinite loop in our ON_FCF_SavePositionAndDimensions function
if chat:GetLeft() then
FCF_SavePositionAndDimensions(chat, true)
end
tab:SetParent(RightChatPanel)
chat:SetParent(RightChatPanel)
if chat:IsMovable() then
chat:SetUserPlaced(true)
end
if self.db.panelBackdrop == "HIDEBOTH" or self.db.panelBackdrop == "LEFT" then
CH:SetupChatTabs(tab, fadeTabsNoBackdrop and true or false)
else
CH:SetupChatTabs(tab, false)
end
elseif not chat.isDocked and chat:IsShown() then
tab:SetParent(UIParent)
chat:SetParent(UIParent)
CH:SetupChatTabs(tab, fadeUndockedTabs and true or false)
else
if id ~= 2 and (id <= NUM_CHAT_WINDOWS) then
chat:ClearAllPoints()
if E.db.datatexts.leftChatPanel then
chat:Point("BOTTOMLEFT", LeftChatToggleButton, "TOPLEFT", 1, 4)
else
BASE_OFFSET = BASE_OFFSET - 24
chat:Point("BOTTOMLEFT", LeftChatToggleButton, "BOTTOMLEFT", 1, 2)
end
chat:Size(self.db.panelWidth - 11, (self.db.panelHeight - BASE_OFFSET))
--Pass a 2nd argument which prevents an infinite loop in our ON_FCF_SavePositionAndDimensions function
if chat:GetLeft() then
FCF_SavePositionAndDimensions(chat, true)
end
end
chat:SetParent(LeftChatPanel)
if id > 2 then
tab:SetParent(GeneralDockManagerScrollFrameChild)
else
tab:SetParent(GeneralDockManager)
end
if chat:IsMovable() then
chat:SetUserPlaced(true)
end
if self.db.panelBackdrop == "HIDEBOTH" or self.db.panelBackdrop == "RIGHT" then
CH:SetupChatTabs(tab, fadeTabsNoBackdrop and true or false)
else
CH:SetupChatTabs(tab, false)
end
end
end
LO:RepositionChatDataPanels()
self.initialMove = true
end
function CH:Panels_ColorUpdate()
local panelColor = self.db.panelColor
LeftChatPanel.backdrop:SetBackdropColor(panelColor.r, panelColor.g, panelColor.b, panelColor.a)
RightChatPanel.backdrop:SetBackdropColor(panelColor.r, panelColor.g, panelColor.b, panelColor.a)
end
function CH:UpdateChatTabColors()
for _, frameName in ipairs(CHAT_FRAMES) do
local tab = _G[format("%sTab", frameName)]
CH:FCFTab_UpdateColors(tab, tab.selected)
end
end
E.valueColorUpdateFuncs[CH.UpdateChatTabColors] = true
function CH:ScrollToBottom(frame)
frame:ScrollToBottom()
self:CancelTimer(frame.ScrollTimer, true)
end
function CH:PrintURL(url)
return "|cFFFFFFFF[|Hurl:"..url.."|h"..url.."|h]|r "
end
local tempURLs = {}
local tempURLsCount = 0
local function tempReplaceURL(url)
tempURLsCount = tempURLsCount + 1
local id = "|Hurl:"..tempURLsCount.."|h"
tempURLs[id] = CH:PrintURL(url)
return id
end
function CH:FindURL(event, msg, author, ...)
if not CH.db.url then
msg = CH:CheckKeyword(msg, author)
msg = CH:GetSmileyReplacementText(msg)
return false, msg, author, ...
end
local text, tag = msg, strmatch(msg, "{(.-)}")
if tag and ICON_TAG_LIST[strlower(tag)] then
text = gsub(gsub(text, "(%S)({.-})", "%1 %2"), "({.-})(%S)", "%1 %2")
end
local x, found = 0
local newMsg = gsub(gsub(text, "(%S)(|c.-|H.-|h.-|h|r)", "%1 %2"), "(|c.-|H.-|h.-|h|r)(%S)", "%1 %2")
-- https://example.com
newMsg, found = gsub(newMsg, "([A-z][A-z0-9+-%.]+://%S+)", tempReplaceURL)
x = x + found
-- www.example.com
newMsg, found = gsub(newMsg, "(www%.[A-z0-9-]+%.%S+)", tempReplaceURL)
x = x + found
-- example@example.com
newMsg, found = gsub(newMsg, "(%S+@[A-z][A-z0-9-]+%.[A-z0-9-]+)", tempReplaceURL)
x = x + found
-- 1.1.1.1[:1337]
newMsg, found = gsub(newMsg, "(%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?[:%d]*)", tempReplaceURL)
x = x + found
if x > 0 then
newMsg = gsub(newMsg, "(|Hurl:%d+|h)", tempURLs)
wipe(tempURLs)
tempURLsCount = 0
newMsg = CH:CheckKeyword(newMsg, author)
newMsg = CH:GetSmileyReplacementText(newMsg)
return false, newMsg, author, ...
end
msg = CH:CheckKeyword(msg, author)
msg = CH:GetSmileyReplacementText(msg)
return false, msg, author, ...
end
function CH:SetChatEditBoxMessage(message)
local ChatFrameEditBox = ChatEdit_ChooseBoxForSend()
local editBoxText = ChatFrameEditBox:GetText()
if not ChatFrameEditBox:IsShown() then
ChatEdit_ActivateChat(ChatFrameEditBox)
end
if editBoxText and editBoxText ~= "" then
ChatFrameEditBox:SetText("")
end
ChatFrameEditBox:Insert(message)
ChatFrameEditBox:HighlightText()
end
local function HyperLinkedURL(data)
if strsub(data, 1, 3) == "url" then
local currentLink = strsub(data, 5)
if currentLink and currentLink ~= "" then
CH:SetChatEditBoxMessage(currentLink)
end
end
end
local SetHyperlink = ItemRefTooltip.SetHyperlink
function ItemRefTooltip:SetHyperlink(data, ...)
if strsub(data, 1, 3) == "url" then
HyperLinkedURL(data)
else
SetHyperlink(self, data, ...)
end
end
local hyperLinkEntered
function CH:OnHyperlinkEnter(frame, refString)
if InCombatLockdown() then return end
local linkToken = strmatch(refString, "^([^:]+)")
if hyperlinkTypes[linkToken] then
GameTooltip:SetOwner(frame, "ANCHOR_CURSOR")
GameTooltip:SetHyperlink(refString)
GameTooltip:Show()
hyperLinkEntered = frame
end
end
function CH:OnHyperlinkLeave()
if hyperLinkEntered then
hyperLinkEntered = nil
GameTooltip:Hide()
end
end
function CH:OnMessageScrollChanged(frame)
if hyperLinkEntered == frame then
hyperLinkEntered = nil
GameTooltip:Hide()
end
end
function CH:ToggleHyperlink(enable)
for _, frameName in ipairs(CHAT_FRAMES) do
local frame = _G[frameName]
local hooked = self.hooks and self.hooks[frame] and self.hooks[frame].OnHyperlinkEnter
if enable and not hooked then
self:HookScript(frame, "OnHyperlinkEnter")
self:HookScript(frame, "OnHyperlinkLeave")
self:HookScript(frame, "OnMessageScrollChanged")
elseif not enable and hooked then
self:Unhook(frame, "OnHyperlinkEnter")
self:Unhook(frame, "OnHyperlinkLeave")
self:Unhook(frame, "OnMessageScrollChanged")
end
end
end
function CH:DisableChatThrottle()
wipe(throttle)
end
function CH:ShortChannel()
return format("|Hchannel:%s|h[%s]|h", self, DEFAULT_STRINGS[strupper(self)] or gsub(self, "channel:", ""))
end
function CH:HandleShortChannels(msg)
msg = gsub(msg, "|Hchannel:(.-)|h%[(.-)%]|h", self.ShortChannel)
msg = gsub(msg, "CHANNEL:", "")
msg = gsub(msg, "^(.-|h) "..L["whispers"], "%1")
msg = gsub(msg, "^(.-|h) "..L["says"], "%1")
msg = gsub(msg, "^(.-|h) "..L["yells"], "%1")
msg = gsub(msg, "<"..AFK..">", "[|cffFF0000"..AFK.."|r] ")
msg = gsub(msg, "<"..DND..">", "[|cffE7E716"..DND.."|r] ")
msg = gsub(msg, "^%["..RAID_WARNING.."%]", "["..L["RW"].."]")
return msg
end
local PluginIconsCalls = {}
function CH:AddPluginIcons(func)
tinsert(PluginIconsCalls, func)
end
function CH:GetPluginIcon(sender, name, realm)
local icon
for _,func in ipairs(PluginIconsCalls) do
icon = func(sender, name, realm)
if icon and icon ~= "" then break end
end
return icon
end
function CH:GetColoredName(event, _, arg2, _, _, _, _, _, arg8, _, _, _, arg12)
local chatType = strsub(event, 10)
if strsub(chatType, 1, 7) == "WHISPER" then
chatType = "WHISPER"
elseif strsub(chatType, 1, 7) == "CHANNEL" then
chatType = "CHANNEL"..arg8
end
local info = ChatTypeInfo[chatType]
if info and info.colorNameByClass and arg12 ~= "" then
local _, englishClass = GetPlayerInfoByGUID(arg12)
if englishClass then
local classColorTable = RAID_CLASS_COLORS[englishClass]
if arg12 == UnitName("player") then
classColorTable = E.media.herocolor
end
if not classColorTable then
return arg2
end
return format("\124cff%.2x%.2x%.2x", classColorTable.r*255, classColorTable.g*255, classColorTable.b*255)..arg2.."\124r"
end
end
return arg2
end
function CH:ChatFrame_MessageEventHandler(frame, event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, isHistory, historyTime, historyName)
if strsub(event, 1, 8) == "CHAT_MSG" then
local historySavedName --we need to extend the arguments on CH.ChatFrame_MessageEventHandler so we can properly handle saved names without overriding
if isHistory == "ElvUI_ChatHistory" then
historySavedName = historyName
end
local chatType = strsub(event, 10)
local info = ChatTypeInfo[chatType]
local chatFilters = ChatFrame_GetMessageEventFilters(event)
if chatFilters then
for _, filterFunc in next, chatFilters do
local filter, newarg1, newarg2, newarg3, newarg4, newarg5, newarg6, newarg7, newarg8, newarg9, newarg10, newarg11, newarg12 = filterFunc(frame, event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12)
if filter then
return true
elseif newarg1 then
arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12 = newarg1, newarg2, newarg3, newarg4, newarg5, newarg6, newarg7, newarg8, newarg9, newarg10, newarg11, newarg12
end
end
end
local _, _, englishClass, _, _, _, name, realm = pcall(GetPlayerInfoByGUID, arg12)
local coloredName = historySavedName or CH:GetColoredName(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12)
local nameWithRealm = strmatch(realm ~= "" and realm or E.myrealm, "%s*(%S+)$")
if name and name ~= "" then
nameWithRealm = name.."-"..nameWithRealm
CH.ClassNames[strlower(name)] = englishClass
CH.ClassNames[strlower(nameWithRealm)] = englishClass
end
local channelLength = strlen(arg4)
local infoType = chatType
if (strsub(chatType, 1, 7) == "CHANNEL") and (chatType ~= "CHANNEL_LIST") and ((arg1 ~= "INVITE") or (chatType ~= "CHANNEL_NOTICE_USER")) then
if arg1 == "WRONG_PASSWORD" then
local staticPopup = _G[StaticPopup_Visible("CHAT_CHANNEL_PASSWORD") or ""]
if staticPopup and strupper(staticPopup.data) == strupper(arg9) then
-- Don't display invalid password messages if we're going to prompt for a password (bug 102312)
return
end
end
local found = 0
for index, value in pairs(frame.channelList) do
if channelLength > strlen(value) then
-- arg9 is the channel name without the number in front...
if ((arg7 > 0) and (frame.zoneChannelList[index] == arg7)) or (strupper(value) == strupper(arg9)) then
found = 1
infoType = "CHANNEL"..arg8
info = ChatTypeInfo[infoType]
if (chatType == "CHANNEL_NOTICE") and (arg1 == "YOU_LEFT") then
frame.channelList[index] = nil
frame.zoneChannelList[index] = nil
end
break
end
end
end
if (found == 0) or not info then
return true
end
end
local chatGroup = Chat_GetChatCategory(chatType)
local chatTarget
if chatGroup == "CHANNEL" or chatGroup == "BN_CONVERSATION" then
chatTarget = tostring(arg8)
elseif chatGroup == "WHISPER" or chatGroup == "BN_WHISPER" then
chatTarget = strupper(arg2)
end
if FCFManager_ShouldSuppressMessage(frame, chatGroup, chatTarget) then
return true
end
if chatGroup == "WHISPER" or chatGroup == "BN_WHISPER" then
if frame.privateMessageList and not frame.privateMessageList[strlower(arg2)] then
return true
elseif frame.excludePrivateMessageList and frame.excludePrivateMessageList[strlower(arg2)] then
return true
end
elseif chatGroup == "BN_CONVERSATION" then
if frame.bnConversationList and not frame.bnConversationList[arg8] then
return true
elseif frame.excludeBNConversationList and frame.excludeBNConversationList[arg8] then
return true
end
end
if chatType == "SYSTEM" or chatType == "SKILL" or chatType == "LOOT" or chatType == "MONEY"
or chatType == "OPENING" or chatType == "TRADESKILLS" or chatType == "PET_INFO" or chatType == "TARGETICONS" then
frame:AddMessage(arg1, info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
elseif strsub(chatType,1,7) == "COMBAT_" then
frame:AddMessage(arg1, info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
elseif strsub(chatType,1,6) == "SPELL_" then
frame:AddMessage(arg1, info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
elseif strsub(chatType,1,10) == "BG_SYSTEM_" then
frame:AddMessage(arg1, info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
elseif strsub(chatType,1,11) == "ACHIEVEMENT" then
frame:AddMessage(format(arg1, "|Hplayer:"..arg2.."|h".."["..coloredName.."]".."|h"), info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
elseif strsub(chatType,1,18) == "GUILD_ACHIEVEMENT" then
frame:AddMessage(format(arg1, "|Hplayer:"..arg2.."|h".."["..coloredName.."]".."|h"), info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
elseif chatType == "IGNORED" then
frame:AddMessage(format(CHAT_IGNORED, arg2), info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
elseif chatType == "FILTERED" then
frame:AddMessage(format(CHAT_FILTERED, arg2), info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
elseif chatType == "RESTRICTED" then
frame:AddMessage(CHAT_RESTRICTED, info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
elseif chatType == "CHANNEL_LIST" then
if channelLength > 0 then
frame:AddMessage(format(_G["CHAT_"..chatType.."_GET"]..arg1, tonumber(arg8), arg4), info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
else
frame:AddMessage(arg1, info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
end
elseif chatType == "CHANNEL_NOTICE_USER" then
local globalstring = _G["CHAT_"..arg1.."_NOTICE_BN"]
if not globalstring then
globalstring = _G["CHAT_"..arg1.."_NOTICE"]
end
if arg5 ~= "" then
-- TWO users in this notice (E.G. x kicked y)
frame:AddMessage(format(globalstring, arg8, arg4, arg2, arg5), info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
elseif arg1 == "INVITE" then
frame:AddMessage(format(globalstring, arg4, arg2), info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
else
frame:AddMessage(format(globalstring, arg8, arg4, arg2), info.r, info.g, info.b, info.id, false, nil, nil, isHistory, historyTime)
end
elseif chatType == "CHANNEL_NOTICE" then
if arg1 == "NOT_IN_LFG" then return end
local globalstring = _G["CHAT_"..arg1.."_NOTICE_BN"]
if not globalstring then
globalstring = _G["CHAT_"..arg1.."_NOTICE"]
end
if arg10 > 0 then
arg4 = arg4.." "..arg10
end
local accessID = ChatHistory_GetAccessID(Chat_GetChatCategory(chatType), arg8)
local typeID = ChatHistory_GetAccessID(infoType, arg8)
frame:AddMessage(format(globalstring, arg8, arg4), info.r, info.g, info.b, info.id, false, accessID, typeID, isHistory, historyTime)
else
local body
-- Add AFK/DND flags
-- Player Flags
local pflag, chatIcon, pluginChatIcon = "", specialChatIcons[nameWithRealm], CH:GetPluginIcon(nameWithRealm, name, realm)
if type(chatIcon) == "function" then chatIcon = chatIcon() end
if arg6 ~= "" then
if arg6 == "GM" then
--If it was a whisper, dispatch it to the GMChat addon.
if chatType == "WHISPER" then
return
end
--Add Blizzard Icon, this was sent by a GM
pflag = "|TInterface\\ChatFrame\\UI-ChatIcon-Blizz.blp:0:4:4:-3|t "
elseif arg6 == "DEV" then
--Add Blizzard Icon, this was sent by a Dev
pflag = "|TInterface\\ChatFrame\\UI-ChatIcon-Blizz.blp:0:4:4:-3|t "
elseif arg6 == "DND" or arg6 == "AFK" then
pflag = (pflag or "").._G["CHAT_FLAG_"..arg6]
else
pflag = _G["CHAT_FLAG_"..arg6]
end
else
-- Special Chat Icon
if chatIcon then
pflag = pflag..chatIcon
end
-- Plugin Chat Icon
if pluginChatIcon then
pflag = pflag..pluginChatIcon
end
end
if chatType == "WHISPER_INFORM" and GMChatFrame_IsGM and GMChatFrame_IsGM(arg2) then
return
end
local showLink = 1
if strsub(chatType, 1, 7) == "MONSTER" or strsub(chatType, 1, 9) == "RAID_BOSS" then
showLink = nil
else
arg1 = gsub(arg1, "%%", "%%%%")
end
if chatType == "PARTY_LEADER" and HasLFGRestrictions() then
chatType = "PARTY_GUIDE"
end
-- Search for icon links and replace them with texture links.
local term
for tag in gmatch(arg1, "%b{}") do
term = strlower(gsub(tag, "[{}]", ""))
if ICON_TAG_LIST[term] and ICON_LIST[ICON_TAG_LIST[term]] then
arg1 = gsub(arg1, tag, ICON_LIST[ICON_TAG_LIST[term]].."0|t")
end
end
local playerLink
if chatType ~= "BN_WHISPER" and chatType ~= "BN_WHISPER_INFORM" and chatType ~= "BN_CONVERSATION" then
playerLink = "|Hplayer:"..arg2..":"..arg11..":"..chatGroup..(chatTarget and ":"..chatTarget or "").."|h"
else
playerLink = "|HBNplayer:"..arg2..":"..arg13..":"..arg11..":"..chatGroup..(chatTarget and ":"..chatTarget or "").."|h"
end
if arg3 ~= "" and arg3 ~= "Universal" and arg3 ~= frame.defaultLanguage then
local languageHeader = "["..arg3.."] "
if showLink and arg2 ~= "" then
body = format(_G["CHAT_"..chatType.."_GET"]..languageHeader..arg1, pflag..playerLink.."["..coloredName.."]".."|h")
else
body = format(_G["CHAT_"..chatType.."_GET"]..languageHeader..arg1, pflag..arg2)
end
else
if not showLink or strlen(arg2) == 0 then
body = format(_G["CHAT_"..chatType.."_GET"]..arg1, pflag..arg2, arg2)
else
if chatType == "EMOTE" then
body = format(_G["CHAT_"..chatType.."_GET"]..arg1, pflag..playerLink..coloredName.."|h")
elseif chatType == "TEXT_EMOTE" then
body = gsub(arg1, arg2, pflag..playerLink..coloredName.."|h", 1)
else
body = format(_G["CHAT_"..chatType.."_GET"]..arg1, pflag..playerLink.."["..coloredName.."]".."|h")
end
end
end
-- Add Channel
arg4 = gsub(arg4, "%s%-%s.*", "")
if chatGroup == "BN_CONVERSATION" then
body = format(CHAT_BN_CONVERSATION_GET_LINK, arg8, MAX_WOW_CHAT_CHANNELS + arg8)..body
elseif channelLength > 0 then
body = "|Hchannel:channel:"..arg8.."|h["..arg4.."]|h "..body
end
if CH.db.shortChannels and (chatType ~= "EMOTE" and chatType ~= "TEXT_EMOTE") then
body = CH:HandleShortChannels(body)
end
local accessID = ChatHistory_GetAccessID(chatGroup, chatTarget)
local typeID = ChatHistory_GetAccessID(infoType, chatTarget)
if not historySavedName and arg2 ~= E.myname and not CH.SoundTimer and (not CH.db.noAlertInCombat or not InCombatLockdown()) then
local channels = chatGroup ~= "WHISPER" and chatGroup or (chatType == "WHISPER" or chatType == "BN_WHISPER") and "WHISPER"
local alertType = CH.db.channelAlerts[channels]
if alertType and alertType ~= "None" then
PlaySoundFile(LSM:Fetch("sound", alertType), "Master")
CH.SoundTimer = E:Delay(1, CH.ThrottleSound)
end
end
frame:AddMessage(body, info.r, info.g, info.b, info.id, false, accessID, typeID, isHistory, historyTime)
if not historySavedName and (chatType == "WHISPER" or chatType == "BN_WHISPER") then
ChatEdit_SetLastTellTarget(arg2)
end
end
if not historySavedName and not frame:IsShown() then
if (frame == DEFAULT_CHAT_FRAME and info.flashTabOnGeneral) or (frame ~= DEFAULT_CHAT_FRAME and info.flashTab) then
if not CHAT_OPTIONS.HIDE_FRAME_ALERTS or chatType == "WHISPER" or chatType == "BN_WHISPER" then
FCF_StartAlertFlash(frame) --This would taint if we were not using LibChatAnims
end
end
end
return true
end
end
function CH:ChatFrame_ConfigEventHandler(...)
return ChatFrame_ConfigEventHandler(...)
end
function CH:ChatFrame_SystemEventHandler(...)
return ChatFrame_SystemEventHandler(...)
end
function CH:ChatFrame_OnEvent(...)
if CH:ChatFrame_ConfigEventHandler(...) then return end
if CH:ChatFrame_SystemEventHandler(...) then return end
if CH:ChatFrame_MessageEventHandler(...) then return end
end
function CH:FloatingChatFrame_OnEvent(...)
CH:ChatFrame_OnEvent(...)
FloatingChatFrame_OnEvent(...)
end
local function FloatingChatFrameOnEvent(...)
CH:FloatingChatFrame_OnEvent(...)
end
function CH:UpdateDockState()
if self.db.lockPositions then
FCF_SetLocked(ChatFrame1, 1)
GeneralDockManager:SetParent(LeftChatPanel)
GeneralDockManagerOverflowButton:ClearAllPoints()
GeneralDockManagerOverflowButton:Point("BOTTOMRIGHT", LeftChatTab, "BOTTOMRIGHT", -2, 2)
else
GeneralDockManager:SetParent(UIParent)
GeneralDockManagerOverflowButton:ClearAllPoints()
GeneralDockManagerOverflowButton:Point("BOTTOMRIGHT", GeneralDockManager, 0, -1)
end
end
function CH:SetupChat()
if not E.private.chat.enable then return end
for id, frameName in ipairs(CHAT_FRAMES) do
local frame = _G[frameName]
self:StyleChat(frame)
FCFTab_UpdateAlpha(frame)
local _, fontSize = frame:GetFont()
frame:FontTemplate(LSM:Fetch("font", self.db.font), fontSize, self.db.fontOutline)
if self.db.fontOutline ~= "NONE" then
frame:SetShadowColor(0, 0, 0, 0.2)
else
frame:SetShadowColor(0, 0, 0, 1)
end
if self.db.maxLines ~= frame:GetMaxLines() then
frame:SetMaxLines(self.db.maxLines)
end
frame:SetTimeVisible(self.db.inactivityTimer)
frame:SetShadowOffset(E.mult, -E.mult)
frame:SetFading(self.db.fade)
if id ~= 2 and not frame.OldAddMessage then
--Don't add timestamps to combat log, they don't work.
--This usually taints, but LibChatAnims should make sure it doesn't.
frame.OldAddMessage = frame.AddMessage
frame.AddMessage = CH.AddMessage
end
if not frame.scriptsSet then
frame:SetScript("OnMouseWheel", ChatFrame_OnMouseScroll)
if id ~= 2 then
frame:SetScript("OnEvent", FloatingChatFrameOnEvent)
end
hooksecurefunc(frame, "SetScript", function(f, script, func)
if script == "OnMouseWheel" and func ~= ChatFrame_OnMouseScroll then
f:SetScript(script, ChatFrame_OnMouseScroll)
end
end)
frame.scriptsSet = true
end
end
self:ToggleHyperlink(self.db.hyperlinkHover)
self:UpdateDockState()
self:PositionChat(true)
if not self.HookSecured then
self:SecureHook("FCF_OpenTemporaryWindow", "SetupChat")
self.HookSecured = true
end
end
local function PrepareMessage(author, message)
if author ~= "" and message ~= "" then
return format("%s%s", strupper(author), message)
end
end
function CH:ChatThrottleHandler(author, msg, when)
msg = PrepareMessage(author, msg)
if msg then
for message, msgTime in pairs(throttle) do
if (when - msgTime) >= self.db.throttleInterval then
throttle[message] = nil
end
end
if not throttle[msg] then
throttle[msg] = when
end
end
end
function CH:ChatThrottleBlockFlag(author, message, when)
if author ~= E.myname and self.db.throttleInterval ~= 0 then
message = PrepareMessage(author, message)
local msgTime = message and throttle[message]
if msgTime then
if (when - msgTime) > self.db.throttleInterval then
return false, message
end
else
return false
end
else
return false
end
return true
end
function CH:ChatThrottleIntervalHandler(event, message, author, ...)
local when = time()
local blockFlag, formattedMessage = self:ChatThrottleBlockFlag(author, message, when)
if blockFlag then
return true
else
if formattedMessage then
throttle[formattedMessage] = when
end
return self:FindURL(event, message, author, ...)
end
end
function CH:CHAT_MSG_CHANNEL(event, message, author, ...)
return CH:ChatThrottleIntervalHandler(event, message, author, ...)
end
function CH:CHAT_MSG_YELL(event, message, author, ...)
return CH:ChatThrottleIntervalHandler(event, message, author, ...)
end
function CH:CHAT_MSG_SAY(event, message, author, ...)
return CH:ChatThrottleIntervalHandler(event, message, author, ...)
end
function CH:ThrottleSound()
CH.SoundTimer = nil
end
local protectLinks = {}
function CH:CheckKeyword(message, author)
local canPlaySound = author ~= E.myname and not self.SoundTimer and self.db.keywordSound ~= "None" and (not self.db.noAlertInCombat or not InCombatLockdown())
for hyperLink in gmatch(message, "|%x+|H.-|h.-|h|r") do
local tempLink = gsub(hyperLink, "%s", "|s")
message = gsub(message, E:EscapeString(hyperLink), tempLink)
protectLinks[hyperLink] = tempLink
if canPlaySound then
for keyword in pairs(CH.Keywords) do
if hyperLink == keyword then
PlaySoundFile(LSM:Fetch("sound", self.db.keywordSound), "Master")
self.SoundTimer = E:Delay(1, self.ThrottleSound)
canPlaySound = nil
break
end
end
end
end
local rebuiltString
local isFirstWord = true
local protectLinksNext = next(protectLinks)
for word in gmatch(message, "%s-%S+%s*") do
if not protectLinksNext or not protectLinks[gsub(gsub(word, "%s", ""), "|s", " ")] then
local tempWord = gsub(word, "[%s%p]", "")
local lowerCaseWord = strlower(tempWord)
for keyword in pairs(CH.Keywords) do
if lowerCaseWord == strlower(keyword) then
word = gsub(word, tempWord, format("%s%s|r", E.media.hexvaluecolor, tempWord))
if canPlaySound then
PlaySoundFile(LSM:Fetch("sound", self.db.keywordSound), "Master")
self.SoundTimer = E:Delay(1, self.ThrottleSound)
canPlaySound = nil
end
end
end
if self.db.classColorMentionsChat then
tempWord = gsub(word, "^[%s%p]-([^%s%p]+)([%-]?[^%s%p]-)[%s%p]*$", "%1%2")
lowerCaseWord = strlower(tempWord)
local classMatch = CH.ClassNames[lowerCaseWord]
local wordMatch = classMatch and lowerCaseWord
if wordMatch and not E.global.chat.classColorMentionExcludedNames[wordMatch] then
local classColorTable = RAID_CLASS_COLORS[classMatch]
word = gsub(word, gsub(tempWord, "%-", "%%-"), format("\124cff%.2x%.2x%.2x%s\124r", classColorTable.r*255, classColorTable.g*255, classColorTable.b*255, tempWord))
end
end
end
if isFirstWord then
rebuiltString = word
isFirstWord = nil
else
rebuiltString = rebuiltString..word
end
end
for hyperLink, tempLink in pairs(protectLinks) do
rebuiltString = gsub(rebuiltString, E:EscapeString(tempLink), hyperLink)
protectLinks[hyperLink] = nil
end
return rebuiltString
end
function CH:AddLines(lines, ...)
for i = select("#", ...), 1, -1 do
local x = select(i, ...)
if x:IsObjectType("FontString") and not x:GetName() then
tinsert(lines, x:GetText())
end
end
end
function CH:ChatEdit_OnEnterPressed(editBox)
local chatType = editBox:GetAttribute("chatType")
local chatFrame = chatType and editBox:GetParent()
if chatFrame and (not chatFrame.isTemporary) and (ChatTypeInfo[chatType].sticky == 1) then
if not self.db.sticky then chatType = "SAY" end
editBox:SetAttribute("chatType", chatType)
end
end
function CH:SetChatFont(dropDown, chatFrame, fontSize)
if not chatFrame then
chatFrame = FCF_GetCurrentChatFrame()
end
if not fontSize then
fontSize = dropDown.value
end
chatFrame:FontTemplate(LSM:Fetch("font", self.db.font), fontSize, self.db.fontOutline)
if self.db.fontOutline ~= "NONE" then
chatFrame:SetShadowColor(0, 0, 0, 0.2)
else
chatFrame:SetShadowColor(0, 0, 0, 1)
end
chatFrame:SetShadowOffset(E.mult, -E.mult)
end
CH.SecureSlashCMD = {
"^/assist",
"^/camp",
"^/cancelaura",
"^/cancelform",
"^/cast",
"^/castsequence",
"^/equip",
"^/exit",
"^/logout",
"^/reload",
"^/rl",
"^/startattack",
"^/stopattack",
"^/tar",
"^/target",
"^/use"
}
function CH:ChatEdit_AddHistory(_, text)
text = strtrim(text)
if text ~= "" then
for _, command in ipairs(CH.SecureSlashCMD) do
if find(text, command) then
return
end
end
for i, historyText in ipairs(ElvCharacterDB.ChatEditHistory) do
if historyText == text then
tremove(ElvCharacterDB.ChatEditHistory, i)
break
end
end
tinsert(ElvCharacterDB.ChatEditHistory, text)
if #ElvCharacterDB.ChatEditHistory > CH.db.editboxHistorySize then
tremove(ElvCharacterDB.ChatEditHistory, 1)
end
end
end
function CH:UpdateChatKeywords()
wipe(CH.Keywords)
local keywords = self.db.keywords
keywords = gsub(keywords, ",%s", ",")
for stringValue in gmatch(keywords, "[^,]+") do
if stringValue ~= "" then
CH.Keywords[stringValue] = true
end
end
end
function CH:UpdateFading()
for _, frameName in ipairs(CHAT_FRAMES) do
local frame = _G[frameName]
frame:SetTimeVisible(self.db.inactivityTimer)
frame:SetFading(self.db.fade)
end
end
function CH:DisplayChatHistory()
local data = ElvCharacterDB.ChatHistoryLog
if not next(data) then return end
CH.SoundTimer = true
for _, frameName in ipairs(CHAT_FRAMES) do
for _, d in ipairs(data) do
if type(d) == "table" then
for _, messageType in ipairs(_G[frameName].messageTypeList) do
if (not historyTypes[d[50]] or self.db.showHistory[historyTypes[d[50]]]) and gsub(strsub(d[50], 10), "_INFORM", "") == messageType then
self:ChatFrame_MessageEventHandler(_G[frameName],d[50],d[1],d[2],d[3],d[4],d[5],d[6],d[7],d[8],d[9],d[10],d[11],d[12],0,"ElvUI_ChatHistory",d[51],d[52])
end
end
end
end
end
CH.SoundTimer = nil
end
tremove(ChatTypeGroup.GUILD, 2)
function CH:DelayGuildMOTD()
tinsert(ChatTypeGroup.GUILD, 2, "GUILD_MOTD")
local registerGuildEvents = function(msg)
for _, frameName in ipairs(CHAT_FRAMES) do
local chat = _G[frameName]
if chat:IsEventRegistered("CHAT_MSG_GUILD") then
if msg then
CH:ChatFrame_SystemEventHandler(chat, "GUILD_MOTD", msg)
end
chat:RegisterEvent("GUILD_MOTD")
end
end
end
if not IsInGuild() then
registerGuildEvents()
return
end
local delay, checks = 0, 0
CreateFrame("Frame"):SetScript("OnUpdate", function(df, elapsed)
delay = delay + elapsed
if delay < 5 then return end
local msg = GetGuildRosterMOTD()
if msg and msg ~= "" then
registerGuildEvents(msg)
df:SetScript("OnUpdate", nil)
else -- 5 seconds can be too fast for the API response. let's try once every 5 seconds (max 5 checks).
delay, checks = 0, checks + 1
if checks >= 5 then
registerGuildEvents()
df:SetScript("OnUpdate", nil)
end
end
end)
end
function CH:SaveChatHistory(event, ...)
if historyTypes[event] and not self.db.showHistory[historyTypes[event]] then return end
if self.db.throttleInterval ~= 0 and (event == "CHAT_MSG_SAY" or event == "CHAT_MSG_YELL" or event == "CHAT_MSG_CHANNEL") then
local message, author = ...
local when = time()
if not self:ChatThrottleBlockFlag(author, message, when) then
self:ChatThrottleHandler(author, message, when)
else
return
end
end
if not CH.db.chatHistory then return end
if select("#", ...) > 0 then
local historyLog = ElvCharacterDB.ChatHistoryLog
local historyEntry = {...}
historyEntry[50] = event
historyEntry[51] = time()
historyEntry[52] = self:GetColoredName(event, ...)
while #historyLog >= self.db.historySize do
tremove(historyLog, 1)
end
tinsert(historyLog, historyEntry)
end
end
function CH:FCF_SetWindowAlpha(frame, alpha)
frame.oldAlpha = alpha or 1
end
function CH:ON_FCF_SavePositionAndDimensions(_, noLoop)
if not noLoop then
CH:PositionChat()
end
if not self.db.lockPositions then
CH:UpdateChatTabs() --It was not done in PositionChat, so do it now
end
end
local FindURL_Events = {
"CHAT_MSG_WHISPER",
"CHAT_MSG_WHISPER_INFORM",
"CHAT_MSG_BN_WHISPER",
"CHAT_MSG_BN_WHISPER_INFORM",
"CHAT_MSG_GUILD_ACHIEVEMENT",
"CHAT_MSG_GUILD",
"CHAT_MSG_OFFICER",
"CHAT_MSG_PARTY",
"CHAT_MSG_PARTY_LEADER",
"CHAT_MSG_RAID",
"CHAT_MSG_RAID_LEADER",
"CHAT_MSG_RAID_WARNING",
"CHAT_MSG_BATTLEGROUND",
"CHAT_MSG_BATTLEGROUND_LEADER",
"CHAT_MSG_CHANNEL",
"CHAT_MSG_SAY",
"CHAT_MSG_YELL",
"CHAT_MSG_EMOTE",
"CHAT_MSG_TEXT_EMOTE",
"CHAT_MSG_AFK",
"CHAT_MSG_DND",
}
function CH:DefaultSmileys()
local x = ":16:16"
if next(CH.Smileys) then
wipe(CH.Smileys)
end
-- new keys
CH:AddSmiley(":angry:", E:TextureString(E.Media.ChatEmojis.Angry, x))
CH:AddSmiley(":blush:", E:TextureString(E.Media.ChatEmojis.Blush, x))
CH:AddSmiley(":broken_heart:", E:TextureString(E.Media.ChatEmojis.BrokenHeart, x))
CH:AddSmiley(":call_me:", E:TextureString(E.Media.ChatEmojis.CallMe, x))
CH:AddSmiley(":cry:", E:TextureString(E.Media.ChatEmojis.Cry, x))
CH:AddSmiley(":facepalm:", E:TextureString(E.Media.ChatEmojis.Facepalm, x))
CH:AddSmiley(":grin:", E:TextureString(E.Media.ChatEmojis.Grin, x))
CH:AddSmiley(":heart:", E:TextureString(E.Media.ChatEmojis.Heart, x))
CH:AddSmiley(":heart_eyes:", E:TextureString(E.Media.ChatEmojis.HeartEyes, x))
CH:AddSmiley(":joy:", E:TextureString(E.Media.ChatEmojis.Joy, x))
CH:AddSmiley(":kappa:", E:TextureString(E.Media.ChatEmojis.Kappa, x))
CH:AddSmiley(":middle_finger:", E:TextureString(E.Media.ChatEmojis.MiddleFinger, x))
CH:AddSmiley(":murloc:", E:TextureString(E.Media.ChatEmojis.Murloc, x))
CH:AddSmiley(":ok_hand:", E:TextureString(E.Media.ChatEmojis.OkHand, x))
CH:AddSmiley(":open_mouth:", E:TextureString(E.Media.ChatEmojis.OpenMouth, x))
CH:AddSmiley(":poop:", E:TextureString(E.Media.ChatEmojis.Poop, x))
CH:AddSmiley(":rage:", E:TextureString(E.Media.ChatEmojis.Rage, x))
CH:AddSmiley(":sadkitty:", E:TextureString(E.Media.ChatEmojis.SadKitty, x))
CH:AddSmiley(":scream:", E:TextureString(E.Media.ChatEmojis.Scream, x))
CH:AddSmiley(":scream_cat:", E:TextureString(E.Media.ChatEmojis.ScreamCat, x))
CH:AddSmiley(":slight_frown:", E:TextureString(E.Media.ChatEmojis.SlightFrown, x))
CH:AddSmiley(":smile:", E:TextureString(E.Media.ChatEmojis.Smile, x))
CH:AddSmiley(":smirk:", E:TextureString(E.Media.ChatEmojis.Smirk, x))
CH:AddSmiley(":sob:", E:TextureString(E.Media.ChatEmojis.Sob, x))
CH:AddSmiley(":sunglasses:", E:TextureString(E.Media.ChatEmojis.Sunglasses, x))
CH:AddSmiley(":thinking:", E:TextureString(E.Media.ChatEmojis.Thinking, x))
CH:AddSmiley(":thumbs_up:", E:TextureString(E.Media.ChatEmojis.ThumbsUp, x))
CH:AddSmiley(":semi_colon:", E:TextureString(E.Media.ChatEmojis.SemiColon, x))
CH:AddSmiley(":wink:", E:TextureString(E.Media.ChatEmojis.Wink, x))
CH:AddSmiley(":zzz:", E:TextureString(E.Media.ChatEmojis.ZZZ, x))
CH:AddSmiley(":stuck_out_tongue:", E:TextureString(E.Media.ChatEmojis.StuckOutTongue, x))
CH:AddSmiley(":stuck_out_tongue_closed_eyes:", E:TextureString(E.Media.ChatEmojis.StuckOutTongueClosedEyes, x))
-- Darth's keys
CH:AddSmiley(":meaw:", E:TextureString(E.Media.ChatEmojis.Meaw, x))
-- Simpy's keys
CH:AddSmiley(">:%(", E:TextureString(E.Media.ChatEmojis.Rage, x))
CH:AddSmiley(":%$", E:TextureString(E.Media.ChatEmojis.Blush, x))
CH:AddSmiley("<\\3", E:TextureString(E.Media.ChatEmojis.BrokenHeart, x))
CH:AddSmiley(":\'%)", E:TextureString(E.Media.ChatEmojis.Joy, x))
CH:AddSmiley(";\'%)", E:TextureString(E.Media.ChatEmojis.Joy, x))
CH:AddSmiley(",,!,,", E:TextureString(E.Media.ChatEmojis.MiddleFinger, x))
CH:AddSmiley("D:<", E:TextureString(E.Media.ChatEmojis.Rage, x))
CH:AddSmiley(":o3", E:TextureString(E.Media.ChatEmojis.ScreamCat, x))
CH:AddSmiley("XP", E:TextureString(E.Media.ChatEmojis.StuckOutTongueClosedEyes, x))
CH:AddSmiley("8%-%)", E:TextureString(E.Media.ChatEmojis.Sunglasses, x))
CH:AddSmiley("8%)", E:TextureString(E.Media.ChatEmojis.Sunglasses, x))
CH:AddSmiley(":%+1:", E:TextureString(E.Media.ChatEmojis.ThumbsUp, x))
CH:AddSmiley(":;:", E:TextureString(E.Media.ChatEmojis.SemiColon, x))
CH:AddSmiley(";o;", E:TextureString(E.Media.ChatEmojis.Sob, x))
-- old keys
CH:AddSmiley(":%-@", E:TextureString(E.Media.ChatEmojis.Angry, x))
CH:AddSmiley(":@", E:TextureString(E.Media.ChatEmojis.Angry, x))
CH:AddSmiley(":%-%)", E:TextureString(E.Media.ChatEmojis.Smile, x))
CH:AddSmiley(":%)", E:TextureString(E.Media.ChatEmojis.Smile, x))
CH:AddSmiley(":D", E:TextureString(E.Media.ChatEmojis.Grin, x))
CH:AddSmiley(":%-D", E:TextureString(E.Media.ChatEmojis.Grin, x))
CH:AddSmiley(";%-D", E:TextureString(E.Media.ChatEmojis.Grin, x))
CH:AddSmiley(";D", E:TextureString(E.Media.ChatEmojis.Grin, x))
CH:AddSmiley("=D", E:TextureString(E.Media.ChatEmojis.Grin, x))
CH:AddSmiley("xD", E:TextureString(E.Media.ChatEmojis.Grin, x))
CH:AddSmiley("XD", E:TextureString(E.Media.ChatEmojis.Grin, x))
CH:AddSmiley(":%-%(", E:TextureString(E.Media.ChatEmojis.SlightFrown, x))
CH:AddSmiley(":%(", E:TextureString(E.Media.ChatEmojis.SlightFrown, x))
CH:AddSmiley(":o", E:TextureString(E.Media.ChatEmojis.OpenMouth, x))
CH:AddSmiley(":%-o", E:TextureString(E.Media.ChatEmojis.OpenMouth, x))
CH:AddSmiley(":%-O", E:TextureString(E.Media.ChatEmojis.OpenMouth, x))
CH:AddSmiley(":O", E:TextureString(E.Media.ChatEmojis.OpenMouth, x))
CH:AddSmiley(":%-0", E:TextureString(E.Media.ChatEmojis.OpenMouth, x))
CH:AddSmiley(":P", E:TextureString(E.Media.ChatEmojis.StuckOutTongue, x))
CH:AddSmiley(":%-P", E:TextureString(E.Media.ChatEmojis.StuckOutTongue, x))
CH:AddSmiley(":p", E:TextureString(E.Media.ChatEmojis.StuckOutTongue, x))
CH:AddSmiley(":%-p", E:TextureString(E.Media.ChatEmojis.StuckOutTongue, x))
CH:AddSmiley("=P", E:TextureString(E.Media.ChatEmojis.StuckOutTongue, x))
CH:AddSmiley("=p", E:TextureString(E.Media.ChatEmojis.StuckOutTongue, x))
CH:AddSmiley(";%-p", E:TextureString(E.Media.ChatEmojis.StuckOutTongueClosedEyes, x))
CH:AddSmiley(";p", E:TextureString(E.Media.ChatEmojis.StuckOutTongueClosedEyes, x))
CH:AddSmiley(";P", E:TextureString(E.Media.ChatEmojis.StuckOutTongueClosedEyes, x))
CH:AddSmiley(";%-P", E:TextureString(E.Media.ChatEmojis.StuckOutTongueClosedEyes, x))
CH:AddSmiley(";%-%)", E:TextureString(E.Media.ChatEmojis.Wink, x))
CH:AddSmiley(";%)", E:TextureString(E.Media.ChatEmojis.Wink, x))
CH:AddSmiley(":S", E:TextureString(E.Media.ChatEmojis.Smirk, x))
CH:AddSmiley(":%-S", E:TextureString(E.Media.ChatEmojis.Smirk, x))
CH:AddSmiley(":,%(", E:TextureString(E.Media.ChatEmojis.Cry, x))
CH:AddSmiley(":,%-%(", E:TextureString(E.Media.ChatEmojis.Cry, x))
CH:AddSmiley(":\'%(", E:TextureString(E.Media.ChatEmojis.Cry, x))
CH:AddSmiley(":\'%-%(", E:TextureString(E.Media.ChatEmojis.Cry, x))
CH:AddSmiley(":F", E:TextureString(E.Media.ChatEmojis.MiddleFinger, x))
CH:AddSmiley("<3", E:TextureString(E.Media.ChatEmojis.Heart, x))
CH:AddSmiley("</3", E:TextureString(E.Media.ChatEmojis.BrokenHeart, x))
end
function CH:BuildCopyChatFrame()
local frame = CreateFrame("Frame", "CopyChatFrame", E.UIParent)
frame:Hide()
frame:SetTemplate("Transparent")
frame:Size(700, 200)
frame:Point("BOTTOM", E.UIParent, "BOTTOM", 0, 3)
frame:SetFrameStrata("DIALOG")
frame:EnableMouse(true)
frame:SetMovable(true)
frame:SetResizable(true)
frame:SetMinResize(350, 100)
tinsert(UISpecialFrames, "CopyChatFrame")
self.copyChatFrame = frame
local scrollFrame = CreateFrame("ScrollFrame", "$parentScrollFrame", frame, "UIPanelScrollFrameTemplate")
scrollFrame:Point("TOPLEFT", frame, "TOPLEFT", 8, -30)
scrollFrame:Point("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -29, 8)
frame.scrollFrame = scrollFrame
scrollFrame.scrollBar = CopyChatFrameScrollFrameScrollBar
Skins:HandleScrollBar(scrollFrame.scrollBar)
scrollFrame.scrollBar:Point("TOPLEFT", scrollFrame, "TOPRIGHT", 3, -19)
scrollFrame.scrollBar:Point("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", 3, 19)
local editBox = CreateFrame("EditBox", "$parentEditBox", frame)
editBox:Size(scrollFrame:GetWidth(), 200)
editBox:SetFontObject(ChatFontNormal)
editBox:SetAutoFocus(false)
editBox:SetMultiLine(true)
editBox:SetMaxLetters(99999)
scrollFrame:SetScrollChild(editBox)
frame.editBox = editBox
local closeButton = CreateFrame("Button", "$parentCloseButton", frame, "UIPanelCloseButton")
closeButton:Point("TOPRIGHT", -1, 1)
closeButton:SetFrameLevel(closeButton:GetFrameLevel() + 1)
Skins:HandleCloseButton(closeButton)
frame.closeButton = closeButton
frame:SetScript("OnMouseDown", function(f, button)
if button == "LeftButton" and not f.isMoving then
f:StartMoving()
f.isMoving = true
elseif button == "RightButton" and not f.isSizing then
f:StartSizing()
f.isSizing = true
end
end)
frame:SetScript("OnMouseUp", function(f, button)
if button == "LeftButton" and f.isMoving then
f:StopMovingOrSizing()
f.isMoving = nil
elseif button == "RightButton" and f.isSizing then
f:StopMovingOrSizing()
f.isSizing = nil
end
end)
frame:SetScript("OnHide", function(f)
if f.isMoving or f.isSizing then
f:StopMovingOrSizing()
f.isMoving = nil
f.isSizing = nil
end
end)
scrollFrame:SetScript("OnSizeChanged", function(f)
editBox:Size(f:GetSize())
end)
scrollFrame:HookScript("OnVerticalScroll", function(f, offset)
editBox:SetHitRectInsets(0, 0, offset, (editBox:GetHeight() - offset - f:GetHeight()))
end)
editBox:SetScript("OnTextChanged", function(_, userInput)
if userInput then return end
local _, max = scrollFrame.scrollBar:GetMinMaxValues()
for _ = 1, max do
ScrollFrameTemplate_OnMouseWheel(scrollFrame, -1)
end
end)
editBox:SetScript("OnEscapePressed", function()
frame:Hide()
end)
end
CH.TabStyles = {
NONE = "%s",
ARROW = "%s>|r%s%s<|r",
ARROW1 = "%s>|r %s %s<|r",
ARROW2 = "%s<|r%s%s>|r",
ARROW3 = "%s<|r %s %s>|r",
BOX = "%s[|r%s%s]|r",
BOX1 = "%s[|r %s %s]|r",
CURLY = "%s{|r%s%s}|r",
CURLY1 = "%s{|r %s %s}|r",
CURVE = "%s(|r%s%s)|r",
CURVE1 = "%s(|r %s %s)|r"
}
function CH:FCFTab_UpdateColors(tab, selected)
local chat = _G[format("ChatFrame%s", tab:GetID())]
tab.selected = selected
if selected and chat.isDocked then
if self.db.tabSelector ~= "NONE" then
local color = self.db.tabSelectorColor
local hexColor = E:RGBToHex(color.r, color.g, color.b)
tab:SetFormattedText(self.TabStyles[self.db.tabSelector] or self.TabStyles.ARROW1, hexColor, chat.name, hexColor)
tab.textReformatted = true
elseif tab.textReformatted then
tab:SetText(chat.name)
tab.textReformatted = nil
end
if self.db.tabSelectedTextEnabled then
local color = self.db.tabSelectedTextColor
tab:GetFontString():SetTextColor(color.r, color.g, color.b)
return
end
elseif tab.textReformatted then
tab:SetText(chat.name)
tab.textReformatted = nil
end
tab:GetFontString():SetTextColor(unpack(E.media.rgbvaluecolor))
end
function CH:GetPlayerInfoByGUID(guid)
local data = CH.GuidCache[guid]
if not data then
local ok, localizedClass, englishClass, localizedRace, englishRace, sex, name, realm = pcall(GetPlayerInfoByGUID, guid)
if not (ok and englishClass) then return end
local nameWithRealm = name..'-'..GetRealmName()
-- move em into a table
data = {
localizedClass = localizedClass,
englishClass = englishClass,
localizedRace = localizedRace,
englishRace = englishRace,
sex = sex,
name = name,
realm = realm,
nameWithRealm = nameWithRealm -- we use this to correct mobile to link with the realm as well
}
-- add it to ClassNames
if name then
CH.ClassNames[strlower(name)] = englishClass
end
if nameWithRealm then
CH.ClassNames[strlower(nameWithRealm)] = englishClass
end
-- push into the cache
CH.GuidCache[guid] = data
end
-- we still need to recheck this each time because CUSTOM_CLASS_COLORS can change
if data then data.classColor = E:ClassColor(data.englishClass) end
return data
end
function CH:ResetEditboxHistory()
wipe(ElvCharacterDB.ChatEditHistory)
end
function CH:ResetHistory()
wipe(ElvCharacterDB.ChatHistoryLog)
end
function CH:Initialize()
self:DelayGuildMOTD() --Keep this before `is Chat Enabled` check
if not E.private.chat.enable then return end
self.Initialized = true
self.db = E.db.chat
if not ElvCharacterDB.ChatEditHistory then ElvCharacterDB.ChatEditHistory = {} end
if not ElvCharacterDB.ChatHistoryLog or not self.db.chatHistory then ElvCharacterDB.ChatHistoryLog = {} end
FriendsMicroButton:Kill()
ChatFrameMenuButton:Kill()
self:SetupChat()
self:DefaultSmileys()
self:UpdateChatKeywords()
self:UpdateFading()
self:UpdateAnchors()
self:Panels_ColorUpdate()
self:SecureHook("ChatEdit_OnEnterPressed")
self:SecureHook("FCF_SetWindowAlpha")
self:SecureHook("FCFTab_UpdateColors")
self:SecureHook("FCF_SetChatWindowFontSize", "SetChatFont")
self:SecureHook("FCF_SavePositionAndDimensions", "ON_FCF_SavePositionAndDimensions")
self:RegisterEvent("UPDATE_CHAT_WINDOWS", "SetupChat")
self:RegisterEvent("UPDATE_FLOATING_CHAT_WINDOWS", "SetupChat")
if WIM then
WIM.RegisterWidgetTrigger("chat_display", "whisper,chat,w2w,demo", "OnHyperlinkClick", function(self) CH.clickedframe = self end)
WIM.RegisterItemRefHandler("url", HyperLinkedURL)
end
if not self.db.lockPositions then CH:UpdateChatTabs() end --It was not done in PositionChat, so do it now
for _, event in ipairs(FindURL_Events) do
ChatFrame_AddMessageEventFilter(event, CH[event] or CH.FindURL)
local nType = strsub(event, 10)
if nType ~= "AFK" and nType ~= "DND" then
self:RegisterEvent(event, "SaveChatHistory")
end
end
if self.db.chatHistory then self:DisplayChatHistory() end
self:BuildCopyChatFrame()
-- Editbox Backdrop Color
hooksecurefunc("ChatEdit_UpdateHeader", function(editbox)
local chatType = editbox:GetAttribute("chatType")
if not chatType then return end
local chanTarget = editbox:GetAttribute("channelTarget")
local chanName = chanTarget and GetChannelName(chanTarget)
--Increase inset on right side to make room for character count text
local insetLeft, insetRight, insetTop, insetBottom = editbox:GetTextInsets()
editbox:SetTextInsets(insetLeft, insetRight + 30, insetTop, insetBottom)
if chanName and (chatType == "CHANNEL") then
if chanName == 0 then
editbox:SetBackdropBorderColor(unpack(E.media.bordercolor))
else
local info = ChatTypeInfo[chatType..chanName]
editbox:SetBackdropBorderColor(info.r, info.g, info.b)
end
else
local info = ChatTypeInfo[chatType]
editbox:SetBackdropBorderColor(info.r, info.g, info.b)
end
end)
GeneralDockManagerOverflowButton:Size(17)
GeneralDockManagerOverflowButtonList:SetTemplate("Transparent")
hooksecurefunc(GeneralDockManagerScrollFrame, "SetPoint", function(self, point, anchor, attachTo, x, y)
if anchor == GeneralDockManagerOverflowButton and x == 0 and y == 0 then
self:Point(point, anchor, attachTo, -2, -4)
end
end)
CombatLogQuickButtonFrame_Custom:StripTextures()
CombatLogQuickButtonFrame_Custom:CreateBackdrop("Default", true)
CombatLogQuickButtonFrame_Custom.backdrop:Point("TOPLEFT", 0, -1)
CombatLogQuickButtonFrame_Custom.backdrop:Point("BOTTOMRIGHT", -22, -1)
CombatLogQuickButtonFrame_CustomProgressBar:StripTextures()
CombatLogQuickButtonFrame_CustomProgressBar:SetStatusBarTexture(E.media.normTex)
CombatLogQuickButtonFrame_CustomProgressBar:SetStatusBarColor(0.31, 0.31, 0.31)
CombatLogQuickButtonFrame_CustomProgressBar:ClearAllPoints()
CombatLogQuickButtonFrame_CustomProgressBar:SetInside(CombatLogQuickButtonFrame_Custom.backdrop)
Skins:HandleNextPrevButton(CombatLogQuickButtonFrame_CustomAdditionalFilterButton)
CombatLogQuickButtonFrame_CustomAdditionalFilterButton:Size(22)
CombatLogQuickButtonFrame_CustomAdditionalFilterButton:Point("TOPRIGHT", CombatLogQuickButtonFrame_Custom, "TOPRIGHT", 3, -1)
CombatLogQuickButtonFrame_CustomAdditionalFilterButton:SetHitRectInsets(0, 0, 0, 0)
end
local function InitializeCallback()
CH:Initialize()
end
E:RegisterModule(CH:GetName(), InitializeCallback)