-- CHAT PROTOTYPE STUFF SELECTED_DOCK_FRAME = nil; DOCKED_CHAT_FRAMES = {}; DOCK_COPY = {}; MOVING_CHATFRAME = nil; CHAT_TAB_SHOW_DELAY = 0.2; CHAT_TAB_HIDE_DELAY = 1; CHAT_FRAME_FADE_TIME = 0.15; CHAT_FRAME_FADE_OUT_TIME = 2.0; CHAT_FRAME_BUTTON_FRAME_MIN_ALPHA = 0.2; CHAT_FRAME_TAB_SELECTED_MOUSEOVER_ALPHA = 1.0; CHAT_FRAME_TAB_SELECTED_NOMOUSE_ALPHA = 0.4; CHAT_FRAME_TAB_ALERTING_MOUSEOVER_ALPHA = 1.0; CHAT_FRAME_TAB_ALERTING_NOMOUSE_ALPHA = 1.0; CHAT_FRAME_TAB_NORMAL_MOUSEOVER_ALPHA = 0.6; CHAT_FRAME_TAB_NORMAL_NOMOUSE_ALPHA = 0.2; DEFAULT_CHATFRAME_ALPHA = 0.25; DEFAULT_CHATFRAME_COLOR = {r = 0, g = 0, b = 0}; CHAT_FRAME_NORMAL_MIN_HEIGHT = 120; CHAT_FRAME_BIGGER_MIN_HEIGHT = 147; CHAT_FRAME_MIN_WIDTH = 296; CURRENT_CHAT_FRAME_ID = nil; CHAT_FRAME_TEXTURES = { "Background", "TopLeftTexture", "BottomLeftTexture", "TopRightTexture", "BottomRightTexture", "LeftTexture", "RightTexture", "BottomTexture", "TopTexture", --"ResizeButton", "ButtonFrameBackground", "ButtonFrameTopLeftTexture", "ButtonFrameBottomLeftTexture", "ButtonFrameTopRightTexture", "ButtonFrameBottomRightTexture", "ButtonFrameLeftTexture", "ButtonFrameRightTexture", "ButtonFrameBottomTexture", "ButtonFrameTopTexture", } CHAT_FRAMES = {}; function FloatingChatFrame_OnLoad(self) --IMPORTANT NOTE: This function isn't run by ChatFrame1. tinsert(CHAT_FRAMES, self:GetName()); FCF_SetTabPosition(self, 0); FloatingChatFrame_Update(self:GetID()); FCFTab_UpdateColors(_G[self:GetName().."Tab"], true); self:SetClampRectInsets(-35, 35, 26, -50); local chatTab = _G[self:GetName().."Tab"]; chatTab.mouseOverAlpha = CHAT_FRAME_TAB_SELECTED_MOUSEOVER_ALPHA; chatTab.noMouseAlpha = CHAT_FRAME_TAB_SELECTED_NOMOUSE_ALPHA; end function FloatingChatFrame_OnEvent(self, event, ...) if ( (event == "UPDATE_CHAT_WINDOWS") or (event == "UPDATE_FLOATING_CHAT_WINDOWS") ) then FloatingChatFrame_Update(self:GetID(), 1); self.isInitialized = 1; elseif ( event == "UPDATE_CHAT_COLOR" ) then local chatType, r, g, b = ...; if ( self.isTemporary and self.chatType == chatType ) then local tab = _G[self:GetName().."Tab"]; tab.selectedColorTable.r, tab.selectedColorTable.g, tab.selectedColorTable.b = r, g, b; FCFTab_UpdateColors(tab, not self.isDocked or self == FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK)); end end end function FloatingChatFrame_OnMouseScroll(self, delta) if ( delta > 0 ) then self:ScrollUp(); else self:ScrollDown(); end end function FCF_GetChatWindowInfo(id) if ( id > NUM_CHAT_WINDOWS ) then local frame = _G["ChatFrame"..id]; local tab = _G["ChatFrame"..id.."Tab"]; local background = _G["ChatFrame"..id.."Background"]; if ( frame and tab and background ) then local r, g, b, a = background:GetVertexColor(); return tab:GetText(), select(2, frame:GetFont()), r, g, b, a, frame:IsShown(), frame.isLocked, frame.isDocked, frame.isUninteractable; --This is a temporary chat window. Pass this to whatever handles those options. end else return GetChatWindowInfo(id); end end function FCF_CopyChatSettings(copyTo, copyFrom) local name, fontSize, r, g, b, a, shown, locked, docked, uninteractable = FCF_GetChatWindowInfo(copyFrom:GetID()); FCF_SetWindowColor(copyTo, r, g, b, 1); FCF_SetWindowAlpha(copyTo, a, 1); --If we're copying to a docked window, we don't want to copy locked. if ( not copyTo.isDocked ) then FCF_SetLocked(copyTo, locked); end FCF_SetUninteractable(copyTo, uninteractable); FCF_SetChatWindowFontSize(nil, copyTo, fontSize); end function FloatingChatFrame_Update(id, onUpdateEvent) local chatFrame = _G["ChatFrame"..id]; local chatTab = _G["ChatFrame"..id.."Tab"]; local name, fontSize, r, g, b, a, shown, locked, docked, uninteractable = FCF_GetChatWindowInfo(id); -- Set Tab Name FCF_SetWindowName(chatFrame, name, 1) if ( onUpdateEvent ) then -- Set Frame Color and Alpha FCF_SetWindowColor(chatFrame, r, g, b, 1); FCF_SetWindowAlpha(chatFrame, a, 1); FCF_SetLocked(chatFrame, locked); FCF_SetUninteractable(chatFrame, uninteractable); end if ( shown ) then if ( not chatFrame.minimized ) then chatFrame:Show(); end FCF_SetTabPosition(chatFrame, 0); else if ( not chatFrame.isDocked ) then chatFrame:Hide(); chatTab:Hide(); end end if ( docked ) then FCF_DockFrame(chatFrame, docked, (id == 1)); else if ( shown ) then FCF_UnDockFrame(chatFrame); if ( not chatFrame.minimized ) then chatTab:Show(); end else FCF_Close(chatFrame); end end if ( not chatFrame.isTemporary and (chatFrame == DEFAULT_CHAT_FRAME or not chatFrame.isDocked)) then FCF_RestorePositionAndDimensions(chatFrame); end FCF_UpdateButtonSide(chatFrame); end -- Channel Dropdown function FCFOptionsDropDown_OnLoad(self) CURRENT_CHAT_FRAME_ID = self:GetParent():GetID(); UIDropDownMenu_Initialize(self, FCFOptionsDropDown_Initialize, "MENU"); UIDropDownMenu_SetButtonWidth(self, 50); UIDropDownMenu_SetWidth(self, 50); end function FCFOptionsDropDown_Initialize(dropDown) -- Window preferences local name, fontSize, r, g, b, a, shown = FCF_GetChatWindowInfo(FCF_GetCurrentChatFrameID()); local info; local chatFrame = FCF_GetCurrentChatFrame(); local isTemporary = chatFrame and chatFrame.isTemporary; -- If level 2 if ( UIDROPDOWNMENU_MENU_LEVEL == 2 ) then -- If this is the font size menu then create dropdown if ( UIDROPDOWNMENU_MENU_VALUE == FONT_SIZE ) then -- Add the font heights from the font height table local value; for i=1, #CHAT_FONT_HEIGHTS do value = CHAT_FONT_HEIGHTS[i]; info = UIDropDownMenu_CreateInfo(); info.text = format(FONT_SIZE_TEMPLATE, value); info.value = value; info.func = FCF_SetChatWindowFontSize; local fontFile, fontHeight, fontFlags = FCF_GetCurrentChatFrame():GetFont(); if ( value == floor(fontHeight+0.5) ) then info.checked = 1; end UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL); end return; end return; end -- Window options info = UIDropDownMenu_CreateInfo(); if ( FCF_GetCurrentChatFrame(dropDown) and FCF_GetCurrentChatFrame(dropDown).isLocked ) then info.text = UNLOCK_WINDOW; else info.text = LOCK_WINDOW; end info.func = FCF_ToggleLock; info.notCheckable = 1; UIDropDownMenu_AddButton(info); --Add Uninteractable button info = UIDropDownMenu_CreateInfo(); if ( FCF_GetCurrentChatFrame(dropDown) and FCF_GetCurrentChatFrame(dropDown).isUninteractable) then info.text = MAKE_INTERACTABLE; else info.text = MAKE_UNINTERACTABLE; end info.func = FCF_ToggleUninteractable; info.notCheckable = 1; UIDropDownMenu_AddButton(info); if ( not isTemporary ) then -- Add name button info = UIDropDownMenu_CreateInfo(); info.text = RENAME_CHAT_WINDOW; info.func = FCF_RenameChatWindow_Popup; info.notCheckable = 1; UIDropDownMenu_AddButton(info); end if ( chatFrame == DEFAULT_CHAT_FRAME ) then -- Create new chat window info = UIDropDownMenu_CreateInfo(); info.text = NEW_CHAT_WINDOW; info.func = FCF_NewChatWindow; info.notCheckable = 1; if (FCF_GetNumActiveChatFrames() == NUM_CHAT_WINDOWS ) then info.disabled = 1; end UIDropDownMenu_AddButton(info); -- Reset Chat windows to default info = UIDropDownMenu_CreateInfo(); info.text = RESET_ALL_WINDOWS; info.func = FCF_ResetAllWindows; info.notCheckable = 1; UIDropDownMenu_AddButton(info); end -- Close current chat window if ( chatFrame and shown and (chatFrame ~= DEFAULT_CHAT_FRAME and not IsCombatLog(chatFrame)) ) then if ( not chatFrame.isTemporary ) then info = UIDropDownMenu_CreateInfo(); info.text = CLOSE_CHAT_WINDOW; info.func = FCF_PopInWindow; info.arg1 = FCF_GetCurrentChatFrame(dropDown); info.notCheckable = 1; UIDropDownMenu_AddButton(info); elseif ( chatFrame.isTemporary and (chatFrame.chatType == "WHISPER" or chatFrame.chatType == "BN_WHISPER") ) then info = UIDropDownMenu_CreateInfo(); info.text = CLOSE_CHAT_WHISPER_WINDOW; info.func = FCF_PopInWindow; info.arg1 = FCF_GetCurrentChatFrame(dropDown); info.notCheckable = 1; UIDropDownMenu_AddButton(info); elseif ( chatFrame.isTemporary and (chatFrame.chatType == "BN_CONVERSATION" ) ) then if ( GetCVar("conversationMode") == "popout" ) then info = UIDropDownMenu_CreateInfo(); info.text = CLOSE_AND_LEAVE_CHAT_CONVERSATION_WINDOW; info.func = FCF_LeaveConversation; info.arg1 = FCF_GetCurrentChatFrame(dropDown); info.notCheckable = 1; UIDropDownMenu_AddButton(info); else info = UIDropDownMenu_CreateInfo(); info.text = CLOSE_CHAT_CONVERSATION_WINDOW; info.func = FCF_PopInWindow; info.arg1 = FCF_GetCurrentChatFrame(dropDown); info.notCheckable = 1; UIDropDownMenu_AddButton(info); end else error(format("Unhandled temporary window type. chatType: %s, chatTarget %s", tostring(chatFrame.chatType), tostring(chatFrame.chatTarget))); end end -- Display header info = UIDropDownMenu_CreateInfo(); info.text = DISPLAY; info.notClickable = 1; info.isTitle = 1; info.notCheckable = 1; UIDropDownMenu_AddButton(info); -- Font size info = UIDropDownMenu_CreateInfo(); info.text = FONT_SIZE; --info.notClickable = 1; info.hasArrow = 1; info.func = nil; info.notCheckable = 1; UIDropDownMenu_AddButton(info); -- Set Background color info = UIDropDownMenu_CreateInfo(); info.text = BACKGROUND; info.hasColorSwatch = 1; info.r = r; info.g = g; info.b = b; -- Done because the slider is reversed if ( a ) then a = 1- a; end info.opacity = a; info.swatchFunc = FCF_SetChatWindowBackGroundColor; info.func = UIDropDownMenuButton_OpenColorPicker; --info.notCheckable = 1; info.hasOpacity = 1; info.opacityFunc = FCF_SetChatWindowOpacity; info.cancelFunc = FCF_CancelWindowColorSettings; UIDropDownMenu_AddButton(info); if ( not isTemporary ) then -- Filter header info = UIDropDownMenu_CreateInfo(); info.text = FILTERS; --info.notClickable = 1; info.isTitle = 1; info.notCheckable = 1; UIDropDownMenu_AddButton(info); -- Configure settings info = UIDropDownMenu_CreateInfo(); info.text = CHAT_CONFIGURATION; info.func = function() ShowUIPanel(ChatConfigFrame); end; info.notCheckable = 1; UIDropDownMenu_AddButton(info); end end --[[ function FCFDropDown_LoadServerChannels(...) local checked; local channelList = FCF_GetCurrentChatFrame().channelList; local zoneChannelList = FCF_GetCurrentChatFrame().zoneChannelList; local info, channel; -- Server Channels header info = UIDropDownMenu_CreateInfo(); info.text = SERVER_CHANNELS; info.notClickable = 1; info.isTitle = 1; UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL); info = UIDropDownMenu_CreateInfo(); for i=1, select("#", ...) do checked = nil; channel = select(i, ...); if ( channelList ) then for index, value in pairs(channelList) do if ( value == channel ) then checked = 1; end end end if ( zoneChannelList ) then for index, value in pairs(zoneChannelList) do if ( value == channel ) then checked = 1; end end end info.text = channel; info.value = channel; info.func = FCFServerChannelsDropDown_OnClick; info.checked = checked; info.keepShownOnClick = 1; UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL); end end function FCFServerChannelsDropDown_OnClick() if ( UIDropDownMenuButton_GetChecked() ) then ChatFrame_RemoveChannel(FCF_GetCurrentChatFrame(), UIDropDownMenuButton_GetName()); else JoinPermanentChannel(UIDropDownMenuButton_GetName(), nil, FCF_GetCurrentChatFrameID(), 1); ChatFrame_AddChannel(FCF_GetCurrentChatFrame(), UIDropDownMenuButton_GetName()); end end ]] function FCFDropDown_LoadChannels(...) local checked; local channelList = FCF_GetCurrentChatFrame().channelList; local zoneChannelList = FCF_GetCurrentChatFrame().zoneChannelList; local info = UIDropDownMenu_CreateInfo(); local channel, tag; for i=1, select("#", ...), 2 do checked = nil; tag = "CHANNEL"..select(i, ...); channel = select(i+1, ...); if ( channelList ) then for index, value in pairs(channelList) do if ( value == channel ) then checked = 1; end end end if ( zoneChannelList ) then for index, value in pairs(zoneChannelList) do if ( value == channel ) then checked = 1; end end end info.text = channel; info.value = tag; info.func = FCFChannelDropDown_OnClick; info.checked = checked; info.keepShownOnClick = 1; -- Color the chat channel local color = ChatTypeInfo[tag]; info.hasColorSwatch = 1; info.r = color.r; info.g = color.g; info.b = color.b; -- Set the function the color picker calls info.swatchFunc = FCF_SetChatTypeColor; info.cancelFunc = FCF_CancelFontColorSettings; UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL); end end function FCFChannelDropDown_OnClick() if ( UIDropDownMenuButton_GetChecked() ) then ChatFrame_RemoveChannel(FCF_GetCurrentChatFrame(), UIDropDownMenuButton_GetName()); else ChatFrame_AddChannel(FCF_GetCurrentChatFrame(), UIDropDownMenuButton_GetName()); end end -- Used to display chattypegroups function FCFDropDown_LoadChatTypes(menuChatTypeGroups) local checked, chatTypeInfo; local messageTypeList = FCF_GetCurrentChatFrame().messageTypeList; local info, group; for index, value in pairs(menuChatTypeGroups) do checked = nil; if ( messageTypeList ) then for joinedIndex, joinedValue in pairs(messageTypeList) do if ( value == joinedValue ) then checked = 1; end end end info = UIDropDownMenu_CreateInfo(); info.value = value; info.func = FCFMessageTypeDropDown_OnClick; info.checked = checked; -- Set to keep shown on button click info.keepShownOnClick = 1; -- If more than one message type in a Chat Type Group need to show an expand arrow group = ChatTypeGroup[value]; if ( getn(group) > 1 ) then info.text = _G[value]; info.hasArrow = 1; UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL); else info.text = _G[group[1]]; chatTypeInfo = ChatTypeInfo[FCF_StripChatMsg(group[1])]; -- If no chatTypeInfo then don't display if ( chatTypeInfo ) then -- Set the function to be called when a color is set info.swatchFunc = FCF_SetChatTypeColor; -- Set the swatch color info info.hasColorSwatch = 1; info.r = chatTypeInfo.r; info.g = chatTypeInfo.g; info.b = chatTypeInfo.b; info.cancelFunc = FCF_CancelFontColorSettings; UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL); end end end end -- Used to display chatsubtypes function FCF_LoadChatSubTypes(chatGroup) if ( chatGroup ) then chatGroup = ChatTypeGroup[chatGroup]; else chatGroup = ChatTypeGroup[UIDROPDOWNMENU_MENU_VALUE]; end if ( chatGroup ) then local info = UIDropDownMenu_CreateInfo(); local chatTypeInfo for index, value in pairs(chatGroup) do chatTypeInfo = ChatTypeInfo[FCF_StripChatMsg(value)]; if ( chatTypeInfo ) then info.text = _G[value]; info.value = FCF_StripChatMsg(value); -- Disable the button and color the text white info.notClickable = 1; -- Set to be notcheckable info.notCheckable = 1; -- Set the function to be called when a color is set info.swatchFunc = FCF_SetChatTypeColor; -- Set the swatch color info info.hasColorSwatch = 1; info.r = chatTypeInfo.r; info.g = chatTypeInfo.g; info.b = chatTypeInfo.b; -- Set function called when cancel is clicked in the colorpicker info.cancelFunc = FCF_CancelFontColorSettings; UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL); end end end end function FCFMessageTypeDropDown_OnClick(self) if ( UIDropDownMenuButton_GetChecked() ) then ChatFrame_RemoveMessageGroup(FCF_GetCurrentChatFrame(), self.value); else ChatFrame_AddMessageGroup(FCF_GetCurrentChatFrame(), self.value); end end function FCF_OpenNewWindow(name) local count = 1; local chatFrame, chatTab; for i=1, NUM_CHAT_WINDOWS do local _, _, _, _, _, _, shown = FCF_GetChatWindowInfo(i); chatFrame = _G["ChatFrame"..i]; chatTab = _G["ChatFrame"..i.."Tab"]; if ( (not shown and not chatFrame.isDocked) or (count == NUM_CHAT_WINDOWS) ) then if ( not name or name == "" ) then name = format(CHAT_NAME_TEMPLATE, i); end -- initialize the frame FCF_SetWindowName(chatFrame, name); FCF_SetWindowColor(chatFrame, DEFAULT_CHATFRAME_COLOR.r, DEFAULT_CHATFRAME_COLOR.g, DEFAULT_CHATFRAME_COLOR.b); FCF_SetWindowAlpha(chatFrame, DEFAULT_CHATFRAME_ALPHA); SetChatWindowLocked(i, nil); -- clear stale messages chatFrame:Clear(); -- Listen to the standard messages ChatFrame_RemoveAllMessageGroups(chatFrame); ChatFrame_RemoveAllChannels(chatFrame); ChatFrame_ReceiveAllPrivateMessages(chatFrame); ChatFrame_ReceiveAllBNConversations(chatFrame); ChatFrame_AddMessageGroup(chatFrame, "SAY"); ChatFrame_AddMessageGroup(chatFrame, "YELL"); ChatFrame_AddMessageGroup(chatFrame, "GUILD"); ChatFrame_AddMessageGroup(chatFrame, "WHISPER"); ChatFrame_AddMessageGroup(chatFrame, "BN_WHISPER"); ChatFrame_AddMessageGroup(chatFrame, "PARTY"); ChatFrame_AddMessageGroup(chatFrame, "PARTY_LEADER"); ChatFrame_AddMessageGroup(chatFrame, "CHANNEL"); --Clear the edit box history. chatFrame.editBox:ClearHistory(); -- Show the frame and tab chatFrame:Show(); chatTab:Show(); SetChatWindowShown(i, 1); -- Dock the frame by default FCF_DockFrame(chatFrame, (#FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)+1), true); FCF_FadeInChatFrame(FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK)); return chatFrame; end count = count + 1; end end function FCF_SetTemporaryWindowType(chatFrame, chatType, chatTarget) local chatTab = _G[chatFrame:GetName().."Tab"]; --If the frame was already registered, unregister it. if ( chatFrame.isRegistered ) then FCFManager_UnregisterDedicatedFrame(chatFrame, chatFrame.chatType, chatFrame.chatTarget); chatFrame.isRegistered = false; end --Set the title text local name; if ( chatType == "WHISPER" or chatType == "BN_WHISPER" ) then name = chatTarget; elseif ( chatType == "BN_CONVERSATION" ) then name = format(CONVERSATION_NAME, tonumber(chatTarget) + MAX_WOW_CHAT_CHANNELS); end FCF_SetWindowName(chatFrame, name); --Set up the window to receive the message types we want. chatFrame.chatType = chatType; chatFrame.chatTarget = chatTarget; ChatFrame_RemoveAllMessageGroups(chatFrame); ChatFrame_RemoveAllChannels(chatFrame); ChatFrame_ReceiveAllPrivateMessages(chatFrame); ChatFrame_ReceiveAllBNConversations(chatFrame); ChatFrame_AddMessageGroup(chatFrame, chatType); chatFrame.editBox:SetAttribute("chatType", chatType); chatFrame.editBox:SetAttribute("stickyType", chatType); if ( chatType == "WHISPER" or chatType == "BN_WHISPER" ) then chatFrame.editBox:SetAttribute("tellTarget", chatTarget); ChatFrame_AddPrivateMessageTarget(chatFrame, chatTarget); elseif ( chatType == "BN_CONVERSATION" ) then chatFrame.editBox:SetAttribute("channelTarget", chatTarget); ChatFrame_AddBNConversationTarget(chatFrame, chatTarget); end -- Set up the colors local info = ChatTypeInfo[chatType]; chatTab.selectedColorTable = { r = info.r, g = info.g, b = info.b }; FCFTab_UpdateColors(chatTab, not chatFrame.isDocked or chatFrame == FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK)); --If it's a conversation, create the conversation button if ( chatType == "BN_CONVERSATION" or chatType == "BN_WHISPER" ) then if ( chatFrame.conversationButton ) then BNConversationButton_UpdateTarget(chatFrame.conversationButton); chatFrame.conversationButton:Show(); else CreateFrame("Button", chatFrame:GetName().."ConversationButton", chatFrame.buttonFrame, "BNConversationRosterButtonTemplate", chatFrame:GetID()); end if ( chatFrame:GetHeight() < CHAT_FRAME_BIGGER_MIN_HEIGHT ) then chatFrame:SetHeight(CHAT_FRAME_BIGGER_MIN_HEIGHT); end chatFrame:SetMinResize(CHAT_FRAME_MIN_WIDTH, CHAT_FRAME_BIGGER_MIN_HEIGHT); else if ( chatFrame.conversationButton ) then chatFrame.conversationButton:Hide(); end chatFrame:SetMinResize(CHAT_FRAME_MIN_WIDTH, CHAT_FRAME_NORMAL_MIN_HEIGHT); end --If it's a conversation, get it ready to convert to a whisper if needed. if ( chatType == "BN_CONVERSATION" ) then chatFrame:RegisterEvent("BN_CHAT_CHANNEL_CLOSED"); else chatFrame:UnregisterEvent("BN_CHAT_CHANNEL_CLOSED"); end --Set the icon local conversationIcon; if ( chatType == "WHISPER" or chatType == "BN_WHISPER" ) then conversationIcon = "Interface\\ChatFrame\\UI-ChatWhisperIcon"; else conversationIcon = "Interface\\ChatFrame\\UI-ChatConversationIcon"; end chatTab.conversationIcon:SetTexture(conversationIcon); if ( chatFrame.minFrame ) then chatFrame.minFrame.conversationIcon:SetTexture(conversationIcon); end --Register this frame FCFManager_RegisterDedicatedFrame(chatFrame, chatType, chatTarget); chatFrame.isRegistered = true; --The window name may have been updated, so update the dock and tabs. FCF_DockUpdate(); end local maxTempIndex = NUM_CHAT_WINDOWS + 1; function FCF_OpenTemporaryWindow(chatType, chatTarget, sourceChatFrame, selectWindow) local chatFrame, chatTab, conversationIcon; for _, chatFrameName in pairs(CHAT_FRAMES) do local frame = _G[chatFrameName]; if ( frame.isTemporary ) then if ( not frame.inUse and not frame.isDocked ) then chatFrame = frame; chatTab = _G[chatFrame:GetName().."Tab"]; break; end end end if ( not chatFrame ) then chatTab = CreateFrame("Button", "ChatFrame"..maxTempIndex.."Tab", UIParent, "ChatTabTemplate", maxTempIndex); conversationIcon = chatTab:CreateTexture(chatTab:GetName().."ConversationIcon", "ARTWORK", "ChatTabConversationIconTemplate"); conversationIcon:ClearAllPoints(); conversationIcon:SetPoint("RIGHT", chatTab:GetFontString(), "LEFT", 0, -2); chatTab.conversationIcon = conversationIcon; local tabText = _G[chatTab:GetName().."Text"]; tabText:SetPoint("LEFT", chatTab.leftTexture, "RIGHT", 10, -6); tabText:SetJustifyH("LEFT"); chatTab.sizePadding = 10; chatFrame = CreateFrame("ScrollingMessageFrame", "ChatFrame"..maxTempIndex, UIParent, "FloatingChatFrameTemplate", maxTempIndex); if ( GetCVarBool("chatMouseScroll") ) then chatFrame:SetScript("OnMouseWheel", FloatingChatFrame_OnMouseScroll); chatFrame:EnableMouseWheel(true); end maxTempIndex = maxTempIndex + 1; end --Copy chat settings from the source frame. FCF_CopyChatSettings(chatFrame, sourceChatFrame or DEFAULT_CHAT_FRAME); -- clear stale messages chatFrame:Clear(); chatFrame.inUse = true; chatFrame.isTemporary = true; FCF_SetTemporaryWindowType(chatFrame, chatType, chatTarget); --Clear the edit box history. chatFrame.editBox:ClearHistory(); if ( sourceChatFrame ) then --Stop displaying this type of chat in the old chat frame. if ( chatType == "WHISPER" or chatType == "BN_WHISPER" ) then ChatFrame_ExcludePrivateMessageTarget(sourceChatFrame, chatTarget); elseif ( chatType == "BN_CONVERSATION" ) then ChatFrame_ExcludeBNConversationTarget(sourceChatFrame, chatTarget); end --Copy over messages local accessID = ChatHistory_GetAccessID(chatType, chatTarget); for i = 1, sourceChatFrame:GetNumMessages(accessID) do local text, accessID, lineID, extraData = sourceChatFrame:GetMessageInfo(i, accessID); local cType, cTarget = ChatHistory_GetChatType(extraData); local info = ChatTypeInfo[cType]; chatFrame:AddMessage(text, info.r, info.g, info.b, lineID, false, accessID, extraData); end --Remove the messages from the old frame. sourceChatFrame:RemoveMessagesByAccessID(accessID); end --Close the Editbox ChatEdit_DeactivateChat(chatFrame.editBox); -- Show the frame and tab chatFrame:Show(); chatTab:Show(); -- Dock the frame by default FCF_DockFrame(chatFrame, (#FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)+1), selectWindow); return chatFrame; end function FCF_GetNumActiveChatFrames() local count = 0; local chatFrame; for i=1, NUM_CHAT_WINDOWS do local _, _, _, _, _, _, shown = FCF_GetChatWindowInfo(i); chatFrame = _G["ChatFrame"..i]; if ( chatFrame ) then if ( shown or chatFrame.isDocked ) then count = count + 1; end end end return count; end function FCF_RenameChatWindow_Popup() local dialog = StaticPopup_Show("NAME_CHAT"); dialog.data = FCF_GetCurrentChatFrameID(); end function FCF_NewChatWindow() StaticPopup_Show("NAME_CHAT"); end function FCF_ResetAllWindows() StaticPopup_Show("RESET_CHAT"); end --[[function FCF_ChatChannels() ToggleFriendsFrame(4); end]]-- function FCF_SetWindowName(frame, name, doNotSave) if ( not name or name == "") then -- Hack to initialize the chat window names, since globalstrings are not available on init if ( frame:GetID() == 1 ) then name = GENERAL; doNotSave = nil; elseif ( frame:GetID() == 2 ) then name = COMBAT_LOG; doNotSave = nil; else name = format(CHAT_NAME_TEMPLATE, frame:GetID()); end else FCFDock_SetDirty(GENERAL_CHAT_DOCK); end frame.name = name; local tab = _G[frame:GetName().."Tab"]; tab:SetText(name); PanelTemplates_TabResize(tab, tab.sizePadding or 0); -- Save this off so we know how big the tab should always be, even if it gets shrunken on the dock. tab.textWidth = _G[tab:GetName().."Text"]:GetWidth(); if ( not doNotSave ) then SetChatWindowName(frame:GetID(), name); end if ( frame.minFrame ) then frame.minFrame:SetText(name); end end function FCF_SetWindowColor(frame, r, g, b, doNotSave) local name = frame:GetName(); for index, value in pairs(CHAT_FRAME_TEXTURES) do --NOTE - If this is changed, please change the equivalent code in GMChatFrame_OnLoad. local object = _G[name..value]; local objectType = object:GetObjectType(); if ( objectType == "Button" ) then object:GetNormalTexture():SetVertexColor(r, g, b); object:GetHighlightTexture():SetVertexColor(r, g, b); object:GetPushedTexture():SetVertexColor(r, g, b); elseif ( objectType == "Texture" ) then _G[name..value]:SetVertexColor(r,g,b); else --error("Unhandled frame type..."); end end if ( not doNotSave ) then SetChatWindowColor(frame:GetID(), r, g, b); end end function FCF_SetWindowAlpha(frame, alpha, doNotSave) local name = frame:GetName(); for index, value in pairs(CHAT_FRAME_TEXTURES) do _G[name..value]:SetAlpha(alpha); end if ( not doNotSave ) then SetChatWindowAlpha(frame:GetID(), alpha); end -- Remember the alpha frame.oldAlpha = alpha; end function FCF_GetCurrentChatFrameID() return CURRENT_CHAT_FRAME_ID; end function FCF_GetCurrentChatFrame(child) local currentChatFrame = nil; if ( CURRENT_CHAT_FRAME_ID ) then currentChatFrame = _G["ChatFrame"..CURRENT_CHAT_FRAME_ID]; end if ( not currentChatFrame and child ) then currentChatFrame = _G["ChatFrame"..child:GetParent():GetID()]; end return currentChatFrame; end function FCF_SetChatTypeColor() local r,g,b = ColorPickerFrame:GetColorRGB(); ChangeChatColor(UIDROPDOWNMENU_MENU_VALUE, r, g, b); end function FCF_SetChatWindowBackGroundColor() local r,g,b = ColorPickerFrame:GetColorRGB(); FCF_SetWindowColor(FCF_GetCurrentChatFrame(), r, g, b) SetChatWindowColor(FCF_GetCurrentChatFrameID(), r, g, b); end function FCF_SetChatWindowOpacity() local alpha = 1.0 - OpacitySliderFrame:GetValue(); FCF_SetWindowAlpha(FCF_GetCurrentChatFrame(), alpha); end function FCF_SetChatWindowFontSize(self, chatFrame, fontSize) if ( not chatFrame ) then chatFrame = FCF_GetCurrentChatFrame(); end if ( not fontSize ) then fontSize = self.value; end local fontFile, unused, fontFlags = chatFrame:GetFont(); chatFrame:SetFont(fontFile, fontSize, fontFlags); if ( GMChatFrame and chatFrame == DEFAULT_CHAT_FRAME ) then GMChatFrame:SetFont(fontFile, fontSize, fontFlags); end SetChatWindowSize(chatFrame:GetID(), fontSize); end function FCF_CancelFontColorSettings(previousValues) if ( previousValues.r ) then ChangeChatColor(UIDROPDOWNMENU_MENU_VALUE, previousValues.r, previousValues.g, previousValues.b); end end function FCF_CancelWindowColorSettings(previousValues) if ( previousValues.r ) then FCF_SetWindowColor(FCF_GetCurrentChatFrame(), previousValues.r, previousValues.g, previousValues.b) SetChatWindowColor(FCF_GetCurrentChatFrameID(), previousValues.r, previousValues.g, previousValues.b); end if ( previousValues.opacity ) then FCF_SetWindowAlpha(FCF_GetCurrentChatFrame(), 1 - previousValues.opacity); end end function FCF_StripChatMsg(string) if ( strsub(string,1,8) == "CHAT_MSG" ) then return strsub(string,10); else return string; end end function FCF_ToggleLock() local chatFrame = FCF_GetCurrentChatFrame(); if ( chatFrame.isLocked ) then -- If unlocking a docked frame then undock it and center it on the screen if ( chatFrame.isDocked and chatFrame ~= DEFAULT_CHAT_FRAME ) then FCF_UnDockFrame(chatFrame); chatFrame:ClearAllPoints(); chatFrame:SetPoint("CENTER", "UIParent", "CENTER", 0, 0); FCF_SetTabPosition(chatFrame, 0); chatFrame:Show(); end FCF_SetLocked(chatFrame, nil); else FCF_SetLocked(chatFrame, 1); end end function FCF_SetLocked(chatFrame, isLocked) chatFrame.isLocked = isLocked; if ( chatFrame.isUninteractable or isLocked ) then chatFrame.resizeButton:Hide(); else chatFrame.resizeButton:Show(); --chatFrame.resizeButton:SetAlpha(_G[chatFrame:GetName().."Background"]:GetAlpha()); end SetChatWindowLocked(chatFrame:GetID(), isLocked); end function FCF_ToggleUninteractable() local chatFrame = FCF_GetCurrentChatFrame(); if ( chatFrame.isUninteractable ) then FCF_SetExpandedUninteractable(chatFrame, false) else FCF_SetExpandedUninteractable(chatFrame, true) end end function FCF_SetExpandedUninteractable(chatFrame, isUninteractable) if ( chatFrame.isDocked ) then for _, frame in pairs(GENERAL_CHAT_DOCK.DOCKED_CHAT_FRAMES) do FCF_SetUninteractable(frame, isUninteractable); end else FCF_SetUninteractable(chatFrame, isUninteractable); end end function FCF_SetUninteractable(chatFrame, isUninteractable) --No, uninteractable is not really a word. chatFrame.isUninteractable = isUninteractable; SetChatWindowUninteractable(chatFrame:GetID(), isUninteractable); if ( not chatFrame.overrideHyperlinksEnabled ) then chatFrame:SetHyperlinksEnabled(not isUninteractable); end local chatFrameName = chatFrame:GetName(); if ( isUninteractable or chatFrame.isLocked ) then _G[chatFrameName.."ResizeButton"]:Hide(); else _G[chatFrameName.."ResizeButton"]:Show(); end end function FCF_FadeInChatFrame(chatFrame) local frameName = chatFrame:GetName(); chatFrame.hasBeenFaded = true; for index, value in pairs(CHAT_FRAME_TEXTURES) do local object = _G[frameName..value]; if ( object:IsShown() ) then UIFrameFadeIn(object, CHAT_FRAME_FADE_TIME, object:GetAlpha(), max(chatFrame.oldAlpha, DEFAULT_CHATFRAME_ALPHA)); end end if ( chatFrame == FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK) ) then for _, frame in pairs(FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)) do if ( frame ~= chatFrame ) then FCF_FadeInChatFrame(frame); end end if ( GENERAL_CHAT_DOCK.overflowButton:IsShown() ) then UIFrameFadeIn(GENERAL_CHAT_DOCK.overflowButton, CHAT_FRAME_FADE_TIME, GENERAL_CHAT_DOCK.overflowButton:GetAlpha(), CHAT_FRAME_TAB_SELECTED_MOUSEOVER_ALPHA); end end local chatTab = _G[frameName.."Tab"]; UIFrameFadeIn(chatTab, CHAT_FRAME_FADE_TIME, chatTab:GetAlpha(), chatTab.mouseOverAlpha); --Fade in the button frame if ( not chatFrame.isDocked ) then UIFrameFadeIn(chatFrame.buttonFrame, CHAT_FRAME_FADE_TIME, chatFrame.buttonFrame:GetAlpha(), 1); end end function FCF_FadeOutChatFrame(chatFrame) local frameName = chatFrame:GetName(); chatFrame.hasBeenFaded = nil; for index, value in pairs(CHAT_FRAME_TEXTURES) do -- Fade out chat frame local object = _G[frameName..value]; if ( object:IsShown() ) then UIFrameFadeOut(object, CHAT_FRAME_FADE_OUT_TIME, max(object:GetAlpha(), chatFrame.oldAlpha), chatFrame.oldAlpha); end end if ( chatFrame == FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK) ) then for _, frame in pairs(FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)) do if ( frame ~= chatFrame ) then FCF_FadeOutChatFrame(frame); end end if ( GENERAL_CHAT_DOCK.overflowButton:IsShown() ) then UIFrameFadeOut(GENERAL_CHAT_DOCK.overflowButton, CHAT_FRAME_FADE_OUT_TIME, GENERAL_CHAT_DOCK.overflowButton:GetAlpha(), CHAT_FRAME_TAB_SELECTED_NOMOUSE_ALPHA); end end local chatTab = _G[frameName.."Tab"]; UIFrameFadeOut(chatTab, CHAT_FRAME_FADE_OUT_TIME, chatTab:GetAlpha(), chatTab.noMouseAlpha); --Fade out the ButtonFrame if ( not chatFrame.isDocked ) then UIFrameFadeOut(chatFrame.buttonFrame, CHAT_FRAME_FADE_OUT_TIME, chatFrame.buttonFrame:GetAlpha(), CHAT_FRAME_BUTTON_FRAME_MIN_ALPHA); end end local LAST_CURSOR_X, LAST_CURSOR_Y; function FCF_OnUpdate(elapsed) local cursorX, cursorY = GetCursorPosition(); local overSomething = false; for _, frameName in pairs(CHAT_FRAMES) do local chatFrame = _G[frameName]; if ( chatFrame:IsShown() ) then local topOffset = 28; if ( IsCombatLog(chatFrame) ) then topOffset = topOffset + CombatLogQuickButtonFrame_Custom:GetHeight(); end --Items that will always cause the frame to fade in. if ( MOVING_CHATFRAME or chatFrame.resizeButton:GetButtonState() == "PUSHED" or (chatFrame.isDocked and GENERAL_CHAT_DOCK.overflowButton.list:IsShown())) then overSomething = true; chatFrame.mouseOutTime = 0; if ( not chatFrame.hasBeenFaded ) then overSomething = true; FCF_FadeInChatFrame(chatFrame); end --Things that will cause the frame to fade in if the mouse is stationary. elseif ( chatFrame:IsMouseOver(topOffset, -2, -2, 2) or --This should be slightly larger than the hit rect insets to give us some wiggle room. (chatFrame.isDocked and FriendsMicroButton:IsMouseOver()) or (chatFrame.buttonFrame:IsMouseOver())) then overSomething = true; chatFrame.mouseOutTime = 0; if ( cursorX == LAST_CURSOR_X and cursorY == LAST_CURSOR_Y and not chatFrame.hasBeenFaded ) then chatFrame.mouseInTime = (chatFrame.mouseInTime or 0) + elapsed; if ( chatFrame.mouseInTime > CHAT_TAB_SHOW_DELAY ) then FCF_FadeInChatFrame(chatFrame); end else chatFrame.mouseInTime = 0; end elseif ( chatFrame:IsShown() and chatFrame.hasBeenFaded ) then chatFrame.mouseInTime = 0; chatFrame.mouseOutTime = (chatFrame.mouseOutTime or 0) + elapsed; if ( chatFrame.mouseOutTime > CHAT_TAB_HIDE_DELAY ) then FCF_FadeOutChatFrame(chatFrame); end end end end LAST_CURSOR_X, LAST_CURSOR_Y = cursorX, cursorY; end function FCF_SavePositionAndDimensions(chatFrame) local centerX = chatFrame:GetLeft() + chatFrame:GetWidth() / 2; local centerY = chatFrame:GetBottom() + chatFrame:GetHeight() / 2; local horizPoint, vertPoint; local screenWidth, screenHeight = GetScreenWidth(), GetScreenHeight(); local xOffset, yOffset; if ( centerX > screenWidth / 2 ) then horizPoint = "RIGHT"; xOffset = (chatFrame:GetRight() - screenWidth)/screenWidth; else horizPoint = "LEFT"; xOffset = chatFrame:GetLeft()/screenWidth; end if ( centerY > screenHeight / 2 ) then vertPoint = "TOP"; yOffset = (chatFrame:GetTop() - screenHeight)/screenHeight; else vertPoint = "BOTTOM"; yOffset = chatFrame:GetBottom()/screenHeight; end SetChatWindowSavedPosition(chatFrame:GetID(), vertPoint..horizPoint, xOffset, yOffset); SetChatWindowSavedDimensions(chatFrame:GetID(), chatFrame:GetWidth(), chatFrame:GetHeight()); end function FCF_RestorePositionAndDimensions(chatFrame) local width, height = GetChatWindowSavedDimensions(chatFrame:GetID()); if ( width and height ) then chatFrame:SetSize(width, height); end local point, xOffset, yOffset = GetChatWindowSavedPosition(chatFrame:GetID()); if ( point ) then chatFrame:ClearAllPoints(); chatFrame:SetPoint(point, xOffset * GetScreenWidth(), yOffset * GetScreenHeight()); chatFrame:SetUserPlaced(true); else chatFrame:SetUserPlaced(false); end end -- Docking handling functions function FCF_StopDragging(chatFrame) chatFrame:StopMovingOrSizing(); _G[chatFrame:GetName().."Tab"]:UnlockHighlight(); FCFDock_HideInsertHighlight(GENERAL_CHAT_DOCK); if ( GENERAL_CHAT_DOCK:IsMouseOver(10, -10, 0, 10) ) then local mouseX, mouseY = GetCursorPosition(); mouseX, mouseY = mouseX / UIParent:GetScale(), mouseY / UIParent:GetScale(); FCF_DockFrame(chatFrame, FCFDock_GetInsertIndex(GENERAL_CHAT_DOCK, chatFrame, mouseX, mouseY), true); else FCF_SetTabPosition(chatFrame, 0); end FCF_SavePositionAndDimensions(chatFrame); MOVING_CHATFRAME = nil; end function FCFTab_OnUpdate(self, elapsed) local cursorX, cursorY = GetCursorPosition(); cursorX, cursorY = cursorX / UIParent:GetScale(), cursorY / UIParent:GetScale(); local chatFrame = _G["ChatFrame"..self:GetID()]; if ( chatFrame ~= GENERAL_CHAT_DOCK.primary and GENERAL_CHAT_DOCK:IsMouseOver(10, -10, 0, 10) ) then FCFDock_PlaceInsertHighlight(GENERAL_CHAT_DOCK, chatFrame, cursorX, cursorY); else FCFDock_HideInsertHighlight(GENERAL_CHAT_DOCK); end FCF_UpdateButtonSide(chatFrame); if ( chatFrame == GENERAL_CHAT_DOCK.primary ) then for _, frame in pairs(FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)) do FCF_SetButtonSide(frame, FCF_GetButtonSide(GENERAL_CHAT_DOCK.primary)); end end if ( not IsMouseButtonDown(self.dragButton) ) then FCFTab_OnDragStop(self, self.dragButton); self.dragButton = nil; self:SetScript("OnUpdate", nil); end if ( BNToastFrame and BNToastFrame:IsShown() ) then BNToastFrame_UpdateAnchor(); end end function FCFTab_OnDragStop(self, button) FCF_StopDragging(_G["ChatFrame"..self:GetID()]); end DEFAULT_TAB_SELECTED_COLOR_TABLE = { r = 1, g = 0.5, b = 0.25 }; function FCFTab_UpdateColors(self, selected) if ( selected ) then self.leftSelectedTexture:Show(); self.middleSelectedTexture:Show(); self.rightSelectedTexture:Show(); else self.leftSelectedTexture:Hide(); self.middleSelectedTexture:Hide(); self.rightSelectedTexture:Hide(); end local colorTable = self.selectedColorTable or DEFAULT_TAB_SELECTED_COLOR_TABLE; if ( self.selectedColorTable ) then self:GetFontString():SetTextColor(colorTable.r, colorTable.g, colorTable.b); else self:GetFontString():SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b); end self.leftSelectedTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); self.middleSelectedTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); self.rightSelectedTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); self.leftHighlightTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); self.middleHighlightTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); self.rightHighlightTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); self.glow:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); if ( self.conversationIcon ) then self.conversationIcon:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); end local minimizedFrame = _G["ChatFrame"..self:GetID().."Minimized"]; if ( minimizedFrame ) then minimizedFrame.selectedColorTable = self.selectedColorTable; FCFMin_UpdateColors(minimizedFrame); end end function FCFTab_UpdateAlpha(chatFrame) local chatTab = _G[chatFrame:GetName().."Tab"]; if ( not chatFrame.isDocked or chatFrame == FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK) ) then chatTab.mouseOverAlpha = CHAT_FRAME_TAB_SELECTED_MOUSEOVER_ALPHA; chatTab.noMouseAlpha = CHAT_FRAME_TAB_SELECTED_NOMOUSE_ALPHA; else if ( chatTab.alerting ) then chatTab.mouseOverAlpha = CHAT_FRAME_TAB_ALERTING_MOUSEOVER_ALPHA; chatTab.noMouseAlpha = CHAT_FRAME_TAB_ALERTING_NOMOUSE_ALPHA; else chatTab.mouseOverAlpha = CHAT_FRAME_TAB_NORMAL_MOUSEOVER_ALPHA; chatTab.noMouseAlpha = CHAT_FRAME_TAB_NORMAL_NOMOUSE_ALPHA; end end -- If this is in the middle of fading, stop it, since we're about to set the alpha UIFrameFadeRemoveFrame(chatTab); if ( chatFrame.hasBeenFaded ) then chatTab:SetAlpha(chatTab.mouseOverAlpha); else chatTab:SetAlpha(chatTab.noMouseAlpha); end end function FCF_HideOnFadeFinished(frame) frame:Hide(); end function FCF_IsValidChatFrame(chatFrame) -- Break out all the cases individually because the logic gets convoluted if ( chatFrame == MOVING_CHATFRAME ) then return nil; end if ( not chatFrame:IsShown() and not chatFrame.isDocked ) then return nil; end return 1; end function FCF_UpdateButtonSide(chatFrame) local leftDist = chatFrame:GetLeft(); local rightDist = GetScreenWidth() - chatFrame:GetRight(); local changed = nil; if (( leftDist > 0 and leftDist <= rightDist ) or rightDist < 0 ) then if ( chatFrame.buttonSide ~= "left" ) then FCF_SetButtonSide(chatFrame, "left"); changed = 1; end else if ( chatFrame.buttonSide ~= "right" or leftDist < 0 ) then FCF_SetButtonSide(chatFrame, "right"); changed = 1; end end return changed; end function FCF_SetButtonSide(chatFrame, buttonSide, forceUpdate) if ( not forceUpdate and chatFrame.buttonSide == buttonSide ) then return; end chatFrame.buttonFrame:ClearAllPoints(); local topY = 0; if ( IsCombatLog(chatFrame) ) then topY = topY + CombatLogQuickButtonFrame_Custom:GetHeight(); end if ( buttonSide == "left" ) then chatFrame.buttonFrame:SetPoint("TOPRIGHT", chatFrame, "TOPLEFT", -4, topY); chatFrame.buttonFrame:SetPoint("BOTTOMRIGHT", chatFrame, "BOTTOMLEFT", -4, 0); elseif ( buttonSide == "right" ) then chatFrame.buttonFrame:SetPoint("TOPLEFT", chatFrame, "TOPRIGHT", 4, topY); chatFrame.buttonFrame:SetPoint("BOTTOMLEFT", chatFrame, "BOTTOMRIGHT", 4, 0); end chatFrame.buttonSide = buttonSide; if ( chatFrame == DEFAULT_CHAT_FRAME ) then ChatFrameMenu_UpdateAnchorPoint(); end end function FCF_StartAlertFlash(chatFrame) if ( chatFrame.minFrame ) then UIFrameFlash(chatFrame.minFrame.glow, 1.0, 1.0, -1, false, 0, 0, "chat"); chatFrame.minFrame.alerting = true; end local chatTab = _G[chatFrame:GetName().."Tab"]; UIFrameFlash(chatTab.glow, 1.0, 1.0, -1, false, 0, 0, "chat"); chatTab.alerting = true; FCFTab_UpdateAlpha(chatFrame); FCFDockOverflowButton_UpdatePulseState(GENERAL_CHAT_DOCK.overflowButton); end function FCF_StopAlertFlash(chatFrame) if ( chatFrame.minFrame ) then UIFrameFlashStop(chatFrame.minFrame.glow); chatFrame.minFrame.alerting = false; end local chatTab = _G[chatFrame:GetName().."Tab"]; UIFrameFlashStop(chatTab.glow); chatTab.alerting = false; FCFTab_UpdateAlpha(chatFrame); FCFDockOverflowButton_UpdatePulseState(GENERAL_CHAT_DOCK.overflowButton); end function FCF_GetButtonSide(chatFrame) return chatFrame.buttonSide; end function FCF_DockUpdate() FCFDock_UpdateTabs(GENERAL_CHAT_DOCK, true); end --[[ local numDockedFrames = getn(DOCKED_CHAT_FRAMES); local dockRegion, chatTab, previousDockedFrame; local dockWidth = 0; local previousDockRegion; local name; for index, value in pairs(DOCKED_CHAT_FRAMES) do -- If not the initial chatframe then anchor the frame to the base chatframe name = value:GetName(); if ( index ~= 1 ) then value:ClearAllPoints(); value:SetPoint("TOPLEFT", DEFAULT_CHAT_FRAME, "TOPLEFT", 0, 0); value:SetPoint("BOTTOMLEFT", DEFAULT_CHAT_FRAME, "BOTTOMLEFT", 0, 0); value:SetPoint("BOTTOMRIGHT", DEFAULT_CHAT_FRAME, "BOTTOMRIGHT", 0, 0); end -- Select or deselect the frame chatTab = _G[value:GetName().."Tab"]; -- chatTab.textWidth is the original width of the text name of the tab -- We need to use this as an absolute measure of the text's width is altered when the chat dock gets too small -- If the text is shrunken the original width is lost, unless we save it and use it in the following manner -- This is a fix for Bug ID: 71180 PanelTemplates_TabResize(chatTab, 5, nil, nil, chatTab.textWidth); if ( value == SELECTED_DOCK_FRAME ) then value:Show(); if ( chatTab:IsShown() ) then chatTab:SetAlpha(1.0); end else value:Hide(); if ( chatTab:IsShown() ) then chatTab:SetAlpha(0.5); end end -- If there was a frame before this frame then anchor the tab if ( previousDockedFrame ) then chatTab:ClearAllPoints(); FCF_SetTabPosition(value, dockWidth); _G[previousDockedFrame:GetName().."TabDockRegion"]:SetPoint("RIGHT", value:GetName().."Tab", "CENTER", 0, 0); end -- If this is the last frame in the dock then extend the dockRegion, otherwise shrink it to the default width dockRegion = _G[chatTab:GetName().."DockRegion"]; dockRegion:SetPoint("LEFT", chatTab, "CENTER", 0 , 0); if ( numDockedFrames == index ) then dockRegion:SetPoint("RIGHT", "ChatFrame"..chatTab:GetID(), "RIGHT", 0, 0); end dockRegion:Hide(); -- Keep track of the width of the dock for anchoring purposes dockWidth = dockWidth + chatTab:GetWidth(); previousDockedFrame = value; end -- Intelligently resize the chat tabs if dockwidth is greater than the window width if ( dockWidth > DEFAULT_CHAT_FRAME:GetWidth() ) then DOCK_COPY = {}; -- Copy the array for index, value in pairs(DOCKED_CHAT_FRAMES) do DOCK_COPY[index] = DOCKED_CHAT_FRAMES[index]; end sort(DOCK_COPY, FCF_TabCompare); local totalWidth = DEFAULT_CHAT_FRAME:GetWidth(); local avgWidth = totalWidth / numDockedFrames; local chatTabWidth; -- Resize the tabs for index, value in pairs(DOCK_COPY) do chatTab = _G[value:GetName().."Tab"]; chatTabWidth = chatTab:GetWidth(); if ( chatTabWidth < avgWidth ) then -- If tab is smaller than the average then remove it from the list and recalc the average totalWidth = totalWidth - chatTabWidth; numDockedFrames = numDockedFrames - 1; avgWidth = totalWidth / numDockedFrames; else -- Set the tab to the average width PanelTemplates_TabResize(chatTab, 0, avgWidth); end end -- Reanchor the tabs previousDockedFrame = nil; dockWidth = 0; for index, value in pairs(DOCKED_CHAT_FRAMES) do -- If there was a frame before this frame then anchor the tab if ( previousDockedFrame ) then FCF_SetTabPosition(value, dockWidth); end chatTab = _G[value:GetName().."Tab"]; dockWidth = dockWidth + chatTab:GetWidth(); previousDockedFrame = value; end end end]] function FCF_TabCompare(chatFrame1, chatFrame2) local tab1 = _G[chatFrame1:GetName().."Tab"]; local tab2 = _G[chatFrame2:GetName().."Tab"]; return tab1:GetWidth() < tab2:GetWidth(); end function FCF_DockFrame(frame, index, selected) -- Return if already docked if ( frame.isDocked ) then return; end FCFDock_AddChatFrame(GENERAL_CHAT_DOCK, frame, index); -- Save docked state FCF_SaveDock(); if ( selected ) then --FCF_SelectDockFrame(frame); FCFDock_SelectWindow(GENERAL_CHAT_DOCK, frame); end -- Set scroll button side if ( frame == DEFAULT_CHAT_FRAME ) then FCF_UpdateButtonSide(frame); else FCF_SetButtonSide(frame, FCF_GetButtonSide(DEFAULT_CHAT_FRAME)); end -- Lock frame FCF_SetLocked(frame, 1); --If the frame that is being docked and the frame it is docking to have different interactable settings, make them both interactable. if ( frame.isUninteractable ~= DEFAULT_CHAT_FRAME.isUninteractable ) then FCF_SetExpandedUninteractable(frame, false) end if ( frame == COMBATLOG ) then Blizzard_CombatLog_Update_QuickButtons(); end FCF_DockUpdate(); end function FCF_UnDockFrame(frame) if ( frame == DEFAULT_CHAT_FRAME or not frame.isDocked ) then return; end -- Undock frame regardless of whether its docked or not SetChatWindowDocked(frame:GetID(), nil); FCFDock_RemoveChatFrame(GENERAL_CHAT_DOCK, frame); FCF_SaveDock(); -- Set tab to full alpha local chatTab = _G[frame:GetName().."Tab"]; chatTab:SetAlpha(1.0); end function FCF_SelectDockFrame(frame) SELECTED_DOCK_FRAME = frame; -- Stop tab flashing local tabFlash; if ( frame ) then tabFlash = _G["ChatFrame"..frame:GetID().."TabFlash"]; end if ( tabFlash ) then UIFrameFlashRemoveFrame(tabFlash); tabFlash:Hide(); end FCFDock_SelectWindow(GENERAL_CHAT_DOCK, frame); FCF_DockUpdate(); end function FCF_Tab_OnClick(self, button) local chatFrame = _G["ChatFrame"..self:GetID()]; -- If Rightclick bring up the options menu if ( button == "RightButton" ) then chatFrame:StopMovingOrSizing(); CURRENT_CHAT_FRAME_ID = self:GetID(); ToggleDropDownMenu(1, nil, _G[self:GetName().."DropDown"], self:GetName(), 0, 0); return; end -- Close all dropdowns CloseDropDownMenus(); -- If frame is docked assume that a click is to select a chat window, not drag it SELECTED_CHAT_FRAME = chatFrame; if ( chatFrame.isDocked and FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK) ~= chatFrame ) then FCF_SelectDockFrame(chatFrame); FCF_FadeInChatFrame(chatFrame); return; else if ( GetCVar("chatStyle") ~= "classic" ) then ChatEdit_SetLastActiveWindow(chatFrame.editBox); end FCF_FadeInChatFrame(chatFrame); end end function FCF_SetTabPosition(chatFrame, x) local chatTab = _G[chatFrame:GetName().."Tab"]; chatTab:ClearAllPoints(); chatTab:SetPoint("BOTTOMLEFT", chatFrame:GetName().."Background", "TOPLEFT", x+2, 0); end function FCF_SaveDock() for index, value in pairs(FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)) do SetChatWindowDocked(value:GetID(), index); end end function FCF_LeaveConversation(frame, fallback) if ( fallback ) then frame=fallback end if ( not frame ) then frame = FCF_GetCurrentChatFrame(); end assert(frame.chatType == "BN_CONVERSATION"); BNLeaveConversation(tonumber(frame.chatTarget)); FCF_Close(frame); end function FCF_PopInWindow(frame, fallback) if ( fallback ) then frame=fallback end if ( not frame ) then frame = FCF_GetCurrentChatFrame(); end if ( frame == DEFAULT_CHAT_FRAME ) then return; end --Restore any chats this frame had to the DEFAULT_CHAT_FRAME FCF_RestoreChatsToFrame(DEFAULT_CHAT_FRAME, frame); FCF_Close(frame); end function FCF_Close(frame, fallback) if ( fallback ) then frame=fallback end if ( not frame ) then frame = FCF_GetCurrentChatFrame(); end if ( frame == DEFAULT_CHAT_FRAME ) then return; end FCF_UnDockFrame(frame); HideUIPanel(frame); _G[frame:GetName().."Tab"]:Hide(); FCF_FlagMinimizedPositionReset(frame); if ( frame.minFrame and frame.minFrame:IsShown() ) then frame.minFrame:Hide(); end if ( frame.isTemporary ) then FCFManager_UnregisterDedicatedFrame(frame, frame.chatType, frame.chatTarget); frame.isRegistered = false; frame.inUse = false; end if ( PENDING_BN_WHISPER_TO_CONVERSATION_FRAME == frame ) then PENDING_BN_WHISPER_TO_CONVERSATION_FRAME = nil; end --Reset what this window receives. ChatFrame_RemoveAllMessageGroups(frame); ChatFrame_RemoveAllChannels(frame); ChatFrame_ReceiveAllPrivateMessages(frame); ChatFrame_ReceiveAllBNConversations(frame); end function FCF_RestoreChatsToFrame(targetFrame, sourceFrame) --Restore chat types for _, messageType in pairs(sourceFrame.messageTypeList) do ChatFrame_AddMessageGroup(targetFrame, messageType); end --Restore channels for _, channel in pairs(sourceFrame.channelList) do ChatFrame_AddChannel(targetFrame, channel); end --Restore whispers if ( sourceFrame.privateMessageList ) then for name, value in pairs(sourceFrame.privateMessageList) do if ( value ) then ChatFrame_RemoveExcludePrivateMessageTarget(targetFrame, name); end end end if ( sourceFrame.bnConversationList ) then for name, value in pairs(sourceFrame.bnConversationList) do if ( value ) then ChatFrame_RemoveExcludeBNConversationTarget(targetFrame, name); end end end end -- Tab flashing functions function FCF_FlashTab(self) local tabFlash = _G[self:GetName().."TabFlash"]; if ( not self.isDocked or (self == SELECTED_DOCK_FRAME) or UIFrameIsFlashing(tabFlash) ) then return; end tabFlash:Show(); UIFrameFlash(tabFlash, 0.25, 0.25, 60, nil, 0.5, 0.5); end -- Function for repositioning the chat dock depending on if there's a shapeshift bar/stance bar, etc... function FCF_UpdateDockPosition() if ( DEFAULT_CHAT_FRAME:IsUserPlaced() ) then return; end local chatOffset = 85; if ( GetNumShapeshiftForms() > 0 or HasPetUI() or PetHasActionBar() ) then if ( MultiBarBottomLeft:IsShown() ) then chatOffset = chatOffset + 55; else chatOffset = chatOffset + 15; end elseif ( MultiBarBottomLeft:IsShown() ) then chatOffset = chatOffset + 15; end DEFAULT_CHAT_FRAME:SetPoint("BOTTOMLEFT", "UIParent", "BOTTOMLEFT", 32, chatOffset); FCF_DockUpdate(); end function FCF_Set_NormalChat() ChatFrame2:StartMoving(); ChatFrame2:StopMovingOrSizing(); FCF_SetLocked(ChatFrame2, nil); -- to fix a bug with the combat log not repositioning its tab properly when coming out of -- simple chat, we need to update now FCF_DockUpdate(); end -- Functions to set and remove the chat window show delay on mouseover function SetChatMouseOverDelay(noDelay) if ( noDelay == "1" ) then CHAT_TAB_SHOW_DELAY = 0; CHAT_FRAME_FADE_TIME = 0; else CHAT_TAB_SHOW_DELAY = 0.2; CHAT_FRAME_FADE_TIME = 0.15; end end -- Reset the chat windows to default function FCF_ResetChatWindows() ChatFrame1:ClearAllPoints(); ChatFrame1:SetPoint("BOTTOMLEFT", "UIParent", "BOTTOMLEFT", 32, 95); ChatFrame1:SetWidth(430); ChatFrame1:SetHeight(120); ChatFrame1.isInitialized = 0; FCF_SetButtonSide(ChatFrame1, "left") FCF_SetChatWindowFontSize(nil, ChatFrame1, 14); FCF_SetWindowName(ChatFrame1, GENERAL); FCF_SetWindowColor(ChatFrame1, DEFAULT_CHATFRAME_COLOR.r, DEFAULT_CHATFRAME_COLOR.g, DEFAULT_CHATFRAME_COLOR.b); FCF_SetWindowAlpha(ChatFrame1, DEFAULT_CHATFRAME_ALPHA); FCF_UnDockFrame(ChatFrame1); ChatFrame_RemoveAllMessageGroups(ChatFrame1); ChatFrame_RemoveAllChannels(ChatFrame1); ChatFrame_ReceiveAllPrivateMessages(ChatFrame1); ChatFrame_ReceiveAllBNConversations(ChatFrame1); SELECTED_CHAT_FRAME = ChatFrame1; DEFAULT_CHAT_FRAME.chatframe = DEFAULT_CHAT_FRAME; FCF_SetChatWindowFontSize(nil, ChatFrame2, 14); FCF_SetWindowName(ChatFrame2, COMBAT_LOG); FCF_SetWindowColor(ChatFrame2, DEFAULT_CHATFRAME_COLOR.r, DEFAULT_CHATFRAME_COLOR.g, DEFAULT_CHATFRAME_COLOR.b); FCF_SetWindowAlpha(ChatFrame2, DEFAULT_CHATFRAME_ALPHA); ChatFrame_RemoveAllMessageGroups(ChatFrame2); ChatFrame_RemoveAllChannels(ChatFrame2); ChatFrame_ReceiveAllPrivateMessages(ChatFrame2); ChatFrame_ReceiveAllBNConversations(ChatFrame2); FCF_UnDockFrame(ChatFrame2); ChatFrame2.isInitialized = 0; for _, chatFrameName in ipairs(CHAT_FRAMES) do if ( chatFrameName ~= "ChatFrame1" ) then local chatFrame = _G[chatFrameName]; if ( chatFrame.isTemporary and chatFrame.chatType == "BN_CONVERSATION" and BNGetConversationInfo(tonumber(chatFrame.chatTarget)) and GetCVar("conversationMode") == "popout" ) then --We're still in this conversation, so we just want to reset the position, not remove the frame. FCF_DockFrame(chatFrame, 3); --Put it after General and Combat Log else chatFrame.isInitialized = 0; FCF_SetTabPosition(chatFrame, 0); FCF_Close(chatFrame); FCF_UnDockFrame(chatFrame); FCF_SetWindowName(chatFrame, ""); ChatFrame_RemoveAllMessageGroups(chatFrame); ChatFrame_RemoveAllChannels(chatFrame); ChatFrame_ReceiveAllPrivateMessages(chatFrame); ChatFrame_ReceiveAllBNConversations(chatFrame); end FCF_SetChatWindowFontSize(nil, chatFrame, 14); FCF_SetWindowColor(chatFrame, DEFAULT_CHATFRAME_COLOR.r, DEFAULT_CHATFRAME_COLOR.g, DEFAULT_CHATFRAME_COLOR.b); FCF_SetWindowAlpha(chatFrame, DEFAULT_CHATFRAME_ALPHA); end end ChatFrame1.init = 0; FCF_DockFrame(ChatFrame1, 1, true); FCF_DockFrame(ChatFrame2, 2); -- resets to hard coded defaults ResetChatWindows(); UIParent_ManageFramePositions(); FCFDock_SelectWindow(GENERAL_CHAT_DOCK, ChatFrame1); end function IsCombatLog(frame) if ( frame == ChatFrame2 and IsAddOnLoaded("Blizzard_CombatLog") ) then return true; else return false; end end function FCFClickAnywhereButton_OnLoad(self) self:SetFrameLevel(self:GetParent():GetFrameLevel() - 1); self:RegisterEvent("VARIABLES_LOADED"); self:RegisterEvent("CVAR_UPDATE"); self:RegisterForClicks("LeftButtonDown", "RightButtonDown"); FCFClickAnywhereButton_UpdateState(self); end function FCFClickAnywhereButton_OnEvent(self, event, ...) local arg1 = ...; if ( event == "VARIABLES_LOADED" or (event == "CVAR_UPDATE" and (arg1 == "chatStyle" or arg1 == "CHAT_WHOLE_WINDOW_CLICKABLE")) ) then FCFClickAnywhereButton_UpdateState(self); end end function FCFClickAnywhereButton_UpdateState(self) if ( GetCVar("chatStyle") == "im" and GetCVarBool("wholeChatWindowClickable") and LAST_ACTIVE_CHAT_EDIT_BOX ~= self:GetParent().editBox ) then self:Show(); else self:Hide(); end end function FCF_MinimizeFrame(chatFrame, side) local chatTab = _G[chatFrame:GetName().."Tab"]; local createdFrame = false; if ( not chatFrame.minFrame ) then chatFrame.minFrame = FCF_CreateMinimizedFrame(chatFrame); end if ( chatFrame.minFrame.resetPosition ) then chatFrame.minFrame:ClearAllPoints(); chatFrame.minFrame:SetPoint("TOP"..side, chatFrame, "TOP"..side, 0, 0); chatFrame.minFrame.resetPosition = false; end chatFrame.minimized = true; chatFrame.minFrame:Show(); chatFrame:Hide(); chatTab:Hide(); end function FCF_MaximizeFrame(chatFrame) local minFrame = chatFrame.minFrame; local chatTab = _G[chatFrame:GetName().."Tab"]; chatFrame.minimized = false; minFrame:UnlockHighlight(); minFrame:Hide(); chatFrame:Show(); chatTab:Show(); FCF_FadeInChatFrame(chatFrame); end function FCF_CreateMinimizedFrame(chatFrame) local chatTab = _G[chatFrame:GetName().."Tab"]; local minFrame = CreateFrame("Button", chatFrame:GetName().."Minimized", UIParent, "FloatingChatFrameMinimizedTemplate"); minFrame.maxFrame = chatFrame; minFrame:SetText(chatFrame.name); --Copy the colors from the minimized frame. minFrame.selectedColorTable = chatTab.selectedColorTable; FCFMin_UpdateColors(minFrame); if ( not chatFrame.isTemporary ) then minFrame.conversationIcon:Hide(); else local conversationIcon; if ( chatFrame.chatType == "WHISPER" or chatFrame.chatType == "BN_WHISPER" ) then conversationIcon = "Interface\\ChatFrame\\UI-ChatWhisperIcon"; else conversationIcon = "Interface\\ChatFrame\\UI-ChatConversationIcon"; end minFrame.conversationIcon:SetTexture(conversationIcon); end if (chatFrame.isTemporary) then minFrame.Text:SetJustifyH("LEFT"); minFrame.Text:SetPoint("LEFT", minFrame, "LEFT", 30, 0); end --Make sure the position is reset. minFrame.resetPosition = true; return minFrame; end function FCFMin_UpdateColors(minFrame) --Color it. local colorTable = minFrame.selectedColorTable or DEFAULT_TAB_SELECTED_COLOR_TABLE; if ( minFrame.selectedColorTable ) then minFrame:GetFontString():SetTextColor(colorTable.r, colorTable.g, colorTable.b); else minFrame:GetFontString():SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b); end minFrame.leftHighlightTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); minFrame.middleHighlightTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); minFrame.rightHighlightTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); minFrame.glow:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); minFrame.conversationIcon:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); end --This function just makes the position be reset the next time the minimize frame is shown. function FCF_FlagMinimizedPositionReset(chatFrame) if ( chatFrame.minFrame ) then chatFrame.minFrame.resetPosition = true; end end ------Docking related functions for the new docking system --[[ Since we've been discussing allowing multiple docks, this code is designed to be mostly-OO. Please try not to use global variables. (Theoretically, all of these functions may be put in a metatable to allow, e.g., "Dock:SelectWindow(chatWindow)". To keep with this, please ensure that "dock" is the first argument of every function.) ]] function FCFDock_OnLoad(dock) dock.DOCKED_CHAT_FRAMES = {}; dock.isDirty = true; --You dirty, dirty frame end function FCFDock_OnEvent(dock, event, ...) if ( event == "UPDATE_CHAT_WINDOWS" ) then --FCFDock_ForceTabSort(dock); --FCFDock_ForceReanchoring(dock); end end function FCFDock_GetChatFrames(dock) return dock.DOCKED_CHAT_FRAMES; end function FCFDock_SetPrimary(dock, chatFrame) dock.primary = chatFrame; dock:SetPoint("BOTTOMLEFT", chatFrame, "TOPLEFT", 0, 6); dock:SetPoint("BOTTOMRIGHT", chatFrame, "TOPRIGHT", 0, 6); chatFrame:SetScript("OnSizeChanged", function(self) FCFDock_OnPrimarySizeChanged(dock) end); if ( not FCFDock_GetSelectedWindow(dock) ) then FCFDock_SelectWindow(dock, chatFrame); end FCFDock_ForceReanchoring(dock); FCFDock_AddChatFrame(dock, chatFrame, 1); end function FCFDock_OnPrimarySizeChanged(dock) dock.isDirty = true; --We have to save off the current leftmost-tab before we resize the tabs. dock.leftTab = FCFDockScrollFrame_GetLeftmostTab(dock.scrollFrame); --We have to do it on the next frame to deal with issues caused by resizing the WoW client (frame positions may not be valid) dock:SetScript("OnUpdate", FCFDock_OnUpdate); end function FCFDock_OnUpdate(self) --These may fail if we're resizing the WoW client if ( FCFDock_UpdateTabs(self) and FCFDockScrollFrame_JumpToTab(self.scrollFrame, self.leftTab) ) then self.leftTab = nil; self:SetScript("OnUpdate", nil); end end function FCFDock_ForceReanchoring(dock) for index, chatFrame in pairs(dock.DOCKED_CHAT_FRAMES) do if ( dock.primary ~= chatFrame ) then chatFrame:ClearAllPoints(); chatFrame:SetAllPoints(dock.primary); end end end function FCFDock_AddChatFrame(dock, chatFrame, position) if ( not dock.primary ) then error("Need a primary window before another can be added."); end if ( FCFDock_HasDockedChatFrame(dock, chatFrame) ) then return; --We're already docked... end dock.isDirty = true; chatFrame.isDocked = 1; if ( position and position <= #dock.DOCKED_CHAT_FRAMES + 1 ) then assert(position ~=1 or chatFrame == dock.primary); tinsert(dock.DOCKED_CHAT_FRAMES, position, chatFrame); else tinsert(dock.DOCKED_CHAT_FRAMES, chatFrame); end FCFDock_HideInsertHighlight(dock); if ( dock.primary ~= chatFrame ) then chatFrame:ClearAllPoints(); chatFrame:SetAllPoints(dock.primary); chatFrame:SetMovable(false); chatFrame:SetResizable(false); end if ( chatFrame.conversationButton ) then BNConversationButton_UpdateAttachmentPoint(chatFrame.conversationButton); end chatFrame.buttonFrame.minimizeButton:Hide(); chatFrame.buttonFrame:SetAlpha(1.0); dock.overflowButton.list:Hide(); FCFDock_UpdateTabs(dock); end function FCFDock_RemoveChatFrame(dock, chatFrame) assert(chatFrame ~= dock.primary or #dock.DOCKED_CHAT_FRAMES == 1); dock.isDirty = true; tDeleteItem(dock.DOCKED_CHAT_FRAMES, chatFrame); local chatTab = _G[chatFrame:GetName().."Tab"]; chatFrame.isDocked = nil; chatTab:SetParent(UIParent); chatTab:SetFrameStrata("LOW"); chatFrame:SetMovable(true); chatFrame:SetResizable(true); FCFTab_UpdateColors(chatTab, true); PanelTemplates_TabResize(chatTab, chatTab.sizePadding or 0, nil, nil, chatTab.textWidth); if ( FCFDock_GetSelectedWindow(dock) == chatFrame ) then FCFDock_SelectWindow(dock, dock.DOCKED_CHAT_FRAMES[1]); end if ( chatFrame.conversationButton ) then BNConversationButton_UpdateAttachmentPoint(chatFrame.conversationButton); end chatFrame.buttonFrame.minimizeButton:Show(); dock.overflowButton.list:Hide(); chatFrame:Show(); FCFDock_UpdateTabs(dock); end function FCFDock_HasDockedChatFrame(dock, chatFrame) return tContains(dock.DOCKED_CHAT_FRAMES, chatFrame); end function FCFDock_SelectWindow(dock, chatFrame) assert(chatFrame) dock.isDirty = true; dock.selected = chatFrame; dock.overflowButton.list:Hide(); FCFDock_UpdateTabs(dock); if ( ChatFrameMenuButton ) then if ( chatFrame.conversationButton and chatFrame.conversationButton:IsShown() ) then ChatFrameMenuButton:Hide(); else ChatFrameMenuButton:Show(); end end end function FCFDock_GetSelectedWindow(dock) return dock.selected; end function FCFDock_UpdateTabs(dock, forceUpdate) if ( not dock.isDirty and not forceUpdate ) then --No changes have been made since the last update. return; end local scrollChild = dock.scrollFrame:GetScrollChild(); local lastDockedStaticTab = nil; local lastDockedDynamicTab = nil; local numDynFrames = 0; --Number of dynamicly sized frames. local selectedDynIndex = nil; for index, chatFrame in ipairs(dock.DOCKED_CHAT_FRAMES) do local chatTab = _G[chatFrame:GetName().."Tab"]; if ( chatFrame == FCFDock_GetSelectedWindow(dock) ) then chatFrame:Show(); else chatFrame:Hide(); end FCFTab_UpdateAlpha(chatFrame); chatTab:ClearAllPoints(); chatTab:Show(); FCFTab_UpdateColors(chatTab, chatFrame == FCFDock_GetSelectedWindow(dock)); if ( chatFrame.isStaticDocked ) then chatTab:SetParent(dock); PanelTemplates_TabResize(chatTab, chatTab.sizePadding or 0); if ( lastDockedStaticTab ) then chatTab:SetPoint("LEFT", lastDockedStaticTab, "RIGHT", 0, 0); else chatTab:SetPoint("LEFT", dock, "LEFT", 0, 0); end lastDockedStaticTab = chatTab; else chatTab:SetParent(scrollChild); numDynFrames = numDynFrames + 1; if ( FCFDock_GetSelectedWindow(dock) == chatFrame ) then selectedDynIndex = numDynFrames; end if ( lastDockedDynamicTab ) then chatTab:SetPoint("LEFT", lastDockedDynamicTab, "RIGHT", 0, 0); else chatTab:SetPoint("LEFT", scrollChild, "LEFT", 0, 0); end lastDockedDynamicTab = chatTab; end end local dynTabSize, hasOverflow = FCFDock_CalculateTabSize(dock, numDynFrames); for index, chatFrame in ipairs(dock.DOCKED_CHAT_FRAMES) do if ( not chatFrame.isStaticDocked ) then local chatTab = _G[chatFrame:GetName().."Tab"]; PanelTemplates_TabResize(chatTab, chatTab.sizePadding or 0, dynTabSize); end end dock.scrollFrame:SetPoint("LEFT", lastDockedStaticTab, "RIGHT", 0, 0); if ( hasOverflow ) then dock.overflowButton:Show(); dock.scrollFrame:SetPoint("BOTTOMRIGHT", dock.overflowButton, "BOTTOMLEFT", 0, 0); else dock.overflowButton:Hide(); dock.scrollFrame:SetPoint("BOTTOMRIGHT", dock, "BOTTOMRIGHT", 0, -5); end --Cache some of this data on the scroll frame for animating to the selected tab. dock.scrollFrame.dynTabSize = dynTabSize; dock.scrollFrame.numDynFrames = numDynFrames; dock.scrollFrame.selectedDynIndex = selectedDynIndex; dock.isDirty = false; return FCFDock_ScrollToSelectedTab(dock); end --Returns dynTabSize, hasOverflow function FCFDock_CalculateTabSize(dock, numDynFrames) local MIN_SIZE, MAX_SIZE = 60, 90; local scrollSize = dock.scrollFrame:GetWidth() + (dock.overflowButton:IsShown() and dock.overflowButton.width or 0); --We want the total width assuming no overflow button. --First, see if we can fit all the tabs at the maximum size if ( numDynFrames * MAX_SIZE < scrollSize ) then return MAX_SIZE, false; end if ( scrollSize / MIN_SIZE < numDynFrames ) then --Not everything fits, so we'll need room for the overflow button. scrollSize = scrollSize - dock.overflowButton.width; end --Figure out how many tabs we're going to be able to fit at the minimum size local numWholeTabs = min(floor(scrollSize / MIN_SIZE), numDynFrames) if ( numWholeTabs == 0 ) then return scrollSize, true; end --How big each tab should be. local tabSize = scrollSize / numWholeTabs; return tabSize, (numDynFrames > numWholeTabs); end function FCFDock_ScrollToSelectedTab(dock) if ( FCFDockScrollFrame_GetScrollDistanceNeeded(dock.scrollFrame, dock.scrollFrame.selectedDynIndex) ~= 0) then dock.scrollFrame:SetScript("OnUpdate", FCFDockScrollFrame_OnUpdate); return true; else return FCFDockScrollFrame_JumpToTab(dock.scrollFrame, FCFDockScrollFrame_GetLeftmostTab(dock.scrollFrame)); --Make sure we're exactly aligned with the tab. end end ---These functions deal with the scroll frame handling dynamic tabs. function FCFDockScrollFrame_OnUpdate(self, elapsed) local MOVEMENT_SPEED = 10; local totalDistanceNeeded = FCFDockScrollFrame_GetScrollDistanceNeeded(self, self.selectedDynIndex); if ( abs(totalDistanceNeeded) < 1.0 ) then --Delta chosen through experimentation self:SetScript("OnUpdate", nil); FCFDockScrollFrame_JumpToTab(self, FCFDockScrollFrame_GetLeftmostTab(self)); --Make sure we're exactly aligned with the tab. return; end local currentPosition = self:GetHorizontalScroll(); local distanceNoCap = totalDistanceNeeded * MOVEMENT_SPEED * elapsed; local distanceToMove = (totalDistanceNeeded > 0) and min(totalDistanceNeeded, distanceNoCap) or max(totalDistanceNeeded, distanceNoCap); self:SetHorizontalScroll(max(currentPosition + distanceToMove, 0)); end function FCFDock_GetInsertIndex(dock, chatFrame, mouseX, mouseY) if ( chatFrame.isStaticDocked ) then local maxPosition = 0; for index, value in ipairs(dock.DOCKED_CHAT_FRAMES) do if ( value.isStaticDocked ) then local tab = _G[value:GetName().."Tab"]; if ( mouseX < (tab:GetLeft() + tab:GetRight()) / 2 and --Find the first tab we're on the left of. (Being on top of the tab, but left of the center counts) tab:GetID() ~= dock.primary:GetID()) then --We never count as being to the left of the primary tab. return index; end maxPosition = index; end end --We aren't to the left of anything, so we're going into the far-right position. return maxPosition + 1; else --Find the dynamic insertion spot local maxPosition = 9^9; local leftTab = FCFDockScrollFrame_GetLeftmostTab(dock.scrollFrame); local numDynTabsDisplayed = dock.scrollFrame:GetWidth() / dock.scrollFrame.dynTabSize; local currTabNum = 0; for index, value in ipairs(dock.DOCKED_CHAT_FRAMES) do if ( not value.isStaticDocked ) then currTabNum = currTabNum + 1; if ( currTabNum >= leftTab and currTabNum < leftTab + numDynTabsDisplayed ) then local tab = _G[value:GetName().."Tab"]; if ( mouseX < (tab:GetLeft() + tab:GetRight())/2 ) then return index; end maxPosition = index; end end end return min(#dock.DOCKED_CHAT_FRAMES + 1, maxPosition + 1); end end function FCFDock_PlaceInsertHighlight(dock, chatFrame, mouseX, mouseY) local insert = FCFDock_GetInsertIndex(dock, chatFrame, mouseX, mouseY); local attachFrame = dock.primary; local leftDynTab = FCFDockScrollFrame_GetLeftmostTab(dock.scrollFrame); local numDynTabsDisplayed = dock.scrollFrame:GetWidth() / dock.scrollFrame.dynTabSize; local dynamicIndex = 0; for index, value in ipairs(dock.DOCKED_CHAT_FRAMES) do if ( index < insert ) then if ( value.isStaticDocked ) then attachFrame = value; else dynamicIndex = dynamicIndex + 1; if ( dynamicIndex >= leftDynTab and dynamicIndex < leftDynTab + numDynTabsDisplayed ) then attachFrame = value; end end end end dock.insertHighlight:ClearAllPoints(); dock.insertHighlight:SetPoint("BOTTOMLEFT", _G[attachFrame:GetName().."Tab"], "BOTTOMRIGHT", -15, -4); dock.insertHighlight:Show(); end function FCFDock_HideInsertHighlight(dock) dock.insertHighlight:Hide(); end function FCFDock_SetDirty(dock) dock.isDirty = true; end function FCFDockScrollFrame_GetScrollDistanceNeeded(scrollFrame, dynFrameIndex) local firstIndex = (scrollFrame:GetHorizontalScroll() / scrollFrame.dynTabSize) + 1; local numDisplayedFrames = scrollFrame:GetWidth() / scrollFrame.dynTabSize; local lastIndex = firstIndex + numDisplayedFrames - 1; if ( dynFrameIndex and dynFrameIndex < firstIndex ) then --Need to scroll left to get to the selected button return (dynFrameIndex - firstIndex) * scrollFrame.dynTabSize; elseif ( dynFrameIndex and dynFrameIndex > lastIndex ) then --Need to scroll right to get to the selected button return (dynFrameIndex - lastIndex) * scrollFrame.dynTabSize; elseif ( firstIndex > 1 and scrollFrame.numDynFrames < lastIndex ) then --Need to scroll left to fill in empty space at the end. return (scrollFrame.numDynFrames - lastIndex) * scrollFrame.dynTabSize; else return 0; end end function FCFDockScrollFrame_GetLeftmostTab(scrollFrame) return floor((scrollFrame:GetHorizontalScroll() / scrollFrame.dynTabSize) + 0.5) + 1; end function FCFDockScrollFrame_JumpToTab(scrollFrame, leftTab) --If we have a selected frame, make sure it's still in view. local numTabsDisplayed = scrollFrame:GetWidth() / scrollFrame.dynTabSize; if ( scrollFrame.selectedDynIndex ) then if ( scrollFrame.selectedDynIndex >= leftTab + numTabsDisplayed ) then leftTab = scrollFrame.selectedDynIndex - numTabsDisplayed + 1; elseif ( scrollFrame.selectedDynIndex < leftTab ) then leftTab = scrollFrame.selectedDynIndex; end end --Make sure, if we can show more frames, we do. leftTab = min(leftTab, scrollFrame.numDynFrames - numTabsDisplayed + 1); --And make sure we never go to the left of 1 (for example, if we have extra space) leftTab = max(leftTab, 1); scrollFrame:SetHorizontalScroll(scrollFrame.dynTabSize * (leftTab - 1)); return FCFDockOverflowButton_UpdatePulseState(scrollFrame:GetParent().overflowButton); end --Dock list related functions function FCFDockOverflow_CloseLists() local list = GENERAL_CHAT_DOCK.overflowButton.list; if ( list:IsShown() ) then list:Hide(); return true; else return false; end end function FCFDockOverflowButton_UpdatePulseState(self) local dock = self:GetParent(); local shouldPulse = false; for _, chatFrame in pairs(FCFDock_GetChatFrames(dock)) do local chatTab = _G[chatFrame:GetName().."Tab"]; if ( not chatFrame.isStaticDocked and chatTab.alerting) then --Make sure the rects are valid. (Not always the case when resizing the WoW client if ( not chatTab:GetRight() or not dock.scrollFrame:GetRight() ) then return false; end --Check if it's off the screen. local DELTA = 3; --Chosen through experimentation if ( chatTab:GetRight() < (dock.scrollFrame:GetLeft() + DELTA) or chatTab:GetLeft() > (dock.scrollFrame:GetRight() - DELTA) ) then shouldPulse = true; break; end end end if ( shouldPulse ) then UIFrameFlash(self:GetHighlightTexture(), 1.0, 1.0, -1, true, 0, 0, "chat"); self:LockHighlight(); self.alerting = true; else UIFrameFlashStop(self:GetHighlightTexture()); self:UnlockHighlight(); self:GetHighlightTexture():Show(); self.alerting = false; end if ( self.list:IsShown() ) then FCFDockOverflowList_Update(self.list, dock); end return true; end function FCFDockOverflowButton_OnClick(self, button) PlaySound("UChatScrollButton"); if ( self.list:IsShown() ) then self.list:Hide(); else FCFDockOverflowList_Update(self.list, self:GetParent()); self.list:Show(); end end function FCFDockOverflowButton_OnEvent(self, event, ...) if ( event == "UPDATE_CHAT_COLOR" and self.list:IsShown() ) then FCFDockOverflowList_Update(self.list, self:GetParent()); end end function FCFDockOverflowList_Update(list, dock) local dockedFrames = FCFDock_GetChatFrames(dock); list:SetHeight(#dockedFrames *15 + 35); list.numTabs:SetFormattedText(CHAT_WINDOWS_COUNT, #dockedFrames); for i = 1, #dockedFrames do local button = list.buttons[i]; if ( not button ) then list.buttons[i] = CreateFrame("Button", list:GetName().."Button"..i, list, "DockManagerOverflowListButtonTemplate"); button = list.buttons[i]; if ( not list.buttons[i-1] ) then button:SetPoint("TOPLEFT", list, "TOPLEFT", 5, -19); else button:SetPoint("TOPLEFT", list.buttons[i-1], "BOTTOMLEFT", 0, -3); end button:SetWidth(list:GetWidth() - 10); -- buttons are 5 pixels in on both sides end FCFDockOverflowListButton_SetValue(button, dockedFrames[i]); end for i = #dockedFrames + 1, #list.buttons do list.buttons[i]:Hide(); end end function FCFDockOverflowListButton_SetValue(button, chatFrame) local chatTab = _G[chatFrame:GetName().."Tab"]; button.chatFrame = chatFrame; button:SetText(chatFrame.name); local colorTable = chatTab.selectedColorTable or DEFAULT_TAB_SELECTED_COLOR_TABLE; if ( chatTab.selectedColorTable ) then button:GetFontString():SetTextColor(colorTable.r, colorTable.g, colorTable.b); else button:GetFontString():SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b); end button.glow:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); if ( chatTab.conversationIcon ) then button.conversationIcon:SetVertexColor(colorTable.r, colorTable.g, colorTable.b); button.conversationIcon:Show(); else button.conversationIcon:Hide(); end if ( chatTab.alerting ) then button.alerting = true; UIFrameFlash(button.glow, 1.0, 1.0, -1, false, 0, 0, "chat"); else button.alerting = false; UIFrameFlashStop(button.glow); end button:Show(); end function FCFDockOverflowListButton_OnClick(self, button) FCFDock_SelectWindow(self:GetParent():GetParent():GetParent(), self.chatFrame); end --------------------------------------------------- -----------Temp Window Manager------------- ---------------------------------------------------- local dedicatedWindows = {}; local function FCFManager_GetToken(chatType, chatTarget) return strlower(chatType)..(chatTarget and ";;"..strlower(chatTarget) or ""); end function FCFManager_RegisterDedicatedFrame(chatFrame, chatType, chatTarget) local token = FCFManager_GetToken(chatType, chatTarget); if ( not dedicatedWindows[token] ) then dedicatedWindows[token] = {}; end if ( not tContains(dedicatedWindows[token], chatFrame) ) then tinsert(dedicatedWindows[token], chatFrame); end end function FCFManager_UnregisterDedicatedFrame(chatFrame, chatType, chatTarget) local token = FCFManager_GetToken(chatType, chatTarget); local windowList = dedicatedWindows[token]; if ( windowList ) then tDeleteItem(windowList, chatFrame); end end function FCFManager_GetNumDedicatedFrames(chatType, chatTarget) local token = FCFManager_GetToken(chatType, chatTarget); local windowList = dedicatedWindows[token]; return (windowList and #windowList or 0); end function FCFManager_ShouldSuppressMessage(chatFrame, chatType, chatTarget) --Using GetToken probably isn't the best way to do this due to the string concatenation, but it's the easiest to get in quickly. if ( chatFrame.chatType and FCFManager_GetToken(chatType, chatTarget) == FCFManager_GetToken(chatFrame.chatType, chatFrame.chatTarget) ) then --This frame is a dedicated frame of this type, so we should always display. return false; end if ( chatType == "BN_CONVERSATION" and GetCVar("conversationMode") == "popout" ) then return true; end return false; end function FloatingChatFrameManager_OnLoad(self) --Register for BN_CONVERSATION related messages to be able to spawn off new windows as needed for _, event in pairs(ChatTypeGroup["BN_CONVERSATION"]) do self:RegisterEvent(event); end end function FloatingChatFrameManager_OnEvent(self, event, ...) local arg1 = ...; if ( strsub(event, 1, 9) == "CHAT_MSG_" ) then local chatType = strsub(event, 10); local chatGroup = Chat_GetChatCategory(chatType); if ( chatGroup == "BN_CONVERSATION" ) then if ( GetCVar("conversationMode") == "popout" ) then if( not (event == "CHAT_MSG_BN_CONVERSATION_NOTICE" and arg1 == "YOU_LEFT_CONVERSATION") ) then local chatTarget = tostring(select(8, ...)); if ( FCFManager_GetNumDedicatedFrames(chatGroup, chatTarget) == 0 ) then local chatFrame = FCF_OpenTemporaryWindow(chatGroup, chatTarget); chatFrame:GetScript("OnEvent")(chatFrame, event, ...); --Re-fire the event for the frame. end end end end end end