///////////////////////////////////////////////////////////////////////////////////////// // Miranda NG: the free IM client for Microsoft* Windows* // // Copyright (C) 2012-23 Miranda NG team, // Copyright (c) 2000-09 Miranda ICQ/IM project, // all portions of this codebase are copyrighted to the people // listed in contributors.txt. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // you should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // part of tabSRMM messaging plugin for Miranda. // // (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors // // Implements the richedit-based message history display for the group // chat window. #include "stdafx.h" /* * The code for streaming the text is to a large extent copied from * the srmm module and then modified to fit the chat module. */ static uint8_t EventToSymbol(const LOGINFO &lin) { switch (lin.iType) { case GC_EVENT_MESSAGE: return (lin.bIsMe) ? 0x37 : 0x38; case GC_EVENT_JOIN: return 0x34; case GC_EVENT_PART: return 0x33; case GC_EVENT_QUIT: return 0x39; case GC_EVENT_NICK: return 0x71; case GC_EVENT_KICK: return 0x72; case GC_EVENT_NOTICE: return 0x28; case GC_EVENT_INFORMATION: return 0x69; case GC_EVENT_ADDSTATUS: return 0x35; case GC_EVENT_REMOVESTATUS: return 0x36; case GC_EVENT_ACTION: return 0x60; } return 0x73; } ///////////////////////////////////////////////////////////////////////////////////////// // RTF header void CLogWindow::CreateChatRtfHeader(RtfChatLogStreamData *streamData) { // get the number of pixels per logical inch if (g_chatApi.logPixelSY == 0) { HDC hdc = GetDC(nullptr); g_chatApi.logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY); g_chatApi.logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX); ReleaseDC(nullptr, hdc); } // ### RTF HEADER CMStringA &str = streamData->buf; // font table str.Append("{\\rtf1\\ansi\\deff0{\\fonttbl"); for (int i = 0; i < OPTIONS_FONTCOUNT; i++) str.AppendFormat("{\\f%u\\fnil\\fcharset%u%S;}", i, g_chatApi.aFonts[i].lf.lfCharSet, g_chatApi.aFonts[i].lf.lfFaceName); // colour table str.Append("}{\\colortbl ;"); for (auto &it : g_chatApi.aFonts) str.AppendFormat("\\red%u\\green%u\\blue%u;", GetRValue(it.color), GetGValue(it.color), GetBValue(it.color)); for (auto &it : Utils::rtf_clrs) str.AppendFormat("\\red%u\\green%u\\blue%u;", GetRValue(it->clr), GetGValue(it->clr), GetBValue(it->clr)); for (int i = 0; i < STATUSICONCOUNT; i++) str.AppendFormat("\\red%u\\green%u\\blue%u;", GetRValue(g_Settings.nickColors[i]), GetGValue(g_Settings.nickColors[i]), GetBValue(g_Settings.nickColors[i])); // new paragraph str.AppendFormat("}\\pard\\sl%d", 1000); // set tabs and indents int iIndent = 0; if (g_Settings.bLogSymbols) { wchar_t szString[2]; LOGFONT lf; szString[1] = 0; szString[0] = 0x28; g_chatApi.LoadMsgDlgFont(17, &lf, nullptr); lf.lfCharSet = SYMBOL_CHARSET; wcsncpy_s(lf.lfFaceName, L"Webdings", _TRUNCATE); HFONT hFont = CreateFontIndirect(&lf); int iText = Chat_GetTextPixelSize(szString, hFont, true) + 3; DeleteObject(hFont); iIndent += (iText * 1440) / g_chatApi.logPixelSX; str.AppendFormat("\\tx%u", iIndent); } else if (g_Settings.dwIconFlags) { iIndent += ((g_Settings.bScaleIcons ? 14 : 20) * 1440) / g_chatApi.logPixelSX; str.AppendFormat("\\tx%u", iIndent); } if (g_Settings.bShowTime) { int iSize = (g_Settings.LogTextIndent * 1440) / g_chatApi.logPixelSX; str.AppendFormat("\\tx%u", iIndent + iSize); if (g_Settings.bLogIndentEnabled) iIndent += iSize; } str.AppendFormat("\\fi-%u\\li%u", iIndent, iIndent); } ///////////////////////////////////////////////////////////////////////////////////////// // RTF event void CLogWindow::CreateChatRtfEvent(RtfChatLogStreamData *streamData, const LOGINFO &lin) { SESSION_INFO *si = streamData->si; CMStringA &str = streamData->buf; if (streamData->idx != 0) str.Append("\\par "); if (m_pDlg.m_bDividerWanted) { static char szStyle_div[128] = "\0"; if (szStyle_div[0] == 0) mir_snprintf(szStyle_div, "\\f%u\\cf%u\\ul0\\b%d\\i%d\\fs%u", 17, 18, 0, 0, 5); if (streamData->idx != si->arEvents.getCount()-1 || !streamData->bRedraw) str.AppendFormat("\\qc\\sl-1\\highlight%d %s ---------------------------------------------------------------------------------------\\par ", 18, szStyle_div); m_pDlg.m_bDividerWanted = false; } // create new line, and set font and color str.AppendFormat("\\ql\\sl0%s ", g_chatApi.Log_SetStyle(0)); str.AppendFormat("\\v~-+%p+-~\\v0 ", &lin); // Insert icon if (g_Settings.bLogSymbols) // use symbols str.AppendFormat("%s %c", g_chatApi.Log_SetStyle(17), EventToSymbol(lin)); else if (g_Settings.dwIconFlags) { int iIndex = lin.bIsHighlighted ? ICON_HIGHLIGHT : lin.getIcon(); str.Append("\\f0\\fs14"); str.Append(g_chatApi.pLogIconBmpBits[iIndex]); } if (g_Settings.bTimeStampEventColour) { // colored timestamps static char szStyle[256]; LOGFONT &F = g_chatApi.aFonts[0].lf; int iii; if (lin.ptszNick && lin.iType == GC_EVENT_MESSAGE) { iii = lin.bIsHighlighted ? 16 : (lin.bIsMe ? 2 : 1); mir_snprintf(szStyle, "\\f0\\cf%u\\ul0\\highlight0\\b%d\\i%d\\ul%d\\fs%u", iii + 1, F.lfWeight >= FW_BOLD ? 1 : 0, F.lfItalic, F.lfUnderline, 2 * abs(F.lfHeight) * 74 / g_chatApi.logPixelSY); str.Append(szStyle); } else { iii = lin.bIsHighlighted ? 16 : lin.getIndex(); mir_snprintf(szStyle, "\\f0\\cf%u\\ul0\\highlight0\\b%d\\i%d\\ul%d\\fs%u", iii + 1, F.lfWeight >= FW_BOLD ? 1 : 0, F.lfItalic, F.lfUnderline, 2 * abs(F.lfHeight) * 74 / g_chatApi.logPixelSY); str.Append(szStyle); } } else str.Append(g_chatApi.Log_SetStyle(0)); str.AppendChar(' '); // insert a TAB if necessary to put the timestamp in the right position if (g_Settings.dwIconFlags) str.Append("\\tab "); // insert timestamp if (g_Settings.bShowTime) { wchar_t szTimeStamp[30], szOldTimeStamp[30]; wcsncpy_s(szTimeStamp, g_chatApi.MakeTimeStamp(g_Settings.pszTimeStamp, lin.time), _TRUNCATE); wcsncpy_s(szOldTimeStamp, g_chatApi.MakeTimeStamp(g_Settings.pszTimeStamp, si->LastTime), _TRUNCATE); if (!g_Settings.bShowTimeIfChanged || si->LastTime == 0 || mir_wstrcmp(szTimeStamp, szOldTimeStamp)) { si->LastTime = lin.time; lin.write(streamData, true, str, szTimeStamp); } str.Append("\\tab "); } // Insert the nick if (lin.ptszNick && lin.iType == GC_EVENT_MESSAGE) { char pszIndicator[3] = "\0\0"; int crNickIndex = 0; if (g_Settings.bLogClassicIndicators || g_Settings.bColorizeNicksInLog) pszIndicator[0] = GetIndicator(si, lin.ptszNick, &crNickIndex); str.Append(g_chatApi.Log_SetStyle(lin.bIsMe ? 2 : 1)); str.AppendChar(' '); if (g_Settings.bLogClassicIndicators) str.Append(pszIndicator); CMStringW pszTemp(lin.bIsMe ? g_Settings.pszOutgoingNick : g_Settings.pszIncomingNick); if (!lin.bIsMe) { if (g_Settings.bClickableNicks) pszTemp.Replace(L"%n", CLICKNICK_BEGIN L"%n" CLICKNICK_END); if (g_Settings.bColorizeNicksInLog && pszIndicator[0]) str.AppendFormat("\\cf%u ", OPTIONS_FONTCOUNT + Utils::rtf_clrs.getCount() + crNickIndex); } pszTemp.Replace(L"%n", lin.ptszNick); if (g_Settings.bNewLineAfterNames) pszTemp.AppendChar('\n'); lin.write(streamData, true, str, pszTemp); str.AppendChar(' '); } // Insert the message CreateChatRtfMessage(streamData, lin, str); }