diff --git a/rChatPlus/core.lua b/rChatPlus/core.lua
new file mode 100644
index 0000000..c2d2f41
--- /dev/null
+++ b/rChatPlus/core.lua
@@ -0,0 +1,869 @@
+local A, L = ...
+local options = L.C
+
+--------------------------------------
+-- grab from elvui chat module
+local _G = _G
+local gsub, strfind, gmatch, format, max = gsub, strfind, gmatch, format, max
+local ipairs, sort, wipe, time, difftime = ipairs, sort, wipe, time, difftime
+local pairs, unpack, select, tostring, pcall, next, tonumber, type = pairs, unpack, select, tostring, pcall, next, tonumber, type
+local strlower, strsub, strlen, strupper, strtrim, strmatch = strlower, strsub, strlen, strupper, strtrim, strmatch
+local tinsert, tremove, tconcat = tinsert, tremove, table.concat
+
+local Ambiguate = Ambiguate
+local BetterDate = BetterDate
+local BNGetFriendInfo = BNGetFriendInfo
+local BNGetFriendInfoByID = BNGetFriendInfoByID
+local BNGetNumFriendInvites = BNGetNumFriendInvites
+local BNGetNumFriends = BNGetNumFriends
+local CreateFrame = CreateFrame
+local FlashClientIcon = FlashClientIcon
+local GetBNPlayerCommunityLink = GetBNPlayerCommunityLink
+local GetBNPlayerLink = GetBNPlayerLink
+local GetChannelName = GetChannelName
+local GetChatWindowInfo = GetChatWindowInfo
+local GetCursorPosition = GetCursorPosition
+local GetCVar, GetCVarBool = GetCVar, GetCVarBool
+local GetGuildRosterMOTD = GetGuildRosterMOTD
+local GetInstanceInfo = GetInstanceInfo
+local GetMouseFocus = GetMouseFocus
+local GetNumGroupMembers = GetNumGroupMembers
+local GetPlayerCommunityLink = GetPlayerCommunityLink
+local GetPlayerInfoByGUID = GetPlayerInfoByGUID
+local GetPlayerLink = GetPlayerLink
+local GetRaidRosterInfo = GetRaidRosterInfo
+local GMChatFrame_IsGM = GMChatFrame_IsGM
+local GMError = GMError
+local hooksecurefunc = hooksecurefunc
+local InCombatLockdown = InCombatLockdown
+local IsAltKeyDown = IsAltKeyDown
+local IsInRaid, IsInGroup = IsInRaid, IsInGroup
+local IsShiftKeyDown = IsShiftKeyDown
+local PlaySoundFile = PlaySoundFile
+local RemoveExtraSpaces = RemoveExtraSpaces
+local RemoveNewlines = RemoveNewlines
+local ToggleFrame = ToggleFrame
+local UnitName = UnitName
+
+local C_DateAndTime_GetCurrentCalendarTime = C_DateAndTime.GetCurrentCalendarTime
+local C_Club_GetInfoFromLastCommunityChatLine = C_Club.GetInfoFromLastCommunityChatLine
+local Chat_GetChatCategory = Chat_GetChatCategory
+local ChatHistory_GetAccessID = ChatHistory_GetAccessID
+local ChatFrame_ResolvePrefixedChannelName = ChatFrame_ResolvePrefixedChannelName
+local BNet_GetValidatedCharacterName = BNet_GetValidatedCharacterName
+local BNet_GetClientEmbeddedTexture = BNet_GetClientEmbeddedTexture
+local BNET_CLIENT_WOW = BNET_CLIENT_WOW
+
+local tabTexs = {
+ '',
+ 'Selected',
+ 'Highlight'
+}
+
+local function GetDockerParent(docker, chat)
+ if not docker then return end
+
+ local _, relativeTo = chat:GetPoint()
+ if relativeTo == docker then
+ return docker:GetParent()
+ end
+end
+
+local function UpdateEditboxAnchors(self)
+ local cvar = (type(self) == 'string' and self) or GetCVar('chatStyle')
+
+ local classic = cvar == 'classic'
+ local leftChat = classic and _G.LeftChatPanel
+ local panel = 22
+
+ for _, name in ipairs(_G.CHAT_FRAMES) do
+ local frame = _G[name]
+ local editbox = frame and frame.editBox
+ if not editbox then return end
+ editbox.chatStyle = cvar
+ editbox:ClearAllPoints()
+
+ local anchorTo = leftChat or frame
+ local below, belowInside = options.editBoxPosition == 'BELOW_CHAT', options.editBoxPosition == 'BELOW_CHAT_INSIDE'
+ if below or belowInside then
+ -- local showLeftPanel = E.db.datatexts.panels.LeftChatDataPanel.enable
+ editbox:SetPoint('TOPLEFT', anchorTo, 'BOTTOMLEFT', classic and 0 or -2, (classic and (belowInside and 1 or 0) or -5))
+ editbox:SetPoint('BOTTOMRIGHT', anchorTo, 'BOTTOMRIGHT', classic and 0 or -2, (classic and (belowInside and 1 or 0) or -5) + (belowInside and panel or -panel))
+ else
+ local aboveInside = options.editBoxPosition == 'ABOVE_CHAT_INSIDE'
+ editbox:SetPoint('BOTTOMLEFT', anchorTo, 'TOPLEFT', classic and (aboveInside and 1 or 0) or -2, (classic and (aboveInside and -1 or 0) or 2))
+ editbox:SetPoint('TOPRIGHT', anchorTo, 'TOPRIGHT', classic and (aboveInside and -1 or 0) or 2, (classic and (aboveInside and -1 or 0) or 2) + (aboveInside and -panel or panel))
+ end
+ end
+end
+
+local function RepositionOverflowButton()
+ _G.GeneralDockManagerOverflowButton:ClearAllPoints()
+ _G.GeneralDockManagerOverflowButton:SetPoint('RIGHT', _G.ChatFrameChannelButton, 'LEFT', -4, 0)
+end
+
+local function CreateChatVoicePanel()
+ local Holder = CreateFrame('Frame', 'ChatButtonHolder', UIParent)
+ Holder:ClearAllPoints()
+ Holder:SetPoint('BOTTOMLEFT', _G.LeftChatPanel, 'TOPLEFT', 0, 1)
+ Holder:SetSize(30, 86)
+ Holder.backdrop = L.CreateBackdrop(Holder)
+ Holder.backdrop:SetBackdropColor(options.panelColor.r, options.panelColor.g, options.panelColor.b, options.panelColor.a)
+ --create drag frame
+ rLib:CreateDragFrame(Holder, L.dragFrames, -2, true)
+
+ _G.ChatFrameChannelButton:ClearAllPoints()
+ _G.ChatFrameChannelButton:SetPoint('TOP', Holder, 'TOP', 0, -2)
+
+ _G.ChatFrameChannelButton.Icon:SetParent(_G.ChatFrameChannelButton)
+ _G.ChatFrameChannelButton.Icon:SetDesaturated(options.desaturateVoiceIcons)
+ _G.ChatFrameChannelButton:SetParent(Holder)
+
+ _G.ChatAlertFrame:ClearAllPoints()
+ _G.ChatAlertFrame:SetPoint('BOTTOM', _G.ChatFrameChannelButton, 'TOP', 1, 3)
+end
+
+local function HandleChatVoiceIcons()
+ if options.hideVoiceButtons then
+ _G.ChatFrameChannelButton:Hide()
+ elseif options.pinVoiceButtons then
+ _G.ChatFrameChannelButton.Icon:SetDesaturated(options.desaturateVoiceIcons)
+ _G.ChatFrameChannelButton:ClearAllPoints()
+ _G.ChatFrameChannelButton:SetPoint('RIGHT', _G.GeneralDockManager, 'RIGHT', 2, 0)
+
+ RepositionOverflowButton()
+ else
+ CreateChatVoicePanel()
+ end
+
+ if not options.hideVoiceButtons then
+ _G.GeneralDockManagerOverflowButtonList:SetFrameStrata('LOW')
+ _G.GeneralDockManagerOverflowButtonList:SetFrameLevel(5)
+ end
+
+ if not options.pinVoiceButtons then
+ _G.GeneralDockManagerOverflowButton:ClearAllPoints()
+ _G.GeneralDockManagerOverflowButton:SetPoint('RIGHT', _G.GeneralDockManager, 'RIGHT', -4, 0)
+ end
+end
+
+local function UpdateFading()
+ for _, frameName in ipairs(_G.CHAT_FRAMES) do
+ local frame = _G[frameName]
+ if frame then
+ frame:SetTimeVisible(options.inactivityTimer)
+ frame:SetFading(options.fade)
+ end
+ end
+end
+
+local canChangeMessage = function(arg1, id)
+ if id and arg1 == '' then return id end
+end
+
+local function MessageIsProtected(message)
+ return message and (message ~= gsub(message, '(:?|?)|K(.-)|k', canChangeMessage))
+end
+
+local function GetOwner(tab)
+ if not tab.owner then
+ tab.owner = _G[format('ChatFrame%s', tab:GetID())]
+ end
+
+ return tab.owner
+end
+
+local function GetTab(chat)
+ if not chat.tab then
+ chat.tab = _G[format('ChatFrame%sTab', chat:GetID())]
+ end
+
+ return chat.tab
+end
+
+local function ChatFrameTab_SetAlpha(self, _, skip)
+ if skip then return end
+ local chat = GetOwner(self)
+ self:SetAlpha((not chat.isDocked or self.selected) and 1 or 0.6, true)
+end
+
+local function ShowBackground(background, show)
+ if not background then return end
+
+ if show then
+ background.Show = nil
+ background:Show()
+ else
+ L.Kill(background)
+ end
+end
+
+local function TabOnEnter(tab)
+ tab.Text:Show()
+
+ if tab.conversationIcon then
+ tab.conversationIcon:Show()
+ end
+
+ if not options.hideCopyButton then
+ local chat = GetOwner(tab)
+ if chat and chat.copyButton and GetMouseFocus() ~= chat.copyButton then
+ chat.copyButton:SetAlpha(0.35)
+ end
+ end
+end
+
+local function TabOnLeave(tab)
+ tab.Text:Hide()
+
+ if tab.conversationIcon then
+ tab.conversationIcon:Hide()
+ end
+
+ if not options.hideCopyButton then
+ local chat = GetOwner(tab)
+ if chat and chat.copyButton and GetMouseFocus() ~= chat.copyButton then
+ chat.copyButton:SetAlpha(0)
+ end
+ end
+end
+
+local function ChatOnEnter(chat)
+ TabOnEnter(GetTab(chat))
+end
+
+local function ChatOnLeave(chat)
+ TabOnLeave(GetTab(chat))
+end
+
+local function IsUndocked(chat, docker)
+ if not docker then docker = _G.GeneralDockManager.primary end
+
+ local primaryUndocked = docker ~= rChatPlus.LeftChatWindow and docker ~= rChatPlus.RightChatWindow
+ return not chat.isDocked or (primaryUndocked and ((chat == docker) or GetDockerParent(docker, chat)))
+end
+
+local function HandleFadeTabs(chat, hook)
+ local tab = GetTab(chat)
+
+ if hook then
+ chat:SetScript('OnEnter', ChatOnEnter)
+ chat:SetScript('OnLeave', ChatOnLeave)
+
+ tab:SetScript('OnEnter', TabOnEnter)
+ tab:SetScript('OnLeave', TabOnLeave)
+ else
+ chat:SetScript('OnEnter', nil)
+ chat:SetScript('OnLeave', nil)
+
+ tab:SetScript('OnEnter', nil)
+ tab:SetScript('OnLeave', nil)
+ end
+
+ local focus = GetMouseFocus()
+ if not hook then
+ TabOnEnter(tab)
+ elseif focus ~= tab and focus ~= chat then
+ TabOnLeave(tab)
+ end
+end
+
+local function UpdateChatTab(chat)
+ local fadeLeft, fadeRight
+ if options.fadeTabsNoBackdrop then
+ local both = options.panelBackdrop == 'HIDEBOTH'
+ fadeLeft = (both or options.panelBackdrop == 'RIGHT')
+ fadeRight = (both or options.panelBackdrop == 'LEFT')
+ end
+
+ if chat == rChatPlus.LeftChatWindow then
+ GetTab(chat):SetParent(_G.LeftChatPanel or _G.UIParent)
+ chat:SetParent(_G.LeftChatPanel or _G.UIParent)
+
+ HandleFadeTabs(chat, fadeLeft)
+ elseif chat == rChatPlus.RightChatWindow then
+ GetTab(chat):SetParent(_G.RightChatPanel or _G.UIParent)
+ chat:SetParent(_G.RightChatPanel or _G.UIParent)
+
+ HandleFadeTabs(chat, fadeRight)
+ else
+ local docker = _G.GeneralDockManager.primary
+ local parent = GetDockerParent(docker, chat)
+
+ -- we need to update the tab parent to mimic the docker
+ GetTab(chat):SetParent(parent or _G.UIParent)
+ chat:SetParent(parent or _G.UIParent)
+
+ if parent and docker == rChatPlus.LeftChatWindow then
+ HandleFadeTabs(chat, fadeLeft)
+ elseif parent and docker == rChatPlus.RightChatWindow then
+ HandleFadeTabs(chat, fadeRight)
+ else
+ HandleFadeTabs(chat, options.fadeUndockedTabs and IsUndocked(chat, docker))
+ end
+ end
+end
+
+local function UpdateChatTabs()
+ for _, name in ipairs(_G.CHAT_FRAMES) do
+ UpdateChatTab(_G[name])
+ end
+end
+
+local function GetAnchorParents(chat)
+ local Left = (chat == rChatPlus.LeftChatWindow and _G.LeftChatPanel)
+ local Right = (chat == rChatPlus.RightChatWindow and _G.RightChatPanel)
+ local Chat, TabPanel = Left or Right or _G.UIParent
+
+ return TabPanel or Chat, Chat
+end
+
+local function ReparentVoiceChatIcon(parent)
+ if not parent then
+ parent = GetAnchorParents(_G.GeneralDockManager.primary)
+ end
+
+ _G.ChatFrameChannelButton:SetParent(parent)
+end
+
+local function FindChatWindows()
+ if not options.panelSnapping then return end
+
+ local left, right = rChatPlus.LeftChatWindow, rChatPlus.RightChatWindow
+
+ -- they already exist just return them :)
+ if left and right then
+ return left, right
+ end
+
+ local docker = _G.GeneralDockManager.primary
+ for _, name in ipairs(_G.CHAT_FRAMES) do
+ local chat = _G[name]
+ if (chat.isDocked and docker) or chat:IsShown() then
+ if not left and L.FramesOverlap(chat, _G.LeftChatPanel) then
+ left = chat
+ elseif not right and L.FramesOverlap(chat, _G.RightChatPanel) then
+ right = chat
+ end
+
+ -- if both are found just return now, don't wait
+ if left and right then
+ return left, right
+ end
+ end
+ end
+
+ -- none or one was found
+ return left, right
+end
+
+local function PositionChat(chat)
+ rChatPlus.LeftChatWindow, rChatPlus.RightChatWindow = FindChatWindows()
+
+ local docker = _G.GeneralDockManager.primary
+ if chat == docker then
+ local iconParent, chatParent = GetAnchorParents(chat)
+ _G.GeneralDockManager:SetParent(chatParent)
+
+ if options.pinVoiceButtons and not options.hideVoiceButtons then
+ ReparentVoiceChatIcon(iconParent or chatParent)
+ end
+ end
+
+ UpdateChatTab(chat)
+
+ if chat:IsMovable() then
+ chat:SetUserPlaced(true)
+ end
+
+ if chat.FontStringContainer then
+ chat.FontStringContainer:ClearAllPoints()
+ chat.FontStringContainer:SetPoint('TOPLEFT', chat, 'TOPLEFT', -1, 1)
+ chat.FontStringContainer:SetPoint('BOTTOMRIGHT', chat, 'BOTTOMRIGHT', 1, -1)
+ end
+
+ if chat:IsShown() then
+ -- that chat font container leaks outside of its frame
+ -- we cant clip it, so lets force that leak sooner so
+ -- i can position it properly, patch: 8.3.0 ~Simpy
+ chat:Hide()
+ chat:Show()
+ end
+
+ local BASE_OFFSET = 32
+ if chat == rChatPlus.LeftChatWindow then
+ local LOG_OFFSET = chat:GetID() == 2 and (_G.LeftChatTab:GetHeight() + 4) or 0
+
+ chat:ClearAllPoints()
+ chat:SetPoint('BOTTOMLEFT', _G.LeftChatPanel, 'BOTTOMLEFT', 5, 5)
+ chat:SetSize(options.panelWidth - 10, options.panelHeight - BASE_OFFSET - LOG_OFFSET)
+
+ ShowBackground(chat.Background, false)
+ elseif chat == rChatPlus.RightChatWindow then
+ local LOG_OFFSET = chat:GetID() == 2 and (_G.LeftChatTab:GetHeight() + 4) or 0
+
+ chat:ClearAllPoints()
+ chat:SetPoint('BOTTOMLEFT', _G.RightChatPanel, 'BOTTOMLEFT', 5, 5)
+ chat:SetSize((options.separateSizes and options.panelWidthRight or options.panelWidth) - 10, (options.separateSizes and options.panelHeightRight or options.panelHeight) - BASE_OFFSET - LOG_OFFSET)
+
+ ShowBackground(chat.Background, false)
+ else -- show if: not docked, or ChatFrame1, or attached to ChatFrame1
+ ShowBackground(chat.Background, IsUndocked(chat, docker))
+ end
+end
+
+local function PositionChats()
+ for _, name in ipairs(_G.CHAT_FRAMES) do
+ PositionChat(_G[name])
+ end
+end
+
+local copyLines = {}
+function GetLines(frame)
+ local index = 1
+ for i = 1, frame:GetNumMessages() do
+ local message, r, g, b = frame:GetMessageInfo(i)
+ if message and not MessageIsProtected(message) then
+ --Set fallback color values
+ r, g, b = r or 1, g or 1, b or 1
+
+ copyLines[index] = message
+ index = index + 1
+ end
+ end
+
+ return index - 1
+end
+
+local function CopyChat(frame)
+ if not _G.CopyChatFrame:IsShown() then
+ local _, fontSize = _G.FCF_GetChatWindowInfo(frame:GetID())
+ if fontSize < 10 then fontSize = 12 end
+ _G.FCF_SetChatWindowFontSize(frame, frame, 0.01)
+ _G.CopyChatFrame:Show()
+ local lineCt = GetLines(frame)
+ local text = tconcat(copyLines, ' \n', 1, lineCt)
+ _G.FCF_SetChatWindowFontSize(frame, frame, fontSize)
+ _G.CopyChatFrameEditBox:SetText(text)
+ else
+ _G.CopyChatFrame:Hide()
+ end
+end
+
+local function CopyButtonOnMouseUp(self, btn)
+ local chat = self:GetParent()
+ if btn == 'RightButton' and chat:GetID() == 1 then
+ ToggleFrame(_G.ChatMenu)
+ else
+ CopyChat(chat)
+ end
+end
+
+local function CopyButtonOnEnter(self)
+ self:SetAlpha(1)
+end
+
+local function CopyButtonOnLeave(self)
+ local chat = self:GetParent()
+ if _G[chat:GetName()..'TabText']:IsShown() then
+ self:SetAlpha(0.35)
+ else
+ self:SetAlpha(0)
+ end
+end
+
+local function ToggleChatButton(button)
+ if button then
+ button:SetShown(not options.hideCopyButton)
+ end
+end
+
+local function Panels_ColorUpdate()
+ local panelColor = options.panelColor
+ _G.LeftChatPanel.backdrop:SetBackdropColor(panelColor.r, panelColor.g, panelColor.b, panelColor.a)
+
+ if _G.ChatButtonHolder then
+ _G.ChatButtonHolder:SetBackdropColor(panelColor.r, panelColor.g, panelColor.b, panelColor.a)
+ end
+end
+
+local function StyleChat(frame)
+ local name = frame:GetName()
+ local tab = GetTab(frame)
+
+ frame:SetTimeVisible(options.inactivityTimer)
+ frame:SetMaxLines(options.maxLines)
+ frame:SetFading(options.fade)
+
+ if frame.styled then return end
+
+ frame:SetFrameLevel(4)
+ frame:SetClampRectInsets(0,0,0,0)
+ frame:SetClampedToScreen(false)
+ L.StripTextures(frame, true)
+
+ L.Kill(_G[name..'ButtonFrame'])
+
+ local editbox = frame.editBox
+
+ for _, texName in pairs(tabTexs) do
+ _G[name..'Tab'..texName..'Left']:SetTexture()
+ _G[name..'Tab'..texName..'Middle']:SetTexture()
+ _G[name..'Tab'..texName..'Right']:SetTexture()
+ end
+
+ hooksecurefunc(tab, 'SetAlpha', ChatFrameTab_SetAlpha)
+
+ if not tab.left then tab.left = _G[name..'TabLeft'] end
+ tab.Text:ClearAllPoints()
+ tab.Text:SetPoint('LEFT', tab, 'LEFT', tab.left:GetWidth(), 0)
+ tab:SetHeight(22)
+
+ if tab.conversationIcon then
+ tab.conversationIcon:ClearAllPoints()
+ tab.conversationIcon:SetPoint('RIGHT', tab.Text, 'LEFT', -1, 0)
+ end
+
+ --local a, b, c = select(6, editbox:GetRegions()); a:Kill(); b:Kill(); c:Kill()
+ L.Kill(_G[name..'EditBoxLeft'])
+ L.Kill(_G[name..'EditBoxMid'])
+ L.Kill(_G[name..'EditBoxRight'])
+
+ editbox:SetAltArrowKeyMode(options.useAltKey)
+
+ --copy chat button
+ local copyButton = CreateFrame('Frame', format('%sCopyChatButton%d', A, id), frame)
+ copyButton:EnableMouse(true)
+ copyButton:SetAlpha(0.35)
+ copyButton:SetSize(20, 22)
+ copyButton:SetPoint('TOPRIGHT', 0, -4)
+ copyButton:SetFrameLevel(frame:GetFrameLevel() + 5)
+ frame.copyButton = copyButton
+
+ local copyTexture = L.CreateIcon(frame.copyButton, 'OVERLAY')
+ copyTexture:SetTexture(options.mediapath .. "Copy.tga")
+ copyButton.texture = copyTexture
+
+ copyButton:SetScript('OnMouseUp', CopyButtonOnMouseUp)
+ copyButton:SetScript('OnEnter', CopyButtonOnEnter)
+ copyButton:SetScript('OnLeave', CopyButtonOnLeave)
+ ToggleChatButton(copyButton)
+
+ frame.styled = true
+end
+
+local function SetupChat()
+ for _, frameName in ipairs(_G.CHAT_FRAMES) do
+ local frame = _G[frameName]
+ local id = frame:GetID()
+ StyleChat(frame)
+
+ _G.FCFTab_UpdateAlpha(frame)
+ end
+
+ local chat = _G.GeneralDockManager.primary
+ _G.GeneralDockManager:ClearAllPoints()
+ _G.GeneralDockManager:SetPoint('BOTTOMLEFT', chat, 'TOPLEFT', 0, 3)
+ _G.GeneralDockManager:SetPoint('BOTTOMRIGHT', chat, 'TOPRIGHT', 0, 3)
+ _G.GeneralDockManager:SetHeight(22)
+ _G.GeneralDockManagerScrollFrame:SetHeight(22)
+ _G.GeneralDockManagerScrollFrameChild:SetHeight(22)
+
+ PositionChats()
+
+ if not rChatPlus.HookSecured then
+ hooksecurefunc("FCF_OpenTemporaryWindow", SetupChat)
+ rChatPlus.HookSecured = true
+ end
+end
+
+local function UpdateEditboxFont(chatFrame)
+ local style = GetCVar('chatStyle')
+ if style == 'classic' and rChatPlus.LeftChatWindow then
+ chatFrame = rChatPlus.LeftChatWindow
+ end
+
+ if chatFrame == _G.GeneralDockManager.primary then
+ chatFrame = _G.GeneralDockManager.selected
+ end
+
+ local editbox = _G.ChatEdit_ChooseBoxForSend(chatFrame)
+ -- the header and text will not update the placement without focus
+ if editbox and editbox:IsShown() then
+ editbox:SetFocus()
+ end
+end
+
+local function ChatEdit_SetLastActiveWindow(editbox)
+ local style = editbox.chatStyle or GetCVar('chatStyle')
+ if style == 'im' then editbox:SetAlpha(0.5) end
+end
+
+local function ChatEdit_DeactivateChat(editbox)
+ local style = editbox.chatStyle or GetCVar('chatStyle')
+ if style == 'im' then editbox:Hide() end
+end
+
+local function ChatEdit_ActivateChat(editbox)
+ if editbox and editbox.chatFrame then
+ UpdateEditboxFont(editbox.chatFrame)
+ end
+end
+
+local function ChatEdit_OnEnterPressed(editBox)
+ -- editBox:ClearHistory() -- we will use our own editbox history so keeping them populated on blizzards end is pointless
+
+ local chatType = editBox:GetAttribute('chatType')
+ local chatFrame = chatType and editBox:GetParent()
+ if chatFrame and (not chatFrame.isTemporary) and (_G.ChatTypeInfo[chatType].sticky == 1) then
+ if not options.sticky then chatType = 'SAY' end
+ editBox:SetAttribute('chatType', chatType)
+ end
+end
+
+local function GetCombatLog()
+ local LOG = _G.ChatFrame2 -- ChatFrame2
+ if LOG then return LOG, GetTab(LOG) end
+end
+
+local function FCFDock_UpdateTabs(dock)
+ if dock == _G.GeneralDockManager then
+ local logchat, logchattab = GetCombatLog()
+ dock.scrollFrame:ClearAllPoints()
+ dock.scrollFrame:SetPoint('RIGHT', dock.overflowButton, 'LEFT')
+ dock.scrollFrame:SetPoint('TOPLEFT', (logchat.isDocked and logchattab) or GetTab(dock.primary), 'TOPRIGHT')
+ end
+end
+
+local function FCF_Close(chat)
+ -- clear these off when it's closed, used by FCFTab_UpdateColors
+ local tab = GetTab(chat)
+ tab.whisperName = nil
+ tab.classColor = nil
+end
+
+local function FCF_SetWindowAlpha(frame, alpha)
+ frame.oldAlpha = alpha or 1
+end
+
+local function FCFTab_UpdateColors(tab, selected)
+ if not tab then return end
+
+ if tab:GetParent() == _G.ChatConfigFrameChatTabManager then
+ if selected then
+ tab.Text:SetTextColor(1, 1, 1)
+ end
+
+ local name = GetChatWindowInfo(tab:GetID())
+ if name then
+ tab.Text:SetText(name)
+ end
+
+ tab:SetAlpha(1) -- for some reason blizzard likes to change the alpha here? idk
+ else -- actual chat tab and other
+ local chat = GetOwner(tab)
+ if not chat then return end
+
+ tab.selected = selected
+
+ local whisper = tab.conversationIcon and chat.chatTarget
+ local name = chat.name or UNKNOWN
+
+ if whisper and not tab.whisperName then
+ tab.whisperName = gsub(name, '([%S]-)%-[%S]+', '%1|cFF999999*|r')
+ end
+
+ if selected then
+ if options.tabSelector == 'NONE' then
+ tab:SetFormattedText(rChatPlus.TabStyles.NONE, tab.whisperName or name)
+ else
+ local color = options.tabSelectorColor
+ local hexColor = L.RGBToHex(color.r, color.g, color.b)
+ tab:SetFormattedText(rChatPlus.TabStyles[options.tabSelector] or rChatPlus.TabStyles.ARROW1, hexColor, tab.whisperName or name, hexColor)
+ end
+
+ if options.tabSelectedTextEnabled then
+ local color = options.tabSelectedTextColor
+ tab.Text:SetTextColor(color.r, color.g, color.b)
+ return -- using selected text color
+ end
+ end
+
+ if whisper then
+ if not selected then
+ tab:SetText(tab.whisperName or name)
+ end
+ else
+ if not selected then
+ tab:SetText(name)
+ end
+ end
+ end
+end
+
+local function FCFDock_SelectWindow(_, chatFrame)
+ if chatFrame then
+ UpdateEditboxFont(chatFrame)
+ end
+end
+
+local function Unsnapped(chat)
+ if chat == rChatPlus.LeftChatWindow then
+ rChatPlus.LeftChatWindow = nil
+ elseif chat == rChatPlus.RightChatWindow then
+ rChatPlus.RightChatWindow = nil
+ end
+end
+
+local function ClearSnapping()
+ rChatPlus.LeftChatWindow = nil
+ rChatPlus.RightChatWindow = nil
+end
+
+local function SnappingChanged(chat)
+ Unsnapped(chat)
+
+ if chat == _G.GeneralDockManager.primary then
+ for _, frame in ipairs(_G.GeneralDockManager.DOCKED_CHAT_FRAMES) do
+ PositionChat(frame)
+ end
+ else
+ PositionChat(chat)
+ end
+end
+
+local function BuildCopyChatFrame()
+ local frame = CreateFrame('Frame', 'CopyChatFrame', UIParent, 'BackdropTemplate')
+ tinsert(_G.UISpecialFrames, 'CopyChatFrame')
+ frame.backdrop = L.CreateBackdrop(frame)
+ frame:SetSize(700, 200)
+ frame:SetPoint('CENTER', UIParent, 'CENTER')
+ frame:Hide()
+ frame:SetMovable(true)
+ frame:EnableMouse(true)
+ frame:SetResizable(true)
+ frame:SetMinResize(350, 100)
+ frame:SetScript('OnMouseDown', function(copyChat, button)
+ if button == 'LeftButton' and not copyChat.isMoving then
+ copyChat:StartMoving()
+ copyChat.isMoving = true
+ elseif button == 'RightButton' and not copyChat.isSizing then
+ copyChat:StartSizing()
+ copyChat.isSizing = true
+ end
+ end)
+ frame:SetScript('OnMouseUp', function(copyChat, button)
+ if button == 'LeftButton' and copyChat.isMoving then
+ copyChat:StopMovingOrSizing()
+ copyChat.isMoving = false
+ elseif button == 'RightButton' and copyChat.isSizing then
+ copyChat:StopMovingOrSizing()
+ copyChat.isSizing = false
+ end
+ end)
+ frame:SetScript('OnHide', function(copyChat)
+ if copyChat.isMoving or copyChat.isSizing then
+ copyChat:StopMovingOrSizing()
+ copyChat.isMoving = false
+ copyChat.isSizing = false
+ end
+ end)
+ frame:SetFrameStrata('DIALOG')
+
+ local scrollArea = CreateFrame('ScrollFrame', 'CopyChatScrollFrame', frame, 'UIPanelScrollFrameTemplate')
+ scrollArea:SetPoint('TOPLEFT', frame, 'TOPLEFT', 8, -30)
+ scrollArea:SetPoint('BOTTOMRIGHT', frame, 'BOTTOMRIGHT', -30, 8)
+ scrollArea:SetScript('OnSizeChanged', function(scroll)
+ _G.CopyChatFrameEditBox:SetWidth(scroll:GetWidth())
+ _G.CopyChatFrameEditBox:SetHeight(scroll:GetHeight())
+ end)
+ scrollArea:SetScript('OnVerticalScroll', function(scroll, offset)
+ _G.CopyChatFrameEditBox:SetHitRectInsets(0, 0, offset, (_G.CopyChatFrameEditBox:GetHeight() - offset - scroll:GetHeight()))
+ end)
+
+ local editBox = CreateFrame('EditBox', 'CopyChatFrameEditBox', frame)
+ editBox:SetMultiLine(true)
+ editBox:SetMaxLetters(99999)
+ editBox:EnableMouse(true)
+ editBox:SetAutoFocus(false)
+ editBox:SetFontObject(_G.ChatFontNormal)
+ editBox:SetWidth(scrollArea:GetWidth())
+ editBox:SetHeight(200)
+ editBox:SetScript('OnEscapePressed', function() _G.CopyChatFrame:Hide() end)
+ scrollArea:SetScrollChild(editBox)
+ _G.CopyChatFrameEditBox:SetScript('OnTextChanged', function(_, userInput)
+ if userInput then return end
+ local _, Max = _G.CopyChatScrollFrameScrollBar:GetMinMaxValues()
+ for _ = 1, Max do
+ _G.ScrollFrameTemplate_OnMouseWheel(_G.CopyChatScrollFrame, -1)
+ end
+ end)
+
+ local close = CreateFrame('Button', 'CopyChatFrameCloseButton', frame, 'UIPanelCloseButton, BackdropTemplate')
+ close:SetPoint('TOPRIGHT')
+ close:SetFrameLevel(close:GetFrameLevel() + 1)
+ close:EnableMouse(true)
+end
+
+function rChatPlus:Initialize()
+ SetupChat()
+ UpdateFading()
+ Panels_ColorUpdate()
+ HandleChatVoiceIcons()
+ UpdateEditboxAnchors()
+
+ hooksecurefunc("ChatEdit_SetLastActiveWindow", ChatEdit_SetLastActiveWindow)
+ hooksecurefunc("ChatEdit_DeactivateChat", ChatEdit_DeactivateChat)
+ hooksecurefunc("ChatEdit_ActivateChat", ChatEdit_ActivateChat)
+ hooksecurefunc("ChatEdit_OnEnterPressed", ChatEdit_OnEnterPressed)
+ hooksecurefunc("FCFDock_UpdateTabs", FCFDock_UpdateTabs)
+ hooksecurefunc("FCF_Close", FCF_Close)
+ hooksecurefunc("FCF_SetWindowAlpha", FCF_SetWindowAlpha)
+ hooksecurefunc("FCFTab_UpdateColors", FCFTab_UpdateColors)
+ hooksecurefunc('FCFDock_SelectWindow', FCFDock_SelectWindow)
+ hooksecurefunc('FCF_SavePositionAndDimensions', SnappingChanged)
+ hooksecurefunc('FCF_UnDockFrame', SnappingChanged)
+ hooksecurefunc('FCF_DockFrame', SnappingChanged)
+ hooksecurefunc('FCF_ResetChatWindows', ClearSnapping)
+ hooksecurefunc('RedockChatWindows', ClearSnapping)
+
+ self:RegisterEvent('UPDATE_CHAT_WINDOWS')
+ self:RegisterEvent('UPDATE_FLOATING_CHAT_WINDOWS')
+ self:SetScript('OnEvent', function(self, event)
+ if event == 'UPDATE_CHAT_WINDOWS' or event == 'UPDATE_FLOATING_CHAT_WINDOWS' then
+ SetupChat()
+ end
+ end)
+
+ BuildCopyChatFrame()
+
+ -- Editbox Backdrop Color
+ hooksecurefunc('ChatEdit_UpdateHeader', function(editbox)
+ local chatType = editbox:GetAttribute('chatType')
+ if not chatType then return end
+
+ local ChatTypeInfo = _G.ChatTypeInfo
+ local info = ChatTypeInfo[chatType]
+ 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 not editbox.backdrop then
+ editbox.backdrop = L.CreateBackdrop(editbox)
+ end
+
+ if chanName and (chatType == 'CHANNEL') then
+ if chanName == 0 then
+ editbox.backdrop:SetBackdropBorderColor(unpack(options.backdrop.edgeColor))
+ else
+ info = ChatTypeInfo[chatType..chanName]
+ editbox.backdrop:SetBackdropBorderColor(info.r, info.g, info.b)
+ end
+ else
+ editbox.backdrop:SetBackdropBorderColor(info.r, info.g, info.b)
+ end
+ end)
+end
\ No newline at end of file
diff --git a/rChatPlus/init.lua b/rChatPlus/init.lua
new file mode 100644
index 0000000..1a0a0ba
--- /dev/null
+++ b/rChatPlus/init.lua
@@ -0,0 +1,64 @@
+local A, L = ...
+
+L.dragFrames = {}
+L.addonName = A
+L.addonColor = "ff27c400"
+L.addonShortcut = "rchat"
+
+-----------------------------
+-- rChatPlus Global
+-----------------------------
+
+rChatPlus = CreateFrame("frame")
+rChatPlus.addonName = A
+rChatPlus.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',
+}
+
+-----------------------------
+-- Configs
+-----------------------------
+
+L.C = {
+ mediapath = "interface\\addons\\"..A.."\\medias\\",
+ backdrop = rLib.CopyTable(oUF_SimpleConfig.backdrop),
+ panelSnapping = true,
+ fade = true,
+ inactivityTimer = 100,
+ fontOutline = 'NONE',
+ sticky = true,
+ maxLines = 100,
+ tabSelector = 'ARROW1',
+ tabSelectedTextEnabled = true,
+ tabSelectedTextColor = { r = 1, g = 1, b = 1 },
+ tabSelectorColor = { r = .3, g = 1, b = .3 },
+ separateSizes = false,
+ panelWidth = 412,
+ panelHeight = 180,
+ panelWidthRight = 412,
+ panelHeightRight = 180,
+ panelBackdrop = 'SHOWBOTH',
+ editBoxPosition = 'ABOVE_CHAT_INSIDE',
+ fadeUndockedTabs = false,
+ fadeTabsNoBackdrop = true,
+ hideCopyButton = false,
+ useAltKey = false,
+ panelColor = {r = .06, g = .06, b = .06, a = 0.5},
+ pinVoiceButtons = true,
+ hideVoiceButtons = false,
+ desaturateVoiceIcons = true,
+}
+
+L.C.backdrop.edgeSize = 2
+L.C.backdrop.inset = 2
+L.C.backdrop.insets = {left=2,right=2,top=2,bottom=2}
\ No newline at end of file
diff --git a/rChatPlus/medias/Copy.tga b/rChatPlus/medias/Copy.tga
new file mode 100644
index 0000000..d04ad68
Binary files /dev/null and b/rChatPlus/medias/Copy.tga differ
diff --git a/rChatPlus/rChatPlus.toc b/rChatPlus/rChatPlus.toc
new file mode 100644
index 0000000..7b80b3f
--- /dev/null
+++ b/rChatPlus/rChatPlus.toc
@@ -0,0 +1,10 @@
+## Interface: 20502
+## Author: rawoil
+## Title: rChat|cff27c400Plus|r |cff1a9fc0BCC|r
+## Notes: Chat enhancements advanced
+## RequiredDeps: rLib, oUF_SimpleConfig
+
+init.lua
+utils.lua
+core.lua
+setup.lua
\ No newline at end of file
diff --git a/rChatPlus/setup.lua b/rChatPlus/setup.lua
new file mode 100644
index 0000000..fcb1cae
--- /dev/null
+++ b/rChatPlus/setup.lua
@@ -0,0 +1,19 @@
+local A, L = ...
+
+local options = L.C
+local chatPanel = CreateFrame('Frame', 'LeftChatPanel', UIParent)
+chatPanel:SetFrameStrata('BACKGROUND')
+chatPanel:SetFrameLevel(300)
+chatPanel:SetSize(options.panelWidth, options.panelHeight)
+chatPanel:SetPoint('BOTTOMLEFT', UIParent, 4, 4)
+chatPanel.backdrop = L.CreateBackdrop(chatPanel)
+
+--Left Chat Tab
+local chatTab = CreateFrame('Frame', 'LeftChatTab', chatPanel, 'BackdropTemplate')
+
+rChatPlus:Initialize()
+
+--create drag frame
+rLib:CreateDragFrame(chatPanel, L.dragFrames, -2, true)
+--create slash commands
+rLib:CreateSlashCmd(L.addonName, L.addonShortcut, L.dragFrames, L.addonColor)
\ No newline at end of file
diff --git a/rChatPlus/utils.lua b/rChatPlus/utils.lua
new file mode 100644
index 0000000..7732904
--- /dev/null
+++ b/rChatPlus/utils.lua
@@ -0,0 +1,101 @@
+local A, L = ...
+
+L.HiddenFrame = CreateFrame('Frame')
+L.HiddenFrame:Hide()
+
+--CreateBackdrop
+local function CreateBackdrop(self, relativeTo, anotherBackdrop)
+ local backdrop = anotherBackdrop or L.C.backdrop
+ local parent = self.IsObjectType and self:IsObjectType('Texture') and self:GetParent() or self
+ local bd = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate")
+ if (parent:GetFrameLevel() - 1) >= 0 then
+ bd:SetFrameLevel(parent:GetFrameLevel() - 1)
+ else
+ bd:SetFrameLevel(0)
+ end
+
+ bd:SetPoint("TOPLEFT", relativeTo or self, "TOPLEFT", -backdrop.inset, backdrop.inset)
+ bd:SetPoint("BOTTOMRIGHT", relativeTo or self, "BOTTOMRIGHT", backdrop.inset, -backdrop.inset)
+ bd:SetBackdrop(backdrop)
+ bd:SetBackdropColor(unpack(backdrop.bgColor))
+ bd:SetBackdropBorderColor(unpack(backdrop.edgeColor))
+ return bd
+end
+L.CreateBackdrop = CreateBackdrop
+
+--From http://wow.gamepedia.com/UI_coordinates
+local function FramesOverlap(frameA, frameB)
+ if not frameA or not frameB then return end
+
+ local sA, sB = frameA:GetEffectiveScale(), frameB:GetEffectiveScale()
+ if not sA or not sB then return end
+
+ local frameALeft, frameARight, frameABottom, frameATop = frameA:GetLeft(), frameA:GetRight(), frameA:GetBottom(), frameA:GetTop()
+ local frameBLeft, frameBRight, frameBBottom, frameBTop = frameB:GetLeft(), frameB:GetRight(), frameB:GetBottom(), frameB:GetTop()
+ if not (frameALeft and frameARight and frameABottom and frameATop) then return end
+ if not (frameBLeft and frameBRight and frameBBottom and frameBTop) then return end
+
+ return ((frameALeft*sA) < (frameBRight*sB)) and ((frameBLeft*sB) < (frameARight*sA)) and ((frameABottom*sA) < (frameBTop*sB)) and ((frameBBottom*sB) < (frameATop*sA))
+end
+L.FramesOverlap = FramesOverlap
+
+local function Kill(object)
+ if object.UnregisterAllEvents then
+ object:UnregisterAllEvents()
+ object:SetParent(L.HiddenFrame)
+ else
+ object.Show = object.Hide
+ end
+
+ object:Hide()
+end
+L.Kill = Kill
+
+--SetPoint
+local function SetPoint(self,relativeTo,point)
+ --adjut the setpoint function to make it possible to reference a relativeTo object that is set on runtime and it not available on config init
+ local a,b,c,d,e = unpack(point)
+ if not b then
+ self:SetPoint(a)
+ elseif b and type(b) == "string" and not _G[b] then
+ self:SetPoint(a,relativeTo,b,c,d)
+ else
+ self:SetPoint(a,b,c,d,e)
+ end
+end
+L.SetPoint = SetPoint
+
+--CreateIcon
+local function CreateIcon(self,layer)
+ local icon = self:CreateTexture(nil,layer)
+ icon:SetAllPoints()
+ return icon
+end
+L.CreateIcon = CreateIcon
+
+local function StripTextures(self, kill)
+ for i = 1, self:GetNumRegions() do
+ local Region = select(i, self:GetRegions())
+ if (Region and Region:GetObjectType() == "Texture") then
+ if (kill and type(kill) == "boolean") then
+ L.Kill(Region)
+ elseif (Region:GetDrawLayer() == kill) then
+ Region:SetTexture(nil)
+ elseif (kill and type(kill) == "string" and Region:GetTexture() ~= kill) then
+ Region:SetTexture(nil)
+ else
+ Region:SetTexture(nil)
+ end
+ end
+ end
+end
+L.StripTextures = StripTextures
+
+--RGB to Hex
+local function RGBToHex(r, g, b, header, ending)
+ r = r <= 1 and r >= 0 and r or 1
+ g = g <= 1 and g >= 0 and g or 1
+ b = b <= 1 and b >= 0 and b or 1
+ return format('%s%02x%02x%02x%s', header or '|cff', r*255, g*255, b*255, ending or '')
+end
+L.RGBToHex = RGBToHex
\ No newline at end of file
diff --git a/rSkin/init.lua b/rSkin/init.lua
index 35a3a20..1214ee5 100644
--- a/rSkin/init.lua
+++ b/rSkin/init.lua
@@ -9,7 +9,7 @@ rSkin.addonName = A
rSkin.skins = {}
L.C = {
- style = "ZrokUI",
+ style = "ZorkUI",
backdrop = rLib.CopyTable(oUF_SimpleConfig.backdrop), -- default bd
font = oUF_SimpleConfig.fonts.expressway, -- font
scale = oUF_SimpleConfig.globalscale, -- scale