#include "pch.h" #include "trekctrls.h" #include "trekmdl.h" #include "badwords.h" ////////////////////////////////////////////////////////////////////////////// // // ChatListItem // ////////////////////////////////////////////////////////////////////////////// class ChatListItem : public ListItem { private: long m_lData; ChatInfo* m_pchatInfo; TVector m_vMsgLines; WinPoint m_ptLineSize; public: ChatListItem(ChatInfo* pchatInfo, const WinPoint& ptLineSize) : m_lData((long)pchatInfo), m_pchatInfo(pchatInfo), m_ptLineSize(ptLineSize) { ZString strMsg = CensorBadWords (m_pchatInfo->GetMessage()); IEngineFont* pfont = pchatInfo->IsFromLeader() ? TrekResources::SmallBoldFont() : TrekResources::SmallFont(); int nStrLenLeft = strMsg.GetLength(); int nStrLenLine; while ((nStrLenLine = pfont->GetMaxTextLength(strMsg, ptLineSize.X(), true)) < nStrLenLeft) { int nStrLenWordBreak = nStrLenLine; while (nStrLenWordBreak > 2 && strMsg[nStrLenWordBreak] != ' ' && strMsg[nStrLenWordBreak-1] != ' ') nStrLenWordBreak--; if (nStrLenWordBreak != 2) nStrLenLine = nStrLenWordBreak; if (nStrLenLine <= 2) { // put a blank line without the character nStrLenLine = 3; m_vMsgLines.PushEnd(""); } else { m_vMsgLines.PushEnd(strMsg.Left(nStrLenLine)); } strMsg = " " + strMsg.RightOf(nStrLenLine); nStrLenLeft -= nStrLenLine - 2; ZAssert(strMsg.GetLength() == nStrLenLeft); } m_vMsgLines.PushEnd(strMsg); } ~ChatListItem() { // debugf("~ChatListItem\n"); } short GetItemHeight() { return m_vMsgLines.GetCount(); } long GetItemData() { return m_lData; }; bool Update() { return true; } void SetSortOrder(long lSortOrder) { } bool SetFilter(long lFilter) { return true; } void DrawItem(Surface* pSurface, const WinRect& rect, bool fSelected, int iFirstSlot) { IEngineFont* pfont = m_pchatInfo->IsFromLeader() ? TrekResources::SmallBoldFont() : TrekResources::SmallFont(); WinPoint pt(rect.Min() + WinPoint(3,3)); for ( int i = iFirstSlot; i < m_vMsgLines.GetCount() && pt.Y() < rect.YMax(); i++ ) { pSurface->DrawString(pfont, m_pchatInfo->GetColor(), pt, m_vMsgLines[i]); pt += WinPoint(0, m_ptLineSize.Y()); } } }; ////////////////////////////////////////////////////////////////////////////// // // ChatListPaneImpl // ////////////////////////////////////////////////////////////////////////////// class ChatListPaneImpl : public ChatListPane, public EventTargetContainer, public TrekClientEventSink, public IKeyboardInput { private: TRef m_pListPane; WinPoint m_ptItemSize; TList m_listChannels; TRef m_keyboardDelegate; TRef m_targetAutoscrollOn; bool m_bAutoscroll; bool m_bPlayerChatsOnly; bool m_bIgnoreScrollingEvents; public: ChatListPaneImpl(const WinPoint& ptSize): m_ptItemSize(ptSize.X(), 12), m_bAutoscroll(true), m_bIgnoreScrollingEvents(false) { m_bPlayerChatsOnly = true; m_pListPane = ListPaneOld::Create(ptSize, 12, true, NULL), InsertAtBottom(m_pListPane); AddEventTarget(OnListSelect, m_pListPane->GetEventSource()); AddEventTarget(OnScroll, m_pListPane->GetScrollEvent()); UpdateContents(); m_keyboardDelegate = IKeyboardInput::CreateDelegate(this); GetWindow()->AddKeyboardInputFilter(m_keyboardDelegate); } ~ChatListPaneImpl() { GetWindow()->RemoveKeyboardInputFilter(m_keyboardDelegate); if (m_targetAutoscrollOn) m_targetAutoscrollOn->Disconnect(); } void AutoscrollOff() { m_bAutoscroll = false; // set/reset a timer to turn it back on after 10 seconds if (m_targetAutoscrollOn) m_targetAutoscrollOn->Disconnect(); m_targetAutoscrollOn = new TEventTarget(this, OnAutoscrollTimeout, GetWindow(), 10); } void AutoscrollOn() { m_bAutoscroll = true; if (m_targetAutoscrollOn) m_targetAutoscrollOn->Disconnect(); } bool OnAutoscrollTimeout() { m_pListPane->ScrollBottom(); AutoscrollOn(); return false; } bool OnListSelect(int nSel) { return true; } void OnClearChat() { m_pListPane->RemoveAll(); } void OnChatMessageChange() { // save some info about the current state of the chat list int countOld = m_pListPane->GetCountItems(); int posOld = m_pListPane->GetScrollPosition(); UpdateContents(); // if the number of items did not change, assume we were just doing // something like marking a player as dead so keep the current scroll // position. if (countOld == m_pListPane->GetCountItems()) m_pListPane->SetScrollPosition(posOld); } bool ChatIsNonCriticalMessage(ChatInfo * pchatinfo) { return !pchatinfo->IsFromPlayer() && !pchatinfo->IsFromObjectModel() && pchatinfo->GetChatTarget() != CHAT_INDIVIDUAL_NOFILTER && m_bPlayerChatsOnly; } void OnDeleteChatMessage(ChatInfo* pchatInfo) { m_bIgnoreScrollingEvents = true; m_pListPane->RemoveItemByData((long)pchatInfo); m_bIgnoreScrollingEvents = false; } void OnNewChatMessage() { ChatInfo * pchatinfo = &(trekClient.m_chatList.last()->data()); if (ChatIsNonCriticalMessage(pchatinfo)) { trekClient.PostText(false, CensorBadWords (pchatinfo->GetMessage())); } else { AddChatItem(pchatinfo); } } void PageUp() { m_pListPane->ScrollPageUp(); AutoscrollOff(); } void PageDown() { m_pListPane->ScrollPageDown(); // if we are at the bottom of the list, turn auto-scroll back on if (m_pListPane->IsItemVisibleByIdx(m_pListPane->GetCountItems() - 1)) AutoscrollOn(); else AutoscrollOff(); } bool OnScroll() { if (!m_bIgnoreScrollingEvents) { // if we are at the bottom of the list, turn auto-scroll back on if (m_pListPane->IsItemVisibleByIdx(m_pListPane->GetCountItems() - 1)) AutoscrollOn(); else AutoscrollOff(); } return true; } void UpdateContents() { m_pListPane->RemoveAll(); for (ChatLink* link = trekClient.m_chatList.first(); link; link = link->next()) { AddChatItem(&(link->data())); } m_pListPane->ScrollBottom(); AutoscrollOn(); } bool ChatPassesFilter(ChatInfo* pchatInfo) { if (ChatIsNonCriticalMessage(pchatInfo)) { return false; } if (m_listChannels.IsEmpty()) { return true; } else { TList::Iterator iter(m_listChannels); while (!iter.End()) { if (iter.Value() == pchatInfo->GetChatTarget()) return true; iter.Next(); } } return false; } void AddChatItem(ChatInfo* pchatInfo) { if (ChatPassesFilter(pchatInfo)) { m_bIgnoreScrollingEvents = true; int idx = m_pListPane->AppendItem(new ChatListItem(pchatInfo, m_ptItemSize)); if (m_bAutoscroll) { m_pListPane->SetSelItemByIdx(idx); m_pListPane->ScrollBottom(); } m_bIgnoreScrollingEvents = false; } } void AddChannel(ChatTarget channel) { m_listChannels.PushEnd(channel); UpdateContents(); } void RemoveChannel(ChatTarget channel) { m_listChannels.Remove(channel); UpdateContents(); } void ResetChannels() { m_listChannels.SetEmpty(); UpdateContents(); } void UpdateLayout() { DefaultUpdateLayout(); } }; TRef ChatListPaneFactory::Apply(ObjectStack& stack) { TRef ppointSize; CastTo(ppointSize, stack.Pop()); return (Pane*)new ChatListPaneImpl( WinPoint( (int)ppointSize->GetValue().X(), (int)ppointSize->GetValue().Y() ) ); }