diff --git a/modules/chat.lua b/modules/chat.lua new file mode 100644 index 0000000..bcf9618 --- /dev/null +++ b/modules/chat.lua @@ -0,0 +1,886 @@ +pfUI:RegisterModule("chat", "vanilla:tbc", function () + local panelfont = C.panel.use_unitfonts == "1" and pfUI.font_unit or pfUI.font_default + local panelfont_size = C.panel.use_unitfonts == "1" and C.global.font_unit_size or C.global.font_size + local rawborder, default_border = GetBorderSize("chat") + + if pfUI.client <= 11200 then + -- The 'GetChatWindowInfo' function returns shown as 'false' while the UIParent is hidden (Alt+Z). + -- Based on that return value, the 'FloatingChatFrame_Update' would remove every chat frame, + -- that isn't tabbed while the interface is hidden. With this hook we prevent the original + -- 'FloatingChatFrame_Update' function from being called during that time. + + local HookFloatingChatFrame_Update = _G.FloatingChatFrame_Update + _G.FloatingChatFrame_Update = function(id, onUpdateEvent) + if not UIParent:IsShown() then return end + HookFloatingChatFrame_Update(id, onUpdateEvent) + end + end + + _G.CHAT_FONT_HEIGHTS = { 8, 10, 12, 14, 16, 18, 20 } + + -- add dropdown menu button to ignore player + UnitPopupButtons["IGNORE_PLAYER"] = { text = IGNORE_PLAYER, dist = 0 } + for index,value in ipairs(UnitPopupMenus["FRIEND"]) do + if value == "GUILD_LEAVE" then + table.insert(UnitPopupMenus["FRIEND"], index+1, "IGNORE_PLAYER") + end + end + + hooksecurefunc("UnitPopup_OnClick", function(self) + if this.value == "IGNORE_PLAYER" then + AddIgnore(_G[UIDROPDOWNMENU_INIT_MENU].name) + end + end) + + local realm = GetRealmName() + local player = UnitName("player") + local history + local function SaveChatHistory(id, msg, r, g, b) + -- create cache tables if not existing + pfUI_cache = pfUI_cache or {} + pfUI_cache["chathistory"] = pfUI_cache["chathistory"] or {} + pfUI_cache["chathistory"][realm] = pfUI_cache["chathistory"][realm] or {} + pfUI_cache["chathistory"][realm][player] = pfUI_cache["chathistory"][realm][player] or {} + pfUI_cache["chathistory"][realm][player][id] = pfUI_cache["chathistory"][realm][player][id] or {} + + if r and g and b then + local color = rgbhex(r*.5+.2, g*.5+.2, b*.5+.2) + msg = string.gsub(msg, "^", color) + msg = string.gsub(msg, "|r", "|r" .. color) + end + + history = pfUI_cache["chathistory"][realm][player][id] + table.insert(history, 1, msg) + if history[30] then table.remove(history, 30) end + end + + local function GetChatHistory(id) + -- create cache tables if not existing + pfUI_cache = pfUI_cache or {} + pfUI_cache["chathistory"] = pfUI_cache["chathistory"] or {} + pfUI_cache["chathistory"][realm] = pfUI_cache["chathistory"][realm] or {} + pfUI_cache["chathistory"][realm][player] = pfUI_cache["chathistory"][realm][player] or {} + pfUI_cache["chathistory"][realm][player][id] = pfUI_cache["chathistory"][realm][player][id] or {} + + return pfUI_cache["chathistory"][realm][player][id] + end + + pfUI.chat = CreateFrame("Frame",nil,UIParent) + + pfUI.chat.left = CreateFrame("Frame", "pfChatLeft", UIParent) + pfUI.chat.left.OnMove = function() + pfUI.chat:RefreshChat() + end + + pfUI.chat.left:SetFrameStrata("BACKGROUND") + pfUI.chat.left:SetWidth(C.chat.left.width) + pfUI.chat.left:SetHeight(C.chat.left.height) + pfUI.chat.left:SetPoint("BOTTOMLEFT", 2*default_border,2*default_border) + pfUI.chat.left:SetScript("OnShow", function() pfUI.chat:RefreshChat() end) + UpdateMovable(pfUI.chat.left) + CreateBackdrop(pfUI.chat.left, default_border, nil, .8) + if C.chat.global.frameshadow == "1" then + CreateBackdropShadow(pfUI.chat.left) + end + + if C.chat.global.custombg == "1" then + local r, g, b, a = strsplit(",", C.chat.global.background) + pfUI.chat.left.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) + + local r, g, b, a = strsplit(",", C.chat.global.border) + pfUI.chat.left.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) + end + + pfUI.chat.left.panelTop = CreateFrame("Frame", "leftChatPanelTop", pfUI.chat.left) + pfUI.chat.left.panelTop:ClearAllPoints() + pfUI.chat.left.panelTop:SetHeight(C.global.font_size+default_border*2) + pfUI.chat.left.panelTop:SetPoint("TOPLEFT", pfUI.chat.left, "TOPLEFT", default_border, -default_border) + pfUI.chat.left.panelTop:SetPoint("TOPRIGHT", pfUI.chat.left, "TOPRIGHT", -default_border, -default_border) + if C.chat.global.tabdock == "1" then + CreateBackdrop(pfUI.chat.left.panelTop, default_border, nil, .8) + end + + pfUI.chat.URLPattern = { + WWW = { + ["rx"]=" (www%d-)%.([_A-Za-z0-9-]+)%.(%S+)%s?", + ["fm"]="%s.%s.%s"}, + PROTOCOL = { + ["rx"]=" (%a+)://(%S+)%s?", + ["fm"]="%s://%s"}, + EMAIL = { + ["rx"]=" ([_A-Za-z0-9-%.:]+)@([_A-Za-z0-9-]+)(%.)([_A-Za-z0-9-]+%.?[_A-Za-z0-9-]*)%s?", + ["fm"]="%s@%s%s%s"}, + PORTIP = { + ["rx"]=" (%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?):(%d%d?%d?%d?%d?)%s?", + ["fm"]="%s.%s.%s.%s:%s"}, + IP = { + ["rx"]=" (%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%s?", + ["fm"]="%s.%s.%s.%s"}, + SHORTURL = { + ["rx"]=" (%a+)%.(%a+)/(%S+)%s?", + ["fm"]="%s.%s/%s"}, + URLIP = { + ["rx"]=" ([_A-Za-z0-9-]+)%.([_A-Za-z0-9-]+)%.(%S+)%:([_0-9-]+)%s?", + ["fm"]="%s.%s.%s:%s"}, + URL = { + ["rx"]=" ([_A-Za-z0-9-]+)%.([_A-Za-z0-9-]+)%.(%S+)%s?", + ["fm"]="%s.%s.%s"}, + } + + pfUI.chat.URLFuncs = { + ["WWW"] = function(a1,a2,a3) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.WWW.fm,a1,a2,a3) end, + ["PROTOCOL"] = function(a1,a2) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.PROTOCOL.fm,a1,a2) end, + ["EMAIL"] = function(a1,a2,a3,a4) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.EMAIL.fm,a1,a2,a3,a4) end, + ["PORTIP"] = function(a1,a2,a3,a4,a5) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.PORTIP.fm,a1,a2,a3,a4,a5) end, + ["IP"] = function(a1,a2,a3,a4) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.IP.fm,a1,a2,a3,a4) end, + ["SHORTURL"] = function(a1,a2,a3) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.SHORTURL.fm,a1,a2,a3) end, + ["URLIP"] = function(a1,a2,a3,a4) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.URLIP.fm,a1,a2,a3,a4) end, + ["URL"] = function(a1,a2,a3) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.URL.fm,a1,a2,a3) end, + } + + -- url copy dialog + function pfUI.chat:FormatLink(formatter,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) + if not (formatter and a1) then return end + local newtext = string.format(formatter,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) + + -- check the last capture index for consecutive trailing dots (invalid top level domain) + local invalidtld + for _, arg in pairs({a10,a9,a8,a7,a6,a5,a4,a3,a2,a1}) do + if arg then + invalidtld = string.find(arg, "(%.%.)$") + break + end + end + + if (invalidtld) then return newtext end + if formatter == self.URLPattern.EMAIL.fm then -- email parser + local colon = string.find(a1,":") + if (colon) and string.len(a1) > colon then + if not (string.sub(a1,1,6) == "mailto") then + local prefix,address = string.sub(newtext,1,colon),string.sub(newtext,colon+1) + return string.format(" %s|cffccccff|Hurl:%s|h[%s]|h|r ",prefix,address,address) + end + end + end + return " |cffccccff|Hurl:" .. newtext .. "|h[" .. newtext .. "]|h|r " + end + + function pfUI.chat:HandleLink(text) + local URLPattern = pfUI.chat.URLPattern + text = string.gsub (text, URLPattern.WWW.rx, pfUI.chat.URLFuncs.WWW) + text = string.gsub (text, URLPattern.PROTOCOL.rx, pfUI.chat.URLFuncs.PROTOCOL) + text = string.gsub (text, URLPattern.EMAIL.rx, pfUI.chat.URLFuncs.EMAIL) + text = string.gsub (text, URLPattern.PORTIP.rx, pfUI.chat.URLFuncs.PORTIP) + text = string.gsub (text, URLPattern.IP.rx, pfUI.chat.URLFuncs.IP) + text = string.gsub (text, URLPattern.SHORTURL.rx, pfUI.chat.URLFuncs.SHORTURL) + text = string.gsub (text, URLPattern.URLIP.rx, pfUI.chat.URLFuncs.URLIP) + text = string.gsub (text, URLPattern.URL.rx, pfUI.chat.URLFuncs.URL) + return text + end + + pfUI.chat.urlcopy = CreateFrame("Frame", "pfURLCopy", UIParent) + pfUI.chat.urlcopy:Hide() + pfUI.chat.urlcopy:SetWidth(270) + pfUI.chat.urlcopy:SetHeight(65) + pfUI.chat.urlcopy:SetFrameStrata("FULLSCREEN") + pfUI.chat.urlcopy:SetPoint("CENTER", 0, 0) + CreateBackdrop(pfUI.chat.urlcopy, nil, nil, 0.8) + + pfUI.chat.urlcopy:SetMovable(true) + pfUI.chat.urlcopy:EnableMouse(true) + pfUI.chat.urlcopy:RegisterForDrag("LeftButton") + pfUI.chat.urlcopy:SetScript("OnDragStart",function() + this:StartMoving() + end) + + pfUI.chat.urlcopy:SetScript("OnDragStop",function() + this:StopMovingOrSizing() + end) + + pfUI.chat.urlcopy:SetScript("OnShow", function() + this.text:HighlightText() + end) + + pfUI.chat.urlcopy.text = CreateFrame("EditBox", "pfURLCopyEditBox", pfUI.chat.urlcopy) + pfUI.chat.urlcopy.text:SetTextColor(.2,1,.8,1) + pfUI.chat.urlcopy.text:SetJustifyH("CENTER") + + pfUI.chat.urlcopy.text:SetWidth(250) + pfUI.chat.urlcopy.text:SetHeight(20) + pfUI.chat.urlcopy.text:SetPoint("TOP", pfUI.chat.urlcopy, "TOP", 0, -10) + pfUI.chat.urlcopy.text:SetFontObject(GameFontNormal) + CreateBackdrop(pfUI.chat.urlcopy.text) + + pfUI.chat.urlcopy.text:SetScript("OnEscapePressed", function(self) + pfUI.chat.urlcopy:Hide() + end) + + pfUI.chat.urlcopy.text:SetScript("OnEditFocusLost", function(self) + pfUI.chat.urlcopy:Hide() + end) + + pfUI.chat.urlcopy.close = CreateFrame("Button", "pfURLCopyClose", pfUI.chat.urlcopy, "UIPanelButtonTemplate") + pfUI.api.SkinButton(pfUI.chat.urlcopy.close) + pfUI.chat.urlcopy.close:SetWidth(70) + pfUI.chat.urlcopy.close:SetHeight(18) + pfUI.chat.urlcopy.close:SetPoint("BOTTOMRIGHT", pfUI.chat.urlcopy, "BOTTOMRIGHT", -10, 10) + + pfUI.chat.urlcopy.close:SetText(T["Close"]) + pfUI.chat.urlcopy.close:SetScript("OnClick", function() + pfUI.chat.urlcopy:Hide() + end) + + pfUI.chat.urlcopy.SetItemRef = SetItemRef + pfUI.chat.urlcopy.CopyText = function(text) + pfUI.chat.urlcopy.text:SetText(text) + pfUI.chat.urlcopy:Show() + end + + function _G.SetItemRef(link, text, button) + if (strsub(link, 1, 3) == "url") then + if string.len(link) > 4 and string.sub(link,1,4) == "url:" then + pfUI.chat.urlcopy.CopyText(string.sub(link,5, string.len(link))) + end + return + end + pfUI.chat.urlcopy.SetItemRef(link, text, button) + end + + pfUI.chat.right = CreateFrame("Frame", "pfChatRight", UIParent) + pfUI.chat.right:SetFrameStrata("BACKGROUND") + pfUI.chat.right:SetWidth(C.chat.right.width) + pfUI.chat.right:SetHeight(C.chat.right.height) + pfUI.chat.right:SetPoint("BOTTOMRIGHT", -2*default_border,2*default_border) + pfUI.chat.right:SetScript("OnShow", function() pfUI.chat:RefreshChat() end) + UpdateMovable(pfUI.chat.right) + CreateBackdrop(pfUI.chat.right, default_border, nil, .8) + if C.chat.global.frameshadow == "1" then + CreateBackdropShadow(pfUI.chat.right) + end + + if C.chat.global.custombg == "1" then + local r, g, b, a = strsplit(",", C.chat.global.background) + pfUI.chat.right.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) + + local r, g, b, a = strsplit(",", C.chat.global.border) + pfUI.chat.right.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) + end + + pfUI.chat.right.panelTop = CreateFrame("Frame", "rightChatPanelTop", pfUI.chat.right) + pfUI.chat.right.panelTop:ClearAllPoints() + pfUI.chat.right.panelTop:SetHeight(C.global.font_size+default_border*2) + pfUI.chat.right.panelTop:SetPoint("TOPLEFT", pfUI.chat.right, "TOPLEFT", default_border, -default_border) + pfUI.chat.right.panelTop:SetPoint("TOPRIGHT", pfUI.chat.right, "TOPRIGHT", -default_border, -default_border) + if C.chat.global.tabdock == "1" then + CreateBackdrop(pfUI.chat.right.panelTop, default_border, nil, .8) + end + + pfUI.chat:RegisterEvent("PLAYER_ENTERING_WORLD") + pfUI.chat:RegisterEvent("UI_SCALE_CHANGED") + + local function ChatOnMouseWheel() + if (arg1 > 0) then + if IsShiftKeyDown() then + this:ScrollToTop() + else + for i=1, C.chat.global.scrollspeed do + this:ScrollUp() + end + end + elseif (arg1 < 0) then + if IsShiftKeyDown() then + this:ScrollToBottom() + else + for i=1, C.chat.global.scrollspeed do + this:ScrollDown() + end + end + end + end + + function pfUI.chat:RefreshChat() + local panelheight = C.global.font_size*1.5 + default_border*2 + 2 + + if C.chat.global.sticky == "1" then + ChatTypeInfo.WHISPER.sticky = 1 + ChatTypeInfo.OFFICER.sticky = 1 + ChatTypeInfo.RAID_WARNING.sticky = 1 + ChatTypeInfo.CHANNEL.sticky = 1 + end + + ChatFrameMenuButton:Hide() + ChatMenu:SetClampedToScreen(true) + CreateBackdrop(ChatMenu) + CreateBackdrop(EmoteMenu) + CreateBackdrop(LanguageMenu) + CreateBackdrop(VoiceMacroMenu) + + local combatlogpanel = (CombatLogQuickButtonFrame_Custom and CombatLogQuickButtonFrame_Custom:GetHeight() or 0) + + for i=1, NUM_CHAT_WINDOWS do + local frame = _G["ChatFrame"..i] + local tab = _G["ChatFrame"..i.."Tab"] + + local combat = 0 + for _, msg in pairs(_G["ChatFrame"..i].messageTypeList) do + if strfind(msg, "SPELL", 1) or strfind(msg, "COMBAT", 1) then + combat = combat + 1 + end + end + + if combat > 5 then + frame.pfCombatLog = true + else + frame.pfCombatLog = nil + end + + + if not frame.pfStartMoving then + frame.pfStartMoving = frame.StartMoving + frame.StartMoving = function(a1) + pfUI.chat.hideLock = true + frame.pfStartMoving(a1) + end + end + + if not frame.pfStopMovingOrSizing then + frame.pfStopMovingOrSizing = frame.StopMovingOrSizing + frame.StopMovingOrSizing = function(a1) + frame.pfStopMovingOrSizing(a1) + pfUI.chat.RefreshChat() + pfUI.chat.hideLock = false + end + end + + if C.chat.global.fadeout == "1" then + frame:SetFading(true) + frame:SetTimeVisible(tonumber(C.chat.global.fadetime)) + else + frame:SetFading(false) + end + + if i == 3 and C.chat.right.enable == "1" then + -- Loot & Spam + local bottompadding = pfUI.panel and pfUI.panel.right:IsShown() and not pfUI_config.position["pfPanelRight"] and panelheight or default_border + tab:SetParent(pfUI.chat.right.panelTop) + frame:SetParent(pfUI.chat.right) + frame:ClearAllPoints() + frame:SetPoint("TOPLEFT", pfUI.chat.right ,"TOPLEFT", default_border, -panelheight) + frame:SetPoint("BOTTOMRIGHT", pfUI.chat.right ,"BOTTOMRIGHT", -default_border, bottompadding) + frame:Show() + elseif frame.isDocked then + -- Left Chat + local bottompadding = pfUI.panel and pfUI.panel.left:IsShown() and not pfUI_config.position["pfPanelLeft"] and panelheight or default_border + FCF_DockFrame(frame) + tab:SetParent(pfUI.chat.left.panelTop) + frame:SetParent(pfUI.chat.left) + frame:ClearAllPoints() + if i == 2 then + frame:SetPoint("TOPLEFT", pfUI.chat.left ,"TOPLEFT", default_border, -panelheight - combatlogpanel) + else + frame:SetPoint("TOPLEFT", pfUI.chat.left ,"TOPLEFT", default_border, -panelheight) + end + frame:SetPoint("BOTTOMRIGHT", pfUI.chat.left ,"BOTTOMRIGHT", -default_border, bottompadding) + else + FCF_UnDockFrame(frame) + frame:SetParent(UIParent) + tab:SetParent(UIParent) + end + + -- Combat Log + if C.chat.global.combathide == "1" and frame.pfCombatLog then + FCF_UnDockFrame(frame) + FCF_Close(frame) + end + + -- hide textures + for j,v in ipairs({tab:GetRegions()}) do + if j==5 then v:SetTexture(0,0,0,0) end + v:SetHeight(C.global.font_size+default_border*2) + end + + -- remove background on docked frames + for _, tex in pairs(CHAT_FRAME_TEXTURES) do + local texture = _G["ChatFrame"..i..tex] + if tex == "Background" then + texture.oldTexture = texture.oldTexture or texture:GetTexture() + if frame:GetParent() == pfUI.chat.left or frame:GetParent() == pfUI.chat.right then + texture:SetTexture() + texture:Hide() + else + texture:SetTexture(texture.oldTexture) + texture:Show() + end + else + texture:SetTexture() + texture:Hide() + end + end + + _G["ChatFrame" .. i .. "ResizeBottom"]:Hide() + _G["ChatFrame" .. i .. "TabText"]:SetJustifyV("CENTER") + _G["ChatFrame" .. i .. "TabText"]:SetHeight(C.global.font_size+default_border*2) + _G["ChatFrame" .. i .. "TabText"]:SetPoint("BOTTOM", 0, default_border) + _G["ChatFrame" .. i .. "TabLeft"]:SetAlpha(0) + _G["ChatFrame" .. i .. "TabMiddle"]:SetAlpha(0) + _G["ChatFrame" .. i .. "TabRight"]:SetAlpha(0) + + if C.chat.global.chatflash == "1" then + _G["ChatFrame" .. i .. "TabFlash"]:SetAllPoints(_G["ChatFrame" .. i .. "TabText"]) + else + _G["ChatFrame" .. i .. "TabFlash"].Show = function() return end + end + + local _, class = UnitClass("player") + _G["ChatFrame" .. i .. "TabText"]:SetTextColor((RAID_CLASS_COLORS[class].r + .3) * .5, (RAID_CLASS_COLORS[class].g + .3) * .5, (RAID_CLASS_COLORS[class].b + .3) * .5, 1) + _G["ChatFrame" .. i .. "TabText"]:SetFont(panelfont,panelfont_size, "OUTLINE") + + if _G["ChatFrame" .. i].isDocked or _G["ChatFrame" .. i]:IsVisible() then + _G["ChatFrame" .. i .. "Tab"]:Show() + end + + frame:EnableMouseWheel(true) + frame:SetScript("OnMouseWheel", ChatOnMouseWheel) + end + + + -- update dock frame for all windows + for index, value in pairs(DOCKED_CHAT_FRAMES) do + FCF_UpdateButtonSide(value) + end + end + + hooksecurefunc("FCF_SaveDock", pfUI.chat.RefreshChat) + + if C.chat.global.tabmouse == "1" then + pfUI.chat.mouseovertab = CreateFrame("Frame") + pfUI.chat.mouseovertab:SetScript("OnUpdate", function() + -- throttle + if ( this.tick or .1) > GetTime() then return else this.tick = GetTime() + pfUI.throttle:Get("chat_tab") end -- Default: Normal (10 FPS) + + if pfUI.chat.hideLock then return end + + if MouseIsOver(pfUI.chat.left, 10, -10, -10, 10) then + pfUI.chat.left.panelTop:Show() + FCF_DockUpdate() + elseif MouseIsOver(pfUI.chat.right, 10, -10, -10, 10) then + -- disable while dock is active + if pfUI.chat.right:GetAlpha() == 0 then return end + + pfUI.chat.right.panelTop:Show() + FCF_DockUpdate() + else + pfUI.chat.left.panelTop:Hide() + pfUI.chat.right.panelTop:Hide() + end + end) + end + + function pfUI.chat.SetupRightChat(state) + if state then + C.chat.right.enable = "1" + pfUI.chat.right:Show() + else + C.chat.right.enable = "0" + pfUI.chat.right:Hide() + end + end + + function pfUI.chat.SetupPositions() + -- close all chat windows + for i=1, NUM_CHAT_WINDOWS do + FCF_Close(_G["ChatFrame"..i]) + FCF_DockUpdate() + end + + -- Main Window + ChatFrame1:ClearAllPoints() + ChatFrame1:SetPoint("TOPLEFT", pfUI.chat.left ,"TOPLEFT", 5, -25) + ChatFrame1:SetPoint("BOTTOMRIGHT", pfUI.chat.left ,"BOTTOMRIGHT", -5, 25) + + FCF_SetLocked(ChatFrame1, 1) + FCF_SetWindowName(ChatFrame1, GENERAL) + FCF_SetWindowColor(ChatFrame1, 0, 0, 0) + FCF_SetWindowAlpha(ChatFrame1, 0) + FCF_SetChatWindowFontSize(ChatFrame1, 12) + ChatFrame1:SetUserPlaced(1) + + -- Combat Log + FCF_SetLocked(ChatFrame2, 1) + FCF_SetWindowName(ChatFrame2, COMBAT_LOG) + FCF_SetWindowColor(ChatFrame2, 0, 0, 0) + FCF_SetWindowAlpha(ChatFrame2, 0) + FCF_SetChatWindowFontSize(ChatFrame2, 12) + ChatFrame2:SetUserPlaced(1) + + -- Loot & Spam + if C.chat.right.enable == "1" then + -- set position of Loot & Spam + FCF_SetLocked(ChatFrame3, 1) + FCF_SetWindowName(ChatFrame3, T["Loot & Spam"]) + FCF_SetWindowColor(ChatFrame3, 0, 0, 0) + FCF_SetWindowAlpha(ChatFrame3, 0) + FCF_SetChatWindowFontSize(ChatFrame3, 12) + FCF_UnDockFrame(ChatFrame3) + FCF_SetTabPosition(ChatFrame3, 0) + ChatFrame3:ClearAllPoints() + ChatFrame3:SetPoint("TOPLEFT", pfUI.chat.right ,"TOPLEFT", 5, -25) + ChatFrame3:SetPoint("BOTTOMRIGHT", pfUI.chat.right ,"BOTTOMRIGHT", -5, 25) + ChatFrame3:SetUserPlaced(1) + end + + pfUI.chat:RefreshChat() + FCF_DockUpdate() + end + + function pfUI.chat.SetupChannels() + ChatFrame_RemoveAllMessageGroups(ChatFrame1) + ChatFrame_RemoveAllMessageGroups(ChatFrame2) + ChatFrame_RemoveAllMessageGroups(ChatFrame3) + + ChatFrame_RemoveAllChannels(ChatFrame1) + ChatFrame_RemoveAllChannels(ChatFrame2) + ChatFrame_RemoveAllChannels(ChatFrame3) + + local normalg = { "SYSTEM", "SAY", "YELL", "WHISPER", "PARTY", "GUILD", "GUILD_OFFICER", "CREATURE", "CHANNEL", "EMOTE", "RAID", "RAID_LEADER", "RAID_WARNING", "BATTLEGROUND", "BATTLEGROUND_LEADER", "MONSTER_SAY", "MONSTER_EMOTE", "MONSTER_YELL", "MONSTER_WHISPER", "MONSTER_BOSS_EMOTE", "MONSTER_BOSS_WHISPER" } + for _,group in pairs(normalg) do + ChatFrame_AddMessageGroup(ChatFrame1, group) + end + + ChatFrame_ActivateCombatMessages(ChatFrame2) + + if C.chat.right.enable == "1" then + local spamg = { "COMBAT_XP_GAIN", "COMBAT_HONOR_GAIN", "COMBAT_FACTION_CHANGE", "SKILL", "LOOT" } + for _,group in pairs(spamg) do + ChatFrame_AddMessageGroup(ChatFrame3, group) + end + + for _, chan in pairs({EnumerateServerChannels()}) do + ChatFrame_AddChannel(ChatFrame3, chan) + ChatFrame_RemoveChannel(ChatFrame1, chan) + end + + JoinChannelByName("World") + ChatFrame_AddChannel(ChatFrame3, "World") + end + pfUI.chat:RefreshChat() + end + + pfUI.chat:SetScript("OnEvent", function() + -- set the default chat + FCF_SelectDockFrame(SELECTED_CHAT_FRAME) + + -- update all chat settings + pfUI.chat:RefreshChat() + FCF_DockUpdate() + if C.chat.right.enable == "0" then + pfUI.chat.right:Hide() + end + end) + + local function SkipFading(self, alpha) + UIFrameFadeRemoveFrame(self) + if SELECTED_CHAT_FRAME:GetID() == self:GetID() then + self:_SetAlpha(1) + else + self:_SetAlpha(0.5) + end + end + + for i=1, NUM_CHAT_WINDOWS do + _G["ChatFrame" .. i .. "UpButton"]:Hide() + _G["ChatFrame" .. i .. "UpButton"].Show = function() return end + _G["ChatFrame" .. i .. "DownButton"]:Hide() + _G["ChatFrame" .. i .. "DownButton"].Show = function() return end + _G["ChatFrame" .. i .. "BottomButton"]:Hide() + _G["ChatFrame" .. i .. "BottomButton"].Show = function() return end + _G["ChatFrame" .. i .. "Tab"]._SetAlpha = _G["ChatFrame" .. i .. "Tab"].SetAlpha + _G["ChatFrame" .. i .. "Tab"].SetAlpha = SkipFading + end + + pfUI.chat.editbox = CreateFrame("Frame", "pfChatInputBox", UIParent) + pfUI.chat.editbox:SetFrameStrata("DIALOG") + if C.chat.text.input_height == "0" then + pfUI.chat.editbox:SetHeight(22) + + if ChatFrameEditBoxLanguage then + SkinButton(ChatFrameEditBoxLanguage) + ChatFrameEditBoxLanguage:SetWidth(22) + ChatFrameEditBoxLanguage:SetHeight(22) + end + else + pfUI.chat.editbox:SetHeight(C.chat.text.input_height) + + if ChatFrameEditBoxLanguage then + SkinButton(ChatFrameEditBoxLanguage) + ChatFrameEditBoxLanguage:SetWidth(C.chat.text.input_height) + ChatFrameEditBoxLanguage:SetHeight(C.chat.text.input_height) + end + end + + -- to make sure the bars are set up properly, we need to wait. + local pfChatArrangeFrame = CreateFrame("Frame", "pfChatArrange", UIParent) + pfChatArrangeFrame:RegisterEvent("CVAR_UPDATE") + pfChatArrangeFrame:RegisterEvent("PLAYER_ENTERING_WORLD") + pfChatArrangeFrame:SetScript("OnEvent", function() + pfUI.chat.editbox:ClearAllPoints() + + local anchor = pfUI.chat.left + if pfUI.bars and pfUI.bars[6]:IsShown() then + anchor = pfUI.bars[6] + elseif pfUI.bars and pfUI.bars[1]:IsShown() then + anchor = pfUI.bars[1] + end + + if C.chat.text.input_width ~= "0" then + pfUI.chat.editbox:SetPoint("BOTTOM", anchor, "TOP", 0, default_border*3) + pfUI.chat.editbox:SetWidth(C.chat.text.input_width) + else + pfUI.chat.editbox:SetWidth(anchor:GetWidth()) + pfUI.chat.editbox:SetPoint("BOTTOMLEFT", anchor, "TOPLEFT", 0, default_border*3) + pfUI.chat.editbox:SetPoint("BOTTOMRIGHT", anchor, "TOPRIGHT", 0, default_border*3) + end + + UpdateMovable(pfUI.chat.editbox) + end) + + + ChatFrameEditBox:SetParent(pfUI.chat.editbox) + ChatFrameEditBox:SetAllPoints(pfUI.chat.editbox) + CreateBackdrop(ChatFrameEditBox, default_border) + + for i,v in ipairs({ChatFrameEditBox:GetRegions()}) do + if i==6 or i==7 or i==8 then v:Hide() end + if v.SetFont then + v:SetFont(pfUI.font_default, C.global.font_size + 1, "OUTLINE") + end + end + ChatFrameEditBox:SetAltArrowKeyMode(false) + + if C.chat.text.mouseover == "1" then + for i=1, NUM_CHAT_WINDOWS do + local frame = _G["ChatFrame" .. i] + frame:SetScript("OnHyperlinkEnter", function() + local _, _, linktype = string.find(arg1, "^(.-):(.+)$") + if linktype == "item" then + GameTooltip:SetOwner(this, "ANCHOR_CURSOR") + GameTooltip:SetHyperlink(arg1) + GameTooltip:Show() + end + end) + + frame:SetScript("OnHyperlinkLeave", function() + GameTooltip:Hide() + end) + end + end + + -- read and parse whisper color settings + local cr, cg, cb, ca = strsplit(",", C.chat.global.whisper) + cr, cg, cb = tonumber(cr), tonumber(cg), tonumber(cb) + local wcol = rgbhex(cr, cg, cb) + + -- read and parse chat bracket settings + local left = "|r" .. string.sub(C.chat.text.bracket, 1, 1) + local right = string.sub(C.chat.text.bracket, 2, 2) .. "|r" + + -- read and parse chat time bracket settings + local tleft = string.sub(C.chat.text.timebracket, 1, 1) + local tright = string.sub(C.chat.text.timebracket, 2, 2) + + -- shorten chat channel indicators + local default = " " .. "%s" .. "|r:" .. "\32" + _G.CHAT_CHANNEL_GET = "%s" .. "|r:" .. "\32" + _G.CHAT_GUILD_GET = left .. "G" .. right .. default + _G.CHAT_OFFICER_GET = left .. "O" .. right .. default + _G.CHAT_PARTY_GET = left .. "P" .. right .. default + _G.CHAT_RAID_GET = left .. "R" .. right .. default + _G.CHAT_RAID_LEADER_GET = left .. "RL" .. right .. default + _G.CHAT_RAID_WARNING_GET = left .. "RW" .. right .. default + _G.CHAT_BATTLEGROUND_GET = left .. "BG" .. right .. default + _G.CHAT_BATTLEGROUND_LEADER_GET = left .. "BL" .. right .. default + _G.CHAT_SAY_GET = left .. "S" .. right .. default + _G.CHAT_YELL_GET = left .. "Y" .. right ..default + + if C.chat.global.whispermod == "1" then + _G.CHAT_WHISPER_GET = wcol .. '[W]' .. default + _G.CHAT_WHISPER_INFORM_GET = '[W]' .. default + end + + local r,g,b,a = strsplit(",", C.chat.text.timecolor) + local timecolorhex = rgbhex(r,g,b,a) + + local r,g,b = strsplit(",", C.chat.text.unknowncolor) + local unknowncolorhex = rgbhex(r,g,b) + + local nothing = function() return end + local original = FriendsFrame_OnEvent + + local who_query = CreateFrame("Frame") + who_query:RegisterEvent("WHO_LIST_UPDATE") + who_query:SetScript("OnEvent", function() + if this.pending then + -- restore everything once a query is received + _G.FriendsFrame_OnEvent = original + this.pending = nil + SetWhoToUI(0) + end + end) + + local function GetPlayerLevel(name) + if not pfUI_playerDB then return nil end + if not pfUI_playerDB[name] then return nil end + return pfUI_playerDB[name].level + end + + local function ScanWhoName(name) + -- abort if another query is ongoing + if who_query.pending then return end + who_query.pending = true + + -- prepare and send the who query + _G.FriendsFrame_OnEvent = nothing + SetWhoToUI(1) + SendWho("n-"..name) + end + + local function AddMessage(frame, text, a1, a2, a3, a4, a5) + if not text then return end + + -- skip chat parsing on combat log + if frame.pfCombatLog then + return frame:HookAddMessage(text, a1, a2, a3, a4, a5) + end + + -- Remove prat CLINKs + text = gsub(text, "{CLINK:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") -- tbc + text = gsub(text, "{CLINK:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") -- vanilla + + -- Remove chatter CLINKs + text = gsub(text, "{CLINK:item:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") + text = gsub(text, "{CLINK:enchant:(%x+):([%d-]-):([^}]-)}", "|c%1|Henchant:%2|h[%3]|h|r") + text = gsub(text, "{CLINK:spell:(%x+):([%d-]-):([^}]-)}", "|c%1|Hspell:%2|h[%3]|h|r") + text = gsub(text, "{CLINK:quest:(%x+):([%d-]-):([%d-]-):([^}]-)}", "|c%1|Hquest:%2:%3|h[%4]|h|r") + + -- detect urls + if C.chat.text.detecturl == "1" then + text = pfUI.chat:HandleLink(text) + end + + -- display class colors if already indexed + if C.chat.text.classcolor == "1" then + for name in gfind(text, "|Hplayer:(.-)|h") do + local real, _ = strsplit(":", name) + local color = unknowncolorhex + local match = false + local class = GetUnitData(real) + + if class then + if class ~= UNKNOWN then + color = rgbhex(RAID_CLASS_COLORS[class]) + match = true + end + elseif C.chat.text.whosearchunknown == "1" then + ScanWhoName(name) + end + + if C.chat.text.tintunknown == "1" or match then + text = string.gsub(text, "|Hplayer:"..name.."|h%["..real.."%]|h(.-:-)", + left..color.."|Hplayer:"..name.."|h" .. color .. real .. "|h|r"..right.."%1") + end + end + end + + -- display player levels if available + if C.chat.text.playerlevel == "1" then + for name in gfind(text, "|Hplayer:(.-)|h") do + local real, _ = strsplit(":", name) + local level = GetPlayerLevel(real) + + if level and level > 0 then + local levelcolor = rgbhex(GetDifficultyColor(level)) + -- Add level after the player name, before the closing bracket + text = string.gsub(text, "(|Hplayer:" .. name .. "|h.-|h|r)" .. right, + "%1 " .. levelcolor .. level .. "|r" .. right) + elseif level and level <= 0 then + -- Show ?? for unknown levels (e.g. -1 from UnitLevel) + text = string.gsub(text, "(|Hplayer:" .. name .. "|h.-|h|r)" .. right, + "%1 |cffff0000??|r" .. right) + end + end + end + + -- reduce channel name to number + if C.chat.text.channelnumonly == "1" then + local channel = string.gsub(text, ".*%[(.-)%]%s+(.*|Hplayer).+", "%1") + if string.find(channel, "%d+%. ") then + channel = string.gsub(channel, "(%d+)%..*", "channel%1") + channel = string.gsub(channel, "channel", "") + text = string.gsub(text, "%[%d+%..-%]%s+(.*|Hplayer)", left .. channel .. right .. " %1") + end + end + + -- show timestamp in chat + if C.chat.text.time == "1" then + text = timecolorhex .. tleft .. date(C.chat.text.timeformat) .. tright .. "|r " .. text + end + + -- save chat history + if C.chat.global.whispermod == "1" and string.find(text, wcol, 1) == 1 then + SaveChatHistory(frame:GetID(), string.gsub(text, wcol, ""), cr, cg, cb) + else + SaveChatHistory(frame:GetID(), text, a1, a2, a3) + end + + if C.chat.global.whispermod == "1" then + -- patch incoming whisper string to match the colors + if string.find(text, wcol, 1) == 1 then + text = string.gsub(text, "|r", "|r" .. wcol) + end + end + + frame:HookAddMessage(text, a1, a2, a3, a4, a5) + end + + for i=1,NUM_CHAT_WINDOWS do + if C.chat.global.maxlines ~= "128" then + _G["ChatFrame"..i]:SetMaxLines(tonumber(C.chat.global.maxlines) or 128) + end + + if not _G["ChatFrame"..i].HookAddMessage then + if C.chat.text.history == "1" then + -- write history to chat + local history = GetChatHistory(i) + for j=30,0,-1 do + if history[j] then + _G["ChatFrame"..i]:AddMessage(history[j], .7,.7,.7) + end + end + end + + -- add chat parse and history hooks + _G["ChatFrame"..i].HookAddMessage = _G["ChatFrame"..i].AddMessage + _G["ChatFrame"..i].AddMessage = AddMessage + end + end + + -- create playerlinks on shift-click + if C.chat.text.playerlinks == "1" then + local pfHookSetItemRef = SetItemRef + _G.SetItemRef = function(link, text, button) + if ( strsub(link, 1, 6) == "player" ) then + local name = strsub(link, 8) + if ( name and (strlen(name) > 0) ) then + local name, _ = strsplit(":", name) + name = gsub(name, "([^%s]*)%s+([^%s]*)%s+([^%s]*)", "%3") + name = gsub(name, "([^%s]*)%s+([^%s]*)", "%2") + if IsShiftKeyDown() and ChatFrameEditBox:IsVisible() then + ChatFrameEditBox:Insert("|cffffffff|Hplayer:"..name.."|h["..name.."]|h|r") + return + end + end + end + pfHookSetItemRef(link, text, button) + end + end +end)